summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRobert Mustacchi <rm@joyent.com>2015-02-13 13:58:47 +0000
committerRobert Mustacchi <rm@joyent.com>2015-02-13 13:58:47 +0000
commit39fb3e855a4aecad22321e329e58359504b39555 (patch)
treefc78f004574ceb35febc58259402a28df6e94b82
parent8062190bde2b194ecd0aa6e2063cf3dddcc5d741 (diff)
parenteb20fbe2fac1596990392cf5a8ea5030948e4768 (diff)
downloadillumos-joyent-dev-overlay.tar.gz
[illumos-joyent merge]dev-overlay
commit 3b13a1ef7511135ec0c75b5f94de8075454efd79 5322 tree connect from Windows 7 fails commit 200c7a6f5f903a9dcd83c319bddeee9b627406ac OS-3820 lxbrand ptrace(2): the next generation OS-3685 lxbrand PTRACE_O_TRACEFORK race condition OS-3834 lxbrand 64-bit strace(1) reports 64-bit process as using x32 ABI OS-3794 lxbrand panic on init signal death commit f1630c2becf4af570cefc47794212e8110eb79e3 OS-3796 lxbrand remove netstat overlay script commit 653f0ca3cc17876745f96a5e25fb60faa72b33f3 OS-3798 lxbrand populate /proc/net/snmp commit d72cf7bfa828bceb8c81f81282e9b81712d032e2 OS-3797 lxbrand populate /proc/net/unix commit d0fcb88af333aa48dd2b958f3681f1b597b924cc 4545 _t_create(): Use after free in error code paths commit c62da27859e36f9fdd8cee3c6df3ad567543dcf9 4539 _t_checkfd() should not call find_tilink() if force_sync is set commit a2ca8683ba75b01f3468a17061812db2731decb6 OS-3826 lxbrand ipv4 networking broken after OS-3800 commit 6529d7987e7f46c8a923ae661b04c896f3815d91 OS-3800 lx_thunk core dumps on centos 6.6 commit f4cf1a6a363de08977c8db91c119df49e9f6c296 5186 The door_call(3c) man page contains too much commit 23c88c5ab36a96068ae184ebd20bf625426c3773 5594 nodename(4): Duplicate paragraph in the man page commit 5c644dd8f2c88f1e78ae4866f22c749a3d1b0157 5585 Typo in gethostname(3c): current processor commit 7825e891f35613dcf9e7e6d6848401511f3f96eb 4717 Missing period (.) in man pages commit 270be59d332e9c2003ef54b622a67d6f0e3ef7fc 5263 Missing space in getrbuf(9f) man page commit faf5add516ff7b15d67af766e32716c04c75716d 5528 devid_str_free() should be used for devid_get_minor_name() too commit 4812581794004eff0af2b765b832403b30bf64ab 4996 rtld _init race leads to incorrect symbol values commit 5ae8d2a82dbf2dc1b22ae6755ecefed000d7532e OS-3822 OS-3780 creates a life of fd crime in libproc commit 1f2ca518aeecee8616fccc0c46a339773faea7d5 4863 illumos-gate can't be built with fresh perl versions commit 386e9c9ebfe4116f62e7a0950acd30564fc60125 5566 elfexec is overzealous with vpages 5572 elfexec and mapelfexec can disagree on aux vectors commit 8a986bad744c8a479dfacfcdc16bcad15bbb1ec6 5101 privileges(5) manual page missing some privileges commit 9ef283481583d677cd2cf5449ef49b90eacc97d4 5261 libm should stop using synonyms.h (fix studio build) commit ed1591688000a5d179c4ba27793cae55590c55d2 5590 improper use of NULL in tools/protocmp commit ad0b1ea5d69a45fe23c434277599e315f29a5fca 5589 improper use of NULL in tools/ctf commit 8bf1e4f3b335466afe9b85d761b3822ec8c1a371 OS-3816 sync up mdb_v8 with upstream commit 97a9db610324e7db4393415018e0e737485a94cd 4393 /etc/rpc: 100133 and 100169 should be added commit dc1de1110df1be3c207fa275c52056314a438b95 OS-3806 /proc/self/fd/2 is no longer working in LX commit ebb8db03bc1050fa9dd3b184c99634f4c2eae56c OS-3810 tar doesn't properly wait for its children OS-3813 Clean up unused variables and parenthesis warnings in tar commit 4190e41f9d08bc0e41bed63c0b3641af9cfa1a1d OS-3811 LTP signal_test_05 now fails commit 86851d81a7ab61819497cbc95630c7fc812d00c9 OS-3808 lx_boot_zone_redhat relies on /tmp being cleared on reboot commit d41c05b714ac5cd589b3edd49c55f21d1d8f2589 OS-3802 LTP kill11 test case fails after vsyscall signal changes OS-3803 LTP kill12 test failing OS-3804 LTP signal03 failing OS-3805 LTP waitpid05 failing commit e3e63864a2ed092a7da41db4ea4998f461524a18 OS-3807 lx brand: asynchronous I/O operations can hang commit 91600d919baafe4e4d8bdee4168878036351c556 5578 file(1) should validate Elf_Shdr->sh_name commit 30e6ec63ea67bd88d75811ab11b9c115ff026ab3 OS-3795 lxbrand initial centos 6.6 support commit 9d47dec0481d8cd53b2c1053c96bfa3f78357d6a 5592 NULL pointer dereference in dsl_prop_notify_all_cb() commit 71ceaec61a50dff6050c6905ac8352dd58c89311 OS-3801 update boot copyright date commit 823c8a3d4ff8d31f222cb81ed5b0685e318215e1 OS-3793 lxbrand /proc/pid/maps formatted incorrectly commit da4b59e7b4853d1b5018cd3e37eb592574a673b4 OS-3768 snoop could fail more cleanly on large files commit 643588d2256f94df9beb942b812ffaa83665c09a OS-3799 lxbrand panic when accessing /proc/net/tcp from GZ commit 9a3dc1f68894cc036075fdabc3764446d5d5fa52 OS-3485 lxbrand populate /proc for netstat commit 12e2b6203a3d75549383615f039c435ab4418037 OS-3790 lxbrand vsyscall segfault when SIGSEGV handler set to SIG_DFL commit d5fef2f4802f515505a545dfee6c81b5fd377a96 OS-3792 platform uses obsolete nfs mount command commit bd181d5a0c2b96669dcb4aa44619e0b7dbbedab9 1100 cpustat usage message is incorrect commit 2515f5d4dbff605ba645d47a6851d8d0bac5b994 5527 ::spa_space fails with mdb: couldn't find member dd_phys of type struct zfs`dsl_dir' commit 6e062f4a9c9a27ea6e2e980c1b5f4c41e33aba45 5563 Some traverse() callers do strange things commit bb633f5b0e92fa59f65274f8b5637a7107ca29ec OS-3782 lx brand: vsyscall can induce fatal SIGSEGV commit 4d01dc17bafd21a83dfb4383d30137cf0ab74ed1 OS-3777 zlogin -I needs to work with docker run when in logging mode commit de267ec7980943d6c76defc73d2a3d8356d3acb2 OS-3776 project rctls should be in sync with zone rctls commit b9acd3d9851f7716ce41f37dcd04dd6067a21146 OS-3780 libproc could know about .gnu_debuglink for remote symbol tables commit 10648e3fb261910e63f8354af96444b02d016f44 OS-3773 lxbrand ltp shmctl ipcinfo can fail on a machine with >4GB of memory OS-3779 lxbrand shmctl_ipcinfo struct incorrect for 64-bit commit 181d66828bce1fbd366a3b3a9224593577390463 OS-3778 lxbrand panic when ptracing native process commit 63098359d8842cf81b6fb1b81567e12c671db06f 5511 stat.h(3head) manpage #define typo commit 7eb15eeb0b1a3f960946b7563765e128425fc13b 5568 'allthreads' needs to be global commit 32b5e9f0cda85eef94eb578dd053e155df43fed3 5554 kmdb can't trace stacks that begin within itself
-rw-r--r--manifest5
-rw-r--r--usr/src/Makefile.master3
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_capture.c5
-rw-r--r--usr/src/cmd/cpc/common/cpustat.c6
-rw-r--r--usr/src/cmd/file/elf_read.c20
-rw-r--r--usr/src/cmd/fs.d/nfs/mount/Makefile14
-rw-r--r--usr/src/cmd/mdb/common/modules/smbsrv/smbsrv.c30
-rw-r--r--usr/src/cmd/mdb/common/modules/v8/mdb_v8.c1535
-rw-r--r--usr/src/cmd/mdb/common/modules/v8/v8dbg.h12
-rw-r--r--usr/src/cmd/mdb/common/modules/zfs/zfs.c37
-rw-r--r--usr/src/cmd/mdb/intel/mdb/mdb_amd64util.c39
-rw-r--r--usr/src/cmd/mdb/intel/mdb/mdb_ia32util.c40
-rw-r--r--usr/src/cmd/perl/Makefile.perl8
-rw-r--r--usr/src/cmd/perl/Makefile.targ6
-rw-r--r--usr/src/cmd/ptools/pflags/pflags.c5
-rw-r--r--usr/src/cmd/rpcsvc/net_files/rpc26
-rw-r--r--usr/src/cmd/sgs/packages/common/SUNWonld-README1
-rw-r--r--usr/src/cmd/sgs/rtld/common/util.c14
-rw-r--r--usr/src/cmd/tar/Makefile2
-rw-r--r--usr/src/cmd/tar/tar.c38
-rw-r--r--usr/src/cmd/zlogin/zlogin.c35
-rw-r--r--usr/src/cmd/zoneadmd/vplat.c49
-rw-r--r--usr/src/common/brand/lx/lx_syscall.h47
-rw-r--r--usr/src/common/util/getresponse.c2
-rw-r--r--usr/src/lib/brand/lx/lx_brand/common/aio.c4
-rw-r--r--usr/src/lib/brand/lx/lx_brand/common/clone.c50
-rw-r--r--usr/src/lib/brand/lx/lx_brand/common/fork.c57
-rw-r--r--usr/src/lib/brand/lx/lx_brand/common/lx_brand.c45
-rw-r--r--usr/src/lib/brand/lx/lx_brand/common/misc.c2
-rw-r--r--usr/src/lib/brand/lx/lx_brand/common/ptrace.c1339
-rw-r--r--usr/src/lib/brand/lx/lx_brand/common/signal.c119
-rw-r--r--usr/src/lib/brand/lx/lx_brand/common/sysv_ipc.c69
-rw-r--r--usr/src/lib/brand/lx/lx_brand/common/wait.c118
-rw-r--r--usr/src/lib/brand/lx/lx_brand/sys/lx_misc.h8
-rw-r--r--usr/src/lib/brand/lx/lx_brand/sys/lx_signal.h3
-rw-r--r--usr/src/lib/brand/lx/lx_brand/sys/lx_sysv_ipc.h12
-rw-r--r--usr/src/lib/brand/lx/lx_brand/sys/lx_thread.h10
-rw-r--r--usr/src/lib/brand/lx/lx_brand/sys/lx_types.h4
-rw-r--r--usr/src/lib/brand/lx/testing/ltp_skiplist1
-rw-r--r--usr/src/lib/brand/lx/zone/Makefile5
-rw-r--r--usr/src/lib/brand/lx/zone/lx_boot.ksh26
-rw-r--r--usr/src/lib/brand/lx/zone/lx_boot_zone_redhat.ksh399
-rw-r--r--usr/src/lib/brand/lx/zone/platform.xml5
-rw-r--r--usr/src/lib/libm/amd64/src/locallibm.il14
-rw-r--r--usr/src/lib/libm/i386/src/locallibm.il32
-rw-r--r--usr/src/lib/libm/sparc/src/locallibm.il12
-rw-r--r--usr/src/lib/libm/sparcv9/src/locallibm.il12
-rw-r--r--usr/src/lib/libmvec/Makefile.com1
-rw-r--r--usr/src/lib/libnsl/nsl/_utility.c42
-rw-r--r--usr/src/lib/libproc/common/Pcontrol.c6
-rw-r--r--usr/src/lib/libproc/common/Pcontrol.h4
-rw-r--r--usr/src/lib/libproc/common/Pcore.c1
-rw-r--r--usr/src/lib/libproc/common/Pidle.c1
-rw-r--r--usr/src/lib/libproc/common/Psymtab.c242
-rw-r--r--usr/src/man/man3c/door_call.3c71
-rw-r--r--usr/src/man/man3c/gethostname.3c10
-rw-r--r--usr/src/man/man3devid/devid_get.3devid18
-rw-r--r--usr/src/man/man3head/stat.h.3head7
-rw-r--r--usr/src/man/man3nsl/rpc_soc.3nsl11
-rw-r--r--usr/src/man/man3nsl/t_open.3nsl15
-rw-r--r--usr/src/man/man4/nodename.411
-rw-r--r--usr/src/man/man4/proc.412
-rw-r--r--usr/src/man/man5/privileges.567
-rw-r--r--usr/src/man/man9f/getrbuf.9f11
-rw-r--r--usr/src/pkg/Makefile2
-rw-r--r--usr/src/pkg/manifests/runtime-perl-module-sun-solaris.mf62
-rw-r--r--usr/src/tools/ctf/cvt/ctf.c2
-rw-r--r--usr/src/tools/ctf/cvt/dwarf.c2
-rw-r--r--usr/src/tools/ctf/cvt/output.c8
-rw-r--r--usr/src/tools/ctf/stabs/common/genassym.c7
-rw-r--r--usr/src/tools/env/illumos.sh8
-rw-r--r--usr/src/tools/protocmp/exception_list.c8
-rw-r--r--usr/src/tools/protocmp/protocmp.c8
-rw-r--r--usr/src/tools/protocmp/protodir.c5
-rw-r--r--usr/src/uts/common/brand/lx/os/lx_brand.c508
-rw-r--r--usr/src/uts/common/brand/lx/os/lx_misc.c99
-rw-r--r--usr/src/uts/common/brand/lx/os/lx_pid.c24
-rw-r--r--usr/src/uts/common/brand/lx/os/lx_ptrace.c2270
-rw-r--r--usr/src/uts/common/brand/lx/procfs/lx_proc.h8
-rw-r--r--usr/src/uts/common/brand/lx/procfs/lx_prsubr.c2
-rw-r--r--usr/src/uts/common/brand/lx/procfs/lx_prvnops.c512
-rw-r--r--usr/src/uts/common/brand/lx/sys/lx_brand.h158
-rw-r--r--usr/src/uts/common/brand/lx/sys/lx_misc.h14
-rw-r--r--usr/src/uts/common/brand/lx/syscall/lx_clone.c19
-rw-r--r--usr/src/uts/common/disp/thread.c2
-rw-r--r--usr/src/uts/common/fs/lookup.c21
-rw-r--r--usr/src/uts/common/fs/nfs/nfs4_srv.c15
-rw-r--r--usr/src/uts/common/fs/nfs/nfs4_srv_readdir.c6
-rw-r--r--usr/src/uts/common/fs/proc/prcontrol.c4
-rw-r--r--usr/src/uts/common/fs/proc/prsubr.c3
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb_common_open.c4
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb_delete.c2
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb_dispatch.c22
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb_find.c8
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb_fsops.c2
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb_nt_create_andx.c3
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb_nt_transact_create.c3
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb_odir.c31
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb_ofile.c108
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb_opipe.c4
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb_process_exit.c4
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb_query_fileinfo.c2
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb_server.c103
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb_session.c310
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb_trans2_find.c6
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb_tree.c157
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb_tree_connect.c4
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb_user.c370
-rw-r--r--usr/src/uts/common/fs/zfs/dsl_dataset.c15
-rw-r--r--usr/src/uts/common/os/exit.c76
-rw-r--r--usr/src/uts/common/os/logsubr.c4
-rw-r--r--usr/src/uts/common/os/sig.c49
-rw-r--r--usr/src/uts/common/os/zone.c11
-rw-r--r--usr/src/uts/common/smbsrv/smb_kproto.h24
-rw-r--r--usr/src/uts/common/smbsrv/smb_ktypes.h12
-rw-r--r--usr/src/uts/common/sys/brand.h7
-rw-r--r--usr/src/uts/common/sys/procfs.h2
-rw-r--r--usr/src/uts/common/sys/thread.h7
-rw-r--r--usr/src/uts/common/sys/zone.h1
-rw-r--r--usr/src/uts/intel/Makefile.files3
-rw-r--r--usr/src/uts/intel/lx_proc/Makefile3
121 files changed, 6932 insertions, 3057 deletions
diff --git a/manifest b/manifest
index 1264790e17..3b0f194d99 100644
--- a/manifest
+++ b/manifest
@@ -310,7 +310,7 @@ 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
-f etc/fs/nfs/mount 0555 root bin
+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
@@ -5072,6 +5072,7 @@ f usr/lib/brand/lx/etc_netconfig 0444 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_redhat 0755 root root
f usr/lib/brand/lx/lx_boot_zone_ubuntu 0755 root root
f usr/lib/brand/lx/lx_isaexec_wrapper 0755 root root
f usr/lib/brand/lx/lx_librtld_db.so.1 0755 root root
@@ -5423,7 +5424,7 @@ 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
-s usr/lib/fs/nfs/mount=../../../../etc/fs/nfs/mount
+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
diff --git a/usr/src/Makefile.master b/usr/src/Makefile.master
index 988f34da46..9e7f031913 100644
--- a/usr/src/Makefile.master
+++ b/usr/src/Makefile.master
@@ -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 Garrett D'Amore <garrett@damore.org>
+# Copyright 2015, OmniTI Computer Consulting, Inc. All rights reserved.
#
#
@@ -168,6 +169,8 @@ FIND= /usr/bin/find
PERL= /usr/bin/perl
PERL_VERSION= 5.12
PERL_PKGVERS= -512
+PERL_ARCH = i86pc-solaris-64int
+$(SPARC_BLD)PERL_ARCH = sun4-solaris-64int
PYTHON_26= /usr/bin/python2.6
PYTHON= $(PYTHON_26)
SORT= /usr/bin/sort
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 54fbdc844b..8fbf3fc15f 100644
--- a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_capture.c
+++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_capture.c
@@ -29,6 +29,7 @@
#include <strings.h>
#include <errno.h>
#include <fcntl.h>
+#include <limits.h>
#include <setjmp.h>
#include <sys/types.h>
#include <sys/signal.h>
@@ -618,6 +619,10 @@ cap_open_read(const char *name)
if (fstat(capfile_in, &st) < 0)
pr_err("couldn't stat %s: %m", name);
+ if (st.st_size > INT_MAX)
+ pr_err("input file size (%llu bytes) exceeds maximum "
+ "supported size (%d bytes)",
+ (unsigned long long)st.st_size, INT_MAX);
cap_len = st.st_size;
cap_buffp = mmap(0, cap_len, PROT_READ, MAP_PRIVATE, capfile_in, 0);
diff --git a/usr/src/cmd/cpc/common/cpustat.c b/usr/src/cmd/cpc/common/cpustat.c
index 8d405bb625..965fbadfea 100644
--- a/usr/src/cmd/cpc/common/cpustat.c
+++ b/usr/src/cmd/cpc/common/cpustat.c
@@ -251,9 +251,9 @@ main(int argc, char *argv[])
if (errcnt != 0 || opts->dohelp ||
(opts->nsets = cpc_setgrp_numsets(opts->master)) == 0) {
(void) fprintf(opts->dohelp ? stdout : stderr, gettext(
- "Usage:\n\t%s [-c events] [-p period] [-nstD] "
- "[-T d|u] [interval [count]]\n\n"
- "\t-c events specify processor events to be monitored\n"
+ "Usage:\n\t%s -c spec [-c spec]... [-p period] [-T u|d]\n"
+ "\t\t[-sntD] [interval [count]]\n\n"
+ "\t-c spec\t specify processor events to be monitored\n"
"\t-n\t suppress titles\n"
"\t-p period cycle through event list periodically\n"
"\t-s\t run user soaker thread for system-only events\n"
diff --git a/usr/src/cmd/file/elf_read.c b/usr/src/cmd/file/elf_read.c
index 7ccf8e2bb3..03c73f57a9 100644
--- a/usr/src/cmd/file/elf_read.c
+++ b/usr/src/cmd/file/elf_read.c
@@ -419,7 +419,8 @@ process_shdr(Elf_Info *EI)
int i, j, idx;
FILE_ELF_OFF_T cap_off;
FILE_ELF_SIZE_T csize;
- char *section_name;
+ char *strtab;
+ size_t strtab_sz;
Elf_Cap Chdr;
Elf_Shdr *shdr = &EI_Shdr;
@@ -435,16 +436,18 @@ process_shdr(Elf_Info *EI)
if (get_shdr(EI, EI_Ehdr_shstrndx) == ELF_READ_FAIL)
return (ELF_READ_FAIL);
- if ((section_name = malloc(shdr->sh_size)) == NULL)
+ if ((strtab = malloc(shdr->sh_size)) == NULL)
return (ELF_READ_FAIL);
- if (pread64(EI->elffd, section_name, shdr->sh_size, shdr->sh_offset)
+ if (pread64(EI->elffd, strtab, shdr->sh_size, shdr->sh_offset)
!= shdr->sh_size)
return (ELF_READ_FAIL);
+ strtab_sz = shdr->sh_size;
+
/* read all the sections and process them */
for (idx = 1, i = 0; i < EI_Ehdr_shnum; idx++, i++) {
- char *str;
+ char *shnam;
if (get_shdr(EI, i) == ELF_READ_FAIL)
return (ELF_READ_FAIL);
@@ -538,16 +541,19 @@ process_shdr(Elf_Info *EI)
continue;
}
- str = &section_name[shdr->sh_name];
+ if (shdr->sh_name >= strtab_sz)
+ shnam = NULL;
+ else
+ shnam = &strtab[shdr->sh_name];
if (!(EI->stripped & E_DBGINF) &&
((shdr->sh_type == SHT_SUNW_DEBUG) ||
(shdr->sh_type == SHT_SUNW_DEBUGSTR) ||
- (is_in_list(str)))) {
+ (shnam != NULL && is_in_list(shnam)))) {
EI->stripped |= E_DBGINF;
}
}
- free(section_name);
+ free(strtab);
return (ELF_READ_OKAY);
}
diff --git a/usr/src/cmd/fs.d/nfs/mount/Makefile b/usr/src/cmd/fs.d/nfs/mount/Makefile
index dad33922a3..c2485208a8 100644
--- a/usr/src/cmd/fs.d/nfs/mount/Makefile
+++ b/usr/src/cmd/fs.d/nfs/mount/Makefile
@@ -19,18 +19,12 @@
# CDDL HEADER END
#
# Copyright (c) 1990, 2010, Oracle and/or its affiliates. All rights reserved.
+# Copyright 2014, Joyent, Inc. All rights reserved.
#
# cmd/fs.d/nfs/mount/Makefile
FSTYPE= nfs
LIBPROG= mount
-ROOTFS_PROG= $(LIBPROG)
-
-# duplicate ROOTLIBFSTYPE value needed for installation rule
-# we must define this before including Makefile.fstype
-ROOTLIBFSTYPE = $(ROOT)/usr/lib/fs/$(FSTYPE)
-$(ROOTLIBFSTYPE)/%: $(ROOTLIBFSTYPE) %
- $(RM) $@; $(SYMLINK) ../../../../etc/fs/$(FSTYPE)/$(LIBPROG) $@
include ../../Makefile.fstype
@@ -65,7 +59,7 @@ CLOBBERFILES += $(LIBPROG)
.KEEP_STATE:
-all: $(ROOTFS_PROG)
+all: $(LIBPROG)
$(LIBPROG): webnfs.h $(OBJS)
$(LINK.c) -o $@ $(OBJS) $(LDLIBS)
@@ -114,7 +108,9 @@ $(POFILE): $(SRCS)
sed "/^domain/d" messages.po > $@
$(RM) $(POFILE).i messages.po
-install: $(ROOTETCPROG)
+install: all $(FSTYPEPROG)
+ $(RM) $(ROOTETCPROG)
+ $(SYMLINK) ../../../usr/lib/fs/$(FSTYPE)/$(LIBPROG) $(ROOTETCPROG)
lint: webnfs.h webnfs_xdr.c webnfs_client.c lint_SRCS
diff --git a/usr/src/cmd/mdb/common/modules/smbsrv/smbsrv.c b/usr/src/cmd/mdb/common/modules/smbsrv/smbsrv.c
index 83ffae0634..eaa381cc47 100644
--- a/usr/src/cmd/mdb/common/modules/smbsrv/smbsrv.c
+++ b/usr/src/cmd/mdb/common/modules/smbsrv/smbsrv.c
@@ -688,9 +688,12 @@ static const smb_exp_t smb_session_exp[] =
{ SMB_OPT_REQUEST,
offsetof(smb_session_t, s_req_list.sl_list),
"smbreq", "smb_request"},
- { SMB_OPT_USER | SMB_OPT_TREE | SMB_OPT_OFILE | SMB_OPT_ODIR,
+ { SMB_OPT_USER,
offsetof(smb_session_t, s_user_list.ll_list),
"smbuser", "smb_user"},
+ { SMB_OPT_TREE | SMB_OPT_OFILE | SMB_OPT_ODIR,
+ offsetof(smb_session_t, s_tree_list.ll_list),
+ "smbtree", "smb_tree"},
{ 0, 0, NULL, NULL}
};
@@ -963,17 +966,6 @@ static const char *smb_user_state[SMB_USER_STATE_SENTINEL] =
"LOGGED_OFF"
};
-/*
- * List of objects that can be expanded under a user structure.
- */
-static const smb_exp_t smb_user_exp[] =
-{
- { SMB_OPT_TREE | SMB_OPT_OFILE | SMB_OPT_ODIR,
- offsetof(smb_user_t, u_tree_list.ll_list),
- "smbtree", "smb_tree"},
- { 0, 0, NULL, NULL}
-};
-
static void
smb_dcmd_user_help(void)
{
@@ -983,17 +975,13 @@ smb_dcmd_user_help(void)
mdb_printf("%<b>OPTIONS%</b>\n");
(void) mdb_inc_indent(2);
mdb_printf(
- "-v\tDisplay verbose smb_user information\n"
- "-d\tDisplay the list of smb_odirs attached\n"
- "-f\tDisplay the list of smb_ofiles attached\n"
- "-t\tDisplay the list of smb_trees attached\n");
+ "-v\tDisplay verbose smb_user information\n");
}
static int
smb_dcmd_user(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
{
uint_t opts;
- ulong_t indent = 0;
if (smb_dcmd_getopt(&opts, argc, argv))
return (DCMD_USAGE);
@@ -1009,8 +997,6 @@ smb_dcmd_user(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
smb_user_t *user;
char *account;
- indent = SMB_DCMD_INDENT;
-
user = mdb_alloc(sizeof (*user), UM_SLEEP | UM_GC);
if (mdb_vread(user, sizeof (*user), addr) == -1) {
mdb_warn("failed to read smb_user at %p", addr);
@@ -1058,8 +1044,6 @@ smb_dcmd_user(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
account);
}
}
- if (smb_obj_expand(addr, opts, smb_user_exp, indent))
- return (DCMD_ERR);
return (DCMD_OK);
}
@@ -1217,6 +1201,8 @@ smb_dcmd_odir(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
addr);
mdb_printf("State: %d (%s)\n", od->d_state, state);
mdb_printf("SID: %u\n", od->d_odid);
+ mdb_printf("User: %p\n", od->d_user);
+ mdb_printf("Tree: %p\n", od->d_tree);
mdb_printf("Reference Count: %d\n", od->d_refcnt);
mdb_printf("Pattern: %s\n", od->d_pattern);
mdb_printf("SMB Node: %p\n\n", od->d_dnode);
@@ -1292,6 +1278,8 @@ smb_dcmd_ofile(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
((of->f_flags & SMB_OFLAGS_LLF_POS_VALID) ?
"Valid" : "Invalid"));
mdb_printf("Flags: 0x%08x\n", of->f_flags);
+ mdb_printf("User: %p\n", of->f_user);
+ mdb_printf("Tree: %p\n", of->f_tree);
mdb_printf("Credential: %p\n\n", of->f_cr);
} else {
if (DCMD_HDRSPEC(flags))
diff --git a/usr/src/cmd/mdb/common/modules/v8/mdb_v8.c b/usr/src/cmd/mdb/common/modules/v8/mdb_v8.c
index aa597e7a01..6609097742 100644
--- a/usr/src/cmd/mdb/common/modules/v8/mdb_v8.c
+++ b/usr/src/cmd/mdb/common/modules/v8/mdb_v8.c
@@ -19,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright (c) 2014, Joyent, Inc. All rights reserved.
+ * Copyright (c) 2015, Joyent, Inc. All rights reserved.
*/
/*
@@ -50,6 +50,7 @@
#include <sys/mdb_modapi.h>
#include <assert.h>
#include <ctype.h>
+#include <inttypes.h>
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
@@ -148,10 +149,11 @@ static intptr_t V8_DICT_SHIFT;
static intptr_t V8_DICT_PREFIX_SIZE;
static intptr_t V8_DICT_ENTRY_SIZE;
static intptr_t V8_DICT_START_INDEX;
+static intptr_t V8_FIELDINDEX_MASK;
+static intptr_t V8_FIELDINDEX_SHIFT;
static intptr_t V8_PROP_IDX_CONTENT;
static intptr_t V8_PROP_IDX_FIRST;
static intptr_t V8_PROP_TYPE_FIELD;
-static intptr_t V8_PROP_FIRST_PHANTOM;
static intptr_t V8_PROP_TYPE_MASK;
static intptr_t V8_PROP_DESC_KEY;
static intptr_t V8_PROP_DESC_DETAILS;
@@ -161,6 +163,7 @@ static intptr_t V8_TRANSITIONS_IDX_DESC;
static intptr_t V8_TYPE_JSOBJECT = -1;
static intptr_t V8_TYPE_JSARRAY = -1;
+static intptr_t V8_TYPE_JSFUNCTION = -1;
static intptr_t V8_TYPE_FIXEDARRAY = -1;
static intptr_t V8_ELEMENTS_KIND_SHIFT;
@@ -214,10 +217,12 @@ static ssize_t V8_OFF_SLICEDSTRING_PARENT;
static ssize_t V8_OFF_SLICEDSTRING_OFFSET;
static ssize_t V8_OFF_STRING_LENGTH;
-#define NODE_OFF_EXTSTR_DATA 0x4 /* see node_string.h */
+/* see node_string.h */
+#define NODE_OFF_EXTSTR_DATA sizeof (uintptr_t)
#define V8_CONSTANT_OPTIONAL 1
#define V8_CONSTANT_HASFALLBACK 2
+#define V8_CONSTANT_REMOVED 4
#define V8_CONSTANT_MAJORSHIFT 3
#define V8_CONSTANT_MAJORMASK ((1 << 4) - 1)
@@ -233,6 +238,10 @@ static ssize_t V8_OFF_STRING_LENGTH;
(V8_CONSTANT_OPTIONAL | V8_CONSTANT_HASFALLBACK | \
((maj) << V8_CONSTANT_MAJORSHIFT) | ((min) << V8_CONSTANT_MINORSHIFT))
+#define V8_CONSTANT_REMOVED_SINCE(maj, min) \
+ (V8_CONSTANT_REMOVED | \
+ ((maj) << V8_CONSTANT_MAJORSHIFT) | ((min) << V8_CONSTANT_MINORSHIFT))
+
/*
* Table of constants used directly by this file.
*/
@@ -262,8 +271,10 @@ static v8_constant_t v8_constants[] = {
{ &V8_SlicedStringTag, "v8dbg_SlicedStringTag",
V8_CONSTANT_FALLBACK(0, 0), 0x3 },
{ &V8_ExternalStringTag, "v8dbg_ExternalStringTag" },
- { &V8_FailureTag, "v8dbg_FailureTag" },
- { &V8_FailureTagMask, "v8dbg_FailureTagMask" },
+ { &V8_FailureTag, "v8dbg_FailureTag",
+ V8_CONSTANT_REMOVED_SINCE(3, 28) },
+ { &V8_FailureTagMask, "v8dbg_FailureTagMask",
+ V8_CONSTANT_REMOVED_SINCE(3, 28) },
{ &V8_HeapObjectTag, "v8dbg_HeapObjectTag" },
{ &V8_HeapObjectTagMask, "v8dbg_HeapObjectTagMask" },
{ &V8_SmiTag, "v8dbg_SmiTag" },
@@ -277,7 +288,7 @@ static v8_constant_t v8_constants[] = {
#endif
{ &V8_PointerSizeLog2, "v8dbg_PointerSizeLog2" },
- { &V8_DICT_SHIFT, "v8dbg_dict_shift",
+ { &V8_DICT_SHIFT, "v8dbg_bit_field3_dictionary_map_shift",
V8_CONSTANT_FALLBACK(3, 13), 24 },
{ &V8_DICT_PREFIX_SIZE, "v8dbg_dict_prefix_size",
V8_CONSTANT_FALLBACK(3, 11), 2 },
@@ -285,11 +296,14 @@ static v8_constant_t v8_constants[] = {
V8_CONSTANT_FALLBACK(3, 11), 3 },
{ &V8_DICT_START_INDEX, "v8dbg_dict_start_index",
V8_CONSTANT_FALLBACK(3, 11), 3 },
+ { &V8_FIELDINDEX_MASK, "v8dbg_fieldindex_mask",
+ V8_CONSTANT_FALLBACK(3, 26), 0x3ff00000 },
+ { &V8_FIELDINDEX_SHIFT, "v8dbg_fieldindex_shift",
+ V8_CONSTANT_FALLBACK(3, 26), 20 },
{ &V8_ISSHARED_SHIFT, "v8dbg_isshared_shift",
V8_CONSTANT_FALLBACK(3, 11), 0 },
{ &V8_PROP_IDX_FIRST, "v8dbg_prop_idx_first" },
{ &V8_PROP_TYPE_FIELD, "v8dbg_prop_type_field" },
- { &V8_PROP_FIRST_PHANTOM, "v8dbg_prop_type_first_phantom" },
{ &V8_PROP_TYPE_MASK, "v8dbg_prop_type_mask" },
{ &V8_PROP_IDX_CONTENT, "v8dbg_prop_idx_content",
V8_CONSTANT_OPTIONAL },
@@ -430,6 +444,66 @@ static void conf_class_compute_offsets(v8_class_t *);
static int read_typebyte(uint8_t *, uintptr_t);
static int heap_offset(const char *, const char *, ssize_t *);
+static int jsfunc_name(uintptr_t, char **, size_t *);
+
+/*
+ * When iterating properties, it's useful to keep track of what kinds of
+ * properties were found. This is useful for developers to identify objects of
+ * different kinds in order to debug them.
+ */
+typedef enum {
+ JPI_NONE = 0,
+
+ /*
+ * Indicates how properties are stored in this object. There can be
+ * both numeric properties and some of the other kinds.
+ */
+ JPI_NUMERIC = 0x01, /* numeric-named properties in "elements" */
+ JPI_DICT = 0x02, /* dictionary properties */
+ JPI_INOBJECT = 0x04, /* properties stored inside object */
+ JPI_PROPS = 0x08, /* "properties" array */
+
+ /* error-like cases */
+ JPI_SKIPPED = 0x10, /* some properties were skipped */
+ JPI_BADLAYOUT = 0x20, /* we didn't recognize the layout at all */
+
+ /* fallback cases */
+ JPI_HASTRANSITIONS = 0x100, /* found a transitions array */
+ JPI_HASCONTENT = 0x200, /* found a separate content array */
+} jspropinfo_t;
+
+typedef struct jsobj_print {
+ char **jsop_bufp;
+ size_t *jsop_lenp;
+ int jsop_indent;
+ uint64_t jsop_depth;
+ boolean_t jsop_printaddr;
+ uintptr_t jsop_baseaddr;
+ int jsop_nprops;
+ const char *jsop_member;
+ boolean_t jsop_found;
+ boolean_t jsop_descended;
+ jspropinfo_t jsop_propinfo;
+} jsobj_print_t;
+
+static int jsobj_print_number(uintptr_t, jsobj_print_t *);
+static int jsobj_print_oddball(uintptr_t, jsobj_print_t *);
+static int jsobj_print_jsobject(uintptr_t, jsobj_print_t *);
+static int jsobj_print_jsarray(uintptr_t, jsobj_print_t *);
+static int jsobj_print_jsfunction(uintptr_t, jsobj_print_t *);
+static int jsobj_print_jsdate(uintptr_t, jsobj_print_t *);
+
+/*
+ * Returns 1 if the V8 version v8_major.v8.minor is strictly older than
+ * the V8 version represented by "flags".
+ * Returns 0 otherwise.
+ */
+static int
+v8_version_older(uintptr_t v8_major, uintptr_t v8_minor, uint32_t flags) {
+ return (v8_major < V8_CONSTANT_MAJOR(flags) ||
+ (v8_major == V8_CONSTANT_MAJOR(flags) &&
+ v8_minor < V8_CONSTANT_MINOR(flags)));
+}
/*
* Invoked when this dmod is initially loaded to load the set of classes, enums,
@@ -477,7 +551,9 @@ autoconfigure(v8_cfg_t *cfgp)
continue;
}
- if (!(cnp->v8c_flags & V8_CONSTANT_OPTIONAL)) {
+ if (!(cnp->v8c_flags & V8_CONSTANT_OPTIONAL) &&
+ (!(cnp->v8c_flags & V8_CONSTANT_REMOVED) ||
+ v8_version_older(v8_major, v8_minor, cnp->v8c_flags))) {
mdb_warn("failed to read \"%s\"", cnp->v8c_symbol);
failed++;
continue;
@@ -508,6 +584,9 @@ autoconfigure(v8_cfg_t *cfgp)
if (strcmp(ep->v8e_name, "JSArray") == 0)
V8_TYPE_JSARRAY = ep->v8e_value;
+ if (strcmp(ep->v8e_name, "JSFunction") == 0)
+ V8_TYPE_JSFUNCTION = ep->v8e_value;
+
if (strcmp(ep->v8e_name, "FixedArray") == 0)
V8_TYPE_FIXEDARRAY = ep->v8e_value;
}
@@ -522,6 +601,11 @@ autoconfigure(v8_cfg_t *cfgp)
failed++;
}
+ if (V8_TYPE_JSFUNCTION == -1) {
+ mdb_warn("couldn't find JSFunction type\n");
+ failed++;
+ }
+
if (V8_TYPE_FIXEDARRAY == -1) {
mdb_warn("couldn't find FixedArray type\n");
failed++;
@@ -963,11 +1047,8 @@ v8_warn(const char *format, ...)
}
}
-/*
- * Returns in "offp" the offset of field "field" in C++ class "klass".
- */
-static int
-heap_offset(const char *klass, const char *field, ssize_t *offp)
+static v8_field_t *
+conf_field_lookup(const char *klass, const char *field)
{
v8_class_t *clp;
v8_field_t *flp;
@@ -978,13 +1059,26 @@ heap_offset(const char *klass, const char *field, ssize_t *offp)
}
if (clp == NULL)
- return (-1);
+ return (NULL);
for (flp = clp->v8c_fields; flp != NULL; flp = flp->v8f_next) {
if (strcmp(field, flp->v8f_name) == 0)
break;
}
+ return (flp);
+}
+
+/*
+ * Returns in "offp" the offset of field "field" in C++ class "klass".
+ */
+static int
+heap_offset(const char *klass, const char *field, ssize_t *offp)
+{
+ v8_field_t *flp;
+
+ flp = conf_field_lookup(klass, field);
+
if (flp == NULL)
return (-1);
@@ -1094,6 +1188,46 @@ read_heap_byte(uint8_t *valp, uintptr_t addr, ssize_t off)
}
/*
+ * This is truly horrific. Inside the V8 Script class are a number of
+ * small-integer fields like the function_token_position (an offset into the
+ * script's text where the "function" token appears). For 32-bit processes, V8
+ * stores these as a sequence of SMI fields, which we know how to interpret well
+ * enough. For 64-bit processes, "to avoid wasting space", they use a different
+ * trick: each 8-byte word contains two integer fields. The low word is
+ * represented like an SMI: shifted left by one. They don't bother shifting the
+ * high word, since its low bit will never be looked at (since it's not
+ * word-aligned).
+ *
+ * This function is used for cases where we would use read_heap_smi(), except
+ * that this is one of those fields that might be encoded or might not be,
+ * depending on whether the address is word-aligned.
+ */
+static int
+read_heap_maybesmi(uintptr_t *valp, uintptr_t addr, ssize_t off)
+{
+#ifdef _LP64
+ uint32_t readval;
+
+ if (mdb_vread(&readval, sizeof (readval), addr + off) == -1) {
+ *valp = -1;
+ v8_warn("failed to read offset %d from %p", off, addr);
+ return (-1);
+ }
+
+ /*
+ * If this was the low half-word, it needs to be shifted right.
+ */
+ if ((addr + off) % sizeof (uintptr_t) == 0)
+ readval >>= 1;
+
+ *valp = (uintptr_t)readval;
+ return (0);
+#else
+ return (read_heap_smi(valp, addr, off));
+#endif
+}
+
+/*
* Given a heap object, returns in *valp the byte describing the type of the
* object. This is shorthand for first retrieving the Map at the start of the
* heap object and then retrieving the type byte from the Map object.
@@ -1181,8 +1315,7 @@ read_heap_dict(uintptr_t addr,
if (V8_IS_SMI(dict[i])) {
intptr_t val = V8_SMI_VALUE(dict[i]);
-
- (void) snprintf(buf, sizeof (buf), "%ld", val);
+ (void) snprintf(buf, sizeof (buf), "%" PRIdPTR, val);
} else {
if (jsobj_is_hole(dict[i])) {
/*
@@ -1217,13 +1350,69 @@ out:
}
/*
+ * Given an object, returns in "buf" the name of the constructor function. With
+ * "verbose", prints the pointer to the JSFunction object. Given anything else,
+ * returns an error (and warns the user why).
+ */
+static int
+obj_jsconstructor(uintptr_t addr, char **bufp, size_t *lenp, boolean_t verbose)
+{
+ uint8_t type;
+ uintptr_t map, consfunc, funcinfop;
+ const char *constype;
+
+ if (!V8_IS_HEAPOBJECT(addr) ||
+ read_typebyte(&type, addr) != 0 ||
+ (type != V8_TYPE_JSOBJECT && type != V8_TYPE_JSARRAY)) {
+ mdb_warn("%p is not a JSObject\n", addr);
+ return (-1);
+ }
+
+ if (mdb_vread(&map, sizeof (map), addr + V8_OFF_HEAPOBJECT_MAP) == -1 ||
+ mdb_vread(&consfunc, sizeof (consfunc),
+ map + V8_OFF_MAP_CONSTRUCTOR) == -1) {
+ mdb_warn("unable to read object map\n");
+ return (-1);
+ }
+
+ if (read_typebyte(&type, consfunc) != 0)
+ return (-1);
+
+ constype = enum_lookup_str(v8_types, type, "");
+ if (strcmp(constype, "Oddball") == 0) {
+ jsobj_print_t jsop;
+ bzero(&jsop, sizeof (jsop));
+ jsop.jsop_bufp = bufp;
+ jsop.jsop_lenp = lenp;
+ return (jsobj_print_oddball(consfunc, &jsop));
+ }
+
+ if (strcmp(constype, "JSFunction") != 0) {
+ mdb_warn("constructor: expected JSFunction, found %s\n",
+ constype);
+ return (-1);
+ }
+
+ if (read_heap_ptr(&funcinfop, consfunc, V8_OFF_JSFUNCTION_SHARED) != 0)
+ return (-1);
+
+ if (jsfunc_name(funcinfop, bufp, lenp) != 0)
+ return (-1);
+
+ if (verbose)
+ bsnprintf(bufp, lenp, " (JSFunction: %p)", consfunc);
+
+ return (0);
+}
+
+/*
* Returns in "buf" a description of the type of "addr" suitable for printing.
*/
static int
obj_jstype(uintptr_t addr, char **bufp, size_t *lenp, uint8_t *typep)
{
uint8_t typebyte;
- uintptr_t strptr;
+ uintptr_t strptr, map, consfunc, funcinfop;
const char *typename;
if (V8_IS_FAILURE(addr)) {
@@ -1259,10 +1448,75 @@ obj_jstype(uintptr_t addr, char **bufp, size_t *lenp, uint8_t *typep)
}
}
+ if (strcmp(typename, "JSObject") == 0 &&
+ mdb_vread(&map, sizeof (map), addr + V8_OFF_HEAPOBJECT_MAP) != -1 &&
+ mdb_vread(&consfunc, sizeof (consfunc),
+ map + V8_OFF_MAP_CONSTRUCTOR) != -1 &&
+ read_typebyte(&typebyte, consfunc) == 0 &&
+ strcmp(enum_lookup_str(v8_types, typebyte, ""),
+ "JSFunction") == 0 &&
+ mdb_vread(&funcinfop, sizeof (funcinfop),
+ consfunc + V8_OFF_JSFUNCTION_SHARED) != -1) {
+ (void) bsnprintf(bufp, lenp, ": ");
+ (void) jsfunc_name(funcinfop, bufp, lenp);
+ }
+
return (0);
}
/*
+ * V8 allows implementers (like Node) to store pointer-sized values into
+ * internal fields within V8 heap objects. Implementors access these values by
+ * 0-based index (e.g., SetInternalField(0, value)). These values are stored as
+ * an array directly after the last actual C++ field in the C++ object.
+ *
+ * Node uses internal fields to refer to handles. For example, a socket's C++
+ * HandleWrap object is typically stored as internal field 0 in the JavaScript
+ * Socket object. Similarly, the native-heap-allocated chunk of memory
+ * associated with a Node Buffer is referenced by field 0 in the External array
+ * pointed-to by the Node Buffer JSObject.
+ */
+static int
+obj_v8internal(uintptr_t addr, uint_t idx, uintptr_t *valp)
+{
+ char *bufp;
+ size_t len;
+ ssize_t off;
+ uint8_t type;
+
+ v8_class_t *clp;
+ char buf[256];
+
+ bufp = buf;
+ len = sizeof (buf);
+ if (obj_jstype(addr, &bufp, &len, &type) != 0)
+ return (DCMD_ERR);
+
+ if (type == 0) {
+ mdb_warn("%p: unsupported type\n", addr);
+ return (DCMD_ERR);
+ }
+
+ for (clp = v8_classes; clp != NULL; clp = clp->v8c_next) {
+ if (strcmp(buf, clp->v8c_name) == 0)
+ break;
+ }
+
+ if (clp == NULL) {
+ mdb_warn("%p: didn't find expected class\n", addr);
+ return (DCMD_ERR);
+ }
+
+ off = clp->v8c_end + (idx * sizeof (uintptr_t)) - 1;
+ if (read_heap_ptr(valp, addr, off) != 0) {
+ mdb_warn("%p: failed to read from %p\n", addr, addr + off);
+ return (DCMD_ERR);
+ }
+
+ return (DCMD_OK);
+}
+
+/*
* Print out the fields of the given object that come from the given class.
*/
static int
@@ -1456,11 +1710,6 @@ jsstr_print_seq(uintptr_t addr, uint_t flags, char **bufp, size_t *lenp,
char *buf;
uint16_t chrval;
- if ((blen = MIN(*lenp, 256 * 1024)) == 0)
- return (0);
-
- buf = alloca(blen);
-
if (read_heap_smi(&nstrchrs, addr, V8_OFF_STRING_LENGTH) != 0) {
(void) bsnprintf(bufp, lenp,
"<string (failed to read length)>");
@@ -1470,17 +1719,22 @@ jsstr_print_seq(uintptr_t addr, uint_t flags, char **bufp, size_t *lenp,
if (slicelen != -1)
nstrchrs = slicelen;
+ blen = ((flags & JSSTR_ISASCII) != 0) ? *lenp : 2 * (*lenp);
+ if ((blen = MIN(blen, 256 * 1024)) == 0)
+ return (0);
+
if ((flags & JSSTR_ISASCII) != 0) {
nstrbytes = nstrchrs;
nreadoffset = sliceoffset;
+ nreadbytes = nstrbytes + sizeof ("\"\"") <= *lenp ?
+ nstrbytes : *lenp - sizeof ("\"\"[...]");
} else {
nstrbytes = 2 * nstrchrs;
nreadoffset = 2 * sliceoffset;
+ nreadbytes = nstrchrs + sizeof ("\"\"") <= *lenp ?
+ nstrbytes : 2 * (*lenp - sizeof ("\"\"[...]"));
}
- nreadbytes = nstrbytes + sizeof ("\"\"") <= blen ? nstrbytes :
- blen - sizeof ("\"\"[...]");
-
if (nreadbytes < 0) {
/*
* We don't even have the room to store the ellipsis; zero
@@ -1491,10 +1745,13 @@ jsstr_print_seq(uintptr_t addr, uint_t flags, char **bufp, size_t *lenp,
return (0);
}
- if (verbose)
+ if (verbose) {
mdb_printf("length: %d chars (%d bytes), "
"will read %d bytes from offset %d\n",
nstrchrs, nstrbytes, nreadbytes, nreadoffset);
+ mdb_printf("given buffer size: %d, internal buffer: %d\n",
+ *lenp, blen);
+ }
if (nstrbytes == 0) {
(void) bsnprintf(bufp, lenp, "%s%s",
@@ -1502,6 +1759,7 @@ jsstr_print_seq(uintptr_t addr, uint_t flags, char **bufp, size_t *lenp,
return (0);
}
+ buf = alloca(blen);
buf[0] = '\0';
if ((flags & JSSTR_ISASCII) != 0) {
@@ -1742,9 +2000,126 @@ jsobj_is_hole(uintptr_t addr)
return (jsobj_is_oddball(addr, "hole"));
}
+/*
+ * Iterate the properties of a JavaScript object "addr".
+ *
+ * Every heap object refers to a Map that describes how that heap object is laid
+ * out. The Map includes information like the constructor function used to
+ * create the object, how many bytes each object uses, and how many properties
+ * are stored inside the object. (A single Map object can be shared by many
+ * objects of the same general type, which is why this information is encoded by
+ * reference rather than contained in each object.)
+ *
+ * V8 knows about lots of different kinds of properties:
+ *
+ * o properties with numeric names (e.g., array elements)
+ * o dictionary properties
+ * o "fast" properties stored inside each object, much like a C struct
+ * o properties stored in the separate "properties" array
+ * o getters, setters, and other magic (not supported by this module)
+ *
+ * While property lookup in JavaScript involves traversing an object's prototype
+ * chain, this module only iterates the properties local to the object itself.
+ *
+ *
+ * Numeric properties
+ *
+ * Properties having numeric indexes are stored in the "elements" array attached
+ * to each object. Objects with numeric properties can also have other
+ * properties.
+ *
+ *
+ * Dictionary properties
+ *
+ * An object with dictionary properties is identified by one of the bits in
+ * "bitfield3" in the object's Map. For details on slow properties, see
+ * read_heap_dict().
+ *
+ *
+ * Other properties
+ *
+ * The Map object refers to an array of "instance descriptors". This array has
+ * a few metadata entries at the front, followed by groups of three entries for
+ * each property. In Node v0.10 and later, it looks roughly like this:
+ *
+ * +--------------+ +----------------------+
+ * | JSObject | +--> | Map |
+ * +--------------| | +----------------------+
+ * | map | ---+ | ... |
+ * | ... | | instance_descriptors | --+
+ * in-object | [prop 0 val] | | ... | |
+ * properties | [prop 1 val] | +----------------------+ |
+ * (not for all | ... | |
+ * objects) | [prop N val] | |
+ * +--------------+ |
+ * +------------------------------------------------+
+ * |
+ * +----> +------------------------------+
+ * | FixedArray |
+ * +------------------------------+
+ * | ... |
+ * | prop 0 "key" descriptor |
+ * | prop 0 "details" descriptor |
+ * | prop 0 "value" descriptor |
+ * | prop 1 "key" descriptor |
+ * | prop 1 "details" descriptor |
+ * | prop 1 "value" descriptor |
+ * | ... |
+ * | prop N "key" descriptor |
+ * | prop N "details" descriptor |
+ * | prop N "value" descriptor |
+ * +------------------------------+
+ *
+ * In versions of Node prior to 0.10, there's an extra level of indirection.
+ * The Map refers to a "transitions" array, which has an entry that points to
+ * the instance descriptors. In both cases, the descriptors look roughly the
+ * same.
+ *
+ * Each property is described by three pointer-sized entries:
+ *
+ * o key: a string denoting the name of the property
+ * o details: a bitfield describing attributes of this property
+ * o value: an integer describing where this property's value is stored
+ *
+ * "key" is straightforward: it's just the name of the property as the
+ * JavaScript programmer knows it.
+ *
+ * In versions prior to Node 0.12, "value" is an integer. If "value" is less
+ * than the number of properties stored inside the object (which is also
+ * recorded in the Map), then it denotes which of the in-object property value
+ * slots (shown above inside the JSObject object) stores the value for this
+ * property. If "value" is greater than the number of properties stored inside
+ * the object, then it denotes which index into the separate "properties" array
+ * (a separate field in the JSObject, not shown above) contains the value for
+ * this property.
+ *
+ * In Node 0.12, for properties that are stored inside the object, the offset is
+ * obtained not using "value", but using a bitfield from the "details" part of
+ * the descriptor.
+ *
+ * Terminology notes: it's important to keep straight the different senses of
+ * "object" and "property" here. We use "JavaScript objects" to refer to the
+ * things that JavaScript programmers would call objects, including instances of
+ * Object and Array and subclasses of those. These are a subset of V8 heap
+ * objects, since V8 uses its heap to manage lots of other objects that
+ * JavaScript programmers don't think about. This function iterates JavaScript
+ * properties of these JavaScript objects, not internal properties of heap
+ * objects in general.
+ *
+ * Relatedly, while JavaScript programmers frequently interchange the notions of
+ * property names, property values, and property configurations (e.g., getters
+ * and setters, read-only or not, hidden or not), these are all distinct in the
+ * implementation of the VM, and "property" typically refers to the whole
+ * configuration, which may include a way to get the property name and value.
+ *
+ * The canonical source of the information used here is the implementation of
+ * property lookup in the V8 source code, currently in Object::GetProperty.
+ */
+
static int
jsobj_properties(uintptr_t addr,
- int (*func)(const char *, uintptr_t, void *), void *arg)
+ int (*func)(const char *, uintptr_t, void *), void *arg,
+ jspropinfo_t *propinfop)
{
uintptr_t ptr, map, elements;
uintptr_t *props = NULL, *descs = NULL, *content = NULL, *trans, *elts;
@@ -1754,10 +2129,12 @@ jsobj_properties(uintptr_t addr,
int rval = -1;
size_t ps = sizeof (uintptr_t);
ssize_t off;
+ jspropinfo_t propinfo = JPI_NONE;
/*
- * Objects have either "fast" properties represented with a FixedArray
- * or slow properties represented with a Dictionary.
+ * First, check if the JSObject's "properties" field is a FixedArray.
+ * If not, then this is something we don't know how to deal with, and
+ * we'll just pass the caller a NULL value.
*/
if (mdb_vread(&ptr, ps, addr + V8_OFF_JSOBJECT_PROPERTIES) == -1)
return (-1);
@@ -1766,30 +2143,26 @@ jsobj_properties(uintptr_t addr,
return (-1);
if (type != V8_TYPE_FIXEDARRAY) {
- /*
- * If our properties aren't a fixed array, we'll emit a member
- * that contains the type name, but with a NULL value.
- */
char buf[256];
-
(void) mdb_snprintf(buf, sizeof (buf), "<%s>",
enum_lookup_str(v8_types, type, "unknown"));
-
+ if (propinfop != NULL)
+ *propinfop = JPI_BADLAYOUT;
return (func(buf, NULL, arg));
}
/*
- * To iterate the properties, we need to examine the instance
- * descriptors of the associated Map object. Depending on the version
- * of V8, this might be found directly from the map -- or indirectly
- * via the transitions array.
+ * As described above, we need the Map to figure out how to iterate the
+ * properties for this object.
*/
if (mdb_vread(&map, ps, addr + V8_OFF_HEAPOBJECT_MAP) == -1)
goto err;
/*
* Check to see if our elements member is an array and non-zero; if
- * so, it contains numerically-named properties.
+ * so, it contains numerically-named properties. Whether or not there
+ * are any numerically-named properties, there may be other kinds of
+ * properties.
*/
if (V8_ELEMENTS_KIND_SHIFT != -1 &&
read_heap_ptr(&elements, addr, V8_OFF_JSOBJECT_ELEMENTS) == 0 &&
@@ -1805,6 +2178,7 @@ jsobj_properties(uintptr_t addr,
kind = bit_field2 >> V8_ELEMENTS_KIND_SHIFT;
kind &= (1 << V8_ELEMENTS_KIND_BITCOUNT) - 1;
+ propinfo |= JPI_NUMERIC;
if (kind == V8_ELEMENTS_FAST_ELEMENTS ||
kind == V8_ELEMENTS_FAST_HOLEY_ELEMENTS) {
@@ -1815,7 +2189,7 @@ jsobj_properties(uintptr_t addr,
jsobj_is_hole(elts[ii]))
continue;
- snprintf(name, sizeof (name), "%d", ii);
+ snprintf(name, sizeof (name), "%" PRIdPTR, ii);
if (func(name, elts[ii], arg) != 0) {
mdb_free(elts, sz);
@@ -1823,6 +2197,7 @@ jsobj_properties(uintptr_t addr,
}
}
} else if (kind == V8_ELEMENTS_DICTIONARY_ELEMENTS) {
+ propinfo |= JPI_DICT;
if (read_heap_dict(elements, func, arg) != 0) {
mdb_free(elts, sz);
goto err;
@@ -1833,14 +2208,49 @@ jsobj_properties(uintptr_t addr,
}
if (V8_DICT_SHIFT != -1) {
+ v8_field_t *flp;
uintptr_t bit_field3;
- if (mdb_vread(&bit_field3, sizeof (bit_field3),
- map + V8_OFF_MAP_BIT_FIELD3) == -1)
- goto err;
+ /*
+ * If dictionary properties are supported (the V8_DICT_SHIFT
+ * offset is not -1), then bitfield 3 tells us if the properties
+ * for this object are stored in "properties" field of the
+ * object using a Dictionary representation.
+ *
+ * Versions of V8 prior to Node 0.12 treated bit_field3 as an
+ * SMI, so it was pointer-sized, and it has to be converted from
+ * an SMI before using it. In 0.12, it's treated as a raw
+ * uint32_t, meaning it's always int-sized and it should not be
+ * converted. We can tell which case we're in because the debug
+ * constant (v8dbg_class_map__bit_field3__TYPE) tells us whether
+ * the TYPE is "SMI" or "int".
+ */
- if (V8_SMI_VALUE(bit_field3) & (1 << V8_DICT_SHIFT))
+ flp = conf_field_lookup("Map", "bit_field3");
+ if (flp == NULL || flp->v8f_isbyte) {
+ /*
+ * v8f_isbyte indicates the type is "int", so we're in
+ * the int-sized not-a-SMI world.
+ */
+ unsigned int bf3_value;
+ if (mdb_vread(&bf3_value, sizeof (bf3_value),
+ map + V8_OFF_MAP_BIT_FIELD3) == -1)
+ goto err;
+ bit_field3 = (uintptr_t)bf3_value;
+ } else {
+ /* The metadata indicates this is an SMI. */
+ if (mdb_vread(&bit_field3, sizeof (bit_field3),
+ map + V8_OFF_MAP_BIT_FIELD3) == -1)
+ goto err;
+ bit_field3 = V8_SMI_VALUE(bit_field3);
+ }
+
+ if (bit_field3 & (1 << V8_DICT_SHIFT)) {
+ propinfo |= JPI_DICT;
+ if (propinfop != NULL)
+ *propinfop = propinfo;
return (read_heap_dict(ptr, func, arg));
+ }
} else if (V8_OFF_MAP_INSTANCE_DESCRIPTORS != -1) {
uintptr_t bit_field3;
@@ -1860,6 +2270,9 @@ jsobj_properties(uintptr_t addr,
* dictionary -- an assumption that is assuredly in
* error in some cases.
*/
+ propinfo |= JPI_DICT;
+ if (propinfop != NULL)
+ *propinfop = propinfo;
return (read_heap_dict(ptr, func, arg));
}
}
@@ -1867,7 +2280,12 @@ jsobj_properties(uintptr_t addr,
if (read_heap_array(ptr, &props, &nprops, UM_SLEEP) != 0)
goto err;
- if ((off = V8_OFF_MAP_INSTANCE_DESCRIPTORS) == -1) {
+ /*
+ * Check if we're looking at an older version of V8, where the instance
+ * descriptors are stored not directly in the Map, but in the
+ * "transitions" array that's stored in the Map.
+ */
+ if (V8_OFF_MAP_INSTANCE_DESCRIPTORS == -1) {
if (V8_OFF_MAP_TRANSITIONS == -1 ||
V8_TRANSITIONS_IDX_DESC == -1 ||
V8_PROP_IDX_CONTENT != -1) {
@@ -1877,47 +2295,71 @@ jsobj_properties(uintptr_t addr,
goto err;
}
+ propinfo |= JPI_HASTRANSITIONS;
off = V8_OFF_MAP_TRANSITIONS;
- }
-
- if (mdb_vread(&ptr, ps, map + off) == -1)
- goto err;
+ if (mdb_vread(&ptr, ps, map + off) == -1)
+ goto err;
- if (V8_OFF_MAP_INSTANCE_DESCRIPTORS == -1) {
if (read_heap_array(ptr, &trans, &ntrans, UM_SLEEP) != 0)
goto err;
ptr = trans[V8_TRANSITIONS_IDX_DESC];
mdb_free(trans, ntrans * sizeof (uintptr_t));
+ } else {
+ off = V8_OFF_MAP_INSTANCE_DESCRIPTORS;
+ if (mdb_vread(&ptr, ps, map + off) == -1)
+ goto err;
}
+ /*
+ * Either way, at this point "ptr" should refer to the descriptors
+ * array.
+ */
if (read_heap_array(ptr, &descs, &ndescs, UM_SLEEP) != 0)
goto err;
+ /*
+ * For cases where property values are stored directly inside the object
+ * ("fast properties"), we need to know the whole size of the object and
+ * the number of properties in the object in order to calculate the
+ * correct offset for each property.
+ */
if (read_size(&size, addr) != 0)
size = 0;
-
- if (mdb_vread(&ninprops, 1, map + V8_OFF_MAP_INOBJECT_PROPERTIES) == -1)
- goto err;
-
- if (V8_PROP_IDX_CONTENT != -1 && V8_PROP_IDX_CONTENT < ndescs &&
- read_heap_array(descs[V8_PROP_IDX_CONTENT], &content,
- &ncontent, UM_SLEEP) != 0)
+ if (mdb_vread(&ninprops, ps,
+ map + V8_OFF_MAP_INOBJECT_PROPERTIES) == -1)
goto err;
if (V8_PROP_IDX_CONTENT == -1) {
/*
- * On node v0.8 and later, the content is not stored in an
- * orthogonal FixedArray, but rather with the descriptors.
+ * On node v0.8 and later, the content is not stored in a
+ * separate FixedArray, but rather with the descriptors. The
+ * number of actual properties is the length of the array minus
+ * the first (non-property) elements divided by the number of
+ * elements per property.
*/
content = descs;
ncontent = ndescs;
rndescs = ndescs > V8_PROP_IDX_FIRST ?
(ndescs - V8_PROP_IDX_FIRST) / V8_PROP_DESC_SIZE : 0;
} else {
+ /*
+ * On older versions, the content is stored in a separate array,
+ * and there's one entry per property (rather than three).
+ */
+ if (V8_PROP_IDX_CONTENT < ndescs &&
+ read_heap_array(descs[V8_PROP_IDX_CONTENT], &content,
+ &ncontent, UM_SLEEP) != 0)
+ goto err;
+
rndescs = ndescs - V8_PROP_IDX_FIRST;
+ propinfo |= JPI_HASCONTENT;
}
+ /*
+ * At this point, we've read all the pieces we need to process the list
+ * of instance descriptors.
+ */
for (ii = 0; ii < rndescs; ii++) {
uintptr_t keyidx, validx, detidx, baseidx;
char buf[1024];
@@ -1941,48 +2383,91 @@ jsobj_properties(uintptr_t addr,
detidx = baseidx + V8_PROP_DESC_DETAILS;
}
+ /*
+ * Ignore cases where our understanding doesn't appear to match
+ * what's here.
+ */
if (detidx >= ncontent) {
+ propinfo |= JPI_SKIPPED;
v8_warn("property descriptor %d: detidx (%d) "
"out of bounds for content array (length %d)\n",
ii, detidx, ncontent);
continue;
}
+ /*
+ * We only process fields. There are other entries here
+ * (notably: transitions) that we don't care about (and these
+ * are not errors).
+ */
if (!V8_DESC_ISFIELD(content[detidx]))
continue;
if (keyidx >= ndescs) {
+ propinfo |= JPI_SKIPPED;
v8_warn("property descriptor %d: keyidx (%d) "
"out of bounds for descriptor array (length %d)\n",
ii, keyidx, ndescs);
continue;
}
- if (jsstr_print(descs[keyidx], JSSTR_NUDE, &c, &len) != 0)
+ if (jsstr_print(descs[keyidx], JSSTR_NUDE, &c, &len) != 0) {
+ propinfo |= JPI_SKIPPED;
continue;
+ }
val = (intptr_t)content[validx];
-
if (!V8_IS_SMI(val)) {
+ propinfo |= JPI_SKIPPED;
v8_warn("object %p: property descriptor %d: value "
- "index value is not an SMI: %p\n", addr, ii, val);
+ "index is not an SMI: %p\n", addr, ii, val);
continue;
}
+ /*
+ * The "value" part of each property descriptor tells us whether
+ * the property value is stored directly in the object or in the
+ * related "props" array. See JSObject::RawFastPropertyAt() in
+ * the V8 source.
+ */
val = V8_SMI_VALUE(val) - ninprops;
-
if (val < 0) {
- /* property is stored directly in the object */
- if (mdb_vread(&ptr, sizeof (ptr), addr + V8_OFF_HEAP(
- size + val * sizeof (uintptr_t))) == -1) {
+ uintptr_t propaddr;
+
+ /*
+ * The property is stored directly inside the object.
+ * In Node 0.10, "val - ninprops" is the (negative)
+ * index of the property counted from the end of the
+ * object. In that context, -1 refers to the last
+ * word in the object; -2 refers to the second-last
+ * word, and so on.
+ *
+ * In Node 0.12, we get the 0-based index from the
+ * first property inside the object by reading certain
+ * bits from the property descriptor details word.
+ * These constants are literal here because they're
+ * literal in the V8 source itself.
+ */
+ if (v8_major > 3 || (v8_major == 3 && v8_minor >= 26)) {
+ val = V8_PROP_FIELDINDEX(content[detidx]);
+ propaddr = addr + V8_OFF_HEAP(
+ size - (ninprops - val) * ps);
+ } else {
+ propaddr = addr + V8_OFF_HEAP(size + val * ps);
+ }
+
+ if (mdb_vread(&ptr, sizeof (ptr), propaddr) == -1) {
+ propinfo |= JPI_SKIPPED;
v8_warn("object %p: failed to read in-object "
- "property at %p\n", addr, addr +
- V8_OFF_HEAP(size + val *
- sizeof (uintptr_t)));
+ "property at %p", addr, propaddr);
continue;
}
+
+ propinfo |= JPI_INOBJECT;
} else {
- /* property should be in "props" array */
+ /*
+ * The property is in the separate "props" array.
+ */
if (val >= nprops) {
/*
* This can happen when properties are deleted.
@@ -1992,12 +2477,14 @@ jsobj_properties(uintptr_t addr,
if (val < rndescs)
continue;
+ propinfo |= JPI_SKIPPED;
v8_warn("object %p: property descriptor %d: "
"value index value (%d) out of bounds "
"(%d)\n", addr, ii, val, nprops);
goto err;
}
+ propinfo |= JPI_PROPS;
ptr = props[val];
}
@@ -2006,6 +2493,9 @@ jsobj_properties(uintptr_t addr,
}
rval = 0;
+ if (propinfop != NULL)
+ *propinfop = propinfo;
+
err:
if (props != NULL)
mdb_free(props, nprops * sizeof (uintptr_t));
@@ -2039,9 +2529,14 @@ jsfunc_lineno(uintptr_t lendsp, uintptr_t tokpos,
* The token position is an SMI, but it comes in as its raw
* value so we can more easily compare it to values in the line
* endings table. If we're just printing the position directly,
- * we must convert it here.
+ * we must convert it here, unless we're checking against the
+ * "-1" sentinel.
*/
- mdb_snprintf(buf, buflen, "position %d", V8_SMI_VALUE(tokpos));
+ if (tokpos == V8_VALUE_SMI(-1))
+ mdb_snprintf(buf, buflen, "unknown position");
+ else
+ mdb_snprintf(buf, buflen, "position %d",
+ V8_SMI_VALUE(tokpos));
if (lineno != NULL)
*lineno = 0;
@@ -2167,7 +2662,7 @@ jsfunc_lines(uintptr_t scriptp,
if (startline == -1 || endline == -1) {
mdb_warn("for script %p, could not determine startline/endline"
- " (start %ld, end %ld, nlines %d)",
+ " (start %ld, end %ld, nlines %d)\n",
scriptp, start, end, nlines);
mdb_free(buf, bufsz);
return;
@@ -2256,25 +2751,6 @@ jsfunc_name(uintptr_t funcinfop, char **bufp, size_t *lenp)
/*
* JavaScript-level object printing
*/
-typedef struct jsobj_print {
- char **jsop_bufp;
- size_t *jsop_lenp;
- int jsop_indent;
- uint64_t jsop_depth;
- boolean_t jsop_printaddr;
- uintptr_t jsop_baseaddr;
- int jsop_nprops;
- const char *jsop_member;
- boolean_t jsop_found;
- boolean_t jsop_descended;
-} jsobj_print_t;
-
-static int jsobj_print_number(uintptr_t, jsobj_print_t *);
-static int jsobj_print_oddball(uintptr_t, jsobj_print_t *);
-static int jsobj_print_jsobject(uintptr_t, jsobj_print_t *);
-static int jsobj_print_jsarray(uintptr_t, jsobj_print_t *);
-static int jsobj_print_jsfunction(uintptr_t, jsobj_print_t *);
-static int jsobj_print_jsdate(uintptr_t, jsobj_print_t *);
static int
jsobj_print(uintptr_t addr, jsobj_print_t *jsop)
@@ -2377,7 +2853,7 @@ jsobj_print_prop(const char *desc, uintptr_t val, void *arg)
char **bufp = jsop->jsop_bufp;
size_t *lenp = jsop->jsop_lenp;
- (void) bsnprintf(bufp, lenp, "%s\n%*s%s: ", jsop->jsop_nprops == 0 ?
+ (void) bsnprintf(bufp, lenp, "%s\n%*s\"%s\": ", jsop->jsop_nprops == 0 ?
"{" : "", jsop->jsop_indent + 4, "", desc);
descend = *jsop;
@@ -2438,7 +2914,8 @@ jsobj_print_jsobject(uintptr_t addr, jsobj_print_t *jsop)
size_t *lenp = jsop->jsop_lenp;
if (jsop->jsop_member != NULL)
- return (jsobj_properties(addr, jsobj_print_prop_member, jsop));
+ return (jsobj_properties(addr, jsobj_print_prop_member,
+ jsop, &jsop->jsop_propinfo));
if (jsop->jsop_depth == 0) {
(void) bsnprintf(bufp, lenp, "[...]");
@@ -2447,7 +2924,8 @@ jsobj_print_jsobject(uintptr_t addr, jsobj_print_t *jsop)
jsop->jsop_nprops = 0;
- if (jsobj_properties(addr, jsobj_print_prop, jsop) != 0)
+ if (jsobj_properties(addr, jsobj_print_prop, jsop,
+ &jsop->jsop_propinfo) != 0)
return (-1);
if (jsop->jsop_nprops > 0) {
@@ -2760,7 +3238,7 @@ dcmd_v8function(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
}
if (read_heap_ptr(&funcinfop, addr, V8_OFF_JSFUNCTION_SHARED) != 0 ||
- read_heap_ptr(&tokpos, funcinfop,
+ read_heap_maybesmi(&tokpos, funcinfop,
V8_OFF_SHAREDFUNCTIONINFO_FUNCTION_TOKEN_POSITION) != 0 ||
read_heap_ptr(&scriptp, funcinfop,
V8_OFF_SHAREDFUNCTIONINFO_SCRIPT) != 0 ||
@@ -2768,6 +3246,13 @@ dcmd_v8function(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
read_heap_ptr(&lendsp, scriptp, V8_OFF_SCRIPT_LINE_ENDS) != 0)
goto err;
+ /*
+ * The token position is normally a SMI, so read_heap_maybesmi() will
+ * interpret the value for us. However, this code uses its SMI-encoded
+ * value, so convert it back here.
+ */
+ tokpos = V8_VALUE_SMI(tokpos);
+
bufp = buf;
len = sizeof (buf);
if (jsfunc_name(funcinfop, &bufp, &len) != 0)
@@ -2800,6 +3285,28 @@ err:
return (DCMD_ERR);
}
+/*
+ * Access an internal field of a V8 object.
+ */
+/* ARGSUSED */
+static int
+dcmd_v8internal(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+ uintptr_t idx;
+ uintptr_t fieldaddr;
+
+ if (mdb_getopts(argc, argv, NULL) != argc - 1 ||
+ argv[argc - 1].a_type != MDB_TYPE_STRING)
+ return (DCMD_USAGE);
+
+ idx = mdb_strtoull(argv[argc - 1].a_un.a_str);
+ if (obj_v8internal(addr, idx, &fieldaddr) != 0)
+ return (DCMD_ERR);
+
+ mdb_printf("%p\n", fieldaddr);
+ return (DCMD_OK);
+}
+
/* ARGSUSED */
static int
dcmd_v8frametypes(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
@@ -2921,11 +3428,39 @@ load_current_context(uintptr_t *fpp, uintptr_t *raddrp)
return (0);
}
+typedef struct jsframe {
+ boolean_t jsf_showall; /* show hidden frames and pointers */
+ boolean_t jsf_verbose; /* show arguments and JS code */
+ char *jsf_func; /* filter frames for named function */
+ char *jsf_prop; /* filter arguments */
+ uintptr_t jsf_nlines; /* lines of context (for verbose) */
+ uint_t jsf_nskipped; /* skipped frames */
+} jsframe_t;
+
+static void
+jsframe_skip(jsframe_t *jsf)
+{
+ jsf->jsf_nskipped++;
+}
+
+static void
+jsframe_print_skipped(jsframe_t *jsf)
+{
+ if (jsf->jsf_nskipped == 1)
+ mdb_printf(" (1 internal frame elided)\n");
+ else if (jsf->jsf_nskipped > 1)
+ mdb_printf(" (%d internal frames elided)\n",
+ jsf->jsf_nskipped);
+ jsf->jsf_nskipped = 0;
+}
+
static int
-do_jsframe_special(uintptr_t fptr, uintptr_t raddr, char *prop)
+do_jsframe_special(uintptr_t fptr, uintptr_t raddr, jsframe_t *jsf)
{
+ uint_t count;
uintptr_t ftype;
const char *ftypename;
+ char *prop = jsf->jsf_prop;
/*
* First see if this looks like a native frame rather than a JavaScript
@@ -2933,11 +3468,21 @@ do_jsframe_special(uintptr_t fptr, uintptr_t raddr, char *prop)
* symbolically. If that works, we assume this was NOT a V8 frame,
* since those are never in the symbol table.
*/
- if (mdb_snprintf(NULL, 0, "%A", raddr) > 1) {
+ count = mdb_snprintf(NULL, 0, "%A", raddr);
+ if (count > 1) {
if (prop != NULL)
return (0);
- mdb_printf("%p %a\n", fptr, raddr);
+ jsframe_print_skipped(jsf);
+ if (jsf->jsf_showall) {
+ mdb_printf("%p %a\n", fptr, raddr);
+ } else if (count <= 65) {
+ mdb_printf("native: %a\n", raddr);
+ } else {
+ char buf[65];
+ mdb_snprintf(buf, sizeof (buf), "%a", raddr);
+ mdb_printf("native: %s...\n", buf);
+ }
return (0);
}
@@ -2952,7 +3497,12 @@ do_jsframe_special(uintptr_t fptr, uintptr_t raddr, char *prop)
if (prop != NULL)
return (0);
- mdb_printf("%p %a <%s>\n", fptr, raddr, ftypename);
+ if (jsf->jsf_showall) {
+ jsframe_print_skipped(jsf);
+ mdb_printf("%p %a <%s>\n", fptr, raddr, ftypename);
+ } else {
+ jsframe_skip(jsf);
+ }
return (0);
}
@@ -2967,10 +3517,12 @@ do_jsframe_special(uintptr_t fptr, uintptr_t raddr, char *prop)
ftypename = enum_lookup_str(v8_frametypes, V8_SMI_VALUE(ftype),
NULL);
- if (ftypename != NULL)
+ if (jsf->jsf_showall && ftypename != NULL) {
+ jsframe_print_skipped(jsf);
mdb_printf("%p %a <%s>\n", fptr, raddr, ftypename);
- else
- mdb_printf("%p %a\n", fptr, raddr);
+ } else {
+ jsframe_skip(jsf);
+ }
return (0);
}
@@ -2979,9 +3531,14 @@ do_jsframe_special(uintptr_t fptr, uintptr_t raddr, char *prop)
}
static int
-do_jsframe(uintptr_t fptr, uintptr_t raddr, boolean_t verbose,
- char *func, char *prop, uintptr_t nlines)
+do_jsframe(uintptr_t fptr, uintptr_t raddr, jsframe_t *jsf)
{
+ boolean_t showall = jsf->jsf_showall;
+ boolean_t verbose = jsf->jsf_verbose;
+ const char *func = jsf->jsf_func;
+ const char *prop = jsf->jsf_prop;
+ uintptr_t nlines = jsf->jsf_nlines;
+
uintptr_t funcp, funcinfop, tokpos, endpos, scriptp, lendsp, ptrp;
uintptr_t ii, nargs;
const char *typename;
@@ -2994,7 +3551,7 @@ do_jsframe(uintptr_t fptr, uintptr_t raddr, boolean_t verbose,
/*
* Check for non-JavaScript frames first.
*/
- if (func == NULL && do_jsframe_special(fptr, raddr, prop) == 0)
+ if (func == NULL && do_jsframe_special(fptr, raddr, jsf) == 0)
return (DCMD_OK);
/*
@@ -3017,7 +3574,12 @@ do_jsframe(uintptr_t fptr, uintptr_t raddr, boolean_t verbose,
if (func != NULL || prop != NULL)
return (DCMD_OK);
- mdb_printf("%p %a\n", fptr, raddr);
+ if (showall) {
+ jsframe_print_skipped(jsf);
+ mdb_printf("%p %a\n", fptr, raddr);
+ } else {
+ jsframe_skip(jsf);
+ }
return (DCMD_OK);
}
@@ -3025,7 +3587,13 @@ do_jsframe(uintptr_t fptr, uintptr_t raddr, boolean_t verbose,
if (func != NULL || prop != NULL)
return (DCMD_OK);
- mdb_printf("%p %a internal (Code: %p)\n", fptr, raddr, funcp);
+ if (showall) {
+ jsframe_print_skipped(jsf);
+ mdb_printf("%p %a internal (Code: %p)\n",
+ fptr, raddr, funcp);
+ } else {
+ jsframe_skip(jsf);
+ }
return (DCMD_OK);
}
@@ -3033,8 +3601,13 @@ do_jsframe(uintptr_t fptr, uintptr_t raddr, boolean_t verbose,
if (func != NULL || prop != NULL)
return (DCMD_OK);
- mdb_printf("%p %a unknown (%s: %p)", fptr, raddr, typename,
- funcp);
+ if (showall) {
+ jsframe_print_skipped(jsf);
+ mdb_printf("%p %a unknown (%s: %p)",
+ fptr, raddr, typename, funcp);
+ } else {
+ jsframe_skip(jsf);
+ }
return (DCMD_OK);
}
@@ -3049,19 +3622,33 @@ do_jsframe(uintptr_t fptr, uintptr_t raddr, boolean_t verbose,
if (func != NULL && strcmp(buf, func) != 0)
return (DCMD_OK);
- if (prop == NULL)
- mdb_printf("%p %a %s (%p)\n", fptr, raddr, buf, funcp);
+ if (prop == NULL) {
+ jsframe_print_skipped(jsf);
+ if (showall)
+ mdb_printf("%p %a ", fptr, raddr);
+ else
+ mdb_printf("js: ");
+ mdb_printf("%s", buf);
+ if (showall)
+ mdb_printf(" (JSFunction: %p)\n", funcp);
+ else
+ mdb_printf("\n");
+ }
if (!verbose && prop == NULL)
return (DCMD_OK);
+ if (verbose)
+ jsframe_print_skipped(jsf);
+
/*
* Although the token position is technically an SMI, we're going to
* byte-compare it to other SMI values so we don't want decode it here.
*/
- if (read_heap_ptr(&tokpos, funcinfop,
+ if (read_heap_maybesmi(&tokpos, funcinfop,
V8_OFF_SHAREDFUNCTIONINFO_FUNCTION_TOKEN_POSITION) != 0)
return (DCMD_ERR);
+ tokpos = V8_VALUE_SMI(tokpos);
if (read_heap_ptr(&scriptp, funcinfop,
V8_OFF_SHAREDFUNCTIONINFO_SCRIPT) != 0)
@@ -3080,25 +3667,54 @@ do_jsframe(uintptr_t fptr, uintptr_t raddr, boolean_t verbose,
}
if (prop == NULL) {
- (void) mdb_inc_indent(4);
+ (void) mdb_inc_indent(10);
mdb_printf("file: %s\n", buf);
}
if (read_heap_ptr(&lendsp, scriptp, V8_OFF_SCRIPT_LINE_ENDS) != 0)
return (DCMD_ERR);
- if (read_heap_smi(&nargs, funcinfop,
+ (void) jsfunc_lineno(lendsp, tokpos, buf, sizeof (buf), &lineno);
+
+ if (prop != NULL && strcmp(prop, "posn") == 0) {
+ mdb_printf("%s\n", buf);
+ return (DCMD_OK);
+ }
+
+ if (prop == NULL)
+ mdb_printf("posn: %s\n", buf);
+
+ if (read_heap_maybesmi(&nargs, funcinfop,
V8_OFF_SHAREDFUNCTIONINFO_LENGTH) == 0) {
- for (ii = 0; ii < nargs; ii++) {
- uintptr_t argptr;
- char arg[10];
+ uintptr_t argptr;
+ char arg[10];
+
+ if (mdb_vread(&argptr, sizeof (argptr),
+ fptr + V8_OFF_FP_ARGS + nargs * sizeof (uintptr_t)) != -1 &&
+ argptr != NULL) {
+ (void) snprintf(arg, sizeof (arg), "this");
+ if (prop != NULL && strcmp(arg, prop) == 0) {
+ mdb_printf("%p\n", argptr);
+ return (DCMD_OK);
+ }
+
+ if (prop == NULL) {
+ bufp = buf;
+ len = sizeof (buf);
+ (void) obj_jstype(argptr, &bufp, &len, NULL);
+ mdb_printf("this: %p (%s)\n", argptr, buf);
+ }
+ }
+
+ for (ii = 0; ii < nargs; ii++) {
if (mdb_vread(&argptr, sizeof (argptr),
fptr + V8_OFF_FP_ARGS + (nargs - ii - 1) *
sizeof (uintptr_t)) == -1)
continue;
- (void) snprintf(arg, sizeof (arg), "arg%d", ii + 1);
+ (void) snprintf(arg, sizeof (arg), "arg%" PRIuPTR,
+ ii + 1);
if (prop != NULL) {
if (strcmp(arg, prop) != 0)
@@ -3116,28 +3732,20 @@ do_jsframe(uintptr_t fptr, uintptr_t raddr, boolean_t verbose,
}
}
- (void) jsfunc_lineno(lendsp, tokpos, buf, sizeof (buf), &lineno);
if (prop != NULL) {
- if (strcmp(prop, "posn") == 0) {
- mdb_printf("%s\n", buf);
- return (DCMD_OK);
- }
-
mdb_warn("unknown frame property '%s'\n", prop);
return (DCMD_ERR);
}
- mdb_printf("posn: %s", buf);
-
- if (nlines != 0 && read_heap_smi(&endpos, funcinfop,
+ if (nlines != 0 && read_heap_maybesmi(&endpos, funcinfop,
V8_OFF_SHAREDFUNCTIONINFO_END_POSITION) == 0) {
jsfunc_lines(scriptp,
V8_SMI_VALUE(tokpos), endpos, nlines, "%5d ");
+ mdb_printf("\n");
}
- mdb_printf("\n");
- (void) mdb_dec_indent(4);
+ (void) mdb_dec_indent(10);
return (DCMD_OK);
}
@@ -3155,6 +3763,7 @@ typedef struct findjsobjects_instance {
typedef struct findjsobjects_obj {
findjsobjects_prop_t *fjso_props;
findjsobjects_prop_t *fjso_last;
+ jspropinfo_t fjso_propinfo;
size_t fjso_nprops;
findjsobjects_instance_t fjso_instances;
int fjso_ninstances;
@@ -3164,6 +3773,17 @@ typedef struct findjsobjects_obj {
char fjso_constructor[80];
} findjsobjects_obj_t;
+typedef struct findjsobjects_func {
+ findjsobjects_instance_t fjsf_instances;
+ int fjsf_ninstances;
+ avl_node_t fjsf_node;
+ struct findjsobjects_func *fjsf_next;
+ uintptr_t fjsf_shared;
+ char fjsf_funcname[40];
+ char fjsf_scriptname[80];
+ char fjsf_location[20];
+} findjsobjects_func_t;
+
typedef struct findjsobjects_stats {
int fjss_heapobjs;
int fjss_cached;
@@ -3172,6 +3792,9 @@ typedef struct findjsobjects_stats {
int fjss_objects;
int fjss_arrays;
int fjss_uniques;
+ int fjss_funcs;
+ int fjss_funcs_skipped;
+ int fjss_funcs_unique;
} findjsobjects_stats_t;
typedef struct findjsobjects_reference {
@@ -3200,10 +3823,12 @@ typedef struct findjsobjects_state {
boolean_t fjs_referred;
avl_tree_t fjs_tree;
avl_tree_t fjs_referents;
+ avl_tree_t fjs_funcinfo;
findjsobjects_referent_t *fjs_head;
findjsobjects_referent_t *fjs_tail;
findjsobjects_obj_t *fjs_current;
findjsobjects_obj_t *fjs_objects;
+ findjsobjects_func_t *fjs_funcs;
findjsobjects_stats_t fjs_stats;
} findjsobjects_state_t;
@@ -3268,6 +3893,14 @@ findjsobjects_cmp(findjsobjects_obj_t *lhs, findjsobjects_obj_t *rhs)
}
int
+findjsobjects_cmp_funcinfo(findjsobjects_func_t *lhs,
+ findjsobjects_func_t *rhs)
+{
+ int diff = lhs->fjsf_shared - rhs->fjsf_shared;
+ return (diff < 0 ? -1 : diff > 0 ? 1 : 0);
+}
+
+int
findjsobjects_cmp_referents(findjsobjects_referent_t *lhs,
findjsobjects_referent_t *rhs)
{
@@ -3369,6 +4002,71 @@ out:
v8_silent--;
}
+static void
+findjsobjects_jsfunc(findjsobjects_state_t *fjs, uintptr_t addr)
+{
+ findjsobjects_func_t *func, *ofunc;
+ findjsobjects_instance_t *inst;
+ uintptr_t funcinfo, script, name;
+ avl_index_t where;
+ int err;
+ char *bufp;
+ size_t len;
+
+ /*
+ * This may be somewhat expensive to do for all JSFunctions, but in most
+ * core files, there aren't that many. We could defer some of this work
+ * until the user tries to print the function ::jsfunctions, but this
+ * step is useful to do early to filter out garbage data.
+ */
+
+ v8_silent++;
+ if (read_heap_ptr(&funcinfo, addr, V8_OFF_JSFUNCTION_SHARED) != 0 ||
+ read_heap_ptr(&script, funcinfo,
+ V8_OFF_SHAREDFUNCTIONINFO_SCRIPT) != 0 ||
+ read_heap_ptr(&name, script, V8_OFF_SCRIPT_NAME) != 0) {
+ fjs->fjs_stats.fjss_funcs_skipped++;
+ v8_silent--;
+ return;
+ }
+
+ func = mdb_zalloc(sizeof (findjsobjects_func_t), UM_SLEEP);
+ func->fjsf_ninstances = 1;
+ func->fjsf_instances.fjsi_addr = addr;
+ func->fjsf_shared = funcinfo;
+
+ bufp = func->fjsf_funcname;
+ len = sizeof (func->fjsf_funcname);
+ err = jsfunc_name(funcinfo, &bufp, &len);
+
+ bufp = func->fjsf_scriptname;
+ len = sizeof (func->fjsf_scriptname);
+ err |= jsstr_print(name, JSSTR_NUDE, &bufp, &len);
+
+ v8_silent--;
+ if (err != 0) {
+ fjs->fjs_stats.fjss_funcs_skipped++;
+ mdb_free(func, sizeof (findjsobjects_func_t));
+ return;
+ }
+
+ fjs->fjs_stats.fjss_funcs++;
+ ofunc = avl_find(&fjs->fjs_funcinfo, func, &where);
+ if (ofunc == NULL) {
+ avl_add(&fjs->fjs_funcinfo, func);
+ func->fjsf_next = fjs->fjs_funcs;
+ fjs->fjs_funcs = func;
+ fjs->fjs_stats.fjss_funcs_unique++;
+ } else {
+ inst = mdb_alloc(sizeof (findjsobjects_instance_t), UM_SLEEP);
+ inst->fjsi_addr = addr;
+ inst->fjsi_next = ofunc->fjsf_instances.fjsi_next;
+ ofunc->fjsf_instances.fjsi_next = inst;
+ ofunc->fjsf_ninstances++;
+ mdb_free(func, sizeof (findjsobjects_func_t));
+ }
+}
+
int
findjsobjects_range(findjsobjects_state_t *fjs, uintptr_t addr, uintptr_t size)
{
@@ -3376,6 +4074,7 @@ findjsobjects_range(findjsobjects_state_t *fjs, uintptr_t addr, uintptr_t size)
findjsobjects_stats_t *stats = &fjs->fjs_stats;
uint8_t type;
int jsobject = V8_TYPE_JSOBJECT, jsarray = V8_TYPE_JSARRAY;
+ int jsfunction = V8_TYPE_JSFUNCTION;
caddr_t range = mdb_alloc(size, UM_SLEEP);
uintptr_t base = addr, mapaddr;
@@ -3414,6 +4113,11 @@ findjsobjects_range(findjsobjects_state_t *fjs, uintptr_t addr, uintptr_t size)
continue;
}
+ if (type == jsfunction) {
+ findjsobjects_jsfunc(fjs, addr);
+ continue;
+ }
+
if (type != jsobject && type != jsarray)
continue;
@@ -3423,7 +4127,8 @@ findjsobjects_range(findjsobjects_state_t *fjs, uintptr_t addr, uintptr_t size)
if (type == jsobject) {
if (jsobj_properties(addr,
- findjsobjects_prop, fjs) != 0) {
+ findjsobjects_prop, fjs,
+ &fjs->fjs_current->fjso_propinfo) != 0) {
findjsobjects_free(fjs->fjs_current);
fjs->fjs_current = NULL;
continue;
@@ -3625,7 +4330,7 @@ findjsobjects_references(findjsobjects_state_t *fjs)
fjs->fjs_addr = inst->fjsi_addr;
(void) jsobj_properties(inst->fjsi_addr,
- findjsobjects_references_prop, fjs);
+ findjsobjects_references_prop, fjs, NULL);
}
}
@@ -3716,6 +4421,26 @@ findjsobjects_match_constructor(findjsobjects_obj_t *obj,
mdb_printf("%p\n", obj->fjso_instances.fjsi_addr);
}
+static void
+findjsobjects_match_kind(findjsobjects_obj_t *obj, const char *propkind)
+{
+ jspropinfo_t p = obj->fjso_propinfo;
+
+ if (((p & JPI_NUMERIC) != 0 && strstr(propkind, "numeric") != NULL) ||
+ ((p & JPI_DICT) != 0 && strstr(propkind, "dict") != NULL) ||
+ ((p & JPI_INOBJECT) != 0 && strstr(propkind, "inobject") != NULL) ||
+ ((p & JPI_PROPS) != 0 && strstr(propkind, "props") != NULL) ||
+ ((p & JPI_HASTRANSITIONS) != 0 &&
+ strstr(propkind, "transitions") != NULL) ||
+ ((p & JPI_HASCONTENT) != 0 &&
+ strstr(propkind, "content") != NULL) ||
+ ((p & JPI_SKIPPED) != 0 && strstr(propkind, "skipped") != NULL) ||
+ ((p & JPI_BADLAYOUT) != 0 &&
+ strstr(propkind, "badlayout") != NULL)) {
+ mdb_printf("%p\n", obj->fjso_instances.fjsi_addr);
+ }
+}
+
static int
findjsobjects_match(findjsobjects_state_t *fjs, uintptr_t addr,
uint_t flags, void (*func)(findjsobjects_obj_t *, const char *),
@@ -3823,84 +4548,71 @@ dcmd_findjsobjects_help(void)
" -v Provide verbose statistics\n");
}
+static findjsobjects_state_t findjsobjects_state;
+
static int
-dcmd_findjsobjects(uintptr_t addr,
- uint_t flags, int argc, const mdb_arg_t *argv)
+findjsobjects_run(findjsobjects_state_t *fjs)
{
- static findjsobjects_state_t fjs;
- static findjsobjects_stats_t *stats = &fjs.fjs_stats;
- findjsobjects_obj_t *obj;
struct ps_prochandle *Pr;
- boolean_t references = B_FALSE, listlike = B_FALSE;
- const char *propname = NULL;
- const char *constructor = NULL;
-
- fjs.fjs_verbose = B_FALSE;
- fjs.fjs_brk = B_FALSE;
- fjs.fjs_marking = B_FALSE;
- fjs.fjs_allobjs = B_FALSE;
-
- if (mdb_getopts(argc, argv,
- 'a', MDB_OPT_SETBITS, B_TRUE, &fjs.fjs_allobjs,
- 'b', MDB_OPT_SETBITS, B_TRUE, &fjs.fjs_brk,
- 'c', MDB_OPT_STR, &constructor,
- 'l', MDB_OPT_SETBITS, B_TRUE, &listlike,
- 'm', MDB_OPT_SETBITS, B_TRUE, &fjs.fjs_marking,
- 'p', MDB_OPT_STR, &propname,
- 'r', MDB_OPT_SETBITS, B_TRUE, &references,
- 'v', MDB_OPT_SETBITS, B_TRUE, &fjs.fjs_verbose,
- NULL) != argc)
- return (DCMD_USAGE);
+ findjsobjects_obj_t *obj;
+ findjsobjects_stats_t *stats = &fjs->fjs_stats;
- if (!fjs.fjs_initialized) {
- avl_create(&fjs.fjs_tree,
+ if (!fjs->fjs_initialized) {
+ avl_create(&fjs->fjs_tree,
(int(*)(const void *, const void *))findjsobjects_cmp,
sizeof (findjsobjects_obj_t),
offsetof(findjsobjects_obj_t, fjso_node));
- avl_create(&fjs.fjs_referents,
+ avl_create(&fjs->fjs_referents,
(int(*)(const void *, const void *))
findjsobjects_cmp_referents,
sizeof (findjsobjects_referent_t),
offsetof(findjsobjects_referent_t, fjsr_node));
- fjs.fjs_initialized = B_TRUE;
+ avl_create(&fjs->fjs_funcinfo,
+ (int(*)(const void *, const void*))
+ findjsobjects_cmp_funcinfo,
+ sizeof (findjsobjects_func_t),
+ offsetof(findjsobjects_func_t, fjsf_node));
+
+ fjs->fjs_initialized = B_TRUE;
}
- if (avl_is_empty(&fjs.fjs_tree)) {
+ if (avl_is_empty(&fjs->fjs_tree)) {
findjsobjects_obj_t **sorted;
int nobjs, i;
hrtime_t start = gethrtime();
if (mdb_get_xdata("pshandle", &Pr, sizeof (Pr)) == -1) {
mdb_warn("couldn't read pshandle xdata");
- return (DCMD_ERR);
+ return (-1);
}
v8_silent++;
if (Pmapping_iter(Pr,
- (proc_map_f *)findjsobjects_mapping, &fjs) != 0) {
+ (proc_map_f *)findjsobjects_mapping, fjs) != 0) {
v8_silent--;
- return (DCMD_ERR);
+ return (-1);
}
- if ((nobjs = avl_numnodes(&fjs.fjs_tree)) != 0) {
+ if ((nobjs = avl_numnodes(&fjs->fjs_tree)) != 0) {
/*
* We have the objects -- now sort them.
*/
sorted = mdb_alloc(nobjs * sizeof (void *),
UM_SLEEP | UM_GC);
- for (obj = fjs.fjs_objects, i = 0; obj != NULL;
+ for (obj = fjs->fjs_objects, i = 0; obj != NULL;
obj = obj->fjso_next, i++) {
sorted[i] = obj;
}
- qsort(sorted, avl_numnodes(&fjs.fjs_tree),
+ qsort(sorted, avl_numnodes(&fjs->fjs_tree),
sizeof (void *), findjsobjects_cmp_ninstances);
- for (i = 1, fjs.fjs_objects = sorted[0]; i < nobjs; i++)
+ for (i = 1, fjs->fjs_objects = sorted[0];
+ i < nobjs; i++)
sorted[i - 1]->fjso_next = sorted[i];
sorted[nobjs - 1]->fjso_next = NULL;
@@ -3908,7 +4620,7 @@ dcmd_findjsobjects(uintptr_t addr,
v8_silent--;
- if (fjs.fjs_verbose) {
+ if (fjs->fjs_verbose) {
const char *f = "findjsobjects: %30s => %d\n";
int elapsed = (int)((gethrtime() - start) / NANOSEC);
@@ -3920,12 +4632,54 @@ dcmd_findjsobjects(uintptr_t addr,
mdb_printf(f, "processed objects", stats->fjss_objects);
mdb_printf(f, "processed arrays", stats->fjss_arrays);
mdb_printf(f, "unique objects", stats->fjss_uniques);
+ mdb_printf(f, "functions found", stats->fjss_funcs);
+ mdb_printf(f, "unique functions",
+ stats->fjss_funcs_unique);
+ mdb_printf(f, "functions skipped",
+ stats->fjss_funcs_skipped);
}
}
+ return (0);
+}
+
+static int
+dcmd_findjsobjects(uintptr_t addr,
+ uint_t flags, int argc, const mdb_arg_t *argv)
+{
+ findjsobjects_state_t *fjs = &findjsobjects_state;
+ findjsobjects_obj_t *obj;
+ boolean_t references = B_FALSE, listlike = B_FALSE;
+ const char *propname = NULL;
+ const char *constructor = NULL;
+ const char *propkind = NULL;
+
+ fjs->fjs_verbose = B_FALSE;
+ fjs->fjs_brk = B_FALSE;
+ fjs->fjs_marking = B_FALSE;
+ fjs->fjs_allobjs = B_FALSE;
+
+ if (mdb_getopts(argc, argv,
+ 'a', MDB_OPT_SETBITS, B_TRUE, &fjs->fjs_allobjs,
+ 'b', MDB_OPT_SETBITS, B_TRUE, &fjs->fjs_brk,
+ 'c', MDB_OPT_STR, &constructor,
+ 'k', MDB_OPT_STR, &propkind,
+ 'l', MDB_OPT_SETBITS, B_TRUE, &listlike,
+ 'm', MDB_OPT_SETBITS, B_TRUE, &fjs->fjs_marking,
+ 'p', MDB_OPT_STR, &propname,
+ 'r', MDB_OPT_SETBITS, B_TRUE, &references,
+ 'v', MDB_OPT_SETBITS, B_TRUE, &fjs->fjs_verbose,
+ NULL) != argc)
+ return (DCMD_USAGE);
+
+ if (findjsobjects_run(fjs) != 0)
+ return (DCMD_ERR);
+
if (listlike && !(flags & DCMD_ADDRSPEC)) {
- if (propname != NULL || constructor != NULL) {
- char opt = propname != NULL ? 'p' : 'c';
+ if (propname != NULL || constructor != NULL ||
+ propkind != NULL) {
+ char opt = propname != NULL ? 'p' :
+ propkind != NULL ? 'k' :'c';
mdb_warn("cannot specify -l with -%c; instead, pipe "
"output of ::findjsobjects -%c to "
@@ -3933,38 +4687,50 @@ dcmd_findjsobjects(uintptr_t addr,
return (DCMD_ERR);
}
- return (findjsobjects_match(&fjs, addr, flags,
+ return (findjsobjects_match(fjs, addr, flags,
findjsobjects_match_all, NULL));
}
if (propname != NULL) {
- if (constructor != NULL) {
+ if (constructor != NULL || propkind != NULL) {
mdb_warn("cannot specify both a property name "
- "and a constructor\n");
+ "and a %s\n", constructor != NULL ?
+ "constructor" : "property kind");
return (DCMD_ERR);
}
- return (findjsobjects_match(&fjs, addr, flags,
+ return (findjsobjects_match(fjs, addr, flags,
findjsobjects_match_propname, propname));
}
if (constructor != NULL) {
- return (findjsobjects_match(&fjs, addr, flags,
+ if (propkind != NULL) {
+ mdb_warn("cannot specify both a constructor name "
+ "and a property kind\n");
+ return (DCMD_ERR);
+ }
+
+ return (findjsobjects_match(fjs, addr, flags,
findjsobjects_match_constructor, constructor));
}
+ if (propkind != NULL) {
+ return (findjsobjects_match(fjs, addr, flags,
+ findjsobjects_match_kind, propkind));
+ }
+
if (references && !(flags & DCMD_ADDRSPEC) &&
- avl_is_empty(&fjs.fjs_referents)) {
+ avl_is_empty(&fjs->fjs_referents)) {
mdb_warn("must specify or mark an object to find references\n");
return (DCMD_ERR);
}
- if (fjs.fjs_marking && !(flags & DCMD_ADDRSPEC)) {
+ if (fjs->fjs_marking && !(flags & DCMD_ADDRSPEC)) {
mdb_warn("must specify an object to mark\n");
return (DCMD_ERR);
}
- if (references && fjs.fjs_marking) {
+ if (references && fjs->fjs_marking) {
mdb_warn("can't both mark an object and find its references\n");
return (DCMD_ERR);
}
@@ -3978,14 +4744,14 @@ dcmd_findjsobjects(uintptr_t addr,
* specified/marked objects (-r). (Note that the absence of
* any of these options implies -l.)
*/
- inst = findjsobjects_instance(&fjs, addr, &head);
+ inst = findjsobjects_instance(fjs, addr, &head);
if (inst == NULL) {
mdb_warn("%p is not a valid object\n", addr);
return (DCMD_ERR);
}
- if (!references && !fjs.fjs_marking) {
+ if (!references && !fjs->fjs_marking) {
for (inst = head; inst != NULL; inst = inst->fjsi_next)
mdb_printf("%p\n", inst->fjsi_addr);
@@ -3993,24 +4759,24 @@ dcmd_findjsobjects(uintptr_t addr,
}
if (!listlike) {
- findjsobjects_referent(&fjs, inst->fjsi_addr);
+ findjsobjects_referent(fjs, inst->fjsi_addr);
} else {
for (inst = head; inst != NULL; inst = inst->fjsi_next)
- findjsobjects_referent(&fjs, inst->fjsi_addr);
+ findjsobjects_referent(fjs, inst->fjsi_addr);
}
}
if (references)
- findjsobjects_references(&fjs);
+ findjsobjects_references(fjs);
- if (references || fjs.fjs_marking)
+ if (references || fjs->fjs_marking)
return (DCMD_OK);
mdb_printf("%?s %8s %8s %s\n", "OBJECT",
"#OBJECTS", "#PROPS", "CONSTRUCTOR: PROPS");
- for (obj = fjs.fjs_objects; obj != NULL; obj = obj->fjso_next) {
- if (obj->fjso_malformed && !fjs.fjs_allobjs)
+ for (obj = fjs->fjs_objects; obj != NULL; obj = obj->fjso_next) {
+ if (obj->fjso_malformed && !fjs->fjs_allobjs)
continue;
findjsobjects_print(obj);
@@ -4019,21 +4785,90 @@ dcmd_findjsobjects(uintptr_t addr,
return (DCMD_OK);
}
+/*
+ * Given a Node Buffer object, print out details about it. With "-a", just
+ * print the address.
+ */
+/* ARGSUSED */
+static int
+dcmd_nodebuffer(uintptr_t addr, uint_t flags, int argc,
+ const mdb_arg_t *argv)
+{
+ boolean_t opt_f = B_FALSE;
+ char buf[80];
+ char *bufp = buf;
+ size_t len = sizeof (buf);
+ uintptr_t elts, rawbuf;
+
+ /*
+ * The undocumented "-f" option allows users to override constructor
+ * checks.
+ */
+ if (mdb_getopts(argc, argv,
+ 'f', MDB_OPT_SETBITS, B_TRUE, &opt_f, NULL) != argc)
+ return (DCMD_USAGE);
+
+ if (!opt_f) {
+ if (obj_jsconstructor(addr, &bufp, &len, B_FALSE) != 0)
+ return (DCMD_ERR);
+
+ if (strcmp(buf, "Buffer") != 0) {
+ mdb_warn("%p does not appear to be a buffer\n", addr);
+ return (DCMD_ERR);
+ }
+ }
+
+ if (read_heap_ptr(&elts, addr, V8_OFF_JSOBJECT_ELEMENTS) != 0)
+ return (DCMD_ERR);
+
+ if (obj_v8internal(elts, 0, &rawbuf) != 0)
+ return (DCMD_ERR);
+
+ mdb_printf("%p\n", rawbuf);
+ return (DCMD_OK);
+}
+
+/* ARGSUSED */
+static int
+dcmd_jsconstructor(uintptr_t addr, uint_t flags, int argc,
+ const mdb_arg_t *argv)
+{
+ boolean_t opt_v = B_FALSE;
+ char buf[80];
+ char *bufp;
+ size_t len = sizeof (buf);
+
+ if (mdb_getopts(argc, argv, 'v', MDB_OPT_SETBITS, B_TRUE, &opt_v,
+ NULL) != argc)
+ return (DCMD_USAGE);
+
+ bufp = buf;
+ if (obj_jsconstructor(addr, &bufp, &len, opt_v))
+ return (DCMD_ERR);
+
+ mdb_printf("%s\n", buf);
+ return (DCMD_OK);
+}
+
/* ARGSUSED */
static int
dcmd_jsframe(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
{
uintptr_t fptr, raddr;
- boolean_t opt_v = B_FALSE, opt_i = B_FALSE;
- char *opt_f = NULL, *opt_p = NULL;
- uintptr_t opt_n = 5;
+ boolean_t opt_i = B_FALSE;
+ jsframe_t jsf;
+ int rv;
+
+ bzero(&jsf, sizeof (jsf));
+ jsf.jsf_nlines = 5;
if (mdb_getopts(argc, argv,
- 'v', MDB_OPT_SETBITS, B_TRUE, &opt_v,
+ 'a', MDB_OPT_SETBITS, B_TRUE, &jsf.jsf_showall,
+ 'v', MDB_OPT_SETBITS, B_TRUE, &jsf.jsf_verbose,
'i', MDB_OPT_SETBITS, B_TRUE, &opt_i,
- 'f', MDB_OPT_STR, &opt_f,
- 'n', MDB_OPT_UINTPTR, &opt_n,
- 'p', MDB_OPT_STR, &opt_p, NULL) != argc)
+ 'f', MDB_OPT_STR, &jsf.jsf_func,
+ 'n', MDB_OPT_UINTPTR, &jsf.jsf_nlines,
+ 'p', MDB_OPT_STR, &jsf.jsf_prop, NULL) != argc)
return (DCMD_USAGE);
/*
@@ -4043,8 +4878,12 @@ dcmd_jsframe(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
* actually stored with the next frame. For debugging, this can be
* overridden with the "-i" option (for "immediate").
*/
- if (opt_i)
- return (do_jsframe(addr, 0, opt_v, opt_f, opt_p, opt_n));
+ if (opt_i) {
+ rv = do_jsframe(addr, 0, &jsf);
+ if (rv == 0)
+ jsframe_print_skipped(&jsf);
+ return (rv);
+ }
if (mdb_vread(&raddr, sizeof (raddr),
addr + sizeof (uintptr_t)) == -1) {
@@ -4061,7 +4900,43 @@ dcmd_jsframe(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
if (fptr == NULL)
return (DCMD_OK);
- return (do_jsframe(fptr, raddr, opt_v, opt_f, opt_p, opt_n));
+ rv = do_jsframe(fptr, raddr, &jsf);
+ if (rv == 0)
+ jsframe_print_skipped(&jsf);
+ return (rv);
+}
+
+static void
+jsobj_print_propinfo(jspropinfo_t propinfo)
+{
+ if (propinfo == JPI_NONE)
+ return;
+
+ mdb_printf("property kind: ");
+ if ((propinfo & JPI_NUMERIC) != 0)
+ mdb_printf("numeric-named ");
+ if ((propinfo & JPI_DICT) != 0)
+ mdb_printf("dictionary ");
+ if ((propinfo & JPI_INOBJECT) != 0)
+ mdb_printf("in-object ");
+ if ((propinfo & JPI_PROPS) != 0)
+ mdb_printf("\"properties\" array ");
+ mdb_printf("\n");
+
+ if ((propinfo & (JPI_HASTRANSITIONS | JPI_HASCONTENT)) != 0) {
+ mdb_printf("fallbacks: ");
+ if ((propinfo & JPI_HASTRANSITIONS) != 0)
+ mdb_printf("transitions ");
+ if ((propinfo & JPI_HASCONTENT) != 0)
+ mdb_printf("content ");
+ mdb_printf("\n");
+ }
+
+ if ((propinfo & JPI_SKIPPED) != 0)
+ mdb_printf(
+ "some properties skipped due to unexpected layout\n");
+ if ((propinfo & JPI_BADLAYOUT) != 0)
+ mdb_printf("object has unexpected layout\n");
}
/* ARGSUSED */
@@ -4072,6 +4947,7 @@ dcmd_jsprint(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
size_t bufsz = 262144, len = bufsz;
jsobj_print_t jsop;
boolean_t opt_b = B_FALSE;
+ boolean_t opt_v = B_FALSE;
int rv, i;
bzero(&jsop, sizeof (jsop));
@@ -4081,7 +4957,8 @@ dcmd_jsprint(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
i = mdb_getopts(argc, argv,
'a', MDB_OPT_SETBITS, B_TRUE, &jsop.jsop_printaddr,
'b', MDB_OPT_SETBITS, B_TRUE, &opt_b,
- 'd', MDB_OPT_UINT64, &jsop.jsop_depth, NULL);
+ 'd', MDB_OPT_UINT64, &jsop.jsop_depth,
+ 'v', MDB_OPT_SETBITS, B_TRUE, &opt_v, NULL);
if (opt_b)
jsop.jsop_baseaddr = addr;
@@ -4139,9 +5016,209 @@ dcmd_jsprint(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
mdb_printf("\n");
+ if (opt_v)
+ jsobj_print_propinfo(jsop.jsop_propinfo);
+
+ return (DCMD_OK);
+}
+
+/* ARGSUSED */
+static int
+dcmd_jssource(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+ const char *typename;
+ uintptr_t nlines = 5;
+ uintptr_t funcinfop, scriptp, funcnamep;
+ uintptr_t tokpos, endpos;
+ uint8_t type;
+ char buf[256];
+ char *bufp = buf;
+ size_t len = sizeof (buf);
+
+ if (mdb_getopts(argc, argv, 'n', MDB_OPT_UINTPTR, &nlines,
+ NULL) != argc)
+ return (DCMD_USAGE);
+
+ if (!V8_IS_HEAPOBJECT(addr) || read_typebyte(&type, addr) != 0) {
+ mdb_warn("%p is not a heap object\n", addr);
+ return (DCMD_ERR);
+ }
+
+ typename = enum_lookup_str(v8_types, type, "");
+ if (strcmp(typename, "JSFunction") != 0) {
+ mdb_warn("%p is not a JSFunction\n", addr);
+ return (DCMD_ERR);
+ }
+
+ if (read_heap_ptr(&funcinfop, addr, V8_OFF_JSFUNCTION_SHARED) != 0 ||
+ read_heap_ptr(&scriptp, funcinfop,
+ V8_OFF_SHAREDFUNCTIONINFO_SCRIPT) != 0 ||
+ read_heap_ptr(&funcnamep, scriptp, V8_OFF_SCRIPT_NAME) != 0) {
+ mdb_warn("%p: failed to find script for function\n", addr);
+ return (DCMD_ERR);
+ }
+
+ if (read_heap_maybesmi(&tokpos, funcinfop,
+ V8_OFF_SHAREDFUNCTIONINFO_FUNCTION_TOKEN_POSITION) != 0 ||
+ read_heap_maybesmi(&endpos, funcinfop,
+ V8_OFF_SHAREDFUNCTIONINFO_END_POSITION) != 0) {
+ mdb_warn("%p: failed to find function's boundaries\n", addr);
+ }
+
+ if (jsstr_print(funcnamep, JSSTR_NUDE, &bufp, &len) == 0)
+ mdb_printf("file: %s\n", buf);
+
+ if (tokpos != endpos)
+ jsfunc_lines(scriptp, tokpos, endpos, nlines, "%5d ");
+ mdb_printf("\n");
+ return (DCMD_OK);
+}
+
+/* ARGSUSED */
+static int
+dcmd_jsfunctions(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+ findjsobjects_state_t *fjs = &findjsobjects_state;
+ findjsobjects_func_t *func;
+ uintptr_t funcinfo;
+ boolean_t showrange = B_FALSE;
+ const char *name = NULL, *filename = NULL;
+ uintptr_t instr = 0;
+
+ if (mdb_getopts(argc, argv,
+ 'x', MDB_OPT_UINTPTR, &instr,
+ 'X', MDB_OPT_SETBITS, B_TRUE, &showrange,
+ 'n', MDB_OPT_STR, &name,
+ 's', MDB_OPT_STR, &filename,
+ NULL) != argc)
+ return (DCMD_USAGE);
+
+ if (findjsobjects_run(fjs) != 0)
+ return (DCMD_ERR);
+
+ if (!showrange)
+ mdb_printf("%?s %8s %-40s %s\n", "FUNC", "#FUNCS", "NAME",
+ "FROM");
+ else
+ mdb_printf("%?s %8s %?s %?s %-40s %s\n", "FUNC", "#FUNCS",
+ "START", "END", "NAME", "FROM");
+
+ for (func = fjs->fjs_funcs; func != NULL; func = func->fjsf_next) {
+ uintptr_t code, ilen;
+
+ funcinfo = func->fjsf_shared;
+
+ if (func->fjsf_location[0] == '\0') {
+ uintptr_t tokpos, script, lends;
+ ptrdiff_t tokposoff =
+ V8_OFF_SHAREDFUNCTIONINFO_FUNCTION_TOKEN_POSITION;
+
+ /*
+ * We don't want to actually decode the token position
+ * as an SMI here, so we re-encode it when we pass it to
+ * jsfunc_lineno() below.
+ */
+ if (read_heap_maybesmi(&tokpos, funcinfo,
+ tokposoff) != 0 ||
+ read_heap_ptr(&script, funcinfo,
+ V8_OFF_SHAREDFUNCTIONINFO_SCRIPT) != 0 ||
+ read_heap_ptr(&lends, script,
+ V8_OFF_SCRIPT_LINE_ENDS) != 0 ||
+ jsfunc_lineno(lends, V8_VALUE_SMI(tokpos),
+ func->fjsf_location,
+ sizeof (func->fjsf_location), NULL) != 0) {
+ func->fjsf_location[0] = '\0';
+ }
+ }
+
+ if (name != NULL && strstr(func->fjsf_funcname, name) == NULL)
+ continue;
+
+ if (filename != NULL &&
+ strstr(func->fjsf_scriptname, filename) == NULL)
+ continue;
+
+ code = 0;
+ ilen = 0;
+ if ((showrange || instr != 0) &&
+ (read_heap_ptr(&code, funcinfo,
+ V8_OFF_SHAREDFUNCTIONINFO_CODE) != 0 ||
+ read_heap_ptr(&ilen, code,
+ V8_OFF_CODE_INSTRUCTION_SIZE) != 0)) {
+ code = 0;
+ ilen = 0;
+ }
+
+ if ((instr != 0 && ilen != 0) &&
+ (instr < code + V8_OFF_CODE_INSTRUCTION_START ||
+ instr >= code + V8_OFF_CODE_INSTRUCTION_START + ilen))
+ continue;
+
+ if (!showrange) {
+ mdb_printf("%?p %8d %-40s %s %s\n",
+ func->fjsf_instances.fjsi_addr,
+ func->fjsf_ninstances, func->fjsf_funcname,
+ func->fjsf_scriptname, func->fjsf_location);
+ } else {
+ uintptr_t code, ilen;
+
+ if (read_heap_ptr(&code, funcinfo,
+ V8_OFF_SHAREDFUNCTIONINFO_CODE) != 0 ||
+ read_heap_ptr(&ilen, code,
+ V8_OFF_CODE_INSTRUCTION_SIZE) != 0) {
+ mdb_printf("%?p %8d %?s %?s %-40s %s %s\n",
+ func->fjsf_instances.fjsi_addr,
+ func->fjsf_ninstances, "?", "?",
+ func->fjsf_funcname, func->fjsf_scriptname,
+ func->fjsf_location);
+ } else {
+ mdb_printf("%?p %8d %?p %?p %-40s %s %s\n",
+ func->fjsf_instances.fjsi_addr,
+ func->fjsf_ninstances,
+ code + V8_OFF_CODE_INSTRUCTION_START,
+ code + V8_OFF_CODE_INSTRUCTION_START + ilen,
+ func->fjsf_funcname, func->fjsf_scriptname,
+ func->fjsf_location);
+ }
+ }
+ }
+
return (DCMD_OK);
}
+static void
+dcmd_jsfunctions_help(void)
+{
+ mdb_printf("%s\n\n",
+"Lists JavaScript functions, optionally filtered by a substring of the\n"
+"function name or script filename or by the instruction address. This uses\n"
+"the cache created by ::findjsobjects. If ::findjsobjects has not already\n"
+"been run, this command runs it automatically without printing the output.\n"
+"This can take anywhere from a second to several minutes, depending on the\n"
+"size of the core dump.\n"
+"\n"
+"It's important to keep in mind that each time you create a function in\n"
+"JavaScript (even from a function definition that has already been used),\n"
+"the VM must create a new object to represent it. For example, if your\n"
+"program has a function A that returns a closure B, the VM will create new\n"
+"instances of the closure function (B) each time the surrounding function (A)\n"
+"is called. To show this, the output of this command consists of one line \n"
+"per function definition that appears in the JavaScript source, and the\n"
+"\"#FUNCS\" column shows how many different functions were created by VM from\n"
+"this definition.");
+
+ mdb_dec_indent(2);
+ mdb_printf("%<b>OPTIONS%</b>\n");
+ mdb_inc_indent(2);
+
+ mdb_printf("%s\n",
+" -f file List functions that were defined in a file whose name contains\n"
+" this substring.\n"
+" -n func List functions whose name contains this substring\n"
+" -x instr List functions whose compiled instructions include this address\n"
+" -X Show where the function's instructions are stored in memory\n");
+}
+
/* ARGSUSED */
static int
dcmd_v8field(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
@@ -4234,15 +5311,18 @@ dcmd_v8array(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
static int
dcmd_jsstack(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
{
- uintptr_t raddr, opt_n = 5;
- boolean_t opt_v = B_FALSE;
- char *opt_f = NULL, *opt_p = NULL;
+ uintptr_t raddr;
+ jsframe_t jsf;
+
+ bzero(&jsf, sizeof (jsf));
+ jsf.jsf_nlines = 5;
if (mdb_getopts(argc, argv,
- 'v', MDB_OPT_SETBITS, B_TRUE, &opt_v,
- 'f', MDB_OPT_STR, &opt_f,
- 'n', MDB_OPT_UINTPTR, &opt_n,
- 'p', MDB_OPT_STR, &opt_p,
+ 'a', MDB_OPT_SETBITS, B_TRUE, &jsf.jsf_showall,
+ 'v', MDB_OPT_SETBITS, B_TRUE, &jsf.jsf_verbose,
+ 'f', MDB_OPT_STR, &jsf.jsf_func,
+ 'n', MDB_OPT_UINTPTR, &jsf.jsf_nlines,
+ 'p', MDB_OPT_STR, &jsf.jsf_prop,
NULL) != argc)
return (DCMD_USAGE);
@@ -4253,13 +5333,14 @@ dcmd_jsstack(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
*/
if (!(flags & DCMD_ADDRSPEC)) {
if (load_current_context(&addr, &raddr) != 0 ||
- do_jsframe(addr, raddr, opt_v, opt_f, opt_p, opt_n) != 0)
+ do_jsframe(addr, raddr, &jsf) != 0)
return (DCMD_ERR);
}
if (mdb_pwalk_dcmd("jsframe", "jsframe", argc, argv, addr) == -1)
return (DCMD_ERR);
+ jsframe_print_skipped(&jsf);
return (DCMD_OK);
}
@@ -4435,7 +5516,7 @@ walk_jsprop_init(mdb_walk_state_t *wsp)
jspw = mdb_zalloc(sizeof (jsprop_walk_data_t), UM_SLEEP | UM_GC);
- if (jsobj_properties(addr, walk_jsprop_nprops, jspw) == -1) {
+ if (jsobj_properties(addr, walk_jsprop_nprops, jspw, NULL) == -1) {
mdb_warn("couldn't iterate over properties for %p\n", addr);
return (WALK_ERR);
}
@@ -4443,7 +5524,7 @@ walk_jsprop_init(mdb_walk_state_t *wsp)
jspw->jspw_props = mdb_zalloc(jspw->jspw_nprops *
sizeof (uintptr_t), UM_SLEEP | UM_GC);
- if (jsobj_properties(addr, walk_jsprop_props, jspw) == -1) {
+ if (jsobj_properties(addr, walk_jsprop_props, jspw, NULL) == -1) {
mdb_warn("couldn't iterate over properties for %p\n", addr);
return (WALK_ERR);
}
@@ -4476,16 +5557,31 @@ walk_jsprop_step(mdb_walk_state_t *wsp)
static const mdb_dcmd_t v8_mdb_dcmds[] = {
/*
+ * Commands to inspect Node-level state
+ */
+ { "nodebuffer", ":[-a]",
+ "print details about the given Node Buffer", dcmd_nodebuffer },
+
+ /*
* Commands to inspect JavaScript-level state
*/
- { "jsframe", ":[-iv] [-f function] [-p property] [-n numlines]",
+ { "jsconstructor", ":[-v]",
+ "print the constructor for a JavaScript object",
+ dcmd_jsconstructor },
+ { "jsframe", ":[-aiv] [-f function] [-p property] [-n numlines]",
"summarize a JavaScript stack frame", dcmd_jsframe },
{ "jsprint", ":[-ab] [-d depth] [member]", "print a JavaScript object",
dcmd_jsprint },
- { "jsstack", "[-v] [-f function] [-p property] [-n numlines]",
+ { "jssource", ":[-n numlines]",
+ "print the source code for a JavaScript function",
+ dcmd_jssource },
+ { "jsstack", "[-av] [-f function] [-p property] [-n numlines]",
"print a JavaScript stacktrace", dcmd_jsstack },
{ "findjsobjects", "?[-vb] [-r | -c cons | -p prop]", "find JavaScript "
"objects", dcmd_findjsobjects, dcmd_findjsobjects_help },
+ { "jsfunctions", "[-X] [-s file_filter] [-n name_filter] "
+ "[-x instr_filter]", "list JavaScript functions",
+ dcmd_jsfunctions, dcmd_jsfunctions_help },
/*
* Commands to inspect V8-level state
@@ -4500,6 +5596,8 @@ static const mdb_dcmd_t v8_mdb_dcmds[] = {
"manually add a field to a given class", dcmd_v8field },
{ "v8function", ":[-d]", "print JSFunction object details",
dcmd_v8function },
+ { "v8internal", ":[fieldidx]", "print v8 object internal fields",
+ dcmd_v8internal },
{ "v8load", "version", "load canned config for a specific V8 version",
dcmd_v8load, dcmd_v8load_help },
{ "v8frametypes", NULL, "list known V8 frame types",
@@ -4534,19 +5632,24 @@ configure(void)
char *success;
v8_cfg_t *cfgp = NULL;
GElf_Sym sym;
+ int major, minor, build, patch;
- if (mdb_readsym(&v8_major, sizeof (v8_major),
+ if (mdb_readsym(&major, sizeof (major),
"_ZN2v88internal7Version6major_E") == -1 ||
- mdb_readsym(&v8_minor, sizeof (v8_minor),
+ mdb_readsym(&minor, sizeof (minor),
"_ZN2v88internal7Version6minor_E") == -1 ||
- mdb_readsym(&v8_build, sizeof (v8_build),
+ mdb_readsym(&build, sizeof (build),
"_ZN2v88internal7Version6build_E") == -1 ||
- mdb_readsym(&v8_patch, sizeof (v8_patch),
+ mdb_readsym(&patch, sizeof (patch),
"_ZN2v88internal7Version6patch_E") == -1) {
mdb_warn("failed to determine V8 version");
return;
}
+ v8_major = major;
+ v8_minor = minor;
+ v8_build = build;
+ v8_patch = patch;
mdb_printf("V8 version: %d.%d.%d.%d\n",
v8_major, v8_minor, v8_build, v8_patch);
diff --git a/usr/src/cmd/mdb/common/modules/v8/v8dbg.h b/usr/src/cmd/mdb/common/modules/v8/v8dbg.h
index 36a30a79ef..b17f241fac 100644
--- a/usr/src/cmd/mdb/common/modules/v8/v8dbg.h
+++ b/usr/src/cmd/mdb/common/modules/v8/v8dbg.h
@@ -19,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright (c) 2013, Joyent, Inc. All rights reserved.
+ * Copyright (c) 2015, Joyent, Inc. All rights reserved.
*/
/*
@@ -42,7 +42,10 @@
* Determine whether a given pointer refers to a SMI, Failure, or HeapObject.
*/
#define V8_IS_SMI(ptr) (((ptr) & V8_SmiTagMask) == V8_SmiTag)
-#define V8_IS_FAILURE(ptr) (((ptr) & V8_FailureTagMask) == V8_FailureTag)
+#define V8_IS_FAILURE(ptr) (V8_FailureTagMask != -1 && \
+ V8_FailureTagMask != -1 && \
+ ((ptr) & V8_FailureTagMask) == V8_FailureTag)
+
#define V8_IS_HEAPOBJECT(ptr) \
(((ptr) & V8_HeapObjectTagMask) == V8_HeapObjectTag)
@@ -51,6 +54,8 @@
* using the upper 31 bits.
*/
#define V8_SMI_VALUE(smi) ((smi) >> (V8_SmiValueShift + V8_SmiShiftSize))
+#define V8_VALUE_SMI(value) \
+ ((value) << (V8_SmiValueShift + V8_SmiShiftSize))
/*
* Determine the encoding and representation of a V8 string.
@@ -80,4 +85,7 @@
#define V8_DESC_ISFIELD(x) \
((V8_SMI_VALUE(x) & V8_PROP_TYPE_MASK) == V8_PROP_TYPE_FIELD)
+#define V8_PROP_FIELDINDEX(value) \
+ ((V8_SMI_VALUE(value) & V8_FIELDINDEX_MASK) >> V8_FIELDINDEX_SHIFT)
+
#endif /* _V8DBG_H */
diff --git a/usr/src/cmd/mdb/common/modules/zfs/zfs.c b/usr/src/cmd/mdb/common/modules/zfs/zfs.c
index 225cf3dee1..21a8c956c3 100644
--- a/usr/src/cmd/mdb/common/modules/zfs/zfs.c
+++ b/usr/src/cmd/mdb/common/modules/zfs/zfs.c
@@ -449,6 +449,7 @@ blkptr(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
typedef struct mdb_dmu_buf_impl {
struct {
uint64_t db_object;
+ uintptr_t db_data;
} db;
uintptr_t db_objset;
uint64_t db_level;
@@ -1691,8 +1692,12 @@ typedef struct mdb_spa {
uintptr_t spa_root_vdev;
} mdb_spa_t;
+typedef struct mdb_dsl_pool {
+ uintptr_t dp_root_dir;
+} mdb_dsl_pool_t;
+
typedef struct mdb_dsl_dir {
- uintptr_t dd_phys;
+ uintptr_t dd_dbuf;
int64_t dd_space_towrite[TXG_SIZE];
} mdb_dsl_dir_t;
@@ -1772,11 +1777,10 @@ static int
spa_space(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
{
mdb_spa_t spa;
- uintptr_t dp_root_dir;
+ mdb_dsl_pool_t dp;
mdb_dsl_dir_t dd;
+ mdb_dmu_buf_impl_t db;
mdb_dsl_dir_phys_t dsp;
- uint64_t children;
- uintptr_t childaddr;
space_data_t sd;
int shift = 20;
char *suffix = "M";
@@ -1793,21 +1797,16 @@ spa_space(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
suffix = "";
}
- if (GETMEMB(addr, "spa", spa_dsl_pool, spa.spa_dsl_pool) ||
- GETMEMB(addr, "spa", spa_root_vdev, spa.spa_root_vdev) ||
- GETMEMB(spa.spa_root_vdev, "vdev", vdev_children, children) ||
- GETMEMB(spa.spa_root_vdev, "vdev", vdev_child, childaddr) ||
- GETMEMB(spa.spa_dsl_pool, "dsl_pool",
- dp_root_dir, dp_root_dir) ||
- GETMEMB(dp_root_dir, "dsl_dir", dd_phys, dd.dd_phys) ||
- GETMEMB(dp_root_dir, "dsl_dir",
- dd_space_towrite, dd.dd_space_towrite) ||
- GETMEMB(dd.dd_phys, "dsl_dir_phys",
- dd_used_bytes, dsp.dd_used_bytes) ||
- GETMEMB(dd.dd_phys, "dsl_dir_phys",
- dd_compressed_bytes, dsp.dd_compressed_bytes) ||
- GETMEMB(dd.dd_phys, "dsl_dir_phys",
- dd_uncompressed_bytes, dsp.dd_uncompressed_bytes)) {
+ if (mdb_ctf_vread(&spa, ZFS_STRUCT "spa", "mdb_spa_t",
+ addr, 0) == -1 ||
+ mdb_ctf_vread(&dp, ZFS_STRUCT "dsl_pool", "mdb_dsl_pool_t",
+ spa.spa_dsl_pool, 0) == -1 ||
+ mdb_ctf_vread(&dd, ZFS_STRUCT "dsl_dir", "mdb_dsl_dir_t",
+ dp.dp_root_dir, 0) == -1 ||
+ mdb_ctf_vread(&db, ZFS_STRUCT "dmu_buf_impl", "mdb_dmu_buf_impl_t",
+ dd.dd_dbuf, 0) == -1 ||
+ mdb_ctf_vread(&dsp, ZFS_STRUCT "dsl_dir_phys",
+ "mdb_dsl_dir_phys_t", db.db.db_data, 0) == -1) {
return (DCMD_ERR);
}
diff --git a/usr/src/cmd/mdb/intel/mdb/mdb_amd64util.c b/usr/src/cmd/mdb/intel/mdb/mdb_amd64util.c
index 5f41df26f3..2465146a38 100644
--- a/usr/src/cmd/mdb/intel/mdb/mdb_amd64util.c
+++ b/usr/src/cmd/mdb/intel/mdb/mdb_amd64util.c
@@ -208,7 +208,7 @@ mdb_amd64_kvm_stack_iter(mdb_tgt_t *t, const mdb_tgt_gregset_t *gsp,
int err;
int i;
- struct {
+ struct fr {
uintptr_t fr_savfp;
uintptr_t fr_savpc;
} fr;
@@ -225,6 +225,8 @@ mdb_amd64_kvm_stack_iter(mdb_tgt_t *t, const mdb_tgt_gregset_t *gsp,
mdb_syminfo_t sip;
mdb_ctf_funcinfo_t mfp;
int xpv_panic = 0;
+ int advance_tortoise = 1;
+ uintptr_t tortoise_fp = 0;
#ifndef _KMDB
int xp;
@@ -237,19 +239,38 @@ mdb_amd64_kvm_stack_iter(mdb_tgt_t *t, const mdb_tgt_gregset_t *gsp,
while (fp != 0) {
int args_style = 0;
- /*
- * Ensure progress (increasing fp), and prevent
- * endless loop with the same FP.
- */
- if (fp <= lastfp) {
- err = EMDB_STKFRAME;
- goto badfp;
- }
if (mdb_tgt_vread(t, &fr, sizeof (fr), fp) != sizeof (fr)) {
err = EMDB_NOMAP;
goto badfp;
}
+ if (tortoise_fp == 0) {
+ tortoise_fp = fp;
+ } else {
+ /*
+ * Advance tortoise_fp every other frame, so we detect
+ * cycles with Floyd's tortoise/hare.
+ */
+ if (advance_tortoise != 0) {
+ struct fr tfr;
+
+ if (mdb_tgt_vread(t, &tfr, sizeof (tfr),
+ tortoise_fp) != sizeof (tfr)) {
+ err = EMDB_NOMAP;
+ goto badfp;
+ }
+
+ tortoise_fp = tfr.fr_savfp;
+ }
+
+ if (fp == tortoise_fp) {
+ err = EMDB_STKFRAME;
+ goto badfp;
+ }
+ }
+
+ advance_tortoise = !advance_tortoise;
+
if ((mdb_tgt_lookup_by_addr(t, pc, MDB_TGT_SYM_FUZZY,
NULL, 0, &s, &sip) == 0) &&
(mdb_ctf_func_info(&s, &sip, &mfp) == 0)) {
diff --git a/usr/src/cmd/mdb/intel/mdb/mdb_ia32util.c b/usr/src/cmd/mdb/intel/mdb/mdb_ia32util.c
index 80ce1c7ad2..d6db4811b2 100644
--- a/usr/src/cmd/mdb/intel/mdb/mdb_ia32util.c
+++ b/usr/src/cmd/mdb/intel/mdb/mdb_ia32util.c
@@ -197,7 +197,7 @@ mdb_ia32_kvm_stack_iter(mdb_tgt_t *t, const mdb_tgt_gregset_t *gsp,
int got_pc = (gsp->kregs[KREG_EIP] != 0);
int err;
- struct {
+ struct fr {
uintptr_t fr_savfp;
uintptr_t fr_savpc;
long fr_argv[32];
@@ -210,6 +210,8 @@ mdb_ia32_kvm_stack_iter(mdb_tgt_t *t, const mdb_tgt_gregset_t *gsp,
ssize_t size;
uint_t argc;
int detect_exception_frames = 0;
+ int advance_tortoise = 1;
+ uintptr_t tortoise_fp = 0;
#ifndef _KMDB
int xp;
@@ -220,15 +222,6 @@ mdb_ia32_kvm_stack_iter(mdb_tgt_t *t, const mdb_tgt_gregset_t *gsp,
bcopy(gsp, &gregs, sizeof (gregs));
while (fp != 0) {
-
- /*
- * Ensure progress (increasing fp), and prevent
- * endless loop with the same FP.
- */
- if (fp <= lastfp) {
- err = EMDB_STKFRAME;
- goto badfp;
- }
if (fp & (STACK_ALIGN - 1)) {
err = EMDB_STKALIGN;
goto badfp;
@@ -242,6 +235,33 @@ mdb_ia32_kvm_stack_iter(mdb_tgt_t *t, const mdb_tgt_gregset_t *gsp,
goto badfp;
}
+ if (tortoise_fp == 0) {
+ tortoise_fp = fp;
+ } else {
+ /*
+ * Advance tortoise_fp every other frame, so we detect
+ * cycles with Floyd's tortoise/hare.
+ */
+ if (advance_tortoise != 0) {
+ struct fr tfr;
+
+ if (mdb_tgt_vread(t, &tfr, sizeof (tfr),
+ tortoise_fp) != sizeof (tfr)) {
+ err = EMDB_NOMAP;
+ goto badfp;
+ }
+
+ tortoise_fp = tfr.fr_savfp;
+ }
+
+ if (fp == tortoise_fp) {
+ err = EMDB_STKFRAME;
+ goto badfp;
+ }
+ }
+
+ advance_tortoise = !advance_tortoise;
+
if (got_pc && func(arg, pc, argc, fr.fr_argv, &gregs) != 0)
break;
diff --git a/usr/src/cmd/perl/Makefile.perl b/usr/src/cmd/perl/Makefile.perl
index 3ee32fef0f..25fdc53f50 100644
--- a/usr/src/cmd/perl/Makefile.perl
+++ b/usr/src/cmd/perl/Makefile.perl
@@ -10,17 +10,15 @@
#
#
# Copyright (c) 2014 Racktop Systems.
+# Copyright 2015, OmniTI Computer Consulting, Inc. All rights reserved.
#
include $(SRC)/lib/Makefile.lib
-# PERL_VERSION used to be set here,
-# but as it is also needed in usr/src/pkg/Makefile,
+# PERL_VERSION and PERL_ARCH used to be set here,
+# but as they were also needed in usr/src/pkg/Makefile,
# the definition was moved to usr/src/Makefile.master
-PERL_ARCH = i86pc-solaris-64int
-$(SPARC_BLD)PERL_ARCH = sun4-solaris-64int
-
PERLDIR = $(ADJUNCT_PROTO)/usr/perl5/$(PERL_VERSION)
PERLLIBDIR = $(PERLDIR)/lib/$(PERL_ARCH)
PERLINCDIR = $(PERLLIBDIR)/CORE
diff --git a/usr/src/cmd/perl/Makefile.targ b/usr/src/cmd/perl/Makefile.targ
index a3211dff7f..b3f5ec9bd3 100644
--- a/usr/src/cmd/perl/Makefile.targ
+++ b/usr/src/cmd/perl/Makefile.targ
@@ -10,7 +10,7 @@
#
#
# Copyright (c) 2014 Racktop Systems.
-# Copyright 2014, OmniTI Computer Consulting, Inc. All rights reserved.
+# Copyright 2015, OmniTI Computer Consulting, Inc. All rights reserved.
#
# Link against libc as perl solaris specs
@@ -23,8 +23,8 @@ $(ROOTPERLEXT) := FILEMODE = 0555
$(ROOTPERLMOD) := FILEMODE = 0444
# CFLAGS for perl, specifically.
-PCFLAGS= -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 -DPERL_USE_SAFE_PUTENV \
- -D_TS_ERRNO
+PCFLAGS= -DPERL_EUPXS_ALWAYS_EXPORT -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 \
+ -DPERL_USE_SAFE_PUTENV -D_TS_ERRNO
$(MACH):
$(INS.dir)
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/rpcsvc/net_files/rpc b/usr/src/cmd/rpcsvc/net_files/rpc
index 9d1b728ab8..a50557dab8 100644
--- a/usr/src/cmd/rpcsvc/net_files/rpc
+++ b/usr/src/cmd/rpcsvc/net_files/rpc
@@ -1,7 +1,4 @@
#
-# Copyright 2009 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
@@ -21,9 +18,14 @@
#
# CDDL HEADER END
#
+# Copyright 2015 Nexenta Systems, Inc. All rights reserved.
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
# rpc
#
-rpcbind 100000 portmap sunrpc rpcbind
+rpcbind 100000 portmap sunrpc
rstatd 100001 rstat rup perfmeter
rusersd 100002 rusers
nfs 100003 nfsprog
@@ -47,9 +49,9 @@ nlockmgr 100021
x25.inr 100022
statmon 100023
status 100024
+bootparam 100026
ypupdated 100028 ypupdate
keyserv 100029 keyserver
-bootparam 100026
sunlink_mapper 100033
tfsd 100037
nsed 100038
@@ -76,14 +78,16 @@ iproutes 100120 na.iproutes
layers 100121 na.layers
snmp 100122 na.snmp snmp-cmc snmp-synoptics snmp-unisys snmp-utk
traffic 100123 na.traffic
+nsm_addr 100133
ktkt_warnd 100134
+smserverd 100155
+fmd_adm 100169
+idmap 100172
nfs_acl 100227
+metad 100229
+metamhd 100230
sadmind 100232
+ufsd 100233
gssd 100234
-ufsd 100233 ufsd
+metamedd 100242
pcnfsd 150001
-metad 100229 metad
-metamhd 100230 metamhd
-metamedd 100242 metamedd
-smserverd 100155 smserverd
-idmap 100172 idmap
diff --git a/usr/src/cmd/sgs/packages/common/SUNWonld-README b/usr/src/cmd/sgs/packages/common/SUNWonld-README
index 6688c34255..f303ba6053 100644
--- a/usr/src/cmd/sgs/packages/common/SUNWonld-README
+++ b/usr/src/cmd/sgs/packages/common/SUNWonld-README
@@ -1654,3 +1654,4 @@ Bugid Risk Synopsis
4270 ld(1) argument error reporting is still pretty bad
4383 libelf can't write extended sections when ELF_F_LAYOUT
4959 completely discarded merged string sections will corrupt output objects
+4996 rtld _init race leads to incorrect symbol values
diff --git a/usr/src/cmd/sgs/rtld/common/util.c b/usr/src/cmd/sgs/rtld/common/util.c
index bd80f05a37..09cabeb31b 100644
--- a/usr/src/cmd/sgs/rtld/common/util.c
+++ b/usr/src/cmd/sgs/rtld/common/util.c
@@ -628,17 +628,17 @@ is_dep_init(Rt_map *dlmp, Rt_map *clmp)
if ((dlmp == clmp) || (rtld_flags & RT_FL_INITFIRST))
return;
- rt_mutex_lock(&dlmp->rt_lock);
+ (void) rt_mutex_lock(&dlmp->rt_lock);
while (dlmp->rt_init_thread != rt_thr_self() && (FLAGS(dlmp) &
(FLG_RT_RELOCED | FLG_RT_INITCALL | FLG_RT_INITDONE)) ==
(FLG_RT_RELOCED | FLG_RT_INITCALL)) {
leave(LIST(dlmp), 0);
(void) _lwp_cond_wait(&dlmp->rt_cv, (mutex_t *)&dlmp->rt_lock);
- rt_mutex_unlock(&dlmp->rt_lock);
+ (void) rt_mutex_unlock(&dlmp->rt_lock);
(void) enter(0);
- rt_mutex_lock(&dlmp->rt_lock);
+ (void) rt_mutex_lock(&dlmp->rt_lock);
}
- rt_mutex_unlock(&dlmp->rt_lock);
+ (void) rt_mutex_unlock(&dlmp->rt_lock);
if ((FLAGS(dlmp) & (FLG_RT_RELOCED | FLG_RT_INITDONE)) ==
(FLG_RT_RELOCED | FLG_RT_INITDONE))
@@ -760,11 +760,11 @@ call_init(Rt_map **tobj, int flag)
* signifies that a .fini must be called should it exist.
* Clear the sort field for use in later .fini processing.
*/
- rt_mutex_lock(&lmp->rt_lock);
+ (void) rt_mutex_lock(&lmp->rt_lock);
FLAGS(lmp) |= FLG_RT_INITDONE;
lmp->rt_init_thread = (thread_t)0;
- _lwp_cond_broadcast(&lmp->rt_cv);
- rt_mutex_unlock(&lmp->rt_lock);
+ (void) _lwp_cond_broadcast(&lmp->rt_cv);
+ (void) rt_mutex_unlock(&lmp->rt_lock);
SORTVAL(lmp) = -1;
/*
diff --git a/usr/src/cmd/tar/Makefile b/usr/src/cmd/tar/Makefile
index 5da69ec0f9..93a02e58e0 100644
--- a/usr/src/cmd/tar/Makefile
+++ b/usr/src/cmd/tar/Makefile
@@ -37,8 +37,6 @@ LINTFLAGS += -u
LDLIBS += -lsec -lcmdutils -lnvpair -ltsol
CFLAGS += $(CCVERBOSE)
-CERRWARN += -_gcc=-Wno-unused-variable
-CERRWARN += -_gcc=-Wno-parentheses
CERRWARN += -_gcc=-Wno-uninitialized
CPPFLAGS += -DEUC
diff --git a/usr/src/cmd/tar/tar.c b/usr/src/cmd/tar/tar.c
index 9e2aef3bee..7b48e927e1 100644
--- a/usr/src/cmd/tar/tar.c
+++ b/usr/src/cmd/tar/tar.c
@@ -565,7 +565,7 @@ static char *myname;
static char *xtract_chdir = NULL;
static int checkflag = 0;
static int Xflag, Fflag, iflag, hflag, Bflag, Iflag;
-static int rflag, xflag, vflag, tflag, mt, svmt, cflag, mflag, pflag;
+static int rflag, xflag, vflag, tflag, mt, cflag, mflag, pflag;
static int uflag;
static int errflag;
static int oflag;
@@ -643,6 +643,8 @@ static int charset_type = 0;
static u_longlong_t xhdr_flgs; /* Bits set determine which items */
/* need to be in extended header. */
+static pid_t comp_pid = 0;
+
#define _X_DEVMAJOR 0x1
#define _X_DEVMINOR 0x2
#define _X_GID 0x4
@@ -725,8 +727,6 @@ main(int argc, char *argv[])
char *cp;
char *tmpdirp;
pid_t thispid;
- pid_t pid;
- int wstat;
(void) setlocale(LC_ALL, "");
#if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */
@@ -1114,10 +1114,8 @@ main(int argc, char *argv[])
if (Aflag && vflag)
(void) printf(
gettext("Suppressing absolute pathnames\n"));
- if (cflag && compress_opt != NULL) {
- pid = compress_file();
- wait_pid(pid);
- }
+ if (cflag && compress_opt != NULL)
+ comp_pid = compress_file();
dorep(argv);
if (rflag && !cflag && (compress_opt != NULL))
compress_back();
@@ -1168,10 +1166,8 @@ main(int argc, char *argv[])
if (strcmp(usefile, "-") != 0) {
check_compression();
- if (compress_opt != NULL) {
- pid = uncompress_file();
- wait_pid(pid);
- }
+ if (compress_opt != NULL)
+ comp_pid = uncompress_file();
}
if (xflag) {
if (xtract_chdir != NULL) {
@@ -4876,6 +4872,13 @@ done(int n)
exit(2);
}
}
+ /*
+ * If we have a compression child, we should have a child process that
+ * we're waiting for to finish compressing or uncompressing the tar
+ * stream.
+ */
+ if (n == 0 && comp_pid != 0)
+ wait_pid(comp_pid);
exit(n);
}
@@ -6109,7 +6112,6 @@ check_prefix(char **namep, char **dirp, char **compp)
if ((tflag || xflag) && !Pflag) {
if (is_absolute(fullname) || has_dot_dot(fullname)) {
char *stripped_prefix;
- size_t prefix_len = 0;
(void) strcpy(savename, fullname);
strcpy(fullname,
@@ -7891,7 +7893,7 @@ xattrs_put(char *longname, char *shortname, char *parent, char *attrparent)
return;
}
- while (dp = readdir(dirp)) {
+ while ((dp = readdir(dirp)) != NULL) {
if (strcmp(dp->d_name, "..") == 0) {
continue;
} else if (strcmp(dp->d_name, ".") == 0) {
@@ -9191,9 +9193,6 @@ static void
compress_back()
{
pid_t pid;
- int status;
- int wret;
- struct stat statb;
if (vflag) {
(void) fprintf(vfile,
@@ -9299,9 +9298,6 @@ void
decompress_file(void)
{
pid_t pid;
- int status;
- char cmdstr[PATH_MAX];
- char fname[PATH_MAX];
char *added_suffix;
@@ -9344,7 +9340,7 @@ compress_file(void)
if (pipe(fd) < 0) {
vperror(1, gettext("Could not create pipe"));
}
- if (pid = fork() > 0) {
+ if ((pid = fork()) > 0) {
mt = fd[1];
(void) close(fd[0]);
return (pid);
@@ -9373,7 +9369,7 @@ uncompress_file(void)
if (pipe(fd) < 0) {
vperror(1, gettext("Could not create pipe"));
}
- if (pid = fork() > 0) {
+ if ((pid = fork()) > 0) {
mt = fd[0];
(void) close(fd[1]);
return (pid);
diff --git a/usr/src/cmd/zlogin/zlogin.c b/usr/src/cmd/zlogin/zlogin.c
index 5f3972db17..909bdfda18 100644
--- a/usr/src/cmd/zlogin/zlogin.c
+++ b/usr/src/cmd/zlogin/zlogin.c
@@ -1821,6 +1821,35 @@ get_username()
return (nptr->pw_name);
}
+static boolean_t
+zlog_mode_logging(char *zonename)
+{
+ boolean_t lm = B_FALSE;
+ zone_dochandle_t handle;
+ struct zone_attrtab attr;
+
+ if ((handle = zonecfg_init_handle()) == NULL)
+ return (lm);
+
+ if (zonecfg_get_handle(zonename, handle) != Z_OK)
+ goto done;
+
+ if (zonecfg_setattrent(handle) != Z_OK)
+ goto done;
+ while (zonecfg_getattrent(handle, &attr) == Z_OK) {
+ if (strcmp("zlog-mode", attr.zone_attr_name) == 0) {
+ if (strncmp("log", attr.zone_attr_value, 3) == 0)
+ lm = B_TRUE;
+ break;
+ }
+ }
+ (void) zonecfg_endattrent(handle);
+
+done:
+ zonecfg_fini_handle(handle);
+ return (lm);
+}
+
int
main(int argc, char **argv)
{
@@ -2056,6 +2085,10 @@ main(int argc, char **argv)
*/
if (console) {
int gz_stderr_fd = -1;
+ boolean_t set_raw = B_TRUE;
+
+ if (imode && zlog_mode_logging(zonename))
+ set_raw = B_FALSE;
/*
* Ensure that zoneadmd for this zone is running.
@@ -2082,7 +2115,7 @@ main(int argc, char **argv)
"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);
diff --git a/usr/src/cmd/zoneadmd/vplat.c b/usr/src/cmd/zoneadmd/vplat.c
index 5a86b1cf50..522de5b779 100644
--- a/usr/src/cmd/zoneadmd/vplat.c
+++ b/usr/src/cmd/zoneadmd/vplat.c
@@ -21,7 +21,7 @@
/*
* Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright 2014, Joyent Inc. All rights reserved.
+ * Copyright 2015, Joyent Inc. All rights reserved.
*/
/*
@@ -163,6 +163,19 @@ 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 *);
@@ -3245,6 +3258,19 @@ get_privset(zlog_t *zlogp, priv_set_t *privs, zone_mnt_t mount_cmd)
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)
{
@@ -3298,6 +3324,7 @@ get_rctls(zlog_t *zlogp, char **bufp, size_t *bufsizep)
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)) {
@@ -3364,6 +3391,26 @@ get_rctls(zlog_t *zlogp, char **bufp, size_t *bufsizep)
}
zonecfg_free_rctl_value_list(rctltab.zone_rctl_valptr);
rctltab.zone_rctl_valptr = NULL;
+
+ /*
+ * With no action on our part we will start zsched with the
+ * project rctl values for our (zoneadmd) current project. For
+ * brands running a variant of Illumos, that's not a problem
+ * since they will setup their own projects, but for a
+ * non-native brand like lx, where there are no projects, we
+ * want to start things up with the same project rctls as the
+ * corresponding zone rctls, since nothing within the zone will
+ * ever change the project rctls.
+ */
+ if ((proj_nm = zone_proj_rctl(name)) != NULL) {
+ if (nvlist_add_nvlist_array(nvl, proj_nm, nvlv, count)
+ != 0) {
+ zerror(zlogp, B_FALSE,
+ "nvlist_add_nvlist_arrays failed");
+ goto out;
+ }
+ }
+
if (nvlist_add_nvlist_array(nvl, (char *)name, nvlv, count)
!= 0) {
zerror(zlogp, B_FALSE, "%s failed",
diff --git a/usr/src/common/brand/lx/lx_syscall.h b/usr/src/common/brand/lx/lx_syscall.h
index e80b0486f5..e9d06fd9bc 100644
--- a/usr/src/common/brand/lx/lx_syscall.h
+++ b/usr/src/common/brand/lx/lx_syscall.h
@@ -35,9 +35,6 @@ extern "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 */
-typedef struct lx_waitid_args {
- int waitid_flags;
-} lx_waitid_args_t;
/* For arch_prctl(2) */
#define LX_ARCH_SET_GS 0x1001
@@ -45,6 +42,50 @@ typedef struct lx_waitid_args {
#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
+
+/*
+ * 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_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_CHILD_SETTID 0x01000000
#ifdef __cplusplus
}
diff --git a/usr/src/common/util/getresponse.c b/usr/src/common/util/getresponse.c
index cc838a7ab1..992e81705f 100644
--- a/usr/src/common/util/getresponse.c
+++ b/usr/src/common/util/getresponse.c
@@ -125,7 +125,7 @@ yes_no(int (*func)(char *))
char ans[LINE_MAX + 1];
/* Get user's answer */
- for (i = 0; b = getchar(); i++) {
+ for (i = 0; (b = getchar()) != 0; i++) {
if (b == '\n' || b == '\0' || b == EOF)
break;
if (i < LINE_MAX)
diff --git a/usr/src/lib/brand/lx/lx_brand/common/aio.c b/usr/src/lib/brand/lx/lx_brand/common/aio.c
index 1e46041f19..36c7cf3afb 100644
--- a/usr/src/lib/brand/lx/lx_brand/common/aio.c
+++ b/usr/src/lib/brand/lx/lx_brand/common/aio.c
@@ -299,7 +299,7 @@ lx_io_getevents(lx_aio_context_t *ctx, long min_nr, long nr,
assert(ctx->lxaio_waiters > 0);
ctx->lxaio_waiters--;
- if (rval == -1 || nget == 0 ||
+ if ((rval == -1 && err != ETIME) || nget == 0 ||
(nget == 1 && list[0].portev_source == PORT_SOURCE_ALERT)) {
/*
* If we're being destroyed, kick our waiter and clear out with
@@ -312,7 +312,7 @@ lx_io_getevents(lx_aio_context_t *ctx, long min_nr, long nr,
mutex_unlock(&ctx->lxaio_lock);
- return (nget == 0 || err == ETIME ? 0 : -err);
+ return (nget == 0 ? 0 : -err);
}
out = SAFE_ALLOCA(nget * sizeof (lx_io_event_t));
diff --git a/usr/src/lib/brand/lx/lx_brand/common/clone.c b/usr/src/lib/brand/lx/lx_brand/common/clone.c
index 58c84c773b..87f966cc89 100644
--- a/usr/src/lib/brand/lx/lx_brand/common/clone.c
+++ b/usr/src/lib/brand/lx/lx_brand/common/clone.c
@@ -49,23 +49,7 @@
#include <sys/lx_debug.h>
#include <sys/lx_thread.h>
#include <sys/fork.h>
-
-#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_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_CHILD_SETTID 0x01000000
+#include <lx_syscall.h>
#define SHARED_AS \
(LX_CLONE_VM | LX_CLONE_FS | LX_CLONE_FILES | LX_CLONE_SIGHAND \
@@ -116,6 +100,7 @@ struct clone_state {
sigset_t c_sigmask; /* signal mask */
lx_affmask_t c_affmask; /* CPU affinity mask */
volatile int *c_clone_res; /* pid/error returned to cloner */
+ int c_ptrace_event; /* ptrace(2) event for child stop */
};
extern void lx_setup_clone(uintptr_t, void *, void *);
@@ -147,7 +132,7 @@ lx_exit(uintptr_t p1)
assert(lx_tsd != 0);
- lx_tsd->lxtsd_exit = LX_EXIT;
+ lx_tsd->lxtsd_exit = LX_ET_EXIT;
lx_tsd->lxtsd_exit_status = status;
lx_ptrace_stop_if_option(LX_PTRACE_O_TRACEEXIT, B_FALSE,
@@ -200,7 +185,7 @@ lx_group_exit(uintptr_t p1)
assert(lx_tsd != 0);
- lx_tsd->lxtsd_exit = LX_EXIT_GROUP;
+ lx_tsd->lxtsd_exit = LX_ET_EXIT_GROUP;
lx_tsd->lxtsd_exit_status = status;
/*
@@ -315,7 +300,7 @@ clone_start(void *arg)
* Do the final stack twiddling, reset %gs, and return to the
* clone(2) path.
*/
- if (lx_tsd.lxtsd_exit == 0) {
+ if (lx_tsd.lxtsd_exit == LX_ET_NONE) {
if (sigprocmask(SIG_SETMASK, &cs->c_sigmask, NULL) < 0) {
*(cs->c_clone_res) = -errno;
@@ -329,6 +314,11 @@ clone_start(void *arg)
*/
*(cs->c_clone_res) = rval;
+ /*
+ * Fire the ptrace(2) event stop in the new thread:
+ */
+ lx_ptrace_stop_if_option(cs->c_ptrace_event, B_TRUE, 0);
+
#if defined(_LP64)
(void) syscall(SYS_brand, B_CLR_NTV_SYSC_FLAG);
lx_setup_clone((uintptr_t)&cs->c_regs, cs->c_retaddr,
@@ -347,12 +337,7 @@ clone_start(void *arg)
* setcontext() to jump to the thread context state saved in
* getcontext(), above.
*/
- if (lx_tsd.lxtsd_exit == LX_EXIT)
- thr_exit((void *)(long)lx_tsd.lxtsd_exit_status);
- else
- exit(lx_tsd.lxtsd_exit_status);
-
- assert(0);
+ lx_exit_common(lx_tsd.lxtsd_exit, lx_tsd.lxtsd_exit_status);
/*NOTREACHED*/
}
@@ -455,6 +440,12 @@ lx_clone(uintptr_t p1, uintptr_t p2, uintptr_t p3, uintptr_t p4,
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));
+
/* See if this is a fork() operation or a thr_create(). */
if (IS_FORK(flags) || IS_VFORK(flags)) {
if (flags & LX_CLONE_PARENT) {
@@ -463,9 +454,6 @@ lx_clone(uintptr_t p1, uintptr_t p2, uintptr_t p3, uintptr_t p4,
return (-ENOTSUP);
}
- if (flags & LX_CLONE_PTRACE)
- lx_ptrace_fork();
-
if ((flags & LX_CSIGNAL) == 0)
fork_flags |= FORK_NOSIGCHLD;
@@ -509,7 +497,6 @@ lx_clone(uintptr_t p1, uintptr_t p2, uintptr_t p3, uintptr_t p4,
return ((rval < 0) ? -errno : rval);
}
-
/*
* Set up additional data in the lx_proc_data structure as
* necessary.
@@ -584,6 +571,7 @@ lx_clone(uintptr_t p1, uintptr_t p2, uintptr_t p3, uintptr_t p4,
cs->c_ldtinfo = ldtinfo;
cs->c_ctidp = ctidp;
cs->c_clone_res = &clone_res;
+ cs->c_ptrace_event = ptrace_event;
#if defined(_LP64)
/*
* The AMD64 ABI says that the kernel clobbers %rcx and %r11. We
@@ -649,7 +637,7 @@ lx_clone(uintptr_t p1, uintptr_t p2, uintptr_t p3, uintptr_t p4,
;
rval = clone_res;
- lx_ptrace_stop_if_option(ptrace_event, B_TRUE, 0);
+ lx_ptrace_stop_if_option(ptrace_event, B_FALSE, (ulong_t)rval);
}
return (rval);
diff --git a/usr/src/lib/brand/lx/lx_brand/common/fork.c b/usr/src/lib/brand/lx/lx_brand/common/fork.c
index 9f2fbd6406..b0edee1adb 100644
--- a/usr/src/lib/brand/lx/lx_brand/common/fork.c
+++ b/usr/src/lib/brand/lx/lx_brand/common/fork.c
@@ -41,18 +41,36 @@
long
lx_fork(void)
{
- int ret = fork1();
+ int ret;
- if (ret == 0) {
- if (lx_is_rpm)
+ /*
+ * 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);
+
+ switch (ret = fork1()) {
+ case -1:
+ return (-errno);
+
+ case 0:
+ /*
+ * Returning in the new child.
+ */
+ if (lx_is_rpm) {
(void) sleep(lx_rpm_delay);
+ }
lx_ptrace_stop_if_option(LX_PTRACE_O_TRACEFORK, B_TRUE, 0);
- } else if (ret != -1) {
+ return (0);
+
+ default:
+ /*
+ * Returning in the new parent.
+ */
lx_ptrace_stop_if_option(LX_PTRACE_O_TRACEFORK, B_FALSE,
(ulong_t)ret);
+ return (ret);
}
-
- return (ret == -1 ? -errno : ret);
}
/*
@@ -65,14 +83,31 @@ lx_fork(void)
long
lx_vfork(void)
{
- int ret = fork1();
+ int ret;
- if (ret == 0) {
+ /*
+ * 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);
+
+ switch (ret = fork1()) {
+ case -1:
+ return (-errno);
+
+ case 0:
+ /*
+ * Returning in the new child.
+ */
lx_ptrace_stop_if_option(LX_PTRACE_O_TRACEVFORK, B_TRUE, 0);
- } else if (ret != -1) {
+ return (0);
+
+ default:
+ /*
+ * Returning in the new parent.
+ */
lx_ptrace_stop_if_option(LX_PTRACE_O_TRACEVFORK, B_FALSE,
(ulong_t)ret);
+ return (ret);
}
-
- return (ret == -1 ? -errno : 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
index b8fdf36b42..abe015c2c4 100644
--- a/usr/src/lib/brand/lx/lx_brand/common/lx_brand.c
+++ b/usr/src/lib/brand/lx/lx_brand/common/lx_brand.c
@@ -172,6 +172,9 @@ struct lx_locale_ending {
int se_size; /* solaris ending string length */
};
+__thread int lx_do_syscall_restart;
+__thread int lx_had_sigchild;
+
#define l2s_locale(lname, sname) \
{(lname), (sname), sizeof ((lname)) - 1, sizeof ((sname)) - 1}
@@ -647,6 +650,7 @@ lx_emulate(lx_regs_t *rp)
}
#endif /* _ILP32 */
+restart_syscall:
if (s->sy_flags & LX_SYS_IKE) {
lx_debug("\tsyscall %d re-vectoring to lx kernel module "
"for %s()", syscall_num, s->sy_name);
@@ -679,6 +683,12 @@ lx_emulate(lx_regs_t *rp)
ret = -stol_errno[-ret];
}
+ if (lx_do_syscall_restart && ret == -stol_errno[EINTR]) {
+ lx_debug("restarting system call due to signal interruption");
+ lx_do_syscall_restart = 0;
+ goto restart_syscall;
+ }
+
out:
/*
* For 32-bit, %eax holds the return code from the system call. For
@@ -962,7 +972,7 @@ lx_init(int argc, char *argv[], char *envp[])
lx_err_fatal("Unable to initialize thread-specific exit "
"context: %s", strerror(errno));
- if (lx_tsd.lxtsd_exit == 0) {
+ if (lx_tsd.lxtsd_exit == LX_ET_NONE) {
#if defined(_LP64)
/* Switch to Linux syscall mode */
(void) syscall(SYS_brand, B_CLR_NTV_SYSC_FLAG);
@@ -978,17 +988,36 @@ lx_init(int argc, char *argv[], char *envp[])
* exit_group() system call. In turn the brand library did a
* setcontext() to jump to the thread context state we saved above.
*/
- if (lx_tsd.lxtsd_exit == 1)
- thr_exit((void *)(long)lx_tsd.lxtsd_exit_status);
- else
- exit(lx_tsd.lxtsd_exit_status);
-
- assert(0);
-
+ lx_exit_common(lx_tsd.lxtsd_exit, lx_tsd.lxtsd_exit_status);
/*NOTREACHED*/
return (0);
}
+void
+lx_exit_common(lx_exit_type_t exit_type, uintptr_t exit_value)
+{
+ int ev = 0xff & exit_value;
+
+ switch (exit_type) {
+ case LX_ET_EXIT:
+ /*
+ * The native thread return value is never seen so we pass
+ * NULL.
+ */
+ thr_exit(NULL);
+ break;
+
+ case LX_ET_EXIT_GROUP:
+ exit(ev);
+ break;
+
+ default:
+ abort();
+ }
+
+ abort();
+}
+
/*
* Walk back through the stack until we find the lx_emulate() frame.
*/
diff --git a/usr/src/lib/brand/lx/lx_brand/common/misc.c b/usr/src/lib/brand/lx/lx_brand/common/misc.c
index f60f3f290f..750af869a4 100644
--- a/usr/src/lib/brand/lx/lx_brand/common/misc.c
+++ b/usr/src/lib/brand/lx/lx_brand/common/misc.c
@@ -572,8 +572,6 @@ lx_execve(uintptr_t p1, uintptr_t p2, uintptr_t p3)
if (argv == NULL)
argv = nullist;
- lx_ptrace_stop_if_option(LX_PTRACE_O_TRACEEXEC, B_FALSE, 0);
-
/*
* Emulate PR_SET_KEEPCAPS which is reset on execve. If this is not done
* the emulated capabilities could be reduced more than expected.
diff --git a/usr/src/lib/brand/lx/lx_brand/common/ptrace.c b/usr/src/lib/brand/lx/lx_brand/common/ptrace.c
index 2efc64a43e..174dbe8c19 100644
--- a/usr/src/lib/brand/lx/lx_brand/common/ptrace.c
+++ b/usr/src/lib/brand/lx/lx_brand/common/ptrace.c
@@ -51,82 +51,17 @@
#include <ieeefp.h>
#include <assert.h>
#include <libintl.h>
+#include <lx_syscall.h>
/*
- * Linux ptrace compatibility.
- *
- * The brand support for ptrace(2) is built on top of the Solaris /proc
- * interfaces, mounted at /native/proc in the zone. This gets quite
- * complicated due to the way ptrace works and the Solaris realization of the
- * Linux threading model.
- *
- * ptrace can only interact with a process if we are tracing it, and it is
- * currently stopped. There are two ways a process can begin tracing another
- * process:
- *
- * PTRACE_TRACEME
- *
- * A child process can use PTRACE_TRACEME to indicate that it wants to be
- * traced by the parent. This sets the ptrace compatibility flag in /proc
- * which causes ths ptrace consumer to be notified through the wait(2)
- * system call of events of interest. PTRACE_TRACEME is typically used by
- * the debugger by forking a process, using PTRACE_TRACEME, and finally
- * doing an exec of the specified program.
- *
- *
- * PTRACE_ATTACH
- *
- * We can attach to a process using PTRACE_ATTACH. This is considerably
- * more complicated than the previous case. On Linux, the traced process is
- * effectively reparented to the ptrace consumer so that event notification
- * can go through the normal wait(2) system call. Solaris has no such
- * ability to reparent a process (nor should it) so some trickery was
- * required.
- *
- * When the ptrace consumer uses PTRACE_ATTACH it forks a monitor child
- * process. The monitor enables the /proc ptrace flag for itself and uses
- * the native /proc mechanisms to observe the traced process and wait for
- * events of interest. When the traced process stops, the monitor process
- * sends itself a SIGTRAP thus rousting its parent process (the ptrace
- * consumer) out of wait(2). We then translate the process id and status
- * code from wait(2) to those of the traced process.
- *
- * To detach from the process we just have to clean up tracing flags and
- * clean up the monitor.
- *
- * ptrace can only interact with a process if we have traced it, and it is
- * currently stopped (see is_traced()). For threads, there's no way to
- * distinguish whether ptrace() has been called for all threads or some
- * subset. Since most clients will be tracing all threads, and erroneously
- * allowing ptrace to access a non-traced thread is non-fatal (or at least
- * would be fatal on linux), we ignore this aspect of the problem.
+ * 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.
*/
-#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
-
/* execve syscall numbers for 64-bit vs. 32-bit */
#if defined(_LP64)
-#define LX_SYS_execve 520
+#define LX_SYS_execve 59
#else
#define LX_SYS_execve 11
#endif
@@ -237,22 +172,12 @@ typedef struct lx_user {
int lxu_debugreg[8];
} lx_user_t;
-typedef struct ptrace_monitor_map {
- struct ptrace_monitor_map *pmm_next; /* next pointer */
- pid_t pmm_monitor; /* monitor child process */
- pid_t pmm_target; /* traced Linux pid */
- pid_t pmm_pid; /* Solaris pid */
- lwpid_t pmm_lwpid; /* Solaris lwpid */
- uint_t pmm_exiting; /* detached */
-} ptrace_monitor_map_t;
-
typedef struct ptrace_state_map {
struct ptrace_state_map *psm_next; /* next pointer */
pid_t psm_pid; /* Solaris pid */
uintptr_t psm_debugreg[8]; /* debug registers */
} ptrace_state_map_t;
-static ptrace_monitor_map_t *ptrace_monitor_map = NULL;
static ptrace_state_map_t *ptrace_state_map = NULL;
static mutex_t ptrace_map_mtx = DEFAULTMUTEX;
@@ -260,6 +185,8 @@ extern void *_START_;
static sigset_t blockable_sigs;
+static long lx_ptrace_kernel(int, pid_t, uintptr_t, uintptr_t);
+
void
lx_ptrace_init(void)
{
@@ -298,24 +225,6 @@ open_lwpfile(pid_t pid, lwpid_t lwpid, int mode, const char *name)
}
static int
-get_status(pid_t pid, pstatus_t *psp)
-{
- int fd;
-
- if ((fd = open_procfile(pid, O_RDONLY, "status")) < 0)
- return (-ESRCH);
-
- if (read(fd, psp, sizeof (pstatus_t)) != sizeof (pstatus_t)) {
- (void) close(fd);
- return (-EIO);
- }
-
- (void) close(fd);
-
- return (0);
-}
-
-static int
get_lwpstatus(pid_t pid, lwpid_t lwpid, lwpstatus_t *lsp)
{
int fd;
@@ -869,22 +778,6 @@ debug_registers(pid_t pid)
return (p != NULL? p->psm_debugreg : NULL);
}
-static void
-free_debug_registers(pid_t pid)
-{
- ptrace_state_map_t **pp;
- ptrace_state_map_t *p;
-
- /* ASSERT(MUTEX_HELD(&ptrace_map_mtx) */
- for (pp = &ptrace_state_map; (p = *pp) != NULL; pp = &p->psm_next) {
- if (p->psm_pid == pid) {
- *pp = p->psm_next;
- free(p);
- break;
- }
- }
-}
-
static int
setup_watchpoints(pid_t pid, uintptr_t *debugreg)
{
@@ -952,156 +845,33 @@ setup_watchpoints(pid_t pid, uintptr_t *debugreg)
}
/*
- * Returns TRUE if the process is traced, FALSE otherwise. This is only true
- * if the process is currently stopped, and has been traced using
- * PTRACE_TRACEME, PTRACE_ATTACH or one of the Linux-specific trace options.
+ * Returns B_TRUE if the target LWP, identified by its Linux pid, is traced by
+ * this LWP and is waiting in "ptrace-stop". Returns B_FALSE otherwise.
*/
-static int
-is_traced(pid_t pid)
+static boolean_t
+is_ptrace_stopped(pid_t lxpid)
{
- ptrace_monitor_map_t *p;
- pstatus_t status;
- uint_t curr_opts;
- pid_t mypid;
+ ulong_t dummy;
/*
- * First get the stop options since that is an indication that the
- * process is being traced.
+ * We attempt a PTRACE_GETEVENTMSG request to determine if the tracee
+ * is stopped appropriately. As we are not in the kernel, this is not
+ * an atomic check; the process is not guaranteed to remain stopped
+ * once we have dropped the locks protecting that state and left the
+ * kernel.
*/
- if (syscall(SYS_brand, B_PTRACE_EXT_OPTS, B_PTRACE_EXT_OPTS_GET, pid,
- &curr_opts) != 0)
- return (0);
-
- mypid = getpid();
-
- if (get_status(pid, &status) != 0)
- return (0);
-
- /*
- * When we look to see if we are tracing a process we have to take the
- * PTRACE_SETOPTIONS handling into account. In particular, if we are
- * tracing with PTRACE_O_TRACEFORK, etc. then we may be dealing with
- * the child of a child that we started tracing. We can determine this
- * by checking the EMUL_PTRACE_IS_TRACED flag and checking the parent
- * of the parent. We cannot check for the presence of the options since
- * those will be cleared during the process of detaching from a tracee.
- */
- if (curr_opts & EMUL_PTRACE_IS_TRACED && status.pr_ppid != mypid) {
- pstatus_t par_status;
- pid_t chkpid = status.pr_ppid;
-
- if (get_status(status.pr_ppid, &par_status) == 0) {
- chkpid = par_status.pr_ppid;
- } else {
- /* parent is gone, re-get our ppid */
- if (get_status(pid, &par_status) == 0)
- chkpid = par_status.pr_ppid;
- }
-
- if (chkpid == mypid)
- return (1);
+ if (lx_ptrace_kernel(LX_PTRACE_GETEVENTMSG, lxpid, NULL,
+ (uintptr_t)&dummy) == 0) {
+ return (B_TRUE);
}
- if ((status.pr_flags & PR_PTRACE ||
- curr_opts & EMUL_PTRACE_IS_TRACED) &&
- (status.pr_ppid == mypid) &&
- (status.pr_lwp.pr_flags & PR_ISTOP))
- return (1);
-
- (void) mutex_lock(&ptrace_map_mtx);
- for (p = ptrace_monitor_map; p != NULL; p = p->pmm_next) {
- if (p->pmm_target == pid) {
- (void) mutex_unlock(&ptrace_map_mtx);
- return (1);
- }
- }
- (void) mutex_unlock(&ptrace_map_mtx);
-
- return (0);
-}
-
-static int
-ptrace_trace_common(int fd)
-{
- struct {
- long cmd;
- union {
- long flags;
- sigset_t signals;
- fltset_t faults;
- } arg;
- } ctl;
- size_t size;
-
- ctl.cmd = PCSTRACE;
- prfillset(&ctl.arg.signals);
- size = sizeof (long) + sizeof (sigset_t);
- if (write(fd, &ctl, size) != size)
- return (-1);
-
- ctl.cmd = PCSFAULT;
- premptyset(&ctl.arg.faults);
- size = sizeof (long) + sizeof (fltset_t);
- if (write(fd, &ctl, size) != size)
- return (-1);
-
- ctl.cmd = PCUNSET;
- ctl.arg.flags = PR_FORK;
- size = sizeof (long) + sizeof (long);
- if (write(fd, &ctl, size) != size)
- return (-1);
-
- return (0);
-}
-
-/*
- * Notify that parent that we wish to be traced. This is the equivalent of:
- *
- * 1. Stop on all signals, and nothing else
- * 2. Turn off inherit-on-fork flag
- * 3. Set ptrace compatible flag
- *
- * If we are not the main thread, then the client is trying to request behavior
- * by which one of its own thread is to be traced. We don't support this mode
- * of operation.
- */
-static int
-ptrace_traceme(void)
-{
- int fd, ret;
- int error;
- long ctl[2];
- pstatus_t status;
- pid_t pid = getpid();
-
- if (_lwp_self() != 1) {
- lx_unsupported("thread %d calling PTRACE_TRACEME is "
- "unsupported", _lwp_self());
- return (-ENOTSUP);
- }
-
- if ((ret = get_status(pid, &status)) != 0)
- return (ret);
-
/*
- * Why would a process try to do this twice? I'm not sure, but there's
- * a conformance test which wants this to fail just so.
+ * This call should only fail with ESRCH, which tells us that the
+ * a tracee with that pid was not found in the stopped condition.
*/
- if (status.pr_flags & PR_PTRACE)
- return (-EPERM);
-
- if ((fd = open_procfile(pid, O_WRONLY, "ctl")) < 0)
- return (-errno);
+ assert(errno == ESRCH);
- ctl[0] = PCSET;
- ctl[1] = PR_PTRACE;
- error = 0;
- if (write(fd, ctl, sizeof (ctl)) != sizeof (ctl) ||
- ptrace_trace_common(fd) != 0)
- error = -errno;
-
- (void) close(fd);
- return (error);
+ return (B_FALSE);
}
/*
@@ -1114,9 +884,6 @@ ptrace_peek(pid_t pid, uintptr_t addr, long *ret)
int fd;
long data;
- if (!is_traced(pid))
- return (-ESRCH);
-
if ((fd = open_procfile(pid, O_RDONLY, "as")) < 0)
return (-ESRCH);
@@ -1143,9 +910,6 @@ ptrace_peek_user(pid_t pid, lwpid_t lwpid, uintptr_t off, int *ret)
uintptr_t *debugreg;
int dreg;
- if (!is_traced(pid))
- return (-ESRCH);
-
/*
* The offset specified by the user is an offset into the Linux
* user structure (seriously). Rather than constructing a full
@@ -1239,9 +1003,6 @@ ptrace_poke(pid_t pid, uintptr_t addr, int data)
{
int fd;
- if (!is_traced(pid))
- return (-ESRCH);
-
if (addr & 0x3)
return (-EINVAL);
@@ -1265,9 +1026,6 @@ ptrace_poke_user(pid_t pid, lwpid_t lwpid, uintptr_t off, int data)
uintptr_t *debugreg;
int dreg;
- if (!is_traced(pid))
- return (-ESRCH);
-
if (off & 0x3)
return (-EINVAL);
@@ -1300,187 +1058,13 @@ ptrace_poke_user(pid_t pid, lwpid_t lwpid, uintptr_t off, int data)
}
static int
-ptrace_cont_common(int fd, int sig, int run, int step)
-{
- long ctl[1 + 1 + sizeof (siginfo_t) / sizeof (long) + 2];
- long *ctlp = ctl;
- size_t size;
-
- assert(0 <= sig && sig <= LX_NSIG);
- assert(!step || run);
-
- /*
- * Clear the current signal.
- */
- *ctlp++ = PCCSIG;
-
- /*
- * Send a signal if one was specified.
- */
- if (sig != 0 && sig != LX_SIGSTOP) {
- siginfo_t *infop;
-
- *ctlp++ = PCSSIG;
- infop = (siginfo_t *)ctlp;
- bzero(infop, sizeof (siginfo_t));
- infop->si_signo = ltos_signo[sig];
-
- ctlp += sizeof (siginfo_t) / sizeof (long);
- }
-
- /*
- * If run is true, set the lwp running.
- */
- if (run) {
- *ctlp++ = PCRUN;
- *ctlp++ = step ? PRSTEP : 0;
- }
-
- size = (char *)ctlp - (char *)&ctl[0];
- assert(size <= sizeof (ctl));
-
- if (write(fd, ctl, size) != size) {
- lx_debug("failed to continue %s", strerror(errno));
- return (-EIO);
- }
-
- return (0);
-}
-
-static int
-ptrace_cont_monitor(ptrace_monitor_map_t *p)
-{
- long ctl[2];
- int fd;
-
- fd = open_procfile(p->pmm_monitor, O_WRONLY, "ctl");
- if (fd < 0) {
- lx_debug("failed to open monitor ctl %d",
- errno);
- return (-EIO);
- }
-
- ctl[0] = PCRUN;
- ctl[1] = PRCSIG;
- if (write(fd, ctl, sizeof (ctl)) != sizeof (ctl)) {
- (void) close(fd);
- return (-EIO);
- }
-
- (void) close(fd);
-
- return (0);
-}
-
-static int
-ptrace_cont(pid_t lxpid, pid_t pid, lwpid_t lwpid, int sig, int step)
-{
- ptrace_monitor_map_t *p;
- uintptr_t *debugreg;
- int fd, ret;
-
- if (!is_traced(pid))
- return (-ESRCH);
-
- if (sig < 0 || sig > LX_NSIG)
- return (-EINVAL);
-
- if ((fd = open_lwpfile(pid, lwpid, O_WRONLY, "lwpctl")) < 0)
- return (-ESRCH);
-
- if ((ret = ptrace_cont_common(fd, sig, 1, step)) != 0) {
- (void) close(fd);
- return (ret);
- }
-
- (void) close(fd);
-
- /* kludge: use debugreg[4] to remember the single-step flag */
- if ((debugreg = debug_registers(pid)) != NULL)
- debugreg[4] = step;
-
- /*
- * Check for a monitor and get it moving if we find it. If any of the
- * /proc operations fail, we're kind of sunk so just return an error.
- */
- (void) mutex_lock(&ptrace_map_mtx);
- for (p = ptrace_monitor_map; p != NULL; p = p->pmm_next) {
- if (p->pmm_target == lxpid) {
- if ((ret = ptrace_cont_monitor(p)) != 0)
- return (ret);
- break;
- }
- }
- (void) mutex_unlock(&ptrace_map_mtx);
-
- return (0);
-}
-
-/*
- * If a monitor exists for this traced process, dispose of it.
- * First turn off its ptrace flag so we won't be notified of its
- * impending demise. We ignore errors for this step since they
- * indicate only that the monitor has been damaged due to pilot
- * error. Then kill the monitor, and wait for it. If the wait
- * succeeds we can dispose of the corpse, otherwise another thread's
- * wait call has collected it and we need to set a flag in the
- * structure so that if can be picked up in wait.
- */
-static void
-monitor_kill(pid_t lxpid, pid_t pid)
-{
- ptrace_monitor_map_t *p, **pp;
- pid_t mpid;
- int fd;
- long ctl[2];
-
- (void) mutex_lock(&ptrace_map_mtx);
- free_debug_registers(pid);
- for (pp = &ptrace_monitor_map; (p = *pp) != NULL; pp = &p->pmm_next) {
- if (p->pmm_target == lxpid) {
- mpid = p->pmm_monitor;
- if ((fd = open_procfile(mpid, O_WRONLY, "ctl")) >= 0) {
- ctl[0] = PCUNSET;
- ctl[1] = PR_PTRACE;
- (void) write(fd, ctl, sizeof (ctl));
- (void) close(fd);
- }
-
- (void) kill(mpid, SIGKILL);
-
- if (waitpid(mpid, NULL, 0) == mpid) {
- *pp = p->pmm_next;
- free(p);
- } else {
- p->pmm_exiting = 1;
- }
-
- break;
- }
- }
- (void) mutex_unlock(&ptrace_map_mtx);
-}
-
-static int
-ptrace_kill(pid_t lxpid, pid_t pid)
+ptrace_kill(pid_t pid)
{
int ret;
- if (!is_traced(pid))
- return (-ESRCH);
-
ret = kill(pid, SIGKILL);
- /* kill off the monitor process, if any */
- monitor_kill(lxpid, pid);
-
- return (ret);
-}
-
-static int
-ptrace_step(pid_t lxpid, pid_t pid, lwpid_t lwpid, int sig)
-{
- return (ptrace_cont(lxpid, pid, lwpid, sig, 1));
+ return (ret == 0 ? ret : -errno);
}
static int
@@ -1489,9 +1073,6 @@ ptrace_getregs(pid_t pid, lwpid_t lwpid, uintptr_t addr)
lx_user_regs_t regs;
int ret;
- if (!is_traced(pid))
- return (-ESRCH);
-
if ((ret = getregs(pid, lwpid, &regs)) != 0)
return (ret);
@@ -1506,9 +1087,6 @@ ptrace_setregs(pid_t pid, lwpid_t lwpid, uintptr_t addr)
{
lx_user_regs_t regs;
- if (!is_traced(pid))
- return (-ESRCH);
-
if (uucopy((void *)addr, &regs, sizeof (regs)) != 0)
return (-errno);
@@ -1521,9 +1099,6 @@ ptrace_getfpregs(pid_t pid, lwpid_t lwpid, uintptr_t addr)
lx_user_fpregs_t regs;
int ret;
- if (!is_traced(pid))
- return (-ESRCH);
-
if ((ret = getfpregs(pid, lwpid, &regs)) != 0)
return (ret);
@@ -1538,9 +1113,6 @@ ptrace_setfpregs(pid_t pid, lwpid_t lwpid, uintptr_t addr)
{
lx_user_fpregs_t regs;
- if (!is_traced(pid))
- return (-ESRCH);
-
if (uucopy((void *)addr, &regs, sizeof (regs)) != 0)
return (-errno);
@@ -1553,9 +1125,6 @@ ptrace_getfpxregs(pid_t pid, lwpid_t lwpid, uintptr_t addr)
lx_user_fpxregs_t regs;
int ret;
- if (!is_traced(pid))
- return (-ESRCH);
-
if ((ret = getfpxregs(pid, lwpid, &regs)) != 0)
return (ret);
@@ -1570,412 +1139,124 @@ ptrace_setfpxregs(pid_t pid, lwpid_t lwpid, uintptr_t addr)
{
lx_user_fpxregs_t regs;
- if (!is_traced(pid))
- return (-ESRCH);
-
if (uucopy((void *)addr, &regs, sizeof (regs)) != 0)
return (-errno);
return (setfpxregs(pid, lwpid, &regs));
}
-static void __NORETURN
-ptrace_monitor(int fd)
+void
+lx_ptrace_stop_if_option(int option, boolean_t child, ulong_t msg)
{
- struct {
- long cmd;
- union {
- long flags;
- sigset_t signals;
- fltset_t faults;
- } arg;
- } ctl;
- size_t size;
- int monfd;
- int rv;
-
- monfd = open_procfile(getpid(), O_WRONLY, "ctl");
-
- ctl.cmd = PCSTRACE; /* trace only SIGTRAP */
- premptyset(&ctl.arg.signals);
- praddset(&ctl.arg.signals, SIGTRAP);
- size = sizeof (long) + sizeof (sigset_t);
- (void) write(monfd, &ctl, size); /* can't fail */
-
- ctl.cmd = PCSFAULT;
- premptyset(&ctl.arg.faults);
- size = sizeof (long) + sizeof (fltset_t);
- (void) write(monfd, &ctl, size); /* can't fail */
-
- ctl.cmd = PCUNSET;
- ctl.arg.flags = PR_FORK;
- size = sizeof (long) + sizeof (long);
- (void) write(monfd, &ctl, size); /* can't fail */
-
- ctl.cmd = PCSET; /* wait()able by the parent */
- ctl.arg.flags = PR_PTRACE;
- size = sizeof (long) + sizeof (long);
- (void) write(monfd, &ctl, size); /* can't fail */
-
- (void) close(monfd);
-
- ctl.cmd = PCWSTOP;
- size = sizeof (long);
-
- for (;;) {
- /*
- * Wait for the traced process to stop.
- */
- if (write(fd, &ctl, size) != size) {
- rv = (errno == ENOENT)? 0 : 1;
- lx_debug("monitor failed to wait for LWP to stop: %s",
+ /*
+ * 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)", option,
+ child ? "TRUE [child]" : "FALSE [parent]", msg);
+ if (syscall(SYS_brand, B_PTRACE_STOP_FOR_OPT, option, child,
+ msg) != 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));
- _exit(rv);
}
-
- lx_debug("monitor caught traced LWP");
-
- /*
- * Pull the ptrace trigger by sending ourself a SIGTRAP. This
- * will cause this, the monitor process, to stop which will
- * cause the parent's waitid(2) call to return this process
- * id. In lx_wait(), we remap the monitor process's pid and
- * status to those of the traced LWP. When the parent process
- * uses ptrace to resume the traced LWP, it will additionally
- * restart this process.
- */
- (void) _lwp_kill(_lwp_self(), SIGTRAP);
-
- lx_debug("monitor was resumed");
}
}
-static int
-ptrace_attach_common(int fd, pid_t lxpid, pid_t pid, lwpid_t lwpid, int run)
+/*
+ * 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)
{
- pid_t child;
- ptrace_monitor_map_t *p;
- sigset_t unblock;
- pstatus_t status;
- long ctl[1 + sizeof (sysset_t) / sizeof (long) + 2];
- long *ctlp = ctl;
- size_t size;
- sysset_t *sysp;
- int ret;
-
- /*
- * We're going to need this structure so better to fail now before its
- * too late to turn back.
- */
- if ((p = malloc(sizeof (ptrace_monitor_map_t))) == NULL)
- return (-EIO);
-
- if ((ret = get_status(pid, &status)) != 0) {
- free(p);
- return (ret);
+ lx_debug("lx_ptrace_clone_begin(%d, %sPTRACE_CLONE)", option,
+ inherit_flag ? "" : "!");
+ if (syscall(SYS_brand, B_PTRACE_CLONE_BEGIN, option,
+ inherit_flag) != 0) {
+ lx_err_fatal("B_PTRACE_CLONE_BEGIN failed: %s",
+ strerror(errno));
}
-
- /*
- * If this process is already traced, bail.
- */
- if (status.pr_flags & PR_PTRACE) {
- free(p);
- return (-EPERM);
- }
-
- /*
- * Turn on the appropriate tracing flags. It's exceedingly unlikely
- * that this operation will fail; any failure would probably be due
- * to another /proc consumer mucking around.
- */
- if (ptrace_trace_common(fd) != 0) {
- free(p);
- return (-EIO);
- }
-
- /*
- * Native ptrace automatically catches processes when they exec so we
- * have to do that explicitly here.
- */
- *ctlp++ = PCSEXIT;
- sysp = (sysset_t *)ctlp;
- ctlp += sizeof (sysset_t) / sizeof (long);
- premptyset(sysp);
- praddset(sysp, SYS_execve);
- if (run) {
- *ctlp++ = PCRUN;
- *ctlp++ = 0;
- }
-
- size = (char *)ctlp - (char *)&ctl[0];
-
- if (write(fd, ctl, size) != size) {
- free(p);
- return (-EIO);
- }
-
- /*
- * Spawn the monitor proceses to notify this process of events of
- * interest in the traced process. We block signals here both so
- * we're not interrupted during this operation and so that the
- * monitor process doesn't accept signals.
- */
- (void) sigprocmask(SIG_BLOCK, &blockable_sigs, &unblock);
- if ((child = fork1()) == 0)
- ptrace_monitor(fd);
- (void) sigprocmask(SIG_SETMASK, &unblock, NULL);
-
- if (child == -1) {
- lx_debug("failed to fork monitor process\n");
- free(p);
- return (-EIO);
- }
-
- p->pmm_monitor = child;
- p->pmm_target = lxpid;
- p->pmm_pid = pid;
- p->pmm_lwpid = lwpid;
- p->pmm_exiting = 0;
-
- (void) mutex_lock(&ptrace_map_mtx);
- p->pmm_next = ptrace_monitor_map;
- ptrace_monitor_map = p;
- (void) mutex_unlock(&ptrace_map_mtx);
-
- return (0);
}
-static int
-ptrace_attach(pid_t lxpid, pid_t pid, lwpid_t lwpid)
+static long
+lx_ptrace_kernel(int ptrace_op, pid_t lxpid, uintptr_t addr, uintptr_t data)
{
- int fd, ret;
- long ctl;
+ int ret;
/*
- * Linux doesn't let you trace process 1 -- go figure.
+ * Call into the in-kernel ptrace(2) emulation code.
*/
- if (lxpid == 1)
- return (-EPERM);
-
- if ((fd = open_lwpfile(pid, lwpid, O_WRONLY | O_EXCL, "lwpctl")) < 0)
- return (errno == EBUSY ? -EPERM : -ESRCH);
-
- ctl = PCSTOP;
- if (write(fd, &ctl, sizeof (ctl)) != sizeof (ctl)) {
- lx_err("failed to stop %d/%d\n", (int)pid, (int)lwpid);
- assert(0);
+ lx_debug("revectoring to B_PTRACE_KERNEL(%d, %d, %p, %p)", ptrace_op,
+ lxpid, addr, data);
+ ret = syscall(SYS_brand, B_PTRACE_KERNEL, ptrace_op, lxpid, addr,
+ data);
+ if (ret == 0) {
+ lx_debug("\t= %d", ret);
+ } else {
+ lx_debug("\t= %d (%s)", ret, strerror(errno));
}
- ret = ptrace_attach_common(fd, lxpid, pid, lwpid, 0);
-
- (void) close(fd);
-
- return (ret);
+ return (ret == 0 ? ret : -errno);
}
-static int
-ptrace_detach(pid_t lxpid, pid_t pid, lwpid_t lwpid, int sig)
+long
+lx_ptrace(uintptr_t p1, uintptr_t p2, uintptr_t p3, uintptr_t p4)
{
- long ctl[2];
- int fd, ret;
-
- if (!is_traced(pid))
- return (-ESRCH);
-
- if (sig < 0 || sig > LX_NSIG)
- return (-EINVAL);
-
- if ((fd = open_lwpfile(pid, lwpid, O_WRONLY, "lwpctl")) < 0)
- return (-ESRCH);
-
- if (syscall(SYS_brand, B_PTRACE_EXT_OPTS, B_PTRACE_DETACH, pid, 0) != 0)
- return (-ESRCH);
+ int ptrace_op = (int)p1;
+ pid_t pid, lxpid = (pid_t)p2;
+ lwpid_t lwpid;
/*
- * The /proc ptrace flag may not be set, but we clear it
- * unconditionally since doing so doesn't hurt anything.
+ * Some PTRACE_* requests are emulated entirely in the kernel.
*/
- ctl[0] = PCUNSET;
- ctl[1] = PR_PTRACE;
- if (write(fd, ctl, sizeof (ctl)) != sizeof (ctl)) {
- (void) close(fd);
- return (-EIO);
- }
-
+ switch (ptrace_op) {
/*
- * Clear the brand-specific system call tracing flag to ensure that
- * the target doesn't stop unexpectedly some time in the future.
+ * PTRACE_TRACEME and PTRACE_ATTACH operations induce the tracing of
+ * one LWP by another. The target LWP must not be traced already.
+ * Both `data' and `addr' are ignored in both cases.
*/
- if ((ret = syscall(SYS_brand, B_PTRACE_SYSCALL, pid, lwpid, 0)) != 0) {
- (void) close(fd);
- return (-ret);
- }
+ case LX_PTRACE_TRACEME:
+ return (lx_ptrace_kernel(ptrace_op, 0, 0, 0));
- /* kill off the monitor process, if any */
- monitor_kill(lxpid, pid);
+ case LX_PTRACE_ATTACH:
+ return (lx_ptrace_kernel(ptrace_op, lxpid, 0, 0));
/*
- * Turn on the run-on-last-close flag so that all tracing flags will be
- * cleared when we close the control file descriptor.
+ * PTRACE_DETACH, PTRACE_SYSCALL, PTRACE_SINGLESTEP and PTRACE_CONT
+ * are all restarting actions. They are only allowed when attached
+ * to the target LWP and when that target LWP is in a "ptrace-stop"
+ * condition.
*/
- ctl[0] = PCSET;
- ctl[1] = PR_RLC;
- if (write(fd, ctl, sizeof (ctl)) != sizeof (ctl)) {
- (void) close(fd);
- return (-EIO);
+ case LX_PTRACE_DETACH:
+ case LX_PTRACE_SYSCALL:
+ case LX_PTRACE_CONT:
+ case LX_PTRACE_SINGLESTEP:
+ /*
+ * These actions also require the LWP to be traced and stopped, but do
+ * not restart the target LWP.
+ */
+ case LX_PTRACE_SETOPTIONS:
+ case LX_PTRACE_GETEVENTMSG:
+ return (lx_ptrace_kernel(ptrace_op, lxpid, p3, p4));
}
/*
- * Clear the current signal (if any) and possibly send the traced
- * process a new signal.
+ * The rest of the emulated PTRACE_* actions are emulated in userland.
+ * They require the target LWP to be traced and in currently
+ * "ptrace-stop", but do not subsequently restart the target LWP.
*/
- ret = ptrace_cont_common(fd, sig, 0, 0);
-
- (void) close(fd);
-
- return (ret);
-}
-
-static int
-ptrace_syscall(pid_t lxpid, pid_t pid, lwpid_t lwpid, int sig)
-{
- int ret;
-
- if (!is_traced(pid))
+ if (lx_lpid_to_spair(lxpid, &pid, &lwpid) < 0 ||
+ !is_ptrace_stopped(lxpid)) {
return (-ESRCH);
-
- if ((ret = syscall(SYS_brand, B_PTRACE_SYSCALL, pid, lwpid, 1)) != 0)
- return (-ret);
-
- return (ptrace_cont(lxpid, pid, lwpid, sig, 0));
-}
-
-static int
-ptrace_setoptions(pid_t pid, int options)
-{
- int ret;
- int fd;
- int error = 0;
- struct {
- long cmd;
- union {
- long flags;
- sigset_t signals;
- fltset_t faults;
- } arg;
- } ctl;
- size_t size;
- pstatus_t status;
-
- if ((ret = get_status(pid, &status)) != 0)
- return (ret);
-
- if ((fd = open_procfile(pid, O_WRONLY, "ctl")) < 0)
- return (-errno);
-
- /* since we're doing option tracing now, only catch sigtrap */
- ctl.cmd = PCSTRACE;
- premptyset(&ctl.arg.signals);
- praddset(&ctl.arg.signals, SIGTRAP);
- size = sizeof (long) + sizeof (sigset_t);
- if (write(fd, &ctl, size) != size) {
- error = -errno;
- } else {
- /*
- * If we're tracing fork, set inherit-on-fork, otherwise clear
- * it.
- */
- if (options & LX_PTRACE_O_TRACEFORK) {
- ctl.cmd = PCSET;
- } else {
- ctl.cmd = PCUNSET;
- }
- ctl.arg.flags = PR_FORK;
- size = sizeof (long) + sizeof (long);
- if (write(fd, &ctl, size) != size)
- error = -errno;
}
- (void) close(fd);
-
- if (error != 0)
- return (error);
-
- ret = syscall(SYS_brand, B_PTRACE_EXT_OPTS, B_PTRACE_EXT_OPTS_SET, pid,
- options);
-
- return ((ret != 0) ? -errno : 0);
-}
-
-void
-lx_ptrace_stop_if_option(int option, boolean_t child, ulong_t msg)
-{
- pid_t pid;
- uint_t curr_opts;
-
- pid = getpid();
- if (pid == 1)
- pid = zoneinit_pid;
-
- /* first we have to see if the stop option is set for this process */
- if (syscall(SYS_brand, B_PTRACE_EXT_OPTS, B_PTRACE_EXT_OPTS_GET, pid,
- &curr_opts) != 0)
- return;
-
- if (child) {
- /*
- * If we just forked/cloned, then the trace flags only carry
- * over to the child if the specific flag was enabled on the
- * parent. For example, if only TRACEFORK is enabled and we
- * clone, then we must clear the trace flags. If TRACEFORK is
- * enabled and we fork, then we keep the flags.
- */
- if (option == LX_PTRACE_O_TRACECLONE ||
- option == LX_PTRACE_O_TRACEFORK ||
- option == LX_PTRACE_O_TRACEVFORK) {
-
- if ((curr_opts & option) == 0)
- (void) syscall(SYS_brand, B_PTRACE_EXT_OPTS,
- B_PTRACE_EXT_OPTS_SET, pid, 0);
-
- /*
- * Since we know we're the child we have to modify how
- * we stop. Set the emulation's child flag in the
- * option.
- */
- option |= EMUL_PTRACE_O_CHILD;
- }
- }
-
- /* now if the option is/was set, this brand call will stop us */
- if (curr_opts & option)
- (void) syscall(SYS_brand, B_PTRACE_STOP_FOR_OPT, option, msg);
-}
-
-static int
-ptrace_geteventmsg(pid_t pid, ulong_t *msgp)
-{
- int ret;
-
- ret = syscall(SYS_brand, B_PTRACE_GETEVENTMSG, pid, msgp);
-
- return ((ret != 0) ? -errno : 0);
-}
-
-long
-lx_ptrace(uintptr_t p1, uintptr_t p2, uintptr_t p3, uintptr_t p4)
-{
- pid_t pid, lxpid = (pid_t)p2;
- lwpid_t lwpid;
-
- if ((p1 != LX_PTRACE_TRACEME) &&
- (lx_lpid_to_spair(lxpid, &pid, &lwpid) < 0))
- return (-ESRCH);
-
- switch (p1) {
- case LX_PTRACE_TRACEME:
- return (ptrace_traceme());
-
+ switch (ptrace_op) {
case LX_PTRACE_PEEKTEXT:
case LX_PTRACE_PEEKDATA:
return (ptrace_peek(pid, p3, (long *)p4));
@@ -1990,14 +1271,8 @@ lx_ptrace(uintptr_t p1, uintptr_t p2, uintptr_t p3, uintptr_t p4)
case LX_PTRACE_POKEUSER:
return (ptrace_poke_user(pid, lwpid, p3, (int)p4));
- case LX_PTRACE_CONT:
- return (ptrace_cont(lxpid, pid, lwpid, (int)p4, 0));
-
case LX_PTRACE_KILL:
- return (ptrace_kill(lxpid, pid));
-
- case LX_PTRACE_SINGLESTEP:
- return (ptrace_step(lxpid, pid, lwpid, (int)p4));
+ return (ptrace_kill(pid));
case LX_PTRACE_GETREGS:
return (ptrace_getregs(pid, lwpid, p4));
@@ -2011,419 +1286,13 @@ lx_ptrace(uintptr_t p1, uintptr_t p2, uintptr_t p3, uintptr_t p4)
case LX_PTRACE_SETFPREGS:
return (ptrace_setfpregs(pid, lwpid, p4));
- case LX_PTRACE_ATTACH:
- return (ptrace_attach(lxpid, pid, lwpid));
-
- case LX_PTRACE_DETACH:
- return (ptrace_detach(lxpid, pid, lwpid, (int)p4));
-
case LX_PTRACE_GETFPXREGS:
return (ptrace_getfpxregs(pid, lwpid, p4));
case LX_PTRACE_SETFPXREGS:
return (ptrace_setfpxregs(pid, lwpid, p4));
- case LX_PTRACE_SYSCALL:
- return (ptrace_syscall(lxpid, pid, lwpid, (int)p4));
-
- case LX_PTRACE_SETOPTIONS:
- return (ptrace_setoptions(pid, (int)p4));
-
- case LX_PTRACE_GETEVENTMSG:
- return (ptrace_geteventmsg(pid, (ulong_t *)p4));
-
default:
return (-EINVAL);
}
}
-
-void
-lx_ptrace_fork(void)
-{
- /*
- * Send a special signal (that has no Linux equivalent) to indicate
- * that we're in this particularly special case. The signal will be
- * ignored by this process, but noticed by /proc consumers tracing
- * this process.
- */
- (void) _lwp_kill(_lwp_self(), SIGWAITING);
-}
-
-static void
-ptrace_catch_fork(pid_t pid, int monitor)
-{
- long ctl[14 + 2 * sizeof (sysset_t) / sizeof (long)];
- long *ctlp;
- sysset_t *sysp;
- size_t size;
- pstatus_t ps;
- pid_t child;
- int fd, err;
-
- /*
- * If any of this fails, we're really sunk since the child
- * will be stuck in the middle of lx_ptrace_fork().
- * Fortunately it's practically assured to succeed unless
- * something is seriously wrong on the system.
- */
- if ((fd = open_procfile(pid, O_WRONLY, "ctl")) < 0) {
- lx_debug("lx_catch_fork: failed to control %d",
- (int)pid);
- return;
- }
-
- /*
- * Turn off the /proc PR_PTRACE flag so the parent doesn't get
- * spurious wake ups while we're working our dark magic. Arrange to
- * catch the process when it exits from fork, and turn on the /proc
- * inherit-on-fork flag so we catcht the child as well. We then run
- * the process, wait for it to stop on the fork1(2) call and reset
- * the tracing flags to their original state.
- */
- ctlp = ctl;
- *ctlp++ = PCCSIG;
- if (!monitor) {
- *ctlp++ = PCUNSET;
- *ctlp++ = PR_PTRACE;
- }
- *ctlp++ = PCSET;
- *ctlp++ = PR_FORK;
- *ctlp++ = PCSEXIT;
- sysp = (sysset_t *)ctlp;
- ctlp += sizeof (sysset_t) / sizeof (long);
- premptyset(sysp);
- praddset(sysp, SYS_forksys); /* fork1() is forksys(0, 0) */
- *ctlp++ = PCRUN;
- *ctlp++ = 0;
- *ctlp++ = PCWSTOP;
- if (!monitor) {
- *ctlp++ = PCSET;
- *ctlp++ = PR_PTRACE;
- }
- *ctlp++ = PCUNSET;
- *ctlp++ = PR_FORK;
- *ctlp++ = PCSEXIT;
- sysp = (sysset_t *)ctlp;
- ctlp += sizeof (sysset_t) / sizeof (long);
- premptyset(sysp);
- if (monitor)
- praddset(sysp, SYS_execve);
-
- size = (char *)ctlp - (char *)&ctl[0];
- assert(size <= sizeof (ctl));
-
- if (write(fd, ctl, size) != size) {
- (void) close(fd);
- lx_debug("lx_catch_fork: failed to set %d running",
- (int)pid);
- return;
- }
-
- /*
- * Get the status so we can find the value returned from fork1() --
- * the child process's pid.
- */
- if (get_status(pid, &ps) != 0) {
- (void) close(fd);
- lx_debug("lx_catch_fork: failed to get status for %d",
- (int)pid);
- return;
- }
-
- child = (pid_t)ps.pr_lwp.pr_reg[R_R0];
-
- /*
- * We're done with the parent -- off you go.
- */
- ctl[0] = PCRUN;
- ctl[1] = 0;
- size = 2 * sizeof (long);
-
- if (write(fd, ctl, size) != size) {
- (void) close(fd);
- lx_debug("lx_catch_fork: failed to set %d running",
- (int)pid);
- return;
- }
-
- (void) close(fd);
-
- /*
- * If fork1(2) failed, we're done.
- */
- if (child < 0) {
- lx_debug("lx_catch_fork: fork1 failed");
- return;
- }
-
- /*
- * Now we need to screw with the child process.
- */
- if ((fd = open_lwpfile(child, 1, O_WRONLY, "lwpctl")) < 0) {
- lx_debug("lx_catch_fork: failed to control %d",
- (int)child);
- return;
- }
-
- ctlp = ctl;
- *ctlp++ = PCUNSET;
- *ctlp++ = PR_FORK;
- *ctlp++ = PCSEXIT;
- sysp = (sysset_t *)ctlp;
- ctlp += sizeof (sysset_t) / sizeof (long);
- premptyset(sysp);
- size = (char *)ctlp - (char *)&ctl[0];
-
- if (write(fd, ctl, size) != size) {
- (void) close(fd);
- lx_debug("lx_catch_fork: failed to clear trace flags for %d",
- (int)child);
- return;
- }
-
- /*
- * Now treat the child as though we had attached to it explicitly.
- */
- err = ptrace_attach_common(fd, child, child, 1, 1);
- assert(err == 0);
-
- (void) close(fd);
-}
-
-static void
-set_dr6(pid_t pid, siginfo_t *infop)
-{
- uintptr_t *debugreg;
- uintptr_t addr;
- uintptr_t base;
- size_t size = NULL;
- int dr7;
- int lrw;
- int i;
-
- if ((debugreg = debug_registers(pid)) == NULL)
- return;
-
- debugreg[6] = 0xffff0ff0; /* read as ones */
- switch (infop->si_code) {
- case TRAP_TRACE:
- debugreg[6] |= 0x4000; /* single-step */
- break;
- case TRAP_RWATCH:
- case TRAP_WWATCH:
- case TRAP_XWATCH:
- dr7 = debugreg[7];
- addr = (uintptr_t)infop->si_addr;
- for (i = 0; i < 4; i++) {
- if ((dr7 & (1 << (2 * i))) == 0) /* enabled? */
- continue;
- lrw = (dr7 >> (16 + (4 * i))) & 0xf;
- switch (lrw >> 2) { /* length */
- case 0: size = 1; break;
- case 1: size = 2; break;
- case 2: size = 8; break;
- case 3: size = 4; break;
- }
- base = debugreg[i];
- if (addr >= base && addr < base + size)
- debugreg[6] |= (1 << i);
- }
- /*
- * Were we also attempting a single-step?
- * (kludge: we use debugreg[4] for this flag.)
- */
- if (debugreg[4])
- debugreg[6] |= 0x4000;
- break;
- default:
- break;
- }
-}
-
-/*
- * This is called from the emulation of the wait4, waitpid and waitid system
- * calls to take into account:
- * - the monitor processes which we spawn to observe other processes from
- * ptrace_attach().
- * - the extended si_status result we can get when extended ptrace options
- * are enabled.
- */
-int
-lx_ptrace_wait(siginfo_t *infop)
-{
- ptrace_monitor_map_t *p, **pp;
- pid_t lxpid, pid = infop->si_pid;
- lwpid_t lwpid;
- int fd;
- pstatus_t status;
-
- /*
- * If the process observed by waitid(2) corresponds to the monitor
- * process for a traced thread, we need to rewhack the siginfo_t to
- * look like it came from the traced thread with the flags set
- * according to the current state.
- */
- (void) mutex_lock(&ptrace_map_mtx);
- for (pp = &ptrace_monitor_map; (p = *pp) != NULL; pp = &p->pmm_next) {
- if (p->pmm_monitor == pid) {
- assert(infop->si_code == CLD_EXITED ||
- infop->si_code == CLD_KILLED ||
- infop->si_code == CLD_DUMPED ||
- infop->si_code == CLD_TRAPPED);
- goto found;
- }
- }
- (void) mutex_unlock(&ptrace_map_mtx);
-
- if (infop->si_code == CLD_TRAPPED) {
- /*
- * If the traced process got a SIGWAITING, we must be in the
- * middle of a clone(2) with CLONE_PTRACE set.
- */
- if (infop->si_status == SIGWAITING) {
- ptrace_catch_fork(pid, 0);
- return (-1);
- }
-
- /*
- * If the traced process got a SIGTRAP then Linux ptrace
- * options might have been set, so setup the extended
- * si_status to contain the (possible) event. Note that
- * our definitions for the ptrace events (e.g.
- * LX_PTRACE_EVENT_FORK) is already shifted <<8 as documented
- * on the Linux ptrace(2) man page.
- */
- if (infop->si_status == SIGTRAP) {
- uint_t event;
-
- if (syscall(SYS_brand, B_PTRACE_EXT_OPTS,
- B_PTRACE_EXT_OPTS_EVT, pid, &event) == 0)
- infop->si_status |= event;
- }
- }
-
- if (get_status(pid, &status) == 0 &&
- (status.pr_lwp.pr_flags & PR_STOPPED) &&
- status.pr_lwp.pr_why == PR_SIGNALLED &&
- status.pr_lwp.pr_info.si_signo == SIGTRAP)
- set_dr6(pid, &status.pr_lwp.pr_info);
-
- return (0);
-
-found:
- /*
- * If the monitor is in the exiting state, ignore the event and free
- * the monitor structure if the monitor has exited. By returning -1 we
- * indicate to the caller that this was a spurious return from
- * waitid(2) and that it should ignore the result and try again.
- */
- if (p->pmm_exiting) {
- if (infop->si_code == CLD_EXITED ||
- infop->si_code == CLD_KILLED ||
- infop->si_code == CLD_DUMPED) {
- *pp = p->pmm_next;
- (void) mutex_unlock(&ptrace_map_mtx);
- free(p);
- }
- return (-1);
- }
-
- lxpid = p->pmm_target;
- pid = p->pmm_pid;
- lwpid = p->pmm_lwpid;
- (void) mutex_unlock(&ptrace_map_mtx);
-
- /*
- * If we can't find the traced process, kill off its monitor.
- */
- if ((fd = open_lwpfile(pid, lwpid, O_RDONLY, "lwpstatus")) < 0) {
- assert(errno == ENOENT);
- monitor_kill(lxpid, pid);
- infop->si_code = CLD_EXITED;
- infop->si_status = 0;
- infop->si_pid = lxpid;
- return (0);
- }
-
- if (read(fd, &status.pr_lwp, sizeof (status.pr_lwp)) !=
- sizeof (status.pr_lwp)) {
- lx_err("read lwpstatus failed %d %s", fd, strerror(errno));
- assert(0);
- }
-
- (void) close(fd);
-
- /*
- * If the traced process isn't stopped, this is a truly spurious
- * event probably caused by another /proc consumer tracing the
- * monitor.
- */
- if (!(status.pr_lwp.pr_flags & PR_STOPPED)) {
- (void) ptrace_cont_monitor(p);
- return (-1);
- }
-
- switch (status.pr_lwp.pr_why) {
- case PR_SIGNALLED:
- /*
- * If the traced process got a SIGWAITING, we must be in the
- * middle of a clone(2) with CLONE_PTRACE set.
- */
- if (status.pr_lwp.pr_what == SIGWAITING) {
- ptrace_catch_fork(lxpid, 1);
- (void) ptrace_cont_monitor(p);
- return (-1);
- }
- infop->si_code = CLD_TRAPPED;
- infop->si_status = status.pr_lwp.pr_what;
- if (status.pr_lwp.pr_info.si_signo == SIGTRAP)
- set_dr6(pid, &status.pr_lwp.pr_info);
- break;
-
- case PR_REQUESTED:
- /*
- * Make it look like the traced process stopped on an
- * event of interest.
- */
- infop->si_code = CLD_TRAPPED;
- infop->si_status = SIGTRAP;
- break;
-
- case PR_JOBCONTROL:
- /*
- * Ignore this as it was probably caused by another /proc
- * consumer tracing the monitor.
- */
- (void) ptrace_cont_monitor(p);
- return (-1);
-
- case PR_SYSEXIT:
- /*
- * Processes traced via a monitor (rather than using the
- * native Solaris ptrace support) explicitly trace returns
- * from exec system calls since it's an implicit ptrace
- * trace point. Accordingly we need to present a process
- * in that state as though it had reached the ptrace trace
- * point.
- */
- if (status.pr_lwp.pr_what == SYS_execve) {
- infop->si_code = CLD_TRAPPED;
- infop->si_status = SIGTRAP;
- break;
- }
-
- /*FALLTHROUGH*/
-
- case PR_SYSENTRY:
- case PR_FAULTED:
- case PR_SUSPENDED:
- default:
- lx_err("didn't expect %d (%d %d)", status.pr_lwp.pr_why,
- status.pr_lwp.pr_what, status.pr_lwp.pr_flags);
- assert(0);
- }
-
- infop->si_pid = lxpid;
-
- return (0);
-}
diff --git a/usr/src/lib/brand/lx/lx_brand/common/signal.c b/usr/src/lib/brand/lx/lx_brand/common/signal.c
index b71d712591..9029249b10 100644
--- a/usr/src/lib/brand/lx/lx_brand/common/signal.c
+++ b/usr/src/lib/brand/lx/lx_brand/common/signal.c
@@ -345,6 +345,14 @@ static int lx_sigsegv_depth = 0;
#endif
/*
+ * Setting LX_NO_ABORT_HANDLER in the environment will prevent the emulated
+ * Linux program from modifying the signal handling disposition for SIGSEGV or
+ * SIGABRT. Useful for debugging programs which fall over themselves to
+ * prevent useful core files being generated.
+ */
+static int lx_no_abort_handler = 0;
+
+/*
* Cache result of process.max-file-descriptor to avoid calling getrctl()
* for each lx_ppoll().
*/
@@ -497,6 +505,29 @@ ltos_sigcode(int si_code)
}
}
+/*
+ * 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.
+ */
+int
+stol_status(int s)
+{
+ /*
+ * 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 = stol_signo[s & 0x7f];
+ assert(stat != -1);
+
+ /*
+ * 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
stol_siginfo(siginfo_t *siginfop, lx_siginfo_t *lx_siginfop)
{
@@ -530,7 +561,8 @@ stol_siginfo(siginfo_t *siginfop, lx_siginfo_t *lx_siginfop)
case LX_SIGCHLD:
lx_siginfo.lsi_pid = siginfop->si_pid;
- lx_siginfo.lsi_status = siginfop->si_status;
+ lx_siginfo.lsi_status = stol_status(
+ siginfop->si_status);
lx_siginfo.lsi_utime = siginfop->si_utime;
lx_siginfo.lsi_stime = siginfop->si_stime;
break;
@@ -789,6 +821,19 @@ lx_sigprocmask_common(uintptr_t how, uintptr_t l_setp, uintptr_t l_osetp,
if (err != 0)
return (err);
+
+#if defined(_LP64)
+ /*
+ * To assure that vsyscall operates correctly, we must never
+ * block SIGSEGV. (Fortunately, SIGSEGV is a synchronous
+ * signal for which the default disposition is to nuke the
+ * process -- it would be hard for a correctly-written program
+ * to rely upon its ability to block SIGSEGV.)
+ */
+ if (how == SIG_BLOCK || how == SIG_SETMASK)
+ sigdelset(s_setp, SIGSEGV);
+#endif
+
}
s_osetp = (l_osetp ? &oset : NULL);
@@ -1510,6 +1555,13 @@ lx_vsyscall_return(long ret, ucontext_t *ucp)
&ucp->uc_mcontext.gregs[REG_RIP], sizeof (void*));
lx_debug("\tvsyscall return to %p", ucp->uc_mcontext.gregs[REG_RIP]);
ucp->uc_mcontext.gregs[REG_RSP] += sizeof (void*);
+
+ /*
+ * Make sure that libc's ul_sigmask reflects what the sigmask is about
+ * to become.
+ */
+ thr_sigsetmask(SIG_SETMASK, &ucp->uc_sigmask, NULL);
+
(void) syscall(SYS_brand, B_SIGNAL_RETURN, ucp);
}
#endif
@@ -1532,6 +1584,17 @@ lx_call_user_handler(int sig, siginfo_t *sip, void *p)
size_t stksize;
int lx_sig;
+ switch (sig) {
+ case SIGCLD:
+ /*
+ * Signal to an interrupted waitpid() that it was interrupted
+ * by a SIGCLD, and should restart to grab the wait status
+ * this signal represented.
+ */
+ lx_had_sigchild = 1;
+ break;
+ }
+
/*
* If Illumos signal has no Linux equivalent, effectively ignore it.
*/
@@ -1548,6 +1611,18 @@ lx_call_user_handler(int sig, siginfo_t *sip, void *p)
lx_debug("lxsap @ 0x%p", lxsap);
/*
+ * If the delivery of this signal interrupted a system call, we must
+ * only restart it if sigaction(2) was used to set the SA_RESTART flag
+ * for this signal. The lx_emulate() function checks this per-thread
+ * variable to discover the restart disposition of the most recently
+ * handled signal.
+ *
+ * NOTE: this mechanism may not stand up to close scrutiny in the face
+ * of nested asynchronous signal delivery.
+ */
+ lx_do_syscall_restart = !!(lxsap->lxsa_flags & LX_SA_RESTART);
+
+ /*
* Emulate vsyscall support.
*
* Linux magically maps a single page into the address space of each
@@ -1605,9 +1680,12 @@ lx_call_user_handler(int sig, siginfo_t *sip, void *p)
* 64bit for vsyscall emulation, there are certain cases
* where a SIGSEGV is ignored or forces an exit.
*/
- if (lxsap->lxsa_handler == SIG_IGN) {
+ if (lxsap->lxsa_handler == SIG_IGN &&
+ sip->si_code == SI_USER) {
+ /* Safely ignore a user-sent signal */
return;
} else if (lxsap->lxsa_handler == SIG_DFL ||
+ lxsap->lxsa_handler == SIG_IGN ||
((lxsap->lxsa_flags & LX_SA_NODEFER) == 0 &&
lx_sigsegv_depth > 0)) {
(void) syscall(SYS_brand, B_EXIT_AS_SIG, SIGSEGV);
@@ -1717,6 +1795,18 @@ lx_sigaction_common(int lx_sig, struct lx_sigaction *lxsp,
return (-errno);
if ((sig = ltos_signo[lx_sig]) != -1) {
+ if (lx_no_abort_handler != 0) {
+ /*
+ * 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
*/
@@ -1802,6 +1892,15 @@ lx_sigaction_common(int lx_sig, struct lx_sigaction *lxsp,
return (-err);
}
+#if defined(_LP64)
+ /*
+ * To assure that vsyscall can operate properly
+ * from within signal handlers, we (implicitly)
+ * disallow blocking SEGV in any signal handler.
+ */
+ sigdelset(&sa.sa_mask, SIGSEGV);
+#endif
+
lx_debug("interposing handler @ 0x%p for "
"signal %d (lx %d), flags 0x%x",
lxsa.lxsa_handler, sig, lx_sig,
@@ -1816,6 +1915,16 @@ lx_sigaction_common(int lx_sig, struct lx_sigaction *lxsp,
NULL);
return (-err);
}
+#if defined(_LP64)
+ } else if (sig == SIGSEGV) {
+ /*
+ * If user code attempts to set SIGSEGV to
+ * SIG_IGN or SIG_DFL, the interposing handler
+ * is still required to handle vsyscalls.
+ */
+ lx_debug("interposing handler maintained "
+ "for SIGSEGV");
+#endif
} else if ((sig != SIGPWR) ||
((sig == SIGPWR) &&
(lxsa.lxsa_handler == SIG_IGN))) {
@@ -2026,6 +2135,10 @@ lx_siginit(void)
sigset_t new_set, oset;
int lx_sig, sig;
+ if (getenv("LX_NO_ABORT_HANDLER") != NULL) {
+ lx_no_abort_handler = 1;
+ }
+
/*
* Block all signals possible while setting up the signal imposition
* mechanism.
@@ -2236,7 +2349,7 @@ lx_ppoll(uintptr_t p1, uintptr_t p2, uintptr_t p3, uintptr_t p4, uintptr_t p5)
sfds[i].revents = 0;
}
- if ((rval = ppoll(sfds, nfds, tsp, sp) < 0))
+ if ((rval = ppoll(sfds, nfds, tsp, sp)) < 0)
return (-errno);
/* Convert the Illumos revents bitmask into the Linux equivalent */
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
index 7eb6a6cd12..665d4ce0a7 100644
--- a/usr/src/lib/brand/lx/lx_brand/common/sysv_ipc.c
+++ b/usr/src/lib/brand/lx/lx_brand/common/sysv_ipc.c
@@ -21,7 +21,7 @@
/*
* Copyright 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
- * Copyright 2014 Joyent, Inc. All rights reserved.
+ * Copyright 2015 Joyent, Inc. All rights reserved.
*/
#include <errno.h>
@@ -48,7 +48,7 @@
#define SLOT_MSG 2
static int
-get_rctlval(rctlblk_t *rblk, char *name)
+get_rctlval(rctlblk_t *rblk, char *name, ulong_t limit, uint64_t *val)
{
rctl_qty_t r;
@@ -56,9 +56,11 @@ get_rctlval(rctlblk_t *rblk, char *name)
return (-errno);
r = rctlblk_get_value(rblk);
- if (r > MAXINT)
+ if (r > limit)
return (-EOVERFLOW);
- return (r);
+
+ *val = r;
+ return (0);
}
/*
@@ -206,18 +208,26 @@ lx_semctl_ipcinfo(void *buf)
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));
- if ((i.semmni = get_rctlval(rblk, "project.max-sem-ids")) < 0)
- return (i.semmni);
- if ((i.semmsl = get_rctlval(rblk, "process.max-sem-nsems")) < 0)
- return (i.semmsl);
- if ((i.semopm = get_rctlval(rblk, "process.max-sem-ops")) < 0)
- return (i.semopm);
+ 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
@@ -516,21 +526,30 @@ lx_msgctl_ipcinfo(int cmd, void *buf)
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));
- if ((m.msgmni = get_rctlval(rblk, "project.max-msg-ids")) < 0)
- return (m.msgmni);
- if ((m.msgmnb = get_rctlval(rblk, "process.max-msg-qbytes")) < 0)
- return (m.msgmnb);
+ 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) {
- if ((maxmsgs = get_rctlval(rblk,
- "process.max-msg-messages")) < 0)
- return (maxmsgs);
+ 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;
@@ -693,16 +712,22 @@ 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));
- if ((s.shmmni = get_rctlval(rblk, "project.max-shm-ids")) < 0)
- return (s.shmmni);
- if ((s.shmmax = get_rctlval(rblk, "project.max-shm-memory")) < 0)
- return (s.shmmax);
+ 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
@@ -711,7 +736,7 @@ lx_shmctl_ipcinfo(void *buf)
* coherent about it.
*/
s.shmmin = 1;
- s.shmseg = INT_MAX;
+ s.shmseg = ULONG_MAX;
s.shmall = s.shmmax / getpagesize();
if (uucopy(&s, buf, sizeof (s)))
diff --git a/usr/src/lib/brand/lx/lx_brand/common/wait.c b/usr/src/lib/brand/lx/lx_brand/common/wait.c
index 031eb5e5cd..c3421858eb 100644
--- a/usr/src/lib/brand/lx/lx_brand/common/wait.c
+++ b/usr/src/lib/brand/lx/lx_brand/common/wait.c
@@ -22,7 +22,7 @@
/*
* Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
- * Copyright 2014 Joyent, Inc. All rights reserved.
+ * Copyright 2015 Joyent, Inc.
*/
/*
@@ -70,6 +70,7 @@
#include <sys/wait.h>
#include <sys/lx_types.h>
#include <sys/lx_signal.h>
+#include <sys/lx_debug.h>
#include <sys/lx_misc.h>
#include <sys/lx_syscall.h>
#include <sys/syscall.h>
@@ -100,32 +101,23 @@
extern long max_pid;
+/*
+ * 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)
+ltos_options(uintptr_t options, int *native_options, int *extra_options)
{
int newoptions = 0;
- int rval;
- lx_waitid_args_t extra;
if (((options) & ~(LX_WNOHANG | LX_WUNTRACED | LX_WEXITED |
LX_WCONTINUED | LX_WNOWAIT | LX_WNOTHREAD | LX_WALL |
LX_WCLONE)) != 0) {
return (-1);
}
- /*
- * We use the B_STORE_ARGS command to store any of LX_WNOTHREAD,
- * LX_WALL, and LX_WCLONE that have been set as options on this waitid
- * call. These flags are stored as part of the lwp_brand_data, so that
- * when there is a later syscall to waitid, the brand code there can
- * detect that we added extra flags here and use them as appropriate.
- * We pass them in here rather than the normal channel for flags to
- * prevent polluting the namespace.
- */
- extra.waitid_flags = options & (LX_WNOTHREAD | LX_WALL | LX_WCLONE);
- rval = syscall(SYS_brand, B_STORE_ARGS, &extra,
- sizeof (lx_waitid_args_t), NULL, NULL, NULL, NULL);
- if (rval < 0)
- return (rval);
+
+ *extra_options = options & (LX_WNOTHREAD | LX_WALL | LX_WCLONE);
if (options & LX_WNOHANG)
newoptions |= WNOHANG;
@@ -138,10 +130,13 @@ ltos_options(uintptr_t options)
if (options & LX_WNOWAIT)
newoptions |= WNOWAIT;
- /* The trapped option is implicit on Linux */
+ /*
+ * The trapped option is implicit on Linux.
+ */
newoptions |= WTRAPPED;
- return (newoptions);
+ *native_options = newoptions;
+ return (0);
}
static int
@@ -164,10 +159,7 @@ lx_wstat(int code, int status)
break;
case CLD_TRAPPED:
case CLD_STOPPED:
- stat = stol_signo[status];
- assert(stat != -1);
- stat <<= 8;
- stat |= WSTOPFLG;
+ stat = (stol_status(status) << 8) | WSTOPFLG;
break;
case CLD_CONTINUED:
stat = WCONTFLG;
@@ -177,33 +169,31 @@ lx_wstat(int code, int status)
return (stat);
}
-/* wrapper to make solaris waitid work properly with ptrace */
static int
-lx_waitid_helper(idtype_t idtype, id_t id, siginfo_t *info, int options)
+lx_waitid_helper(idtype_t idtype, id_t id, siginfo_t *sip, int native_options,
+ int extra_options)
{
- do {
- /*
- * It's possible that we return EINVAL here if the idtype is
- * P_PID or P_PGID and id is out of bounds for a valid pid or
- * pgid, but Linux expects to see ECHILD. No good way occurs to
- * handle this so we'll punt for now.
- */
- if (waitid(idtype, id, info, options) < 0)
- return (-errno);
-
- /*
- * If the WNOHANG flag was specified and no child was found
- * return 0.
- */
- if ((options & WNOHANG) && info->si_pid == 0)
- return (0);
-
- /*
- * It's possible that we may have a spurious return for one of
- * the child processes created by the ptrace subsystem. If
- * that's the case, we simply try again.
- */
- } while (lx_ptrace_wait(info) == -1);
+ /*
+ * Call into our in-kernel waitid() wrapper:
+ */
+restart:
+ lx_had_sigchild = 0;
+ if (syscall(SYS_brand, B_HELPER_WAITID, idtype, id, sip,
+ native_options, extra_options) != 0) {
+ if (errno == EINTR && (lx_had_sigchild ||
+ lx_do_syscall_restart)) {
+ /*
+ * If we handled a SIGCLD while blocked in waitid(),
+ * or the SA_RESTART flag was set, we should wait
+ * again.
+ */
+ lx_debug("lx_waitid_helper() restarting due to"
+ " interrupted system call");
+ goto restart;
+ }
+ return (-1);
+ }
+
return (0);
}
@@ -214,11 +204,12 @@ lx_wait4(uintptr_t p1, uintptr_t p2, uintptr_t p3, uintptr_t p4)
struct rusage ru = { 0 };
idtype_t idtype;
id_t id;
- int options, status = 0;
+ int status = 0;
pid_t pid = (pid_t)p1;
int rval;
+ int native_options, extra_options;
- if ((options = ltos_options(p3)) == -1)
+ if (ltos_options(p3, &native_options, &extra_options) == -1)
return (-EINVAL);
if (pid > max_pid)
@@ -260,14 +251,17 @@ lx_wait4(uintptr_t p1, uintptr_t p2, uintptr_t p3, uintptr_t p4)
id = pid;
}
- options |= WEXITED | WTRAPPED;
+ native_options |= WEXITED | WTRAPPED;
+
+ if (lx_waitid_helper(idtype, id, &info, native_options,
+ extra_options) == -1) {
+ return (-errno);
+ }
- if ((rval = lx_waitid_helper(idtype, id, &info, options)) < 0)
- return (rval);
/*
* If the WNOHANG flag was specified and no child was found return 0.
*/
- if ((options & WNOHANG) && info.si_pid == 0)
+ if ((native_options & WNOHANG) && info.si_pid == 0)
return (0);
status = lx_wstat(info.si_code, info.si_status);
@@ -297,9 +291,10 @@ lx_waitpid(uintptr_t p1, uintptr_t p2, uintptr_t p3)
long
lx_waitid(uintptr_t idtype, uintptr_t id, uintptr_t infop, uintptr_t opt)
{
- int rval, options;
+ int native_options, extra_options;
siginfo_t s_info = {0};
- if ((options = ltos_options(opt)) == -1)
+
+ if (ltos_options(opt, &native_options, &extra_options) == -1)
return (-EINVAL);
if (((opt) & (LX_WEXITED | LX_WSTOPPED | LX_WCONTINUED)) == 0)
@@ -318,11 +313,14 @@ lx_waitid(uintptr_t idtype, uintptr_t id, uintptr_t infop, uintptr_t opt)
default:
return (-EINVAL);
}
- if ((rval = lx_waitid_helper(idtype, (id_t)id, &s_info, options)) < 0)
- return (rval);
+
+ if (lx_waitid_helper(idtype, id, &s_info, native_options,
+ extra_options) == -1) {
+ return (-errno);
+ }
/* If the WNOHANG flag was specified and no child was found return 0. */
- if ((options & WNOHANG) && s_info.si_pid == 0)
+ if ((native_options & WNOHANG) && s_info.si_pid == 0)
return (0);
return (stol_siginfo(&s_info, (lx_siginfo_t *)infop));
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
index 7d9c6fae0a..f50535d0c4 100644
--- a/usr/src/lib/brand/lx/lx_brand/sys/lx_misc.h
+++ b/usr/src/lib/brand/lx/lx_brand/sys/lx_misc.h
@@ -55,6 +55,13 @@ extern int lx_rpm_delay;
extern boolean_t lx_is_rpm;
/*
+ * These thread-specific variables allow the signal interposition code
+ * to communicate restart disposition for any interrupting signals.
+ */
+extern __thread int lx_had_sigchild;
+extern __thread int lx_do_syscall_restart;
+
+/*
* Values Linux expects for init
*/
#define LX_INIT_PGID 0
@@ -173,6 +180,7 @@ 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);
+extern void lx_ptrace_clone_begin(int, boolean_t);
extern int lx_check_alloca(size_t);
#define SAFE_ALLOCA(sz) (lx_check_alloca(sz) ? alloca(sz) : NULL)
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
index b4dc47faac..f3d39fca64 100644
--- a/usr/src/lib/brand/lx/lx_brand/sys/lx_signal.h
+++ b/usr/src/lib/brand/lx/lx_brand/sys/lx_signal.h
@@ -21,7 +21,7 @@
/*
* Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
- * Copyright 2014 Joyent, Inc. All rights reserved.
+ * Copyright 2015 Joyent, Inc.
*/
#ifndef _SYS_LX_SIGNAL_H
@@ -396,6 +396,7 @@ extern void lx_sigdeliver(int, siginfo_t *, void *, size_t, void (*)(),
void (*)(), uintptr_t);
extern int stol_siginfo(siginfo_t *siginfop, lx_siginfo_t *lx_siginfop);
+extern int stol_status(int);
#endif /* !defined(_ASM) */
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
index 9cd9cdedb7..f9f49598b7 100644
--- 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
@@ -22,7 +22,7 @@
/*
* Copyright 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
- * Copyright 2014 Joyent, Inc. All rights reserved.
+ * Copyright 2015 Joyent, Inc. All rights reserved.
*/
#ifndef _LX_SYSV_IPC_H
@@ -208,11 +208,11 @@ struct lx_shm_info {
};
struct lx_shminfo {
- long shmmax;
- long shmmin;
- long shmmni;
- long shmseg;
- long shmall;
+ ulong_t shmmax;
+ ulong_t shmmin;
+ ulong_t shmmni;
+ ulong_t shmseg;
+ ulong_t shmall;
};
#ifdef __cplusplus
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
index b4b72c78f9..3d7b9018e1 100644
--- a/usr/src/lib/brand/lx/lx_brand/sys/lx_thread.h
+++ b/usr/src/lib/brand/lx/lx_brand/sys/lx_thread.h
@@ -34,6 +34,12 @@ extern "C" {
#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 {
#if defined(_ILP32)
/* 32-bit thread-specific Linux %gs value */
@@ -42,7 +48,7 @@ typedef struct lx_tsd {
/* 64-bit thread-specific Linux %fsbase value */
uintptr_t lxtsd_fsbase;
#endif
- int lxtsd_exit;
+ lx_exit_type_t lxtsd_exit;
int lxtsd_exit_status;
ucontext_t lxtsd_exit_context;
} lx_tsd_t;
@@ -51,6 +57,8 @@ extern thread_key_t lx_tsd_key;
extern void lx_swap_gs(long, long *);
+extern void lx_exit_common(lx_exit_type_t, uintptr_t) __NORETURN;
+
#ifdef __cplusplus
}
#endif
diff --git a/usr/src/lib/brand/lx/lx_brand/sys/lx_types.h b/usr/src/lib/brand/lx/lx_brand/sys/lx_types.h
index f69ed45820..d98c8bc586 100644
--- a/usr/src/lib/brand/lx/lx_brand/sys/lx_types.h
+++ b/usr/src/lib/brand/lx/lx_brand/sys/lx_types.h
@@ -21,7 +21,7 @@
/*
* Copyright 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
- * Copyright 2014 Joyent, Inc. All rights reserved.
+ * Copyright 2015 Joyent, Inc. All rights reserved.
*/
#ifndef _SYS_LX_TYPES_H
@@ -40,8 +40,10 @@ extern "C" {
#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
#define LX_SYS_UTS_LN 65
diff --git a/usr/src/lib/brand/lx/testing/ltp_skiplist b/usr/src/lib/brand/lx/testing/ltp_skiplist
index 6f1f5f0318..ae23505fcf 100644
--- a/usr/src/lib/brand/lx/testing/ltp_skiplist
+++ b/usr/src/lib/brand/lx/testing/ltp_skiplist
@@ -216,6 +216,7 @@ setxattr03
sgetmask01
shmget05
sighold02
+signal_test_05
signalfd01
signalfd4_01
signalfd4_02
diff --git a/usr/src/lib/brand/lx/zone/Makefile b/usr/src/lib/brand/lx/zone/Makefile
index d72586f9e1..f013697b4e 100644
--- a/usr/src/lib/brand/lx/zone/Makefile
+++ b/usr/src/lib/brand/lx/zone/Makefile
@@ -21,13 +21,12 @@
#
# Copyright 2006 Sun Microsystems, Inc. All rights reserved.
-# Copyright 2014 Joyent, Inc. All rights reserved.
-# Use is subject to license terms.
+# Copyright 2015 Joyent, Inc. All rights reserved.
#
PROGS = lx_install lx_distro_install lx_init_zone lx_boot
PROGS += lx_init_zone_debian lx_init_zone_redhat lx_init_zone_ubuntu
-PROGS += lx_networking lx_boot_zone_ubuntu
+PROGS += lx_networking lx_boot_zone_redhat lx_boot_zone_ubuntu
SUBDIRS = distros
XMLDOCS = config.xml platform.xml
TEMPLATES = SUNWlx.xml SUNWlx26.xml
diff --git a/usr/src/lib/brand/lx/zone/lx_boot.ksh b/usr/src/lib/brand/lx/zone/lx_boot.ksh
index 9fc53240ca..9604a46ce2 100644
--- a/usr/src/lib/brand/lx/zone/lx_boot.ksh
+++ b/usr/src/lib/brand/lx/zone/lx_boot.ksh
@@ -21,7 +21,7 @@
#
#
# Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
-# Copyright 2014, Joyent, Inc. All rights reserved.
+# Copyright 2015, Joyent, Inc.
#
# lx boot script.
#
@@ -60,15 +60,12 @@ EXIT_CODE=1
# kernel runs the linker at /lib/ld.so.1, which doesn't exist in an lx zone.
# In lx, the linker is ld-linux.so.N. Hence when we run the native executable
# from the wrappers, we explicitly specify /native/lib/ld.so.1 as our 32-bit
-# linker (or /native/lib/64/ld.so.1 as our 64-bit linker).
-
-# Setup a native command which uses the thunk library to access the Linux
-# nameservices within the zone.
+# linker (or /native/lib/64/ld.so.1 as our 64-bit linker).
#
# $1 is lx cmd, $2 is native cmd, $3 is an optional inclusion in the script
# the lx cmd path must have already be verified with safe_dir
#
-setup_native_thunk_cmd() {
+setup_native_chroot_cmd() {
cmd_name=$ZONEROOT/$1
if [ -h $cmd_name -o \( -e $cmd_name -a ! -f $cmd_name \) ]; then
@@ -80,10 +77,9 @@ setup_native_thunk_cmd() {
#!/bin/sh
$3
- exec /native/usr/lib/brand/lx/lx_native \
- /native/lib/ld.so.1 -e LD_NOENVIRON=1 -e LD_NOCONFIG=1 \
- -e LD_PRELOAD_32=/native/usr/lib/brand/lx/lx_thunk.so.1 \
- -e LD_LIBRARY_PATH_32="/native/lib:/native/usr/lib" $2 "\$@"
+ exec /native/usr/sbin/chroot /native \
+ /lib/ld.so.1 -e LD_NOENVIRON=1 -e LD_NOCONFIG=1 \
+ $2 "\$@"
DONE
chmod 755 $ZONEROOT/$1
@@ -180,16 +176,12 @@ safe_opt_dir /etc/update-motd.d
#
# Replace Linux binaries with native binaries.
#
-# XXX The lx_thunk code will perform a chroot, so these commands will not work
-# if they are run by a non-privileged user.
-#
-setup_native_thunk_cmd /sbin/ipmgmtd /native/lib/inet/ipmgmtd \
+setup_native_chroot_cmd /sbin/ipmgmtd /lib/inet/ipmgmtd \
"export SMF_FMRI=\"svc:/network/ip-interface-management:default\""
-setup_native_thunk_cmd /sbin/ifconfig-native /native/sbin/ifconfig
-setup_native_thunk_cmd /sbin/dladm /native/usr/sbin/dladm
+setup_native_chroot_cmd /sbin/dladm /usr/sbin/dladm
+setup_native_chroot_cmd /sbin/ifconfig-native /sbin/ifconfig
setup_native_cmd /sbin/route /native/usr/sbin/route
-setup_native_cmd /bin/netstat /native/usr/bin/netstat
#
# STEP THREE
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..57b879cfd0
--- /dev/null
+++ b/usr/src/lib/brand/lx/zone/lx_boot_zone_redhat.ksh
@@ -0,0 +1,399 @@
+#!/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. All rights reserved.
+# Copyright 2015 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.
+#
+
+# This script was taken from an earlier file. Initialize some variables here.
+install_aborted="Install aborted"
+disable_failed="Disable failed"
+create_failed="Create failed"
+tag=lx-redhat.$$
+tmpfile=/tmp/$tag
+cmd2_failed=lx_boot_zone_redhat
+
+# Function for setting up networking in the zone.
+# Generate the /etc/rc.d/init,d/network rc script
+setup_net()
+{
+ zonecfg -z $ZONENAME info net >/tmp/$ZONENAME.$$
+ zonecfg -z $ZONENAME info attr name=resolvers >>/tmp/$ZONENAME.$$
+ rm -f $ZONEROOT/tmp/.lx_net_up
+
+ awk '
+ BEGIN {
+ printf("#! /bin/bash \n\n")
+ printf("# network Bring up/down networking\n#\n")
+ printf("### BEGIN INIT INFO\n");
+ printf("# Provides: $network\n");
+ printf("# Should-Start: iptables ip6tables\n");
+ printf("# Short-Description: Bring up/down networking\n");
+ printf("# Description: Bring up/down networking\n");
+ printf("### END INIT INFO\n\n");
+
+ printf("case \"\$1\" in\n")
+ printf(" start)\n")
+ printf(" [ \"\$EUID\" != \"0\" ] && exit 4\n")
+ printf(" [ -f /tmp/.lx_net_up ] && exit 0\n")
+ printf(" touch /tmp/.lx_net_up\n\n")
+ printf(" /sbin/ipmgmtd || true\n")
+ printf(" /sbin/ifconfig-native lo0 plumb\n")
+ printf(" /sbin/ifconfig-native lo0 up\n")
+ printf(" /sbin/ifconfig-native lo0 inet6 plumb\n")
+ printf(" /sbin/ifconfig-native lo0 inet6 up\n")
+
+ } {
+ if ($1 == "net:") {
+ in_net = 1
+ in_attr = 0
+ next
+ } else if ($1 == "attr:") {
+ in_net = 0
+ in_attr = 1
+ next
+ }
+
+ if (in_net == 1) {
+ if ($1 == "physical:") {
+ phys = $2
+ } else if ($1 == "property:") {
+ split($2, a, ",")
+ split(a[1], k, "=")
+ split(a[2], v, "=")
+
+ val = substr(v[2], 2)
+ val = substr(val, 1, length(val) - 2)
+
+ if (k[2] == "ip")
+ ip = val
+ else if (k[2] == "netmask")
+ mask = val
+ else if (k[2] == "primary")
+ prim = val
+ else if (k[2] == "gateway")
+ gw = val
+ }
+
+ if ($1 == "net:" && phys != "") {
+ printf(" /sbin/ifconfig-native %s plumb || true\n", phys)
+ printf(" /sbin/ifconfig-native %s %s netmask %s up || true\n",
+ phys, ip, mask)
+ printf(" /sbin/ifconfig-native %s inet6 plumb up || true\n", phys)
+ if (prim == "true" && length(gw) > 0)
+
+ printf(" /sbin/route add default %s || true\n", gw)
+
+ phys = ""
+ prim = ""
+ gw = ""
+ }
+ } else if (in_attr == 1) {
+ if ($1 == "value:") {
+ nres = split($2, resolvers, ",")
+ }
+ }
+ }
+ END {
+ printf(" /sbin/ifconfig-native %s plumb || true\n", phys)
+ printf(" /sbin/ifconfig-native %s %s netmask %s up || true\n",
+ phys, ip, mask)
+ printf(" /sbin/ifconfig-native %s inet6 plumb up || true\n", phys)
+ if (prim == "true" && length(gw) > 0)
+ printf(" /sbin/route add default %s || true\n", gw)
+
+ printf(" rm -f /etc/resolv.conf\n")
+ for (i = 1; i <= nres; i++)
+ printf(" echo \"nameserver %s\" >> %s\n", resolvers[i],
+ "/etc/resolv.conf")
+
+ printf(" touch /var/lock/subsys/network\n")
+ printf(" rc=0\n")
+ printf(" ;;\n")
+ printf(" stop)\n")
+ printf(" [ \"\$EUID\" != \"0\" ] && exit 4\n\n")
+ printf(" rm -f /var/lock/subsys/network\n")
+ printf(" rc=0\n")
+ printf(" ;;\n")
+ printf(" status)\n")
+ printf(" echo \"Configured devices:\"\n")
+ printf(" echo \"lo \$(cd /dev/net; ls)\"\n")
+ printf(" echo \"Currently active devices:\"\n")
+ printf(" echo \$(/sbin/ip -o link show up | awk -F \": \" %s{ print \$2 }%s)\n", "\047", "\047")
+ printf(" rc=0\n")
+ printf(" ;;\n")
+ printf(" restart|reload|force-reload)\n")
+ printf(" cd \"\$CWD\"\n")
+ printf(" \$0 stop\n")
+ printf(" \$0 start\n")
+ printf(" rc=\$?\n")
+ printf(" ;;\n")
+ printf(" *)\n")
+ printf(" echo \"Usage: \$0 {start|stop|status|restart|reload|force-reload}\"\n")
+ printf(" exit 2\n")
+ printf("esac\n\n")
+ printf("exit \$rc\n")
+
+ }' /tmp/$ZONENAME.$$ > $fnm
+ chmod +x $fnm
+
+ rm -f /tmp/$ZONENAME.$$
+}
+
+#
+# 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.
+#
+if ! egrep -s "Modified by lx brand" $ZONEROOT/etc/inittab; then
+ sed 's/^[1-6]:/# Disabled by lx brand: &/
+ s/^id:5:initdefault:/id:3:initdefault: &/' \
+ $ZONEROOT/etc/inittab > $tmpfile
+ echo "# Modified by lx brand" >> $tmpfile
+
+ #
+ # Attempt to save off the original inittab
+ # before moving over the modified version.
+ #
+ mv -f $ZONEROOT/etc/inittab $ZONEROOT/etc/inittab.$tag 2>/dev/null
+ mv -f $tmpfile $ZONEROOT/etc/inittab
+ chmod 644 $ZONEROOT/etc/inittab
+fi
+
+#
+# We use our own way of bringing up networking, so don't let the init system
+# try.
+#
+
+mv -f $ZONEROOT/etc/sysconfig/network $ZONEROOT/etc/sysconfig/network.$tag \
+ 2>/dev/null
+
+cat > $ZONEROOT/etc/sysconfig/network <<- EOF
+ NETWORKING="no"
+ #
+ # To enable networking, change the "no" above to "yes" and
+ # uncomment and fill in the following parameters.
+ #
+ # If you are specifying a hostname by name rather than by IP address,
+ # be sure the system can resolve the name properly via the use of a
+ # name service and/or the proper name files, as specified by
+ # nsswitch.conf. See nsswitch.conf(5) for further details.
+ #
+ # HOSTNAME=your_hostname_here
+ #
+EOF
+
+#
+# SELinux must be disabled otherwise we won't get past init.
+#
+egrep -s "^SELINUX=enforcing|^SELINUX=permissive" $ZONEROOT/etc/selinux/config
+if [[ $? -eq 0 ]]; then
+ tmpfile=/tmp/selinux_config.$$
+
+ sed 's/^SELINUX=.*$/SELINUX=disabled/' \
+ $ZONEROOT/etc/selinux/config > $tmpfile
+
+ mv -f $ZONEROOT/etc/selinux/config \
+ $ZONEROOT/etc/selinux/config.$tag 2>/dev/null
+ mv -f $tmpfile $ZONEROOT/etc/selinux/config
+ chmod 644 $ZONEROOT/etc/selinux/config
+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.
+#
+mv -f $ZONEROOT/etc/sysconfig/keyboard $ZONEROOT/etc/sysconfig/keyboard.$tag \
+ 2>/dev/null
+
+#
+# The following 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="
+ auditd
+ gpm
+ hpoj
+ ip6tables
+ iptables
+ irda
+ irqbalance
+ iscsi
+ isdn
+ kudzu
+ mdmpd
+ mdmonitor
+ microcode_ctl
+ netdump
+ pcmcia
+ psacct
+ random
+ rawdevices
+ smartd
+"
+
+for file in $unsupported_rc_services; do
+ if [[ -a "$ZONEROOT/etc/rc.d/init.d/$file" ]]; then
+ mv -f "$ZONEROOT/etc/rc.d/init.d/$file" \
+ "$ZONEROOT/etc/rc.d/init.d/$file.$tag"
+ fi
+
+ 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="ttyS0"
+
+#
+# Now customize upstart
+#
+
+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.
+#
+if ! egrep -s "Disabled by lx brand" $ZONEROOT/etc/rc.d/init.d/halt; then
+ awk 'BEGIN {skip = ""}
+ /^# Save mixer/ {skip = "# Disabled by lx brand: "}
+ /halt.local/ {skip = ""}
+ /./ {print skip $0}' $ZONEROOT/etc/rc.d/init.d/halt > /tmp/halt.$$
+
+ if [[ $? -eq 0 ]]; then
+ mv -f $ZONEROOT/etc/rc.d/init.d/halt \
+ $ZONEROOT/etc/rc.d/init.d/halt.$tag 2>/dev/null
+ mv -f /tmp/halt.$$ $ZONEROOT/etc/rc.d/init.d/halt
+ chmod 755 $ZONEROOT/etc/rc.d/init.d/halt
+ 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.
+#
+# Don't modify the rc.sysinit file if it looks like we already did.
+#
+if ! egrep -s "Disabled by lx brand" $ZONEROOT/etc/rc.d/rc.sysinit; then
+ tmpfile=/tmp/lx_rc.sysinit.$$
+
+ sed 's@^/sbin/hwclock@# Disabled by lx brand: &@
+ s@^HOSTTYPE=@HOSTTYPE=\"s390\" # Spoofed for lx brand: &@
+ s@/bin/dmesg -n@: # Disabled by lx brand: &@
+ s@^dmesg -s@# Disabled by lx brand: &@
+ s@initlog -c \"fsck@: # Disabled by lx brand: &@
+ s@^.*mount .* /dev/pts$@# Disabled by lx brand: &@' \
+ $ZONEROOT/etc/rc.d/rc.sysinit > $tmpfile
+
+ #
+ # Attempt to save off the original rc.sysinit
+ # before moving over the modified version.
+ #
+ mv -f $ZONEROOT/etc/rc.d/rc.sysinit \
+ $ZONEROOT/etc/rc.d/rc.sysinit.$tag 2>/dev/null
+ mv -f $tmpfile $ZONEROOT/etc/rc.d/rc.sysinit
+ chmod 755 $ZONEROOT/etc/rc.d/rc.sysinit
+fi
+
+
+# NOTE: The networking setup assumes an exclusive-stack zone.
+iptype=`/usr/sbin/zonecfg -z $ZONENAME info ip-type | cut -f2 -d' '`
+
+if [[ "$iptype" == "exclusive" ]]; then
+ fnm=$ZONEROOT/etc/rc.d/init.d/network
+ if [[ ! -h $fnm && -f $fnm ]] then
+ setup_net
+ fi
+fi
+
+#
+# upstart modifications are complete
+#
+
+# Hand control back to lx_boot
diff --git a/usr/src/lib/brand/lx/zone/platform.xml b/usr/src/lib/brand/lx/zone/platform.xml
index e6a2ef46e3..bb689d7c58 100644
--- a/usr/src/lib/brand/lx/zone/platform.xml
+++ b/usr/src/lib/brand/lx/zone/platform.xml
@@ -22,7 +22,7 @@
Copyright 2007 Sun Microsystems, Inc. All rights reserved.
Use is subject to license terms.
- Copyright 2014 Sun Microsystems, Inc. All rights reserved.
+ Copyright 2015 Joyent, Inc.
DO NOT EDIT THIS FILE.
-->
@@ -47,7 +47,7 @@
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="/usr/lib/brand/lx/etc_netconfig"
+ <global_mount special="/etc/netconfig"
directory="/native/etc/netconfig" type="lofs" opt="ro" />
<global_mount special="/sbin"
directory="/native/sbin" opt="ro,nodevices" type="lofs" />
@@ -137,6 +137,7 @@
<symlink source="stdout" target="../proc/self/fd/1" />
<symlink source="systty" target="console" />
<symlink source="kmsg" target="console" />
+ <symlink source="conslog" target="log" />
<!-- Create a mount point for for the /dev/initctl fifo -->
<device match="null" name="initctl" />
diff --git a/usr/src/lib/libm/amd64/src/locallibm.il b/usr/src/lib/libm/amd64/src/locallibm.il
index 65921d3c97..375720c84d 100644
--- a/usr/src/lib/libm/amd64/src/locallibm.il
+++ b/usr/src/lib/libm/amd64/src/locallibm.il
@@ -91,7 +91,7 @@
1: movl %edi,%eax
.end
- .inline __copysign,0
+ .inline copysign,0
movq $0x7fffffffffffffff,%rax
movdq %rax,%xmm2
andpd %xmm2,%xmm0
@@ -99,19 +99,19 @@
orpd %xmm2,%xmm0
.end
- .inline __fabs,0
+ .inline fabs,0
movq $0x7fffffffffffffff,%rax
movdq %rax,%xmm1
andpd %xmm1,%xmm0
.end
- .inline __fabsf,0
+ .inline fabsf,0
movl $0x7fffffff,%eax
movdl %eax,%xmm1
andps %xmm1,%xmm0
.end
- .inline _finite,0
+ .inline finite,0
subq $16,%rsp
movlpd %xmm0,(%rsp)
movq (%rsp),%rcx
@@ -123,16 +123,16 @@
addq $16,%rsp
.end
- .inline __signbit,0
+ .inline signbit,0
movmskpd %xmm0,%eax
andq $1,%rax
.end
- .inline __sqrt,0
+ .inline sqrt,0
sqrtsd %xmm0,%xmm0
.end
- .inline __sqrtf,0
+ .inline sqrtf,0
sqrtss %xmm0,%xmm0
.end
diff --git a/usr/src/lib/libm/i386/src/locallibm.il b/usr/src/lib/libm/i386/src/locallibm.il
index ca79724f86..bca43cb8e5 100644
--- a/usr/src/lib/libm/i386/src/locallibm.il
+++ b/usr/src/lib/libm/i386/src/locallibm.il
@@ -127,7 +127,7 @@
addl $8,%esp
.end
- .inline __ceil,0
+ .inline ceil,0
subl $8,%esp
fstcw (%esp)
fldl 8(%esp) ///
@@ -148,7 +148,7 @@
addl $8,%esp
.end
- .inline __copysign,0
+ .inline copysign,0
movl 4(%esp),%eax /// eax <-- hi_32(x)
movl 12(%esp),%ecx /// ecx <-- hi_32(y)
andl $0x7fffffff,%eax / eax <-- hi_32(abs(x))
@@ -170,17 +170,17 @@
fsqrt
.end
- .inline __fabs,0
+ .inline fabs,0
fldl (%esp) ///
fabs
.end
- .inline __fabsf,0
+ .inline fabsf,0
flds (%esp)
fabs
.end
- .inline __fabsl,0
+ .inline fabsl,0
fldt (%esp)
fabs
.end
@@ -188,7 +188,7 @@
/
/ branchless _finite
/
- .inline _finite,0
+ .inline finite,0
movl 4(%esp),%eax /// eax <-- hi_32(x)
notl %eax / not(bexp) = 0 iff bexp = all 1's
andl $0x7ff00000,%eax
@@ -196,7 +196,7 @@
shrl $31,%eax
.end
- .inline __floor,0
+ .inline floor,0
subl $8,%esp
fstcw (%esp)
fldl 8(%esp) ///
@@ -217,7 +217,7 @@
addl $8,%esp
.end
- .inline __isnanf,0
+ .inline isnanf,0
movl (%esp),%eax
andl $0x7fffffff,%eax
negl %eax
@@ -278,7 +278,7 @@
fsqrt
.end
- .inline __rint,0
+ .inline rint,0
fldl (%esp)
movl 4(%esp),%eax
andl $0x7fffffff,%eax
@@ -289,39 +289,39 @@
fwait / in case we jumped around frndint
.end
- .inline __scalbn,0
+ .inline scalbn,0
fildl 8(%esp) /// convert N to extended
fldl (%esp) /// push x
fscale
fstp %st(1)
.end
- .inline __signbit,0
+ .inline signbit,0
movl 4(%esp),%eax /// high part of x
shrl $31,%eax
.end
- .inline __signbitf,0
+ .inline signbitf,0
movl (%esp),%eax
shrl $31,%eax
.end
- .inline __sqrt,0
+ .inline sqrt,0
fldl (%esp)
fsqrt
.end
- .inline __sqrtf,0
+ .inline sqrtf,0
flds (%esp)
fsqrt
.end
- .inline __sqrtl,0
+ .inline sqrtl,0
fldt (%esp)
fsqrt
.end
- .inline __isnanl,0
+ .inline isnanl,0
movl 8(%esp),%eax / ax <-- sign bit and __exp
andl $0x00007fff,%eax
jz 1f / jump if __exp is all 0
diff --git a/usr/src/lib/libm/sparc/src/locallibm.il b/usr/src/lib/libm/sparc/src/locallibm.il
index 3822f5f92d..b6fa0adfc5 100644
--- a/usr/src/lib/libm/sparc/src/locallibm.il
+++ b/usr/src/lib/libm/sparc/src/locallibm.il
@@ -311,13 +311,13 @@
fsqrtd %f0,%f0
.end
- .inline __sqrtf,1
+ .inline sqrtf,1
st %o0,[%sp+0x44]
ld [%sp+0x44],%f0
fsqrts %f0,%f0
.end
- .inline __sqrt,2
+ .inline sqrt,2
std %o0,[%sp+0x48] ! store to 8-aligned address
ldd [%sp+0x48],%f0
fsqrtd %f0,%f0
@@ -814,7 +814,7 @@
.nonvolatile
.end
- .inline __fp_class,2
+ .inline fp_class,2
sethi %hi(0x80000000),%o2 ! o2 gets 80000000
andn %o0,%o2,%o0 ! o0-o1 gets abs(x)
orcc %o0,%o1,%g0 ! set cc as x is zero/nonzero
@@ -859,7 +859,7 @@
2:
.end
- .inline __fp_classf,1
+ .inline fp_classf,1
sethi %hi(0x80000000),%o2
andncc %o0,%o2,%o0
bne 1f
@@ -1229,14 +1229,14 @@
sub %o0,%o1,%o0
.end
- .inline __fabs,2
+ .inline fabs,2
st %o0,[%sp+0x48]
st %o1,[%sp+0x4c]
ldd [%sp+0x48],%f0
fabsd %f0,%f0
.end
- .inline __fabsf,1
+ .inline fabsf,1
st %o0,[%sp+0x44]
ld [%sp+0x44],%f0
fabss %f0,%f0
diff --git a/usr/src/lib/libm/sparcv9/src/locallibm.il b/usr/src/lib/libm/sparcv9/src/locallibm.il
index dcef23826a..2cd9e21470 100644
--- a/usr/src/lib/libm/sparcv9/src/locallibm.il
+++ b/usr/src/lib/libm/sparcv9/src/locallibm.il
@@ -39,11 +39,11 @@
fsqrtd %f0,%f0
.end
- .inline __sqrtf,1
+ .inline sqrtf,1
fsqrts %f1,%f0
.end
- .inline __sqrt,1
+ .inline sqrt,1
fsqrtd %f0,%f0
.end
@@ -420,7 +420,7 @@
.nonvolatile
.end
- .inline __fp_class,1
+ .inline fp_class,1
fabsd %f0,%f0
std %f0,[%sp+0x87f]
ldx [%sp+0x87f],%o0
@@ -460,7 +460,7 @@
2:
.end
- .inline __fp_classf,1
+ .inline fp_classf,1
fabss %f1,%f1
st %f1,[%sp+0x87f]
ld [%sp+0x87f],%o0
@@ -676,11 +676,11 @@
sra %o0,0,%o0
.end
- .inline __fabs,1
+ .inline fabs,1
fabsd %f0,%f0
.end
- .inline __fabsf,1
+ .inline fabsf,1
fabss %f1,%f0
.end
!
diff --git a/usr/src/lib/libmvec/Makefile.com b/usr/src/lib/libmvec/Makefile.com
index b9f39e2279..e574b0df22 100644
--- a/usr/src/lib/libmvec/Makefile.com
+++ b/usr/src/lib/libmvec/Makefile.com
@@ -179,7 +179,6 @@ FLTRPATH = $(FLTRPATH_$(TARGET_ARCH))
sparc_CFLAGS += -_cc=-W0,-xintrinsic
sparcv9_CFLAGS += -_cc=-W0,-xintrinsic
-CPPFLAGS_i386 += -Dfabs=__fabs
SRCS_mvec_i386 = ../common/__vsqrtf.c
diff --git a/usr/src/lib/libnsl/nsl/_utility.c b/usr/src/lib/libnsl/nsl/_utility.c
index f01cea5c6e..994e3158ee 100644
--- a/usr/src/lib/libnsl/nsl/_utility.c
+++ b/usr/src/lib/libnsl/nsl/_utility.c
@@ -26,6 +26,9 @@
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
+/*
+ * Copyright 2014 Nexenta Systems, Inc. All rights reserved.
+ */
#include "mt.h"
#include <stdlib.h>
@@ -82,15 +85,14 @@ _t_checkfd(int fd, int force_sync, int api_semantics)
t_errno = TBADF;
return (NULL);
}
- tiptr = NULL;
- sig_mutex_lock(&_ti_userlock);
- if ((tiptr = find_tilink(fd)) != NULL) {
- if (!force_sync) {
- sig_mutex_unlock(&_ti_userlock);
+
+ if (!force_sync) {
+ sig_mutex_lock(&_ti_userlock);
+ tiptr = find_tilink(fd);
+ sig_mutex_unlock(&_ti_userlock);
+ if (tiptr != NULL)
return (tiptr);
- }
}
- sig_mutex_unlock(&_ti_userlock);
/*
* Not found or a forced sync is required.
@@ -270,7 +272,7 @@ _t_register_lookevent(
* signals are deferred, calls to malloc() are safe.
*/
if ((tlbs->tl_next = malloc(sizeof (struct _ti_lookbufs))) ==
- NULL)
+ NULL)
return (-1); /* error */
tlbs = tlbs->tl_next;
/*
@@ -485,9 +487,9 @@ _t_do_ioctl(int fd, char *buf, int size, int cmd, int *retlenp)
}
if (retval > 0) {
- t_errno = retval&0xff;
+ t_errno = retval & 0xff;
if (t_errno == TSYSERR)
- errno = (retval >> 8)&0xff;
+ errno = (retval >> 8) & 0xff;
return (-1);
}
if (retlenp)
@@ -689,7 +691,7 @@ add_tilink(int s)
* duplicate entry or the end.
*/
for (curptr = hash_bucket[x]; curptr != NULL;
- curptr = curptr->ti_next) {
+ curptr = curptr->ti_next) {
if (curptr->ti_fd == s) {
/*
* This can happen when the user has close(2)'ed
@@ -964,7 +966,6 @@ _t_create(int fd, struct t_info *info, int api_semantics, int *t_capreq_failed)
errno = ENOMEM;
return (NULL);
}
- sig_mutex_lock(&ntiptr->ti_lock);
/*
* Allocate buffers for the new descriptor
@@ -973,7 +974,6 @@ _t_create(int fd, struct t_info *info, int api_semantics, int *t_capreq_failed)
sv_errno = errno;
(void) _t_delete_tilink(fd);
t_errno = TSYSERR;
- sig_mutex_unlock(&ntiptr->ti_lock);
errno = sv_errno;
return (NULL);
}
@@ -1018,7 +1018,6 @@ _t_create(int fd, struct t_info *info, int api_semantics, int *t_capreq_failed)
if ((rstate = _t_adjust_state(fd, T_IDLE)) < 0) {
sv_errno = errno;
(void) _t_delete_tilink(fd);
- sig_mutex_unlock(&ntiptr->ti_lock);
errno = sv_errno;
return (NULL);
}
@@ -1037,7 +1036,6 @@ _t_create(int fd, struct t_info *info, int api_semantics, int *t_capreq_failed)
if ((rstate = _t_adjust_state(fd, T_DATAXFER)) < 0) {
sv_errno = errno;
(void) _t_delete_tilink(fd);
- sig_mutex_unlock(&ntiptr->ti_lock);
errno = sv_errno;
return (NULL);
}
@@ -1052,7 +1050,6 @@ _t_create(int fd, struct t_info *info, int api_semantics, int *t_capreq_failed)
if ((rstate = _t_adjust_state(fd, T_INREL)) < 0) {
sv_errno = errno;
(void) _t_delete_tilink(fd);
- sig_mutex_unlock(&ntiptr->ti_lock);
errno = sv_errno;
return (NULL);
}
@@ -1061,7 +1058,6 @@ _t_create(int fd, struct t_info *info, int api_semantics, int *t_capreq_failed)
default:
t_errno = TSTATECHNG;
(void) _t_delete_tilink(fd);
- sig_mutex_unlock(&ntiptr->ti_lock);
return (NULL);
}
@@ -1078,7 +1074,6 @@ _t_create(int fd, struct t_info *info, int api_semantics, int *t_capreq_failed)
sv_errno = errno;
(void) _t_delete_tilink(fd);
t_errno = TSYSERR;
- sig_mutex_unlock(&ntiptr->ti_lock);
errno = sv_errno;
return (NULL);
}
@@ -1092,7 +1087,6 @@ _t_create(int fd, struct t_info *info, int api_semantics, int *t_capreq_failed)
sv_errno = errno;
(void) _t_delete_tilink(fd);
t_errno = TSYSERR;
- sig_mutex_unlock(&ntiptr->ti_lock);
errno = sv_errno;
return (NULL);
}
@@ -1101,7 +1095,7 @@ _t_create(int fd, struct t_info *info, int api_semantics, int *t_capreq_failed)
tsap->tsa_qlen = 0; /* not needed for TLI */
ntiptr->ti_qlen = tsap->tsa_qlen;
- sig_mutex_unlock(&ntiptr->ti_lock);
+
return (ntiptr);
}
@@ -1162,8 +1156,8 @@ _t_adjust_state(int fd, int instate)
* from the stream head.
*/
if ((arg.ctlbuf.len == 4) &&
- /* LINTED pointer cast */
- ((*(int32_t *)arg.ctlbuf.buf) == T_CONN_CON))
+ /* LINTED pointer cast */
+ ((*(int32_t *)arg.ctlbuf.buf) == T_CONN_CON))
outstate = T_OUTCON;
break;
case T_INREL:
@@ -1375,7 +1369,7 @@ _t_acquire_ctlbuf(
* allocate new buffer and free after use.
*/
if ((ctlbufp->maxlen = _t_cbuf_alloc(tiptr,
- &ctlbufp->buf)) < 0) {
+ &ctlbufp->buf)) < 0) {
t_errno = TSYSERR;
return (-1);
}
@@ -1419,7 +1413,7 @@ _t_acquire_databuf(
* allocate new buffer and free after use.
*/
if ((databufp->maxlen = _t_rbuf_alloc(tiptr,
- &databufp->buf)) < 0) {
+ &databufp->buf)) < 0) {
t_errno = TSYSERR;
return (-1);
}
diff --git a/usr/src/lib/libproc/common/Pcontrol.c b/usr/src/lib/libproc/common/Pcontrol.c
index bde48d1416..afa04c43c7 100644
--- a/usr/src/lib/libproc/common/Pcontrol.c
+++ b/usr/src/lib/libproc/common/Pcontrol.c
@@ -26,6 +26,7 @@
* Portions Copyright 2007 Chad Mynhier
* Copyright 2012 DEY Storage Systems, Inc. All rights reserved.
* Copyright (c) 2013 by Delphix. All rights reserved.
+ * Copyright 2015, Joyent, Inc.
*/
#include <assert.h>
@@ -1758,6 +1759,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;
@@ -1937,6 +1941,7 @@ Pstopstatus(struct ps_prochandle *P,
case PR_FAULTED:
case PR_JOBCONTROL:
case PR_SUSPENDED:
+ case PR_BRAND:
break;
default:
errno = EPROTO;
@@ -3511,6 +3516,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 6697d5736b..9e3aa1ac7d 100644
--- a/usr/src/lib/libproc/common/Pcontrol.h
+++ b/usr/src/lib/libproc/common/Pcontrol.h
@@ -24,7 +24,7 @@
*/
/*
* Copyright 2012 DEY Storage Systems, Inc. All rights reserved.
- * Copyright (c) 2014, Joyent, Inc. All rights reserved.
+ * Copyright (c) 2015, Joyent, Inc. All rights reserved.
* Copyright (c) 2013 by Delphix. All rights reserved.
*/
@@ -96,6 +96,7 @@ typedef struct file_info { /* symbol information for a mapped file */
struct map_info *file_map; /* primary (text) mapping */
int file_ref; /* references from map_info_t structures */
int file_fd; /* file descriptor for the mapped file */
+ int file_dbgfile; /* file descriptor for the debug file */
int file_init; /* 0: initialization yet to be performed */
GElf_Half file_etype; /* ELF e_type from ehdr */
GElf_Half file_class; /* ELF e_ident[EI_CLASS] from ehdr */
@@ -105,6 +106,7 @@ typedef struct file_info { /* symbol information for a mapped file */
char *file_rname; /* resolved on-disk object pathname */
char *file_rbase; /* pointer to basename of file_rname */
Elf *file_elf; /* ELF handle so we can close */
+ Elf *file_dbgelf; /* Debug ELF handle so we can close */
void *file_elfmem; /* data for faked-up ELF handle */
sym_tbl_t file_symtab; /* symbol table */
sym_tbl_t file_dynsym; /* dynamic symbol table */
diff --git a/usr/src/lib/libproc/common/Pcore.c b/usr/src/lib/libproc/common/Pcore.c
index c899ee1b20..454360a8b4 100644
--- a/usr/src/lib/libproc/common/Pcore.c
+++ b/usr/src/lib/libproc/common/Pcore.c
@@ -2695,6 +2695,7 @@ Pfgrab_core(int core_fd, const char *aout_path, int *perr)
fp->file_ref = 1;
fp->file_fd = -1;
+ fp->file_dbgfile = -1;
fp->file_lo = malloc(sizeof (rd_loadobj_t));
fp->file_lname = strdup(execname);
diff --git a/usr/src/lib/libproc/common/Pidle.c b/usr/src/lib/libproc/common/Pidle.c
index 3191f4fa7e..c69bcaf860 100644
--- a/usr/src/lib/libproc/common/Pidle.c
+++ b/usr/src/lib/libproc/common/Pidle.c
@@ -226,6 +226,7 @@ Pgrab_file(const char *fname, int *perr)
}
fp->file_fd = fd;
+ fp->file_dbgfile = -1;
fp->file_lo->rl_lmident = LM_ID_BASE;
if ((fp->file_lname = strdup(fp->file_pname)) == NULL) {
*perr = G_STRANGE;
diff --git a/usr/src/lib/libproc/common/Psymtab.c b/usr/src/lib/libproc/common/Psymtab.c
index 62354f9a7b..41f41f3dde 100644
--- a/usr/src/lib/libproc/common/Psymtab.c
+++ b/usr/src/lib/libproc/common/Psymtab.c
@@ -21,7 +21,7 @@
/*
* Copyright (c) 1997, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright (c) 2013, Joyent, Inc. All rights reserved.
+ * Copyright (c) 2015, Joyent, Inc. All rights reserved.
* Copyright (c) 2013 by Delphix. All rights reserved.
*/
@@ -43,6 +43,7 @@
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/sysmacros.h>
+#include <sys/crc32.h>
#include "libproc.h"
#include "Pcontrol.h"
@@ -61,6 +62,7 @@ static int read_ehdr32(struct ps_prochandle *, Elf32_Ehdr *, uint_t *,
static int read_ehdr64(struct ps_prochandle *, Elf64_Ehdr *, uint_t *,
uintptr_t);
#endif
+static uint32_t psym_crc32[] = { CRC32_TABLE };
#define DATA_TYPES \
((1 << STT_OBJECT) | (1 << STT_FUNC) | \
@@ -184,6 +186,7 @@ file_info_new(struct ps_prochandle *P, map_info_t *mptr)
mptr->map_file = fptr;
fptr->file_ref = 1;
fptr->file_fd = -1;
+ fptr->file_dbgfile = -1;
P->num_files++;
/*
@@ -274,6 +277,10 @@ file_info_free(struct ps_prochandle *P, file_info_t *fptr)
free(fptr->file_elfmem);
if (fptr->file_fd >= 0)
(void) close(fptr->file_fd);
+ if (fptr->file_dbgelf)
+ (void) elf_end(fptr->file_dbgelf);
+ if (fptr->file_dbgfile >= 0)
+ (void) close(fptr->file_dbgfile);
if (fptr->file_ctfp) {
ctf_close(fptr->file_ctfp);
free(fptr->file_ctf_buf);
@@ -1567,6 +1574,170 @@ build_fake_elf(struct ps_prochandle *P, file_info_t *fptr, GElf_Ehdr *ehdr,
}
/*
+ * Try and find the file described by path in the file system and validate that
+ * it matches our CRC before we try and process it for symbol information.
+ *
+ * Before we valiate if it's a crc, we check to ensure that it's a normal file
+ * and not anything else.
+ */
+static boolean_t
+build_alt_debug(file_info_t *fptr, const char *path, uint32_t crc)
+{
+ int fd;
+ struct stat st;
+ Elf *elf;
+ Elf_Scn *scn;
+ GElf_Shdr symshdr, strshdr;
+ Elf_Data *symdata, *strdata;
+ uint32_t c = -1U;
+
+ if ((fd = open(path, O_RDONLY)) < 0)
+ return (B_FALSE);
+
+ if (fstat(fd, &st) != 0) {
+ (void) close(fd);
+ return (B_FALSE);
+ }
+
+ if (S_ISREG(st.st_mode) == 0) {
+ (void) close(fd);
+ return (B_FALSE);
+ }
+
+ for (;;) {
+ char buf[4096];
+ ssize_t ret = read(fd, buf, sizeof (buf));
+ if (ret == -1) {
+ if (ret == EINTR)
+ continue;
+ (void) close(fd);
+ return (B_FALSE);
+ }
+ if (ret == 0) {
+ c = ~c;
+ if (c != crc) {
+ dprintf("crc mismatch, found: 0x%x "
+ "expected 0x%x\n", c, crc);
+ (void) close(fd);
+ return (B_FALSE);
+ }
+ break;
+ }
+ CRC32(c, buf, ret, c, psym_crc32);
+ }
+
+ elf = elf_begin(fd, ELF_C_READ, NULL);
+ if (elf == NULL) {
+ (void) close(fd);
+ return (B_FALSE);
+ }
+
+ if (elf_kind(elf) != ELF_K_ELF) {
+ goto fail;
+ }
+
+ /*
+ * Do two passes, first see if we have a symbol header, then see if we
+ * can find the corresponding linked string table.
+ */
+ scn = NULL;
+ for (scn = elf_nextscn(elf, scn); scn != NULL;
+ scn = elf_nextscn(elf, scn)) {
+
+ if (gelf_getshdr(scn, &symshdr) == NULL)
+ goto fail;
+
+ if (symshdr.sh_type != SHT_SYMTAB)
+ continue;
+
+ if ((symdata = elf_getdata(scn, NULL)) == NULL)
+ goto fail;
+
+ break;
+ }
+ if (scn == NULL)
+ goto fail;
+
+ if ((scn = elf_getscn(elf, symshdr.sh_link)) == NULL)
+ goto fail;
+
+ if (gelf_getshdr(scn, &strshdr) == NULL)
+ goto fail;
+
+ if ((strdata = elf_getdata(scn, NULL)) == NULL)
+ goto fail;
+
+ fptr->file_symtab.sym_data_pri = symdata;
+ fptr->file_symtab.sym_symn += symshdr.sh_size / symshdr.sh_entsize;
+ fptr->file_symtab.sym_strs = strdata->d_buf;
+ fptr->file_symtab.sym_strsz = strdata->d_size;
+ fptr->file_symtab.sym_hdr_pri = symshdr;
+ fptr->file_symtab.sym_strhdr = strshdr;
+
+ dprintf("successfully loaded additional debug symbols for %s from %s\n",
+ fptr->file_rname, path);
+
+ fptr->file_dbgfile = fd;
+ fptr->file_dbgelf = elf;
+ return (B_TRUE);
+fail:
+ (void) elf_end(elf);
+ (void) close(fd);
+ return (B_FALSE);
+}
+
+/*
+ * We're here because the object in question has no symbol information, that's a
+ * bit unfortunate. However, we've found that there's a .gnu_debuglink sitting
+ * around. By convention that means that given the current location of the
+ * object on disk, and the debug name that we found in the binary we need to
+ * search the following locations for a matching file.
+ *
+ * <dirname>/.debug/<debug-name>
+ * /usr/lib/debug/<dirname>/<debug-name>
+ *
+ * In the future, we should consider supporting looking in the prefix's
+ * lib/debug directory for a matching object.
+ */
+static void
+find_alt_debug(file_info_t *fptr, const char *name, uint32_t crc)
+{
+ boolean_t r;
+ char *dup = NULL, *path = NULL, *dname;
+
+ dprintf("find_alt_debug: looking for %s, crc 0x%x\n", name, crc);
+ if (fptr->file_rname == NULL) {
+ dprintf("find_alt_debug: encountered null file_rname\n");
+ return;
+ }
+
+ dup = strdup(fptr->file_rname);
+ if (dup == NULL)
+ return;
+
+ dname = dirname(dup);
+ if (asprintf(&path, "%s/.debug/%s", dname, name) != -1) {
+ dprintf("attempting to load alternate debug information "
+ "from %s\n", path);
+ r = build_alt_debug(fptr, path, crc);
+ free(path);
+ if (r == B_TRUE)
+ goto out;
+ }
+
+ if (asprintf(&path, "/usr/lib/debug/%s/%s", dname, name) != -1) {
+ dprintf("attempting to load alternate debug information "
+ "from %s\n", path);
+ r = build_alt_debug(fptr, path, crc);
+ free(path);
+ if (r == B_TRUE)
+ goto out;
+ }
+out:
+ free(dup);
+}
+
+/*
* Build the symbol table for the given mapped file.
*/
void
@@ -1587,7 +1758,8 @@ Pbuild_file_symtab(struct ps_prochandle *P, file_info_t *fptr)
GElf_Shdr c_shdr;
Elf_Data *c_data;
const char *c_name;
- } *cp, *cache = NULL, *dyn = NULL, *plt = NULL, *ctf = NULL;
+ } *cp, *cache = NULL, *dyn = NULL, *plt = NULL, *ctf = NULL,
+ *dbglink = NULL;
if (fptr->file_init)
return; /* We've already processed this file */
@@ -1813,10 +1985,70 @@ Pbuild_file_symtab(struct ps_prochandle *P, file_info_t *fptr)
continue;
}
ctf = cp;
+ } else if (strcmp(cp->c_name, ".gnu_debuglink") == 0) {
+ dprintf("found .gnu_debuglink section for %s\n",
+ fptr->file_rname);
+ /*
+ * Let's make sure of a few things before we do this.
+ */
+ if (cp->c_shdr.sh_type == SHT_PROGBITS &&
+ cp->c_data->d_buf != NULL) {
+ dprintf(".gnu_debuglink pases initial "
+ "sanity\n");
+ dbglink = cp;
+ }
}
}
/*
+ * If we haven't found any symbol table information and we have found a
+ * .gnu_debuglink, it's time to try and figure out where we might find
+ * this. To do so, we're going to first verify that the elf data seems
+ * somewhat sane, eg. the elf data should be a string, so we want to
+ * verify we have a null-terminator.
+ */
+ if (fptr->file_symtab.sym_data_pri == NULL && dbglink != NULL) {
+ char *c = dbglink->c_data->d_buf;
+ size_t i;
+ boolean_t found = B_FALSE;
+ Elf_Data *ed = dbglink->c_data;
+ uint32_t crc;
+
+ for (i = 0; i < ed->d_size; i++) {
+ if (c[i] == '\0') {
+ uintptr_t off;
+ dprintf("got .gnu_debuglink terminator at "
+ "offset %lu\n", (unsigned long)i);
+ /*
+ * After the null terminator, there should be
+ * padding, followed by a 4 byte CRC of the
+ * file. If we don't see this, we're going to
+ * assume this is bogus.
+ */
+ if ((i % sizeof (uint32_t)) == 0)
+ i += 4;
+ else
+ i += i % sizeof (uint32_t);
+ if (i + sizeof (uint32_t) ==
+ dbglink->c_data->d_size) {
+ found = B_TRUE;
+ off = (uintptr_t)ed->d_buf + i;
+ crc = *(uint32_t *)off;
+ } else {
+ dprintf(".gnu_debuglink size mismatch, "
+ "expected: %lu, found: %lu\n",
+ (unsigned long)i,
+ (unsigned long)ed->d_size);
+ }
+ break;
+ }
+ }
+
+ if (found == B_TRUE)
+ find_alt_debug(fptr, dbglink->c_data->d_buf, crc);
+ }
+
+ /*
* At this point, we've found all the symbol tables we're ever going
* to find: the ones in the loop above and possibly the symtab that
* was included in the core file. Before we perform any lookups, we
@@ -1943,7 +2175,13 @@ bad:
fptr->file_elfmem = NULL;
}
(void) close(fptr->file_fd);
+ if (fptr->file_dbgelf != NULL)
+ (void) elf_end(fptr->file_dbgelf);
+ fptr->file_dbgelf = NULL;
+ if (fptr->file_dbgfile >= 0)
+ (void) close(fptr->file_dbgfile);
fptr->file_fd = -1;
+ fptr->file_dbgfile = -1;
}
/*
diff --git a/usr/src/man/man3c/door_call.3c b/usr/src/man/man3c/door_call.3c
index dda79a90a8..f0c470fb24 100644
--- a/usr/src/man/man3c/door_call.3c
+++ b/usr/src/man/man3c/door_call.3c
@@ -1,9 +1,10 @@
'\" te
+.\" Copyright 2015 Nexenta Systems, Inc. All rights reserved.
.\" Copyright (c) 2005, 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 DOOR_CALL 3C "Mar 22, 2005"
+.TH DOOR_CALL 3C "Feb 7, 2015"
.SH NAME
door_call \- invoke the function associated with a door descriptor
.SH SYNOPSIS
@@ -16,7 +17,6 @@ door_call \- invoke the function associated with a door descriptor
.fi
.SH DESCRIPTION
-.sp
.LP
The \fBdoor_call()\fR function invokes the function associated with the door
descriptor \fId\fR, and passes the arguments (if any) specified in
@@ -26,6 +26,41 @@ Passing \fINULL\fR for \fIparams\fR indicates there are no arguments to be
passed and no results expected.
.sp
.LP
+The \fBdoor_arg_t\fR structure includes the following members:
+.sp
+.in +2
+.nf
+typedef struct {
+ char *data_ptr; /* Argument/result data */
+ size_t data_size; /* Argument/result data size */
+ door_desc_t *desc_ptr; /* Argument/result descriptors */
+ uint_t desc_num; /* Argument/result num discriptors */
+ char *rbuf; /* Result area */
+ size_t rsize; /* Result size */
+} door_arg_t;
+.fi
+.in -2
+
+.sp
+.LP
+The \fBdoor_desc_t\fR structure includes the following members:
+.sp
+.in +2
+.nf
+typedef struct {
+ door_attr_t d_attributes; /* Describes the parameter */
+ union {
+ struct {
+ int d_descriptor; /* Descriptor */
+ door_id_t d_id; /* Unique door id */
+ } d_desc;
+ } d_data;
+} door_desc_t;
+.fi
+.in -2
+
+.sp
+.LP
Arguments are specified using the \fBdata_ptr\fR and \fBdesc_ptr\fR members of
\fIparams\fR. The size of the argument data in bytes is passed in
\fBdata_size\fR and the number of argument descriptors is passed in
@@ -61,29 +96,11 @@ will be closed even if \fBdoor_call()\fR returns an error, unless that error
is \fBEFAULT\fR or \fBEBADF\fR.
.sp
.LP
-The \fBdoor_desc_t\fR structure includes the following members:
-.sp
-.in +2
-.nf
-typedef struct {
- door_attr_t d_attributes; /* Describes the parameter */
- union {
- struct {
- int d_descriptor; /* Descriptor */
- door_id_t d_id; /* Unique door id */
- } d_desc;
- } d_data;
- } door_desc_t;
-.fi
-.in -2
-
-.sp
-.LP
When file descriptors are passed or returned, a new descriptor is created in
the target address space and the \fBd_descriptor\fR member in the target
argument is updated to reflect the new descriptor. In addition, the system
passes a system-wide unique number associated with each door in the
-\fBdoor_id\fR member and marks the \fBd_attributes\fR member with other
+\fBd_id\fR member and marks the \fBd_attributes\fR member with other
attributes associated with a door including the following:
.sp
.ne 2
@@ -160,21 +177,11 @@ generated during a \fBdoor_call()\fR operation. If the client aborts in the
middle of a \fBdoor_call()\fR and the door was not created with the
\fBDOOR_NO_CANCEL\fR flag, the server thread is notified using the POSIX (see
\fBstandards\fR(5)) thread cancellation mechanism. See \fBcancellation\fR(5).
-.sp
-.LP
-The descriptor returned from \fBdoor_create()\fR is marked as close on
-\fBexec(FD_CLOEXEC)\fR. Information about a door is available for all clients
-of a door using \fBdoor_info()\fR. Applications concerned with security should
-not place secure information in door data that is accessible by
-\fBdoor_info()\fR. In particular, secure data should not be stored in the data
-item \fIcookie\fR. See \fBdoor_info\fR(3C).
.SH RETURN VALUES
-.sp
.LP
Upon successful completion, \fB0\fR is returned. Otherwise, \fB\(mi1\fR is
returned and \fBerrno\fR is set to indicate the error.
.SH ERRORS
-.sp
.LP
The \fBdoor_call()\fR function will fail if:
.sp
@@ -283,7 +290,6 @@ System could not create overflow area in caller for results.
.RE
.SH ATTRIBUTES
-.sp
.LP
See \fBattributes\fR(5) for descriptions of the following attributes:
.sp
@@ -303,7 +309,6 @@ MT-Level Safe
.TE
.SH SEE ALSO
-.sp
.LP
\fBmunmap\fR(2), \fBdoor_create\fR(3C), \fBdoor_getparam\fR(3C),
\fBdoor_info\fR(3C), \fBdoor_return\fR(3C), \fBlibdoor\fR(3LIB),
diff --git a/usr/src/man/man3c/gethostname.3c b/usr/src/man/man3c/gethostname.3c
index 900a5fa816..44e1c6a501 100644
--- a/usr/src/man/man3c/gethostname.3c
+++ b/usr/src/man/man3c/gethostname.3c
@@ -1,6 +1,7 @@
'\" te
+.\" Copyright 2015 Nexenta Systems, Inc. All rights reserved.
.\" 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 (c) 2004, Sun Microsystems, Inc. All Rights Reserved.
-.TH GETHOSTNAME 3C "Mar 22, 2004"
+.TH GETHOSTNAME 3C "Feb 7, 2015"
.SH NAME
gethostname, sethostname \- get or set name of current host
.SH SYNOPSIS
@@ -17,10 +18,9 @@ gethostname, sethostname \- get or set name of current host
.fi
.SH DESCRIPTION
-.sp
.LP
The \fBgethostname()\fR function returns the standard host name for the current
-processor, as previously set by \fBsethostname()\fR. The \fInamelen\fR
+machine, as previously set by \fBsethostname()\fR. The \fInamelen\fR
argument specifies the size of the array pointed to by \fIname\fR. The returned
name is null-terminated unless insufficient space is provided.
.sp
@@ -33,12 +33,10 @@ superuser and is normally used only when the system is bootstrapped.
Host names are limited to \fBMAXHOSTNAMELEN\fR characters, currently 256,
defined in the <\fBnetdb.h\fR> header.
.SH RETURN VALUES
-.sp
.LP
Upon successful completion, \fBgethostname()\fR and \fBsethostname()\fR return
0. Otherwise, they return \(mi1 and set \fBerrno\fR to indicate the error.
.SH ERRORS
-.sp
.LP
The \fBgethostname()\fR and \fBsethostname()\fR functions will fail if:
.sp
@@ -64,7 +62,6 @@ the calling process.
.RE
.SH ATTRIBUTES
-.sp
.LP
See \fBattributes\fR(5) for descriptions of the following attributes:
.sp
@@ -82,7 +79,6 @@ MT-Level MT-Safe
.TE
.SH SEE ALSO
-.sp
.LP
\fBsysinfo\fR(2), \fBuname\fR(2), \fBgethostid\fR(3C), \fBattributes\fR(5),
\fBstandards\fR(5)
diff --git a/usr/src/man/man3devid/devid_get.3devid b/usr/src/man/man3devid/devid_get.3devid
index 503f653187..d0a006377a 100644
--- a/usr/src/man/man3devid/devid_get.3devid
+++ b/usr/src/man/man3devid/devid_get.3devid
@@ -1,9 +1,13 @@
'\" te
-.\" Copyright (c) 1999, 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 DEVID_GET 3DEVID "Nov 30, 2001"
+.\"
+.\"
+.\" Copyright 2015 Nexenta Systems, Inc. All rights reserved.
+.\" Copyright (c) 1999, Sun Microsystems, Inc. All Rights Reserved
+.\"
+.TH DEVID_GET 3DEVID "Jan 12, 2015"
.SH NAME
devid_get, devid_compare, devid_deviceid_to_nmlist, devid_free,
devid_free_nmlist, devid_get_minor_name, devid_sizeof, devid_str_decode,
@@ -71,7 +75,6 @@ applications
.fi
.SH DESCRIPTION
-.sp
.LP
These functions provide unique identifiers (device \fBID\fRs) for devices.
Applications and device drivers use these functions to identify and locate
@@ -102,7 +105,7 @@ The \fBdevid_deviceid_to_nmlist()\fR function returns an array of
\fIdevid_nmlist\fR structures, where each entry matches the \fIdevid\fR and
\fIminor_name\fR passed in. If the \fIminor_name\fR specified is one of the
special values (\fBDEVID_MINOR_NAME_ALL\fR, \fBDEVID_MINOR_NAME_ALL_CHR\fR, or
-\fBDEVID_MINOR_NAME_ALL_BLK\fR) , then all minor names associated with
+\fBDEVID_MINOR_NAME_ALL_BLK\fR), then all minor names associated with
\fIdevid\fR which also meet the special \fIminor_name\fR filtering requirements
are returned. The \fIdevid_nmlist\fR structure contains the device name and
device number. The last entry of the array contains a null pointer for the
@@ -116,7 +119,7 @@ complete if called with the device ID of an unattached device.
.sp
.LP
The \fBdevid_free_nmlist()\fR function frees the memory allocated by the
-\fBdevid_deviceid_to_nmlist()\fR function.
+\fBdevid_deviceid_to_nmlist()\fR function and returned in the \fIretlist\fR.
.sp
.LP
The \fBdevid_compare()\fR function compares two device \fBID\fRs and determines
@@ -170,9 +173,8 @@ function. A non-null returned minor name must be freed by calling
.LP
The \fBdevid_str_free()\fR function frees the character string returned by
\fBdevid_str_encode()\fR and the \fIretminor_name\fR argument returned by
-\fBdevid_str_decode()\fR.
+\fBdevid_str_decode()\fR and \fBdevid_get_minor_name()\fR.
.SH RETURN VALUES
-.sp
.LP
Upon successful completion, the \fBdevid_get()\fR,
\fBdevid_get_minor_name()\fR, \fBdevid_str_decode()\fR, and
@@ -283,7 +285,6 @@ devid_free_nmlist(list);
.in -2
.SH ATTRIBUTES
-.sp
.LP
See \fBattributes\fR(5) for description of the following attributes:
.sp
@@ -301,7 +302,6 @@ Interface Stability Stable
.TE
.SH SEE ALSO
-.sp
.LP
\fBfree\fR(3C), \fBlibdevid\fR(3LIB), \fBattributes\fR(5),
\fBddi_devid_compare\fR(9F)
diff --git a/usr/src/man/man3head/stat.h.3head b/usr/src/man/man3head/stat.h.3head
index 81bc0375d8..0e4ccde84a 100644
--- a/usr/src/man/man3head/stat.h.3head
+++ b/usr/src/man/man3head/stat.h.3head
@@ -4,7 +4,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 STAT.H 3HEAD "Sep 1, 2009"
+.TH STAT.H 3HEAD "Jan 22, 2015"
.SH NAME
stat.h, stat \- data returned by stat system call
.SH SYNOPSIS
@@ -15,7 +15,6 @@ stat.h, stat \- data returned by stat system call
.fi
.SH DESCRIPTION
-.sp
.LP
The system calls \fBstat()\fR, \fBlstat()\fR and \fBfstat()\fR return data in a
\fBstat\fR structure, which is defined in <\fBstat.h\fR>.
@@ -88,12 +87,11 @@ of the range [0, 999 999 999], for use with the \fBfutimens()\fR and
.in +2
.nf
#define UTIME_NOW use the current time
-@define UTIME_OMIT no time change
+#define UTIME_OMIT no time change
.fi
.in -2
.SH ATTRIBUTES
-.sp
.LP
See \fBattributes\fR(5) for descriptions of the following attributes:
.sp
@@ -111,7 +109,6 @@ Standard See \fBstandards\fR(5).
.TE
.SH SEE ALSO
-.sp
.LP
\fBfutimens\fR(2), \fBstat\fR(2), \fBtypes.h\fR(3HEAD), \fBattributes\fR(5),
\fBstandards\fR(5)
diff --git a/usr/src/man/man3nsl/rpc_soc.3nsl b/usr/src/man/man3nsl/rpc_soc.3nsl
index b0b2059a09..c01be93ad0 100644
--- a/usr/src/man/man3nsl/rpc_soc.3nsl
+++ b/usr/src/man/man3nsl/rpc_soc.3nsl
@@ -1,9 +1,10 @@
'\" te
+.\" Copyright 2015 Nexenta Systems, Inc. All rights reserved.
.\" Copyright (C) 2001, 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 RPC_SOC 3NSL "Jun 7, 2001"
+.TH RPC_SOC 3NSL "Feb 7, 2015"
.SH NAME
rpc_soc, authdes_create, authunix_create, authunix_create_default, callrpc,
clnt_broadcast, clntraw_create, clnttcp_create, clntudp_bufcreate,
@@ -184,7 +185,6 @@ int svc_fds;
.fi
.SH DESCRIPTION
-.sp
.LP
\fBRPC\fR routines allow C programs to make procedure calls on other machines
across the network. First, the client calls a procedure to send a request to
@@ -198,7 +198,6 @@ routines. The preferred routine is given after the description of the routine.
New programs should use the preferred routines, as support for the older
interfaces may be dropped in future releases.
.SS "File Descriptors"
-.sp
.LP
Transport independent \fBRPC\fR uses \fBTLI\fR as its transport interface
instead of sockets.
@@ -212,7 +211,6 @@ with both \fBlibrpcsoc\fR and \fBlibnsl\fR. If the user passed
\fBlibnsl\fR only, then the routine will return a \fBTLI\fR file descriptor
and not a socket.
.SS "Routines"
-.sp
.LP
The following routines require that the header \fB<rpc/rpc.h>\fR be included.
The symbol \fBPORTMAP\fR should be defined so that the appropriate function
@@ -618,7 +616,7 @@ resultant read file descriptor bit mask. The routine returns when all file
descriptors associated with the value of \fIrdfds\fR have been serviced. This
routine is similar to \fBsvc_getreqset()\fR but is limited to 32 descriptors.
.sp
-This interface is made obsolete by \fBsvc_getreqset()\fR
+This interface is made obsolete by \fBsvc_getreqset()\fR.
.RE
.sp
@@ -801,7 +799,6 @@ This routine exists for backward compatibility only, and is made obsolete by
.RE
.SH ATTRIBUTES
-.sp
.LP
See \fBattributes\fR(5) for descriptions of the following attributes:
.sp
@@ -817,7 +814,6 @@ MT-Level Unsafe
.TE
.SH SEE ALSO
-.sp
.LP
\fBkeyserv\fR(1M), \fBrpcbind\fR(1M), \fBrpcinfo\fR(1M), \fBnetdir\fR(3NSL),
\fBnetdir_getbyname\fR(3NSL), \fBrpc\fR(3NSL), \fBrpc_clnt_auth\fR(3NSL),
@@ -827,7 +823,6 @@ MT-Level Unsafe
\fBsecure_rpc\fR(3NSL), \fBselect\fR(3C), \fBxdr_authsys_parms\fR(3NSL),
\fBlibnsl\fR(3LIB), \fBlibrpcsoc\fR(3LIBUCB), \fBattributes\fR(5)
.SH NOTES
-.sp
.LP
These interfaces are unsafe in multithreaded applications. Unsafe interfaces
should be called only from the main thread.
diff --git a/usr/src/man/man3nsl/t_open.3nsl b/usr/src/man/man3nsl/t_open.3nsl
index 8c0db86557..a3278a991b 100644
--- a/usr/src/man/man3nsl/t_open.3nsl
+++ b/usr/src/man/man3nsl/t_open.3nsl
@@ -1,4 +1,5 @@
'\" te
+.\" Copyright 2015 Nexenta Systems, Inc. All rights reserved.
.\" Copyright 1994, The X/Open Company Ltd. All Rights Reserved Portions Copyright 1989 AT&T Portions Copyright (c) 1998, Sun Microsystems, Inc. All Rights Reserved
.\" Sun Microsystems, Inc. gratefully acknowledges The Open Group for permission to reproduce portions of its copyrighted documentation. Original documentation from The Open Group can be obtained online at
.\" http://www.opengroup.org/bookstore/.
@@ -7,7 +8,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 T_OPEN 3NSL "May 7, 1998"
+.TH T_OPEN 3NSL "Jan 27, 2015"
.SH NAME
t_open \- establish a transport endpoint
.SH SYNOPSIS
@@ -23,7 +24,6 @@ t_open \- establish a transport endpoint
.fi
.SH DESCRIPTION
-.sp
.LP
This routine is part of the \fBXTI\fR interfaces which evolved from the
\fBTLI\fR interfaces. \fBXTI\fR represents the future evolution of these
@@ -234,16 +234,13 @@ time.
If \fIinfo\fR is set to a null pointer by the transport user, no protocol
information is returned by \fBt_open()\fR.
.SH RETURN VALUES
-.sp
.LP
A valid file descriptor is returned upon successful completion. Otherwise, a
value of -1 is returned and \fBt_errno\fR is set to indicate an error.
.SH VALID STATES
-.sp
.LP
\fBT_UNINIT\fR.
.SH ERRORS
-.sp
.LP
On failure, \fBt_errno\fR is set to the following:
.sp
@@ -285,27 +282,23 @@ A system error has occurred during execution of this function.
.RE
.SH TLI COMPATIBILITY
-.sp
.LP
The \fBXTI\fR and \fBTLI\fR interface definitions have common names but use
different header files. This and other semantic differences between the two
interfaces are described in the subsections below.
.SS "Interface Header"
-.sp
.LP
-The \fBXTI\fR interfaces use the \fBxti.h\fR \fBTLI\fR interfaces should
+The \fBXTI\fR interfaces use the \fBxti.h\fR. \fBTLI\fR interfaces should
\fInot\fR use this header. They should use the header:
.br
.in +2
#include <tiuser.h>
.in -2
.SS "Error Description Values"
-.sp
.LP
The \fBt_errno\fR values \fBTPROTO\fR and \fBTBADNAME\fR can be set by the
\fBXTI\fR interface but cannot be set by the \fBTLI\fR interface.
.SS "Notes"
-.sp
.LP
For \fBTLI\fR , the \fBt_info\fR structure referenced by \fIinfo\fR lacks the
following structure member:
@@ -329,7 +322,6 @@ data, and disconnection data. The corresponding structure members are
\fBaddr\fR, \fBoptions\fR, \fBtsdu\fR, \fBestdu\fR, \fBconnect\fR, and
\fBdiscon\fR, respectively.
.SH ATTRIBUTES
-.sp
.LP
See \fBattributes\fR(5) for descriptions of the following attributes:
.sp
@@ -345,6 +337,5 @@ MT Level Safe
.TE
.SH SEE ALSO
-.sp
.LP
\fBopen\fR(2), \fBattributes\fR(5)
diff --git a/usr/src/man/man4/nodename.4 b/usr/src/man/man4/nodename.4
index 82af9a7827..216fc93a50 100644
--- a/usr/src/man/man4/nodename.4
+++ b/usr/src/man/man4/nodename.4
@@ -1,9 +1,10 @@
'\" te
+.\" Copyright 2015 Nexenta Systems, Inc. All rights reserved.
.\" Copyright (c) 2004, 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 NODENAME 4 "Feb 9, 2004"
+.TH NODENAME 4 "Feb 7, 2015"
.SH NAME
nodename \- local source for system name
.SH SYNOPSIS
@@ -13,7 +14,6 @@ nodename \- local source for system name
.fi
.SH DESCRIPTION
-.sp
.LP
When a machine is standalone or its IP address is configured locally, the
\fB/etc/nodename\fR file contains the system name. By convention, the system
@@ -26,11 +26,6 @@ protocol, the \fB/etc/nodename\fR file is not used, as the system name is
delivered by the remote service.
.sp
.LP
-Given a system name value, regardless of source, the \fBuname\fR utility
-invoked with the \fB-S\fR option is used to set the system name of the running
-system.
-.sp
-.LP
If the machine's network configuration is delivered by the DHCP protocol, the
\fB/etc/nodename\fR file is used only if the DHCP server does not provide a
value for the Hostname option (DHCP standard option code 12).
@@ -60,12 +55,10 @@ myhost
.in -2
.SH SEE ALSO
-.sp
.LP
\fBNIS+\fR(1), \fBuname\fR(1), \fBnamed\fR(1M), \fBypbind\fR(1M),
\fBattributes\fR(5)
.SH NOTES
-.sp
.LP
The \fBnodename\fR file is modified by Solaris installation and de-installation
scripts.
diff --git a/usr/src/man/man4/proc.4 b/usr/src/man/man4/proc.4
index e7058c410d..c0a044164a 100644
--- a/usr/src/man/man4/proc.4
+++ b/usr/src/man/man4/proc.4
@@ -665,6 +665,18 @@ the process. \fBpr_what\fR is unused in this case.
.RE
.sp
+.ne 2
+.na
+\fB\fBPR_BRAND\fR\fR
+.ad
+.RS 17n
+indicates that the lwp stopped for a brand-specific reason. Interpretation
+of the value of \fBpr_what\fR 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 \fBproc\fR(4) consumers.
+.RE
+
+.sp
.LP
\fBpr_cursig\fR names the current signal, that is, the next signal to be
delivered to the lwp, if any. \fBpr_info\fR, when the lwp is in a
diff --git a/usr/src/man/man5/privileges.5 b/usr/src/man/man5/privileges.5
index 7fc9c00f45..5f70da9e9a 100644
--- a/usr/src/man/man5/privileges.5
+++ b/usr/src/man/man5/privileges.5
@@ -4,11 +4,10 @@
.\" The contents of this file are subject to the terms of the Common Development and Distribution License (the "License"). You may not use this file except in compliance with the License. You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE or http://www.opensolaris.org/os/licensing.
.\" See the License for the specific language governing permissions and limitations under the License. When distributing Covered Code, include this CDDL HEADER in each file and include the License file at usr/src/OPENSOLARIS.LICENSE. If applicable, add the 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 PRIVILEGES 5 "March 7, 2012"
+.TH PRIVILEGES 5 "Feb 3, 2015"
.SH NAME
privileges \- process privilege model
.SH DESCRIPTION
-.sp
.LP
Solaris software implements a set of privileges that provide fine-grained
control over the actions of processes. The possession of a certain privilege
@@ -194,6 +193,16 @@ Extensions.
.sp
.ne 2
.na
+\fB\fBPRIV_FILE_FLAG_SET\fR\fR
+.ad
+.sp .6
+.RS 4n
+Allows a process to set immutable, nounlink or appendonly file attributes.
+.RE
+
+.sp
+.ne 2
+.na
\fB\fBPRIV_FILE_LINK_ANY\fR\fR
.ad
.sp .6
@@ -222,6 +231,16 @@ modify that file's or directory's permission bits or ACL.
.sp
.ne 2
.na
+\fB\fBPRIV_FILE_READ\fR\fR
+.ad
+.sp .6
+.RS 4n
+Allow a process to read objects in the filesystem.
+.RE
+
+.sp
+.ne 2
+.na
\fB\fBPRIV_FILE_SETID\fR\fR
.ad
.sp .6
@@ -252,11 +271,11 @@ Extensions.
.sp
.ne 2
.na
-\fB\fBPRIV_FILE_FLAG_SET\fR\fR
+\fB\fBPRIV_FILE_WRITE\fR\fR
.ad
.sp .6
.RS 4n
-Allows a process to set immutable, nounlink or appendonly file attributes.
+Allow a process to modify objects in the filesytem.
.RE
.sp
@@ -331,6 +350,16 @@ Segment.
.sp
.ne 2
.na
+\fB\fBPRIV_NET_ACCESS\fR\fR
+.ad
+.sp .6
+.RS 4n
+Allow a process to open a TCP, UDP, SDP, or SCTP network endpoint.
+.RE
+
+.sp
+.ne 2
+.na
\fB\fBPRIV_NET_BINDMLP\fR\fR
.ad
.sp .6
@@ -376,6 +405,21 @@ Extensions.
.sp
.ne 2
.na
+\fB\fBPRIV_NET_MAC_IMPLICIT\fR\fR
+.ad
+.sp .6
+.RS 4n
+Allow a proces to set \fBSO_MAC_IMPLICIT\fR option by using
+\fBsetsockopt\fR(3SOCKET). This allows a privileged process to transmit
+implicitly-labeled packets to a peer.
+.sp
+This privilege is interpreted only if the system is configured with
+Trusted Extensions.
+.RE
+
+.sp
+.ne 2
+.na
\fB\fBPRIV_NET_OBSERVABILITY\fR\fR
.ad
.sp .6
@@ -660,6 +704,16 @@ Allow a process to increase the size of a System V IPC Message Queue buffer.
.sp
.ne 2
.na
+\fB\fBPRIV_SYS_IPTUN_CONFIG\fR\fR
+.ad
+.sp .6
+.RS 4n
+Allow a process to configure IP tunnel links.
+.RE
+
+.sp
+.ne 2
+.na
\fB\fBPRIV_SYS_LINKDIR\fR\fR
.ad
.sp .6
@@ -1187,7 +1241,6 @@ set, the system does not honor the set-uid bit of set-uid root applications.
The following unsafe privileges have been identified: \fBproc_setid\fR,
\fBsys_resource\fR and \fBproc_audit\fR.
.SS "Privilege Escalation"
-.sp
.LP
In certain circumstances, a single privilege could lead to a process gaining
one or more additional privileges that were not explicitly granted to that
@@ -1223,7 +1276,6 @@ privileges they need.
Daemons that never need to \fBexec\fR subprocesses should remove the
\fBPRIV_PROC_EXEC\fR privilege from their permitted and limit sets.
.SS "Assigned Privileges and Safeguards"
-.sp
.LP
When privileges are assigned to a user, the system administrator could give
that user more powers than intended. The administrator should consider whether
@@ -1232,7 +1284,6 @@ privilege is given to a user, the administrator should consider setting the
\fBproject.max-locked-memory\fR resource control as well, to prevent that user
from locking all memory.
.SS "Privilege Debugging"
-.sp
.LP
When a system call fails with a permission error, it is not always immediately
obvious what caused the problem. To debug such a problem, you can use a tool
@@ -1253,7 +1304,6 @@ set priv_debug = 1
.LP
On a running system, you can use \fBmdb\fR(1) to change this variable.
.SS "Privilege Administration"
-.sp
.LP
The Solaris Management Console (see \fBsmc\fR(1M)) is the preferred method of
modifying privileges for a command. Use \fBusermod\fR(1M) or \fBsmrole\fR(1M)
@@ -1261,7 +1311,6 @@ to assign privileges to or modify privileges for, respectively, a user or a
role. Use \fBppriv\fR(1) to enumerate the privileges supported on a system and
\fBtruss\fR(1) to determine which privileges a program requires.
.SH SEE ALSO
-.sp
.LP
\fBmdb\fR(1), \fBppriv\fR(1), \fBadd_drv\fR(1M), \fBifconfig\fR(1M),
\fBlockd\fR(1M), \fBnfsd\fR(1M), \fBpppd\fR(1M), \fBrem_drv\fR(1M),
diff --git a/usr/src/man/man9f/getrbuf.9f b/usr/src/man/man9f/getrbuf.9f
index cba7e2e4cd..d1381ed592 100644
--- a/usr/src/man/man9f/getrbuf.9f
+++ b/usr/src/man/man9f/getrbuf.9f
@@ -1,10 +1,11 @@
'\" te
+.\" Copyright 2015 Nexenta Systems, Inc. All rights reserved.
.\" Copyright 1989 AT&T
.\" Copyright (c) 2006 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 GETRBUF 9F "Jan 16, 2006"
+.TH GETRBUF 9F "Jan 27, 2015"
.SH NAME
getrbuf \- get a raw buffer header
.SH SYNOPSIS
@@ -20,11 +21,9 @@ getrbuf \- get a raw buffer header
.fi
.SH INTERFACE LEVEL
-.sp
.LP
Architecture independent level 1 (DDI/DKI).
.SH PARAMETERS
-.sp
.ne 2
.na
\fB\fIsleepflag\fR\fR
@@ -34,7 +33,6 @@ Indicates whether driver should sleep for free space.
.RE
.SH DESCRIPTION
-.sp
.LP
The \fBgetrbuf()\fR function allocates the space for a buffer header to the
caller. It is used in cases where a block driver is performing raw (character
@@ -43,25 +41,22 @@ the buffer cache.
.sp
.LP
The \fBgetrbuf()\fR function calls \fBkmem_alloc\fR(9F) to perform the memory
-allocation. \fBkmem_alloc()\fRrequires the information included in the
+allocation. \fBkmem_alloc()\fR requires the information included in the
\fIsleepflag\fR argument. If \fIsleepflag\fR is set to \fBKM_SLEEP\fR, the
driver may sleep until the space is freed up. If \fIsleepflag\fR is set to
\fBKM_NOSLEEP\fR, the driver will not sleep. In either case, a pointer to the
allocated space is returned or \fBNULL\fR to indicate that no space was
available.
.SH RETURN VALUES
-.sp
.LP
The \fBgetrbuf()\fR function returns a pointer to the allocated buffer header,
or \fBNULL\fR if no space is available.
.SH CONTEXT
-.sp
.LP
The \fBgetrbuf()\fR function can be called from user, interrupt, or kernel
context. (Drivers must not allow \fBgetrbuf()\fR to sleep if called from an
interrupt routine.)
.SH SEE ALSO
-.sp
.LP
\fBbioinit\fR(9F), \fBfreerbuf\fR(9F), \fBkmem_alloc\fR(9F),
\fBkmem_free\fR(9F)
diff --git a/usr/src/pkg/Makefile b/usr/src/pkg/Makefile
index db17d50104..0e6dd06ffe 100644
--- a/usr/src/pkg/Makefile
+++ b/usr/src/pkg/Makefile
@@ -21,6 +21,7 @@
#
# Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+# Copyright 2015, OmniTI Computer Consulting, Inc. All rights reserved.
#
include $(SRC)/Makefile.master
@@ -176,6 +177,7 @@ PKGMOG_DEFINES= \
PKGVERS_BUILTON=$(PKGVERS_BUILTON) \
PKGVERS_BRANCH=$(PKGVERS_BRANCH) \
PKGVERS=$(PKGVERS) \
+ PERL_ARCH=$(PERL_ARCH) \
PERL_VERSION=$(PERL_VERSION) \
PERL_PKGVERS=$(PERL_PKGVERS)
diff --git a/usr/src/pkg/manifests/runtime-perl-module-sun-solaris.mf b/usr/src/pkg/manifests/runtime-perl-module-sun-solaris.mf
index 4174d35fe5..f444a4548a 100644
--- a/usr/src/pkg/manifests/runtime-perl-module-sun-solaris.mf
+++ b/usr/src/pkg/manifests/runtime-perl-module-sun-solaris.mf
@@ -22,11 +22,9 @@
#
# Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
# Copyright (c) 2014 Racktop Systems.
+# Copyright 2015, OmniTI Computer Consulting, Inc. All rights reserved.
#
-$(i386_ONLY)<transform file dir path=.*PLAT.* -> edit path PLAT i86pc>
-$(sparc_ONLY)<transform file dir path=.*PLAT.* -> edit path PLAT sun4>
-
<transform file path=.*\.(pm|bs) -> default mode 0444>
<transform file path=.*\.so -> default mode 0555>
set name=pkg.fmri \
@@ -39,51 +37,41 @@ dir path=usr group=sys
dir path=usr/perl5
dir path=usr/perl5/$(PERL_VERSION)
dir path=usr/perl5/$(PERL_VERSION)/lib
-dir path=usr/perl5/$(PERL_VERSION)/lib/PLAT-solaris-64int
-dir path=usr/perl5/$(PERL_VERSION)/lib/PLAT-solaris-64int/Sun
-dir path=usr/perl5/$(PERL_VERSION)/lib/PLAT-solaris-64int/Sun/Solaris
-dir path=usr/perl5/$(PERL_VERSION)/lib/PLAT-solaris-64int/auto
-dir path=usr/perl5/$(PERL_VERSION)/lib/PLAT-solaris-64int/auto/Sun
-dir path=usr/perl5/$(PERL_VERSION)/lib/PLAT-solaris-64int/auto/Sun/Solaris
-dir \
- path=usr/perl5/$(PERL_VERSION)/lib/PLAT-solaris-64int/auto/Sun/Solaris/Intrs
-dir \
- path=usr/perl5/$(PERL_VERSION)/lib/PLAT-solaris-64int/auto/Sun/Solaris/Kstat
-dir \
- path=usr/perl5/$(PERL_VERSION)/lib/PLAT-solaris-64int/auto/Sun/Solaris/Lgrp
-dir \
- path=usr/perl5/$(PERL_VERSION)/lib/PLAT-solaris-64int/auto/Sun/Solaris/Project
-dir \
- path=usr/perl5/$(PERL_VERSION)/lib/PLAT-solaris-64int/auto/Sun/Solaris/Task
-dir \
- path=usr/perl5/$(PERL_VERSION)/lib/PLAT-solaris-64int/auto/Sun/Solaris/Utils
+dir path=usr/perl5/$(PERL_VERSION)/lib/$(PERL_ARCH)
+dir path=usr/perl5/$(PERL_VERSION)/lib/$(PERL_ARCH)/Sun
+dir path=usr/perl5/$(PERL_VERSION)/lib/$(PERL_ARCH)/Sun/Solaris
+dir path=usr/perl5/$(PERL_VERSION)/lib/$(PERL_ARCH)/auto
+dir path=usr/perl5/$(PERL_VERSION)/lib/$(PERL_ARCH)/auto/Sun
+dir path=usr/perl5/$(PERL_VERSION)/lib/$(PERL_ARCH)/auto/Sun/Solaris
+dir path=usr/perl5/$(PERL_VERSION)/lib/$(PERL_ARCH)/auto/Sun/Solaris/Intrs
+dir path=usr/perl5/$(PERL_VERSION)/lib/$(PERL_ARCH)/auto/Sun/Solaris/Kstat
+dir path=usr/perl5/$(PERL_VERSION)/lib/$(PERL_ARCH)/auto/Sun/Solaris/Lgrp
+dir path=usr/perl5/$(PERL_VERSION)/lib/$(PERL_ARCH)/auto/Sun/Solaris/Project
+dir path=usr/perl5/$(PERL_VERSION)/lib/$(PERL_ARCH)/auto/Sun/Solaris/Task
+dir path=usr/perl5/$(PERL_VERSION)/lib/$(PERL_ARCH)/auto/Sun/Solaris/Utils
dir path=usr/perl5/$(PERL_VERSION)/lib/Sun
dir path=usr/perl5/$(PERL_VERSION)/lib/Sun/Solaris
dir path=usr/perl5/$(PERL_VERSION)/lib/Sun/Solaris/BSM
dir path=usr/share/man
dir path=usr/share/man/man3perl
+file path=usr/perl5/$(PERL_VERSION)/lib/$(PERL_ARCH)/Sun/Solaris/Intrs.pm
+file path=usr/perl5/$(PERL_VERSION)/lib/$(PERL_ARCH)/Sun/Solaris/Kstat.pm
+file path=usr/perl5/$(PERL_VERSION)/lib/$(PERL_ARCH)/Sun/Solaris/Lgrp.pm
+file path=usr/perl5/$(PERL_VERSION)/lib/$(PERL_ARCH)/Sun/Solaris/Project.pm
+file path=usr/perl5/$(PERL_VERSION)/lib/$(PERL_ARCH)/Sun/Solaris/Task.pm
+file path=usr/perl5/$(PERL_VERSION)/lib/$(PERL_ARCH)/Sun/Solaris/Utils.pm
file \
- path=usr/perl5/$(PERL_VERSION)/lib/PLAT-solaris-64int/Sun/Solaris/Intrs.pm
-file \
- path=usr/perl5/$(PERL_VERSION)/lib/PLAT-solaris-64int/Sun/Solaris/Kstat.pm
-file path=usr/perl5/$(PERL_VERSION)/lib/PLAT-solaris-64int/Sun/Solaris/Lgrp.pm
-file \
- path=usr/perl5/$(PERL_VERSION)/lib/PLAT-solaris-64int/Sun/Solaris/Project.pm
-file path=usr/perl5/$(PERL_VERSION)/lib/PLAT-solaris-64int/Sun/Solaris/Task.pm
-file \
- path=usr/perl5/$(PERL_VERSION)/lib/PLAT-solaris-64int/Sun/Solaris/Utils.pm
-file \
- path=usr/perl5/$(PERL_VERSION)/lib/PLAT-solaris-64int/auto/Sun/Solaris/Intrs/Intrs.so
+ path=usr/perl5/$(PERL_VERSION)/lib/$(PERL_ARCH)/auto/Sun/Solaris/Intrs/Intrs.so
file \
- path=usr/perl5/$(PERL_VERSION)/lib/PLAT-solaris-64int/auto/Sun/Solaris/Kstat/Kstat.so
+ path=usr/perl5/$(PERL_VERSION)/lib/$(PERL_ARCH)/auto/Sun/Solaris/Kstat/Kstat.so
file \
- path=usr/perl5/$(PERL_VERSION)/lib/PLAT-solaris-64int/auto/Sun/Solaris/Lgrp/Lgrp.so
+ path=usr/perl5/$(PERL_VERSION)/lib/$(PERL_ARCH)/auto/Sun/Solaris/Lgrp/Lgrp.so
file \
- path=usr/perl5/$(PERL_VERSION)/lib/PLAT-solaris-64int/auto/Sun/Solaris/Project/Project.so
+ path=usr/perl5/$(PERL_VERSION)/lib/$(PERL_ARCH)/auto/Sun/Solaris/Project/Project.so
file \
- path=usr/perl5/$(PERL_VERSION)/lib/PLAT-solaris-64int/auto/Sun/Solaris/Task/Task.so
+ path=usr/perl5/$(PERL_VERSION)/lib/$(PERL_ARCH)/auto/Sun/Solaris/Task/Task.so
file \
- path=usr/perl5/$(PERL_VERSION)/lib/PLAT-solaris-64int/auto/Sun/Solaris/Utils/Utils.so
+ path=usr/perl5/$(PERL_VERSION)/lib/$(PERL_ARCH)/auto/Sun/Solaris/Utils/Utils.so
file path=usr/perl5/$(PERL_VERSION)/lib/Sun/Solaris/BSM/_BSMparse.pm
file path=usr/perl5/$(PERL_VERSION)/lib/Sun/Solaris/Pg.pm
file path=usr/share/man/man3perl/Kstat.3perl
diff --git a/usr/src/tools/ctf/cvt/ctf.c b/usr/src/tools/ctf/cvt/ctf.c
index 1e425758c2..83f09cb43e 100644
--- a/usr/src/tools/ctf/cvt/ctf.c
+++ b/usr/src/tools/ctf/cvt/ctf.c
@@ -1216,7 +1216,7 @@ decompress_ctf(caddr_t cbuf, size_t cbufsz, caddr_t dbuf, size_t dbufsz)
(rc = inflate(&zstr, Z_NO_FLUSH)) != Z_STREAM_END ||
(rc = inflateEnd(&zstr)) != Z_OK) {
warning("CTF decompress zlib error %s\n", zError(rc));
- return (NULL);
+ return (0);
}
debug(3, "reflated %lu bytes to %lu, pointer at %d\n",
diff --git a/usr/src/tools/ctf/cvt/dwarf.c b/usr/src/tools/ctf/cvt/dwarf.c
index e261818d3a..5fdcd35e33 100644
--- a/usr/src/tools/ctf/cvt/dwarf.c
+++ b/usr/src/tools/ctf/cvt/dwarf.c
@@ -1714,7 +1714,7 @@ static const die_creator_t die_creators[] = {
{ DW_TAG_variable, DW_F_NOTDP, die_variable_create },
{ DW_TAG_volatile_type, 0, die_volatile_create },
{ DW_TAG_restrict_type, 0, die_restrict_create },
- { 0, NULL }
+ { 0, 0, NULL }
};
static const die_creator_t *
diff --git a/usr/src/tools/ctf/cvt/output.c b/usr/src/tools/ctf/cvt/output.c
index f699fbf6d3..90c87dabf3 100644
--- a/usr/src/tools/ctf/cvt/output.c
+++ b/usr/src/tools/ctf/cvt/output.c
@@ -23,8 +23,6 @@
* Use is subject to license terms.
*/
-#pragma ident "%Z%%M% %I% %E% SMI"
-
/*
* Routines for preparing tdata trees for conversion into CTF data, and
* for placing the resulting data into an output file.
@@ -466,7 +464,7 @@ write_file(Elf *src, const char *srcname, Elf *dst, const char *dstname,
int pad;
int i;
- if (gelf_newehdr(dst, gelf_getclass(src)) == NULL)
+ if (gelf_newehdr(dst, gelf_getclass(src)) == 0)
elfterminate(dstname, "Cannot copy ehdr to temp file");
gelf_getehdr(src, &sehdr);
memcpy(&dehdr, &sehdr, sizeof (GElf_Ehdr));
@@ -482,7 +480,7 @@ write_file(Elf *src, const char *srcname, Elf *dst, const char *dstname,
*/
if (sehdr.e_phnum != 0) {
(void) elf_flagelf(dst, ELF_C_SET, ELF_F_LAYOUT);
- if (gelf_newphdr(dst, sehdr.e_phnum) == NULL)
+ if (gelf_newphdr(dst, sehdr.e_phnum) == 0)
elfterminate(dstname, "Cannot make phdrs in temp file");
for (i = 0; i < sehdr.e_phnum; i++) {
@@ -616,7 +614,7 @@ write_file(Elf *src, const char *srcname, Elf *dst, const char *dstname,
}
}
- if (gelf_update_shdr(dscn, &shdr) == NULL)
+ if (gelf_update_shdr(dscn, &shdr) == 0)
elfterminate(dstname, "Cannot update sect %s", sname);
new_offset = (off_t)shdr.sh_offset;
diff --git a/usr/src/tools/ctf/stabs/common/genassym.c b/usr/src/tools/ctf/stabs/common/genassym.c
index 9183e52ffd..7c1aa73b7a 100644
--- a/usr/src/tools/ctf/stabs/common/genassym.c
+++ b/usr/src/tools/ctf/stabs/common/genassym.c
@@ -23,8 +23,6 @@
* Use is subject to license terms.
*/
-#pragma ident "%Z%%M% %I% %E% SMI"
-
/*
* In this mode, we generate header files containg various #defines which can
* be used to access members of various structures, and to walk through arrays.
@@ -182,7 +180,8 @@ ga_process_name(char *line)
if (shdef != NULL) {
ssize_t tsz;
- for (shift = -1, tsz = sz; tsz > 0; tsz >>= 1, shift++);
+ for (shift = -1, tsz = sz; tsz > 0; tsz >>= 1, shift++)
+ ;
if (shift < 0 || 1 << shift != sz) {
return (parse_warn("Can't make shift #define: %s size "
"(%d) isn't a power of 2", curname, sz));
@@ -273,7 +272,7 @@ ga_member_find(ctf_id_t curtype, ga_member_cb_data_t *md)
int rc;
if ((c = strchr(md->gmcb_memname, '.')) != NULL)
- *c++ = NULL;
+ *c++ = '\0';
md->gmcb_submem = c;
if ((rc = ctf_member_iter(ctf, curtype, ga_member_cb, md)) == 0) {
diff --git a/usr/src/tools/env/illumos.sh b/usr/src/tools/env/illumos.sh
index 162f1f702f..c5bf4154d9 100644
--- a/usr/src/tools/env/illumos.sh
+++ b/usr/src/tools/env/illumos.sh
@@ -21,6 +21,7 @@
# Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
# Copyright 2010, 2011 Nexenta Systems, Inc. All rights reserved.
# Copyright 2012 Joshua M. Clulow <josh@sysmgr.org>
+# Copyright 2015, OmniTI Computer Consulting, Inc. All rights reserved.
#
# Configuration variables for the runtime environment of the nightly
@@ -230,6 +231,13 @@ export SPRO_VROOT="$SPRO_ROOT"
# Uncomment this to disable support for SMB printing.
# export ENABLE_SMB_PRINTING='#'
+# If your distro uses certain versions of Perl, make sure either Makefile.master
+# contains your new defaults OR your .env file sets them.
+# These are how you would override for building on OmniOS r151012, for example.
+#export PERL_VERSION=5.16.1
+#export PERL_ARCH=i86pc-solaris-thread-multi-64int
+#export PERL_PKGVERS=-5161
+
#
# These checks ensure that if we accidentally run a program linked against the
# proto area, that we then fail the build.
diff --git a/usr/src/tools/protocmp/exception_list.c b/usr/src/tools/protocmp/exception_list.c
index 5dd98fb6fe..f44f5296e9 100644
--- a/usr/src/tools/protocmp/exception_list.c
+++ b/usr/src/tools/protocmp/exception_list.c
@@ -22,6 +22,8 @@
/*
* Copyright 2010 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
+ *
+ * Copyright 2015 PALO, Richard
*/
#include <stdio.h>
@@ -56,6 +58,10 @@ parse_exception_line(char *line, elem_list *list)
}
e = (elem *) malloc(sizeof (elem));
+ if (e == NULL) {
+ perror("malloc");
+ exit(1);
+ }
e->inode = 0;
e->perm = 0;
@@ -68,7 +74,7 @@ parse_exception_line(char *line, elem_list *list)
e->symsrc = NULL;
e->file_type = DIR_T;
- while ((e->arch = assign_arch(arch)) == NULL) {
+ while ((e->arch = assign_arch(arch)) == 0) {
if ((arch = strtok(NULL, FS)) == NULL) {
return (0);
}
diff --git a/usr/src/tools/protocmp/protocmp.c b/usr/src/tools/protocmp/protocmp.c
index a2897477cb..4cd1b56eb2 100644
--- a/usr/src/tools/protocmp/protocmp.c
+++ b/usr/src/tools/protocmp/protocmp.c
@@ -24,8 +24,6 @@
* Use is subject to license terms.
*/
-#pragma ident "%Z%%M% %I% %E% SMI"
-
#include <stdio.h>
#include <fcntl.h>
#include <stdlib.h>
@@ -685,7 +683,7 @@ main(int argc, char **argv)
{
int errflg = 0;
int i, c;
- int list_filtered_exceptions = NULL;
+ int list_filtered_exceptions = 0;
int n_proto_refs = 0;
int n_exception_files = 0;
char *proto_refs[MAX_PROTO_REFS];
@@ -729,7 +727,7 @@ main(int argc, char **argv)
errflg++;
(void) fprintf(stderr,
"Only %d exception files supported\n",
- MAX_EXCEPTION_FILES);
+ MAX_EXCEPTION_FILES);
} else {
exception_files[n_exception_files++] = optarg;
}
@@ -745,7 +743,7 @@ main(int argc, char **argv)
errflg++;
(void) fprintf(stderr,
"Only %d proto references supported\n",
- MAX_PROTO_REFS);
+ MAX_PROTO_REFS);
} else {
proto_refs[n_proto_refs++] = optarg;
}
diff --git a/usr/src/tools/protocmp/protodir.c b/usr/src/tools/protocmp/protodir.c
index 352c36a8b2..520f5359f3 100644
--- a/usr/src/tools/protocmp/protodir.c
+++ b/usr/src/tools/protocmp/protodir.c
@@ -24,9 +24,6 @@
* Use is subject to license terms.
*/
-#pragma ident "%Z%%M% %I% %E% SMI"
-
-
#include <stdio.h>
#include <sys/param.h>
#include <fcntl.h>
@@ -424,7 +421,7 @@ read_pkginfo(const char *protodir, short *arch, char *basedir)
(void) fclose(pkginfo_fp);
if (architecture[0])
- if ((*arch = assign_arch(architecture)) == NULL) {
+ if ((*arch = assign_arch(architecture)) == 0) {
(void) fprintf(stderr,
"warning: Unknown architecture %s found in %s\n",
architecture, pkginfofile);
diff --git a/usr/src/uts/common/brand/lx/os/lx_brand.c b/usr/src/uts/common/brand/lx/os/lx_brand.c
index b964aab1d3..4507c0303c 100644
--- a/usr/src/uts/common/brand/lx/os/lx_brand.c
+++ b/usr/src/uts/common/brand/lx/os/lx_brand.c
@@ -62,6 +62,7 @@
#include <sys/sdt.h>
#include <sys/x86_archext.h>
#include <sys/controlregs.h>
+#include <sys/core.h>
#include <lx_signum.h>
int lx_debug = 0;
@@ -77,6 +78,10 @@ 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 waitsys32(idtype_t, id_t, siginfo_t *, int);
+#endif
extern void lx_proc_exit(proc_t *, klwp_t *);
static void lx_psig_to_proc(proc_t *, kthread_t *, int);
@@ -106,35 +111,38 @@ static int lx_elfexec(struct vnode *vp, struct execa *uap, struct uarg *args,
caddr_t exec_file, struct cred *cred, int brand_action);
static boolean_t lx_native_exec(uint8_t, const char **);
-static void lx_ptrace_exectrap(proc_t *);
static uint32_t lx_map32limit(proc_t *);
/* lx brand */
struct brand_ops lx_brops = {
- lx_init_brand_data,
- lx_free_brand_data,
- lx_brandsys,
- lx_setbrand,
- lx_getattr,
- lx_setattr,
- lx_copy_procdata,
- lx_proc_exit,
- lx_exec,
- lx_setrval,
- lx_initlwp,
- lx_forklwp,
- lx_freelwp,
- lx_exitlwp,
- lx_elfexec,
- NULL,
- NULL,
- lx_psig_to_proc,
- NSIG,
- lx_exit_with_sig,
- lx_wait_filter,
- lx_native_exec,
- lx_ptrace_exectrap,
- lx_map32limit
+ 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_initlwp, /* b_initlwp */
+ 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_psig_to_proc, /* b_psig_to_proc */
+ NSIG, /* b_nsig */
+ lx_exit_with_sig, /* b_exit_with_sig */
+ lx_wait_filter, /* b_wait_filter */
+ lx_native_exec, /* b_native_exec */
+ NULL, /* b_ptrace_exectrap */
+ lx_map32limit, /* b_map32limit */
+ lx_stop_notify, /* b_stop_notify */
+ lx_waitid_helper, /* b_waitid_helper */
+ lx_sigcld_repost, /* b_sigcld_repost */
+ lx_issig_stop /* b_issig_stop */
};
struct brand_mach_ops lx_mops = {
@@ -166,33 +174,39 @@ static struct modlinkage modlinkage = {
void
lx_proc_exit(proc_t *p, klwp_t *lwp)
{
- zone_t *z = p->p_zone;
int sig = ptolxproc(p)->l_signal;
- ASSERT(p->p_brand != NULL);
- ASSERT(p->p_brand_data != NULL);
-
- /*
- * If init is dying and we aren't explicitly shutting down the zone
- * or the system, then Solaris is about to restart init. The Linux
- * init is not designed to handle a restart, which it interprets as
- * a reboot. To give it a sane environment in which to run, we
- * reboot the zone.
- */
- if (p->p_pid == z->zone_proc_initpid) {
- if (z->zone_boot_err == 0 &&
- z->zone_restart_init &&
- zone_status_get(z) < ZONE_IS_SHUTTING_DOWN &&
- zone_status_get(global_zone) < ZONE_IS_SHUTTING_DOWN)
- (void) zone_kadmin(A_REBOOT, 0, NULL, CRED());
- }
+ VERIFY(p->p_brand == &lx_brand);
+ VERIFY(p->p_brand_data != NULL);
/*
* We might get here if fork failed (e.g. ENOMEM) so we don't always
* have an lwp (see brand_clearbrand).
*/
- if (lwp != NULL)
+ if (lwp != NULL) {
+ boolean_t reenter_mutex = B_FALSE;
+
+ /*
+ * This brand entry point is called variously with and without
+ * the process p_lock held. It would be possible to refactor
+ * the brand infrastructure so that proc_exit() explicitly
+ * calls this hook (b_lwpexit/lx_exitlwp) for the last LWP in a
+ * process prior to detaching the brand with
+ * brand_clearbrand(). Absent such refactoring, we
+ * conditionally exit the mutex for the duration of the call.
+ *
+ * The atomic replacement of both "p_brand" and "p_brand_data"
+ * is not affected by dropping and reacquiring the mutex here.
+ */
+ if (mutex_owned(&p->p_lock) != 0) {
+ mutex_exit(&p->p_lock);
+ reenter_mutex = B_TRUE;
+ }
lx_exitlwp(lwp);
+ if (reenter_mutex) {
+ mutex_enter(&p->p_lock);
+ }
+ }
/*
* The call path here is:
@@ -260,310 +274,6 @@ lx_getattr(zone_t *zone, int attr, void *buf, size_t *bufsize)
return (-EINVAL);
}
-/*
- * Enable/disable ptrace system call tracing for the given LWP. Enabling is
- * done by both setting the flag in that LWP's brand data (in the kernel) and
- * setting the process-wide trace flag (in the brand library of the traced
- * process).
- */
-static int
-lx_ptrace_syscall_set(pid_t pid, id_t lwpid, int set)
-{
- proc_t *p;
- kthread_t *t;
- klwp_t *lwp;
- lx_proc_data_t *lpdp;
- lx_lwp_data_t *lldp;
- uintptr_t addr;
- int ret, flag = 1;
-
- if ((p = sprlock(pid)) == NULL)
- return (ESRCH);
-
- if (priv_proc_cred_perm(curproc->p_cred, p, NULL, VWRITE) != 0) {
- sprunlock(p);
- return (EPERM);
- }
-
- if ((t = idtot(p, lwpid)) == NULL || (lwp = ttolwp(t)) == NULL) {
- sprunlock(p);
- return (ESRCH);
- }
-
- if ((lpdp = p->p_brand_data) == NULL ||
- (lldp = lwp->lwp_brand) == NULL) {
- sprunlock(p);
- return (ESRCH);
- }
-
- if (set) {
- /*
- * Enable the ptrace flag for this LWP and this process. Note
- * that we will turn off the LWP's ptrace flag, but we don't
- * turn off the process's ptrace flag.
- */
- lldp->br_ptrace = 1;
- lpdp->l_ptrace = 1;
-
- addr = lpdp->l_traceflag;
-
- mutex_exit(&p->p_lock);
-
- /*
- * This can fail only in some rare corner cases where the
- * process is exiting or we're completely out of memory. In
- * these cases, it's sufficient to return an error to the ptrace
- * consumer and leave the process-wide flag set.
- */
- ret = uwrite(p, &flag, sizeof (flag), addr);
-
- mutex_enter(&p->p_lock);
-
- /*
- * If we couldn't set the trace flag, unset the LWP's ptrace
- * flag as there ptrace consumer won't expect this LWP to stop.
- */
- if (ret != 0)
- lldp->br_ptrace = 0;
- } else {
- lldp->br_ptrace = 0;
- ret = 0;
- }
-
- sprunlock(p);
-
- if (ret != 0)
- ret = EIO;
-
- return (ret);
-}
-
-static void
-lx_ptrace_fire(void)
-{
- kthread_t *t = curthread;
- klwp_t *lwp = ttolwp(t);
- lx_lwp_data_t *lldp = lwp->lwp_brand;
-
- /*
- * The ptrace flag only applies until the next event is encountered
- * for the given LWP. If it's set, turn off the flag and poke the
- * controlling process by raising a signal.
- */
- if (lldp->br_ptrace) {
- lldp->br_ptrace = 0;
- tsignal(t, SIGTRAP);
- }
-}
-
-/*
- * Supports Linux PTRACE_SETOPTIONS handling which is similar to PTRACE_TRACEME
- * but return an event in the second byte of si_status.
- */
-static int
-lx_ptrace_ext_opts(int cmd, pid_t pid, uintptr_t val, int64_t *rval)
-{
- proc_t *p;
- lx_proc_data_t *lpdp;
- uint_t ret;
-
- if ((p = sprlock(pid)) == NULL)
- return (ESRCH);
-
- /*
- * Note that priv_proc_cred_perm can disallow access to ourself if
- * the proc's SNOCD p_flag is set, so we skip that check for ourself.
- */
- if (curproc != p &&
- priv_proc_cred_perm(curproc->p_cred, p, NULL, VWRITE) != 0) {
- sprunlock(p);
- return (EPERM);
- }
-
- if ((lpdp = p->p_brand_data) == NULL) {
- sprunlock(p);
- return (ESRCH);
- }
-
- switch (cmd) {
- case B_PTRACE_EXT_OPTS_SET:
- lpdp->l_ptrace_opts = (uint_t)val;
- break;
-
- case B_PTRACE_EXT_OPTS_GET:
- ret = lpdp->l_ptrace_opts;
- if (lpdp->l_ptrace_is_traced)
- ret |= EMUL_PTRACE_IS_TRACED;
- break;
-
- case B_PTRACE_EXT_OPTS_EVT:
- ret = lpdp->l_ptrace_event;
- lpdp->l_ptrace_event = 0;
- break;
-
- case B_PTRACE_DETACH:
- lpdp->l_ptrace_is_traced = 0;
- break;
-
- default:
- sprunlock(p);
- return (EINVAL);
- }
-
- sprunlock(p);
-
- if (cmd == B_PTRACE_EXT_OPTS_GET || cmd == B_PTRACE_EXT_OPTS_EVT) {
- if (copyout(&ret, (void *)val, sizeof (uint_t)) != 0)
- return (EFAULT);
- }
-
- *rval = 0;
- return (0);
-}
-
-/*
- * Used to support Linux PTRACE_SETOPTIONS handling and similar to
- * PTRACE_TRACEME. We signal ourselves to stop on return from this syscall and
- * setup the event reason so the emulation can pull this out when someone
- * 'waits' on this process.
- */
-static void
-lx_ptrace_stop_for_option(int option, ulong_t msg)
-{
- proc_t *p = ttoproc(curthread);
- sigqueue_t *sqp;
- lx_proc_data_t *lpdp;
- boolean_t child = B_FALSE;
-
- if ((lpdp = p->p_brand_data) == NULL) {
- /* this should never happen but just to be safe */
- return;
- }
-
- if (option & EMUL_PTRACE_O_CHILD) {
- child = B_TRUE;
- option &= ~EMUL_PTRACE_O_CHILD;
- }
-
- lpdp->l_ptrace_is_traced = 1;
-
- /* Track the event as the reason for stopping */
- switch (option) {
- case LX_PTRACE_O_TRACEFORK:
- if (!child) {
- lpdp->l_ptrace_event = LX_PTRACE_EVENT_FORK;
- lpdp->l_ptrace_eventmsg = msg;
- }
- break;
- case LX_PTRACE_O_TRACEVFORK:
- if (!child) {
- lpdp->l_ptrace_event = LX_PTRACE_EVENT_VFORK;
- lpdp->l_ptrace_eventmsg = msg;
- }
- break;
- case LX_PTRACE_O_TRACECLONE:
- if (!child) {
- lpdp->l_ptrace_event = LX_PTRACE_EVENT_CLONE;
- lpdp->l_ptrace_eventmsg = msg;
- }
- break;
- case LX_PTRACE_O_TRACEEXEC:
- lpdp->l_ptrace_event = LX_PTRACE_EVENT_EXEC;
- break;
- case LX_PTRACE_O_TRACEVFORKDONE:
- lpdp->l_ptrace_event = LX_PTRACE_EVENT_VFORK_DONE;
- lpdp->l_ptrace_eventmsg = msg;
- break;
- case LX_PTRACE_O_TRACEEXIT:
- lpdp->l_ptrace_event = LX_PTRACE_EVENT_EXIT;
- lpdp->l_ptrace_eventmsg = msg;
- break;
- case LX_PTRACE_O_TRACESECCOMP:
- lpdp->l_ptrace_event = LX_PTRACE_EVENT_SECCOMP;
- break;
- }
-
- /*
- * Post the required signal to ourselves so that we stop.
- *
- * Although Linux will send a SIGSTOP to a child process which is
- * stopped due to PTRACE_O_TRACEFORK, etc., we do not send that signal
- * since that leads us down the code path in the kernel which calls
- * stop(PR_JOBCONTROL, SIGSTOP), which in turn means that the TS_XSTART
- * flag gets turned off on the thread and this makes it complex to
- * actually get this process going when the userland application wants
- * to detach. Since consumers don't seem to depend on the specific
- * signal, we'll just stop both the parent and child the same way. We
- * do keep track of both the parent and child via the
- * EMUL_PTRACE_O_CHILD bit, in case we need to revisit this later.
- */
- psignal(p, SIGTRAP);
-
- /*
- * Since we're stopping, we need to post the SIGCHLD to the parent. The
- * code in sigcld expects p_wdata to be set to SIGTRAP before it can
- * send the signal, so do that here. We also need p_wcode to be set as
- * if we are ptracing, even though we're not really (see the code in
- * stop() when procstop is set and p->p_proc_flag has the P_PR_PTRACE
- * bit set). This is needed so that when the application calls waitid,
- * it will properly retrieve the process.
- */
- sqp = kmem_zalloc(sizeof (sigqueue_t), KM_SLEEP);
- mutex_enter(&pidlock);
- p->p_wdata = SIGTRAP;
- p->p_wcode = CLD_TRAPPED;
- sigcld(p, sqp);
- mutex_exit(&pidlock);
-}
-
-static int
-lx_ptrace_geteventmsg(pid_t pid, ulong_t *msgp)
-{
- proc_t *p;
- lx_proc_data_t *lpdp;
- ulong_t msg;
-
- if ((p = sprlock(pid)) == NULL)
- return (ESRCH);
-
- if (curproc != p &&
- priv_proc_cred_perm(curproc->p_cred, p, NULL, VREAD) != 0) {
- sprunlock(p);
- return (EPERM);
- }
-
- if ((lpdp = p->p_brand_data) == NULL) {
- sprunlock(p);
- return (ESRCH);
- }
-
- msg = lpdp->l_ptrace_eventmsg;
- lpdp->l_ptrace_eventmsg = 0;
-
- sprunlock(p);
-
- if (copyout(&msg, (void *)msgp, sizeof (ulong_t)) != 0)
- return (EFAULT);
-
- return (0);
-}
-
-/*
- * Brand entry to allow us to optionally generate the ptrace SIGTRAP on exec().
- * This will only be called if ptrace is enabled -- and we only generate the
- * SIGTRAP if LX_PTRACE_O_TRACEEXEC hasn't been set.
- */
-void
-lx_ptrace_exectrap(proc_t *p)
-{
- lx_proc_data_t *lpdp;
-
- if ((lpdp = p->p_brand_data) == NULL ||
- !(lpdp->l_ptrace_opts & LX_PTRACE_O_TRACEEXEC)) {
- psignal(p, SIGTRAP);
- }
-}
-
uint32_t
lx_map32limit(proc_t *p)
{
@@ -718,6 +428,12 @@ lx_init_brand_data(zone_t *zone)
(void) strlcpy(data->lxzd_kernel_version, "2.4.21", LX_VERS_MAX);
data->lxzd_max_syscall = LX_NSYSCALLS;
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;
}
void
@@ -752,6 +468,8 @@ lx_brandsys(int cmd, int64_t *rval, uintptr_t arg1, uintptr_t arg2,
struct termios *termios;
uint_t termios_len;
int error;
+ int code;
+ int sig;
lx_brand_registration_t reg;
lx_lwp_data_t *lwpd;
@@ -832,6 +550,16 @@ lx_brandsys(int cmd, int64_t *rval, uintptr_t arg1, uintptr_t arg2,
lwpd->br_scms = 1;
#endif
+ if (pd->l_traceflag != NULL && pd->l_ptrace != 0) {
+ /*
+ * If ptrace(2) is active on this process, it is likely
+ * that we just finished an emulated execve(2) in a
+ * traced child. The usermode traceflag will have been
+ * clobbered by the exec, so we set it again here:
+ */
+ (void) suword32((void *)pd->l_traceflag, 1);
+ }
+
*rval = 0;
return (0);
case B_TTYMODES:
@@ -931,11 +659,6 @@ lx_brandsys(int cmd, int64_t *rval, uintptr_t arg1, uintptr_t arg2,
return (0);
}
- case B_PTRACE_SYSCALL:
- *rval = lx_ptrace_syscall_set((pid_t)arg1, (id_t)arg2,
- (int)arg3);
- return (0);
-
case B_SYSENTRY:
if (lx_systrace_enabled) {
ASSERT(lx_systrace_entry_ptr != NULL);
@@ -963,7 +686,7 @@ lx_brandsys(int cmd, int64_t *rval, uintptr_t arg1, uintptr_t arg2,
#endif
}
- lx_ptrace_fire();
+ (void) lx_ptrace_stop(LX_PR_SYSENTRY);
pd = p->p_brand_data;
@@ -984,7 +707,7 @@ lx_brandsys(int cmd, int64_t *rval, uintptr_t arg1, uintptr_t arg2,
(*lx_systrace_return_ptr)(arg1, arg2, arg2, 0, 0, 0, 0);
}
- lx_ptrace_fire();
+ (void) lx_ptrace_stop(LX_PR_SYSEXIT);
pd = p->p_brand_data;
@@ -1010,20 +733,55 @@ lx_brandsys(int cmd, int64_t *rval, uintptr_t arg1, uintptr_t arg2,
*/
return (lx_sched_affinity(cmd, arg1, arg2, arg3, rval));
- case B_PTRACE_EXT_OPTS:
+ case B_PTRACE_STOP_FOR_OPT:
+ return (lx_ptrace_stop_for_option((int)arg1, arg2 == 0 ?
+ B_FALSE : B_TRUE, (ulong_t)arg3));
+
+ case B_PTRACE_CLONE_BEGIN:
+ return (lx_ptrace_set_clone_inherit((int)arg1, arg2 == 0 ?
+ B_FALSE : B_TRUE));
+
+ case B_PTRACE_KERNEL:
+ return (lx_ptrace_kernel((int)arg1, (pid_t)arg2, arg3, arg4));
+
+ case B_HELPER_WAITID: {
+ idtype_t idtype = (idtype_t)arg1;
+ id_t id = (id_t)arg2;
+ siginfo_t *infop = (siginfo_t *)arg3;
+ int options = (int)arg4;
+
+ lwpd = ttolxlwp(curthread);
+
/*
- * Set or get the ptrace extended options or get the event
- * reason for the stop.
+ * Our brand-specific waitid helper only understands a subset of
+ * the possible idtypes. Ensure we keep to that subset here:
*/
- return (lx_ptrace_ext_opts((int)arg1, (pid_t)arg2, arg3, rval));
+ if (idtype != P_ALL && idtype != P_PID && idtype != P_PGID) {
+ return (EINVAL);
+ }
- case B_PTRACE_STOP_FOR_OPT:
- lx_ptrace_stop_for_option((int)arg1, (ulong_t)arg2);
- return (0);
+ /*
+ * 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 = (int)arg5;
+
+#if defined(_SYSCALL32_IMPL)
+ if (get_udatamodel() != DATAMODEL_NATIVE) {
+ return (waitsys32(idtype, id, infop, options));
+ } else
+#endif
+ {
+ return (waitsys(idtype, id, infop, options));
+ }
+
+ lwpd->br_waitid_emulate = B_FALSE;
+ lwpd->br_waitid_flags = 0;
- case B_PTRACE_GETEVENTMSG:
- lx_ptrace_geteventmsg((pid_t)arg1, (ulong_t *)arg2);
return (0);
+ }
case B_UNSUPPORTED:
{
@@ -1186,7 +944,19 @@ lx_brandsys(int cmd, int64_t *rval, uintptr_t arg1, uintptr_t arg2,
return (0);
case B_EXIT_AS_SIG:
- exit(CLD_KILLED, (int)arg1);
+ 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;
@@ -1254,6 +1024,7 @@ lx_copy_procdata(proc_t *child, proc_t *parent)
ppd = parent->p_brand_data;
ASSERT(ppd != NULL);
+ ASSERT(parent->p_brand == &lx_brand);
cpd = kmem_alloc(sizeof (lx_proc_data_t), KM_SLEEP);
*cpd = *ppd;
@@ -1322,13 +1093,14 @@ lx_elfexec(struct vnode *vp, struct execa *uap, struct uarg *args,
struct execenv origenv;
stack_t orig_sigaltstack;
struct user *up = PTOU(ttoproc(curthread));
- lx_elf_data_t *edp =
- &((lx_proc_data_t *)ttoproc(curthread)->p_brand_data)->l_elf_data;
+ lx_elf_data_t *edp;
char *lib_path = NULL;
ASSERT(ttoproc(curthread)->p_brand == &lx_brand);
ASSERT(ttoproc(curthread)->p_brand_data != NULL);
+ edp = &ttolxproc(curthread)->l_elf_data;
+
if (args->to_model == DATAMODEL_NATIVE) {
lib_path = LX_LIB_PATH;
}
@@ -1685,6 +1457,7 @@ _init(void)
/* for lx_futex() */
lx_futex_init();
+ lx_ptrace_init();
err = mod_install(&modlinkage);
if (err != 0) {
@@ -1724,6 +1497,7 @@ _fini(void)
if (brand_zone_count(&lx_brand))
return (EBUSY);
+ lx_ptrace_fini();
lx_pid_fini();
lx_ioctl_fini();
diff --git a/usr/src/uts/common/brand/lx/os/lx_misc.c b/usr/src/uts/common/brand/lx/os/lx_misc.c
index c550ecf9af..abb0ab6e63 100644
--- a/usr/src/uts/common/brand/lx/os/lx_misc.c
+++ b/usr/src/uts/common/brand/lx/os/lx_misc.c
@@ -80,7 +80,7 @@ lx_exec()
klwp_t *lwp = ttolwp(curthread);
struct lx_lwp_data *lwpd = lwptolxlwp(lwp);
proc_t *p = ttoproc(curthread);
- lx_proc_data_t *pd = p->p_brand_data;
+ lx_proc_data_t *pd = ptolxproc(p);
int err;
/*
@@ -113,6 +113,13 @@ lx_exec()
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);
+
/* clear the fsbase values until the app. can reinitialize them */
lwpd->br_lx_fsbase = NULL;
lwpd->br_ntv_fsbase = NULL;
@@ -137,15 +144,21 @@ void
lx_exitlwp(klwp_t *lwp)
{
struct lx_lwp_data *lwpd = lwptolxlwp(lwp);
- proc_t *p;
+ 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)
return; /* second time thru' */
+ mutex_enter(&p->p_lock);
+ lx_ptrace_exit(p, lwp);
+ mutex_exit(&p->p_lock);
+
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,
@@ -226,9 +239,17 @@ lx_freelwp(klwp_t *lwp)
if (lwpd != NULL) {
(void) removectx(lwptot(lwp), lwp, lx_save, lx_restore,
NULL, NULL, lx_save, NULL);
- if (lwpd->br_pid != 0)
+ if (lwpd->br_pid != 0) {
lx_pid_rele(lwptoproc(lwp)->p_pid,
lwptot(lwp)->t_tid);
+ }
+
+ /*
+ * 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));
@@ -238,8 +259,8 @@ lx_freelwp(klwp_t *lwp)
int
lx_initlwp(klwp_t *lwp)
{
- struct lx_lwp_data *lwpd;
- struct lx_lwp_data *plwpd;
+ lx_lwp_data_t *lwpd;
+ lx_lwp_data_t *plwpd = ttolxlwp(curthread);
kthread_t *tp = lwptot(lwp);
lwpd = kmem_zalloc(sizeof (struct lx_lwp_data), KM_SLEEP);
@@ -265,8 +286,7 @@ lx_initlwp(klwp_t *lwp)
if (tp->t_next == tp) {
lwpd->br_ppid = tp->t_procp->p_ppid;
lwpd->br_ptid = -1;
- } else if (ttolxlwp(curthread) != NULL) {
- plwpd = ttolxlwp(curthread);
+ } 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;
@@ -292,6 +312,14 @@ lx_initlwp(klwp_t *lwp)
installctx(lwptot(lwp), lwp, lx_save, lx_restore, NULL, NULL,
lx_save, NULL);
+ /*
+ * 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, lwpd);
+ }
+
return (0);
}
@@ -524,10 +552,7 @@ lx_exit_with_sig(proc_t *cp, sigqueue_t *sqp, void *brand_data)
* SIGCHLD X -
*
* This is an XOR of __WCLONE being set, and SIGCHLD being the signal sent on
- * process exit. Since (flags & __WCLONE) is not guaranteed to have the
- * least-significant bit set when the flags is enabled, !! is used to place
- * that bit into the least significant bit. Then, the bitwise XOR can be
- * used, because there is no logical XOR in the C language.
+ * process exit.
*
* More information on wait in lx brands can be found at
* usr/src/lib/brand/lx/lx_brand/common/wait.c.
@@ -535,29 +560,45 @@ lx_exit_with_sig(proc_t *cp, sigqueue_t *sqp, void *brand_data)
boolean_t
lx_wait_filter(proc_t *pp, proc_t *cp)
{
- int flags;
+ lx_lwp_data_t *lwpd = ttolxlwp(curthread);
+ int flags = lwpd->br_waitid_flags;
boolean_t ret;
- if (LX_ARGS(waitid) != NULL) {
- flags = LX_ARGS(waitid)->waitid_flags;
- mutex_enter(&cp->p_lock);
- if (flags & LX_WALL) {
- ret = B_TRUE;
- } else if (cp->p_stat == SZOMB ||
- cp->p_brand == &native_brand) {
- ret = (((!!(flags & LX_WCLONE)) ^
- (stol_signo[SIGCHLD] == cp->p_exit_data))
- ? B_TRUE : B_FALSE);
+ if (!lwpd->br_waitid_emulate) {
+ return (B_TRUE);
+ }
+
+ mutex_enter(&cp->p_lock);
+ if (flags & LX_WALL) {
+ ret = B_TRUE;
+
+ } else {
+ int exitsig;
+ boolean_t is_clone, _wclone;
+
+ /*
+ * Determine the exit signal for this process:
+ */
+ if (cp->p_stat == SZOMB || cp->p_brand == &native_brand) {
+ exitsig = cp->p_exit_data;
} else {
- ret = (((!!(flags & LX_WCLONE)) ^
- (stol_signo[SIGCHLD] == ptolxproc(cp)->l_signal))
- ? B_TRUE : B_FALSE);
+ exitsig = ptolxproc(cp)->l_signal;
}
- mutex_exit(&cp->p_lock);
- return (ret);
- } else {
- return (B_TRUE);
+
+ /*
+ * To enable the bitwise XOR to stand in for the absent C
+ * logical XOR, we use the logical NOT operator twice to
+ * ensure the least significant bit is populated with the
+ * __WCLONE flag status.
+ */
+ _wclone = !!(flags & LX_WCLONE);
+ is_clone = (stol_signo[SIGCHLD] == exitsig);
+
+ ret = (_wclone ^ is_clone) ? B_TRUE : B_FALSE;
}
+ mutex_exit(&cp->p_lock);
+
+ return (ret);
}
void
diff --git a/usr/src/uts/common/brand/lx/os/lx_pid.c b/usr/src/uts/common/brand/lx/os/lx_pid.c
index aa8c751bc2..8552754c43 100644
--- a/usr/src/uts/common/brand/lx/os/lx_pid.c
+++ b/usr/src/uts/common/brand/lx/os/lx_pid.c
@@ -22,7 +22,7 @@
/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
- * Copyright (c) 2014, Joyent, Inc. All rights reserved.
+ * Copyright 2015, Joyent, Inc.
*/
#include <sys/types.h>
@@ -222,6 +222,28 @@ lx_lpid_to_spair(pid_t l_pid, pid_t *s_pid, id_t *s_tid)
{
struct lx_pid *hp;
+ if (l_pid == 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 (s_pid != NULL)
+ *s_pid = initpid;
+ if (s_tid != NULL)
+ *s_tid = 1;
+
+ return (0);
+ }
+
mutex_enter(&hash_lock);
for (hp = ltos_pid_hash[LTOS_HASH(l_pid)]; hp; hp = hp->ltos_next) {
if (l_pid == hp->l_pid) {
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..6e4b74531d
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/os/lx_ptrace.c
@@ -0,0 +1,2270 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL 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.
+ */
+
+/*
+ * 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_PTRACE_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. 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_PTRACE_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_PTRACE_SYSCALL flag is
+ * cleared after each stop; for ongoing system call tracing the tracee must
+ * be continuously restarted with PTRACE_SYSCALL.
+ *
+ * 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_PTRACE_STOPPING or LX_PTRACE_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 <sys/lx_pid.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;
+
+/*
+ * Macros for checking the state of an LWP via "br_ptrace_flags":
+ */
+#define LX_PTRACE_BUSY \
+ (LX_PTRACE_EXITING | LX_PTRACE_STOPPING | LX_PTRACE_CLONING)
+
+#define VISIBLE(a) (((a)->br_ptrace_flags & LX_PTRACE_EXITING) == 0)
+#define TRACEE_BUSY(a) (((a)->br_ptrace_flags & LX_PTRACE_BUSY) != 0)
+
+#define ACCORD_HELD(a) MUTEX_HELD(&(a)->lxpa_lock)
+
+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;
+ pid_t apid;
+ id_t atid;
+ 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_to_spair(lxpid, &apid, &atid) != 0 ||
+ (aproc = sprlock(apid)) == NULL) {
+ return (ESRCH);
+ }
+
+ /*
+ * Locate the tracer LWP itself and ensure that it is visible to
+ * ptrace(2).
+ */
+ if ((athr = idtot(aproc, atid)) == NULL ||
+ (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_PTRACE_CLDPEND;
+ }
+ thread_unlock(rt);
+}
+
+static void
+lx_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 = 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_whystop = 0;
+ remote->br_ptrace_whatstop = 0;
+ remote->br_ptrace_flags &= ~LX_PTRACE_CLDPEND;
+ }
+}
+
+/*
+ * 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_PTRACE_STOPPING);
+ VERIFY((accord = lwpd->br_ptrace_tracer) != NULL);
+
+ /*
+ * We must drop our process lock to take "pidlock". The
+ * LX_PTRACE_STOPPING flag protects us from an exiting 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 a detaching 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_PTRACE_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;
+
+ /*
+ * 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_PTRACE_PARENT_WAIT) && pp != NULL) {
+ cldpost = B_TRUE;
+ lx_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_PTRACE_STOPPING;
+ lwpd->br_ptrace_flags |= LX_PTRACE_STOPPED;
+ if (cldpend) {
+ /*
+ * We sent the SIGCLD for this new wait condition already.
+ */
+ lwpd->br_ptrace_flags |= LX_PTRACE_CLDPEND;
+ }
+
+ /*
+ * If lx_ptrace_exit_tracer() is trying to detach our tracer, it will
+ * be sleeping on this CV until LX_PTRACE_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.
+ */
+static int
+lx_ptrace_lock_if_stopped(lx_ptrace_accord_t *accord, lx_lwp_data_t *remote)
+{
+ 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_PTRACE_STOPPED)) {
+ /*
+ * 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);
+}
+
+/*
+ * 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;
+
+ /*
+ * Handle the syscall-stop flag if this is a PTRACE_SYSCALL restart:
+ */
+ if (flags & LX_PTC_SYSCALL) {
+ remote->br_ptrace_flags |= LX_PTRACE_SYSCALL;
+ } else {
+ remote->br_ptrace_flags &= ~LX_PTRACE_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 set runnable. If the specified LWP is not
+ * currently in the "ptrace-stop" state, the routine will return ESRCH as if
+ * the LWP did not exist at all.
+ *
+ * The caller must not hold p_lock on any process.
+ */
+static int
+lx_ptrace_detach(lx_ptrace_accord_t *accord, lx_lwp_data_t *remote, int signo,
+ boolean_t *release_hold)
+{
+ klwp_t *rlwp;
+
+ rlwp = remote->br_lwp;
+
+ /*
+ * The tracee LWP was in "ptrace-stop" and we now hold its p_lock.
+ * Detach the LWP from the accord and set it running.
+ */
+ VERIFY(!TRACEE_BUSY(remote));
+ remote->br_ptrace_flags &= ~(LX_PTRACE_SYSCALL | LX_PTRACE_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;
+ *release_hold = B_TRUE;
+
+ /*
+ * The tracer may, as described in lx_ptrace_cont(), choose to suppress
+ * or modify the delivered signal.
+ */
+ remote->br_ptrace_stopsig = signo;
+
+ lx_ptrace_restart_lwp(rlwp);
+
+ return (0);
+}
+
+/*
+ * 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;
+ int32_t one = 1;
+ /*
+ * Our (Tracer) LWP:
+ */
+ lx_ptrace_accord_t *accord;
+ lx_lwp_data_t *lwpd = ttolxlwp(curthread);
+ /*
+ * Remote (Tracee) LWP:
+ */
+ pid_t rpid;
+ id_t rtid;
+ 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_PTRACE_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_to_spair(lx_pid, &rpid, &rtid) != 0 ||
+ (rproc = sprlock(rpid)) == NULL) {
+ /*
+ * We could not find the target process.
+ */
+ goto errout;
+ }
+
+ /*
+ * Locate the tracee LWP.
+ */
+ if ((rthr = idtot(rproc, rtid)) == NULL ||
+ (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;
+
+ /*
+ * Bond the tracee to the accord.
+ */
+ VERIFY0(rlwpd->br_ptrace_flags & LX_PTRACE_EXITING);
+ VERIFY(rlwpd->br_ptrace_attach == LX_PTA_NONE);
+ rlwpd->br_ptrace_attach = LX_PTA_ATTACH;
+ rlwpd->br_ptrace_tracer = accord;
+
+ /*
+ * 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);
+ mutex_exit(&accord->lxpa_tracees_lock);
+
+ /*
+ * Send a thread-directed SIGSTOP.
+ */
+ sigtoproc(rproc, rthr, SIGSTOP);
+
+ /*
+ * Set the in-kernel process-wide ptrace(2) enable flag.
+ * Attempt also to write the usermode trace flag so that the
+ * process knows to enter the kernel for potential ptrace(2)
+ * syscall-stops.
+ */
+ rprocd = ttolxproc(rthr);
+ rprocd->l_ptrace = 1;
+ mutex_exit(&rproc->p_lock);
+ (void) uwrite(rproc, &one, sizeof (one), rprocd->l_traceflag);
+ mutex_enter(&rproc->p_lock);
+
+ 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);
+
+ mutex_enter(&p->p_lock);
+
+ switch (option) {
+ case LX_PTRACE_O_TRACEFORK:
+ case LX_PTRACE_O_TRACEVFORK:
+ case LX_PTRACE_O_TRACECLONE:
+ lwpd->br_ptrace_clone_option = option;
+ break;
+
+ default:
+ return (EINVAL);
+ }
+
+ if (inherit_flag) {
+ lwpd->br_ptrace_flags |= LX_PTRACE_INHERIT;
+ } else {
+ lwpd->br_ptrace_flags &= ~LX_PTRACE_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 unlock = B_FALSE;
+
+ if (srcp == dstp) {
+ /*
+ * This is syslwp_create(), so the process p_lock is already
+ * held.
+ */
+ VERIFY(MUTEX_HELD(&srcp->p_lock));
+ } else {
+ unlock = B_TRUE;
+ 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_PTRACE_INHERIT) != 0) {
+ /*
+ * Condition 2:
+ * If the caller opted in to inheritance with the
+ * PTRACE_CLONE flag to clone(2), the LX_PTRACE_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_PTRACE_INHERIT;
+ src->br_ptrace_clone_option = 0;
+
+ if (dst->br_ptrace_attach == LX_PTA_NONE) {
+ /*
+ * No condition triggered inheritance.
+ */
+ goto out;
+ }
+
+ /*
+ * Set the LX_PTRACE_CLONING flag to prevent us from being detached
+ * while our p_lock is dropped.
+ */
+ src->br_ptrace_flags |= LX_PTRACE_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_PTRACE_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_PTRACE_CLONING;
+
+ /*
+ * If lx_ptrace_exit_tracer() is trying to detach our tracer, it will
+ * be sleeping on this CV until LX_PTRACE_CLONING is clear. Wake it
+ * now.
+ */
+ cv_broadcast(&lx_ptrace_busy_cv);
+
+out:
+ if (unlock) {
+ mutex_exit(&srcp->p_lock);
+ }
+}
+
+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;
+ }
+ 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)) {
+ lx_proc_data_t *procd = ttolxproc(curthread);
+
+ /*
+ * 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);
+
+ /*
+ * Set the in-kernel process-wide ptrace(2) enable
+ * flag. Attempt also to write the usermode trace flag
+ * so that the process knows to enter the kernel for
+ * potential ptrace(2) syscall-stops.
+ */
+ procd->l_ptrace = 1;
+ (void) suword32((void *)procd->l_traceflag, 1);
+
+ 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);
+ }
+
+ /*
+ * 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)
+{
+ VERIFY(MUTEX_HELD(&p->p_lock));
+
+ /*
+ * Mark this LWP as stopping and call stop() to enter "ptrace-stop".
+ */
+ VERIFY0(lwpd->br_ptrace_flags & LX_PTRACE_STOPPING);
+ lwpd->br_ptrace_flags |= LX_PTRACE_STOPPING;
+ stop(PR_BRAND, what);
+
+ /*
+ * We are back from "ptrace-stop" with our process lock held.
+ */
+ lwpd->br_ptrace_flags &= ~(LX_PTRACE_STOPPING | LX_PTRACE_STOPPED |
+ LX_PTRACE_CLDPEND);
+ 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)
+{
+ 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_PTRACE_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;
+ }
+
+ /*
+ * 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);
+ if (lwpd->br_ptrace_tracer == NULL) {
+ VERIFY0(lwpd->br_ptrace_flags & LX_PTRACE_SYSCALL);
+ mutex_exit(&p->p_lock);
+ return (B_FALSE);
+ }
+
+ if (what == LX_PR_SYSENTRY || what == LX_PR_SYSEXIT) {
+ /*
+ * This is a syscall-entry-stop or syscall-exit-stop point.
+ */
+ if (!(lwpd->br_ptrace_flags & LX_PTRACE_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_PTRACE_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));
+}
+
+int
+lx_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 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) {
+ 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;
+ (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_stopsig = 0;
+ return (0);
+}
+
+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);
+
+ /*
+ * 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_NOT_HELD(&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);
+
+ /*
+ * Remove our reference to the accord. We will release our hold
+ * later.
+ */
+ VERIFY(lwpd->br_ptrace_tracer == accord);
+ 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);
+ 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 our hold on the accord.
+ */
+ lx_ptrace_accord_enter(accord);
+ lx_ptrace_accord_rele(accord);
+ lx_ptrace_accord_exit(accord);
+}
+
+/*
+ * 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_PTRACE_EXITING);
+ lwpd->br_ptrace_flags |= LX_PTRACE_EXITING;
+
+ if ((accord = lwpd->br_ptrace_tracer) != NULL) {
+ /*
+ * We are traced by another LWP and must detach ourselves.
+ */
+ mutex_exit(&p->p_lock);
+ lx_ptrace_exit_tracee(p, lwpd, accord);
+ mutex_enter(&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) != 0) {
+ continue;
+ }
+
+ if (remote->br_ptrace_flags & LX_PTRACE_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_PTRACE_CLDPEND)) {
+ /*
+ * No SIGCLD is required for this LWP.
+ */
+ mutex_exit(&rproc->p_lock);
+ continue;
+ }
+
+ if (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_winfo(remote, &sqp->sq_info, B_FALSE, NULL, NULL);
+ remote->br_ptrace_flags &= ~LX_PTRACE_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_PTRACE_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);
+
+ 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);
+
+ /*
+ * If the __WALL option was passed, we unconditionally consider
+ * every possible child.
+ */
+ if (!(local->br_waitid_flags & LX_WALL)) {
+ /*
+ * Otherwise, 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);
+ }
+ }
+
+ /*
+ * 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) != 0) {
+ continue;
+ }
+
+ if (remote->br_ptrace_flags & LX_PTRACE_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_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.
+ * Signal to waitid() that it should block if the provided
+ * flags allow for it.
+ */
+ *brand_wants_wait = B_TRUE;
+ return (-1);
+ }
+
+ /*
+ * Populate the signal information.
+ */
+ lx_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_PTRACE_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);
+}
+
+/*
+ * Some PTRACE_* requests are handled in-kernel by this function. It is called
+ * through brandsys() via the B_PTRACE_KERNEL subcommand.
+ */
+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;
+ boolean_t release_hold = B_FALSE;
+
+ _NOTE(ARGUNUSED(addr));
+
+ /*
+ * These actions do not require the target LWP to be traced or stopped.
+ */
+ 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_PTRACE_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?
+ */
+ 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);
+ }
+
+ /*
+ * Attempt to lock the target LWP.
+ */
+ if ((error = lx_ptrace_lock_if_stopped(accord, remote)) != 0) {
+ /*
+ * The LWP was not in "ptrace-stop".
+ */
+ 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);
+
+ /*
+ * Process the ptrace(2) request:
+ */
+ switch (ptrace_op) {
+ case LX_PTRACE_DETACH:
+ error = lx_ptrace_detach(accord, remote, (int)data,
+ &release_hold);
+ break;
+
+ 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;
+
+ default:
+ error = EINVAL;
+ }
+
+ /*
+ * Drop the lock on both the tracee process and the tracee list.
+ */
+ mutex_exit(&rproc->p_lock);
+ mutex_exit(&accord->lxpa_tracees_lock);
+
+ if (release_hold) {
+ /*
+ * Release a hold from the accord.
+ */
+ lx_ptrace_accord_enter(accord);
+ lx_ptrace_accord_rele(accord);
+ lx_ptrace_accord_exit(accord);
+ }
+
+ return (error);
+}
+
+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/procfs/lx_proc.h b/usr/src/uts/common/brand/lx/procfs/lx_proc.h
index 184a5211db..a5c2391c95 100644
--- a/usr/src/uts/common/brand/lx/procfs/lx_proc.h
+++ b/usr/src/uts/common/brand/lx/procfs/lx_proc.h
@@ -138,6 +138,7 @@ typedef enum lxpr_nodetype {
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 */
@@ -250,4 +251,11 @@ void lxpr_unlock(proc_t *);
}
#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 /* _LXPROC_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
index a15d852793..3d96a1ceb2 100644
--- a/usr/src/uts/common/brand/lx/procfs/lx_prsubr.c
+++ b/usr/src/uts/common/brand/lx/procfs/lx_prsubr.c
@@ -482,8 +482,8 @@ lxpr_getnode(vnode_t *dp, lxpr_nodetype_t type, proc_t *p, int fd)
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 */
+ vp->v_type = VLNK;
break;
case LXPR_PID_FDDIR:
diff --git a/usr/src/uts/common/brand/lx/procfs/lx_prvnops.c b/usr/src/uts/common/brand/lx/procfs/lx_prvnops.c
index df2a4d7fb5..758a9192d7 100644
--- a/usr/src/uts/common/brand/lx/procfs/lx_prvnops.c
+++ b/usr/src/uts/common/brand/lx/procfs/lx_prvnops.c
@@ -79,6 +79,8 @@
#include <inet/tcp.h>
#include <inet/udp_impl.h>
#include <inet/ipclassifier.h>
+#include <sys/socketvar.h>
+#include <fs/sockfs/socktpi.h>
/* Dependent on procfs */
extern kthread_t *prchoose(proc_t *);
@@ -108,6 +110,7 @@ static int lxpr_lookup(vnode_t *, char *, vnode_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_readlink_pid_fd(lxpr_node_t *lxpnp, char *bp, size_t len);
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);
@@ -163,6 +166,7 @@ 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 *);
@@ -320,6 +324,7 @@ static lxpr_dirent_t netdir[] = {
{ 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" },
@@ -502,6 +507,7 @@ static void (*lxpr_read_function[LXPR_NFILES])() = {
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 */
@@ -579,6 +585,7 @@ static vnode_t *(*lxpr_lookup_function[LXPR_NFILES])() = {
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 */
@@ -656,6 +663,7 @@ static int (*lxpr_readdir_function[LXPR_NFILES])() = {
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 */
@@ -976,7 +984,7 @@ lxpr_read_pid_maps(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
int maj = 0;
int min = 0;
- u_longlong_t inode = 0;
+ ino_t inode = 0;
*buf = '\0';
if (pbuf->vp != NULL) {
@@ -993,12 +1001,12 @@ lxpr_read_pid_maps(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
if (p->p_model == DATAMODEL_LP64) {
lxpr_uiobuf_printf(uiobuf,
- "%016llx-%16llx %s %016llx %02d:%03d %lld%s%s\n",
+ "%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 %02d:%03d %lld%s%s\n",
+ "%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);
@@ -1768,9 +1776,9 @@ lxpr_read_net_dev_mcast(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
}
static void
-lxpr_inet6_out(in6_addr_t addr, char buf[33])
+lxpr_inet6_out(const in6_addr_t *addr, char buf[33])
{
- uint8_t *ip = addr.s6_addr;
+ const uint8_t *ip = addr->s6_addr;
char digits[] = "0123456789abcdef";
int i;
for (i = 0; i < 16; i++) {
@@ -1811,7 +1819,7 @@ lxpr_read_net_if_inet6(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
ipif_get_name(ipif, ifname, sizeof (ifname));
lx_ifname_convert(ifname, LX_IFNAME_FROMNATIVE);
- lxpr_inet6_out(ipif->ipif_v6lcl_addr, ip6out);
+ lxpr_inet6_out(&ipif->ipif_v6lcl_addr, ip6out);
/* Scope output is shifted on Linux */
scope = scope << 4;
@@ -1841,6 +1849,66 @@ 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_IFNAME_FROMNATIVE);
+ } else {
+ name[0] = '\0';
+ }
+
+ lxpr_uiobuf_printf(uiobuf, " %08x %08x %08x %08x %8s\n",
+ 0, /* metric */
+ ire->ire_refcnt,
+ 0,
+ flags,
+ name);
+}
+
+/* ARGSUSED */
+static void
+lxpr_read_net_ipv6_route(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ netstack_t *ns;
+ ip_stack_t *ipst;
+
+ ns = netstack_get_current();
+ 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);
+}
+
/* ARGSUSED */
static void
lxpr_read_net_mcfilter(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
@@ -1859,10 +1927,97 @@ 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_IFNAME_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);
+}
+
/* ARGSUSED */
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 = netstack_get_current();
+ 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);
}
/* ARGSUSED */
@@ -1883,10 +2038,146 @@ 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 lxpr_snmp_table_t lxpr_snmp_ip = { "ip",
+ {
+ "forwarding", "defaultTTL", "inReceives", "inHdrErrors",
+ "inAddrErrors", "forwDatagrams", "inUnknownProtos", "inDiscards",
+ "inDelivers", "outRequests", "outDiscards", "outNoRoutes",
+ "reasmTimeout", "reasmReqds", "reasmOKs", "reasmFails", "fragOKs",
+ "fragFails", "fragCreates",
+ NULL
+ }
+};
+static lxpr_snmp_table_t lxpr_snmp_icmp = { "icmp",
+ {
+ "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 lxpr_snmp_table_t lxpr_snmp_tcp = { "tcp",
+ {
+ "rtoAlgorithm", "rtoMin", "rtoMax", "maxConn", "activeOpens",
+ "passiveOpens", "attemptFails", "estabResets", "currEstab", "inSegs",
+ "outSegs", "retransSegs", "inErrs", "outRsts", "inCsumErrors",
+ NULL
+ }
+};
+static lxpr_snmp_table_t lxpr_snmp_udp = { "udp",
+ {
+ "inDatagrams", "noPorts", "inErrors", "outDatagrams", "rcvbufErrors",
+ "sndbufErrors", "inCsumErrors",
+ NULL
+ }
+};
+
+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)
+{
+ 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);
+ 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);
+}
+
/* ARGSUSED */
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;
+
+ ks0.ks_kid = 0;
+ ksr = (kstat_t *)lxpr_kstat_read(&ks0, B_FALSE, &sidx, &nidx);
+ 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]);
+ break;
+ }
+ }
+ }
+ kmem_free(ksr, sidx);
}
/* ARGSUSED */
@@ -1963,13 +2254,13 @@ lxpr_format_tcp(lxpr_uiobuf_t *uiobuf, ushort_t ipver)
* - tx_queue
* - rx_queue
* - uid
+ * - inode
*
* Omitted/invalid fields
* - tr
* - tm->when
* - retrnsmt
* - timeout
- * - inode
*/
ns = netstack_get_current();
@@ -1983,6 +2274,9 @@ lxpr_format_tcp(lxpr_uiobuf_t *uiobuf, ushort_t ipver)
while ((connp =
ipcl_get_next_conn(connfp, connp, IPCL_TCPCONN)) != NULL) {
tcp_t *tcp;
+ vattr_t attr;
+ sonode_t *so = (sonode_t *)connp->conn_upper_handle;
+ vnode_t *vp = (so != NULL) ? so->so_vnode : NULL;
if (connp->conn_ipversion != ipver)
continue;
tcp = connp->conn_tcp;
@@ -2010,9 +2304,15 @@ lxpr_format_tcp(lxpr_uiobuf_t *uiobuf, ushort_t ipver)
connp->conn_faddr_v6.s6_addr32[3],
ntohs(connp->conn_fport));
}
+
+ /* fetch the simulated inode for the socket */
+ if (vp == NULL ||
+ VOP_GETATTR(vp, &attr, 0, CRED(), NULL) != 0)
+ attr.va_nodeid = 0;
+
lxpr_uiobuf_printf(uiobuf,
"%02X %08X:%08X %02X:%08X %08X "
- "%5u %8d %u %d %p %u %u %u %u %d\n",
+ "%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 */
@@ -2020,7 +2320,7 @@ lxpr_format_tcp(lxpr_uiobuf_t *uiobuf, ushort_t ipver)
connp->conn_cred->cr_uid,
0, /* timeout */
/* inode + more */
- 0, 0, NULL, 0, 0, 0, 0, 0);
+ (ino_t)attr.va_nodeid, 0, NULL, 0, 0, 0, 0, 0);
}
}
netstack_rele(ns);
@@ -2093,6 +2393,9 @@ lxpr_format_udp(lxpr_uiobuf_t *uiobuf, ushort_t ipver)
ipcl_get_next_conn(connfp, connp, IPCL_UDPCONN)) != NULL) {
udp_t *udp;
int state = 0;
+ vattr_t attr;
+ sonode_t *so = (sonode_t *)connp->conn_upper_handle;
+ vnode_t *vp = (so != NULL) ? so->so_vnode : NULL;
if (connp->conn_ipversion != ipver)
continue;
udp = connp->conn_udp;
@@ -2120,6 +2423,7 @@ lxpr_format_udp(lxpr_uiobuf_t *uiobuf, ushort_t ipver)
connp->conn_faddr_v6.s6_addr32[3],
ntohs(connp->conn_fport));
}
+
switch (udp->udp_state) {
case TS_UNBND:
case TS_IDLE:
@@ -2129,9 +2433,15 @@ lxpr_format_udp(lxpr_uiobuf_t *uiobuf, ushort_t ipver)
state = 1;
break;
}
+
+ /* fetch the simulated inode for the socket */
+ if (vp == NULL ||
+ VOP_GETATTR(vp, &attr, 0, CRED(), NULL) != 0)
+ attr.va_nodeid = 0;
+
lxpr_uiobuf_printf(uiobuf,
"%02X %08X:%08X %02X:%08X %08X "
- "%5u %8d %u %d %p %d\n",
+ "%5u %8d %lu %d %p %d\n",
state,
0, 0, /* rx/tx queue */
0, 0, /* tr, when */
@@ -2139,7 +2449,7 @@ lxpr_format_udp(lxpr_uiobuf_t *uiobuf, ushort_t ipver)
connp->conn_cred->cr_uid,
0, /* timeout */
/* inode, ref, pointer, drops */
- 0, 0, NULL, 0);
+ (ino_t)attr.va_nodeid, 0, NULL, 0);
}
}
netstack_rele(ns);
@@ -2163,6 +2473,95 @@ lxpr_read_net_udp6(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
static void
lxpr_read_net_unix(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
{
+ sonode_t *so;
+ zoneid_t zoneid = getzoneid();
+
+ 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)
+ name = sti->sti_laddr_sa->sa_data;
+ else if (sti->sti_faddr_sa != NULL)
+ 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);
}
/*
@@ -3170,6 +3569,13 @@ lxpr_getattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr,
vap->va_uid = crgetruid(curproc->p_cred);
vap->va_gid = crgetrgid(curproc->p_cred);
break;
+ case LXPR_PID_FD_FD:
+ /*
+ * Restore VLNK type for lstat-type activity.
+ * See lxpr_readlink for more details.
+ */
+ if ((flags & FOLLOW) == 0)
+ vap->va_type = VLNK;
default:
break;
}
@@ -3451,17 +3857,15 @@ lxpr_lookup_fddir(vnode_t *dp, char *comp)
*/
lxpnp->lxpr_realvp = vp;
VN_HOLD(lxpnp->lxpr_realvp);
- if (lxpnp->lxpr_realvp->v_type == VFIFO) {
- /*
- * lxpr_getnode initially sets the type to be VLNK for
- * the LXPR_PID_FD_FD option, but that breaks fifo
- * file descriptors (which are unlinked named pipes).
- * We set this as a regular file so that open.2 comes
- * into lxpr_open so we can do more work.
- */
- dp = LXPTOV(lxpnp);
- dp->v_type = VREG;
- }
+ /*
+ * 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_pid_fd(lxpnp, NULL, 0) == 0)
+ LXPTOV(lxpnp)->v_type = VNON;
}
mutex_enter(&p->p_lock);
@@ -4053,16 +4457,41 @@ lxpr_readlink(vnode_t *vp, uio_t *uiop, cred_t *cr, caller_context_t *ct)
pid_t pid;
int error = 0;
- /* must be a symbolic link file */
- if (vp->v_type != VLNK)
+ /*
+ * 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);
/* 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);
+ if ((error = vnodetopath(NULL, rvp, bp, buflen, CRED())) != 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_pid_fd(lxpnp, bp, buflen) != 0) {
+ return (error);
+ }
+ }
} else {
switch (lxpnp->lxpr_type) {
case LXPR_SELF:
@@ -4104,6 +4533,37 @@ lxpr_readlink(vnode_t *vp, uio_t *uiop, cred_t *cr, caller_context_t *ct)
}
/*
+ * Attempt to create Linux-proc-style fake symlinks contents for supported
+ * /proc/<pid>/fd/<#> entries.
+ */
+static int
+lxpr_readlink_pid_fd(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);
+}
+
+/*
* lxpr_inactive(): Vnode operation for VOP_INACTIVE()
* Vnode is no longer referenced, deallocate the file
* and all its resources.
diff --git a/usr/src/uts/common/brand/lx/sys/lx_brand.h b/usr/src/uts/common/brand/lx/sys/lx_brand.h
index 942a6e3b44..e7f5ee9867 100644
--- a/usr/src/uts/common/brand/lx/sys/lx_brand.h
+++ b/usr/src/uts/common/brand/lx/sys/lx_brand.h
@@ -80,10 +80,10 @@ extern "C" {
#define B_LPID_TO_SPAIR 128
#define B_SYSENTRY 129
#define B_SYSRETURN 130
-#define B_PTRACE_SYSCALL 131
+#define B_PTRACE_KERNEL 131
#define B_SET_AFFINITY_MASK 132
#define B_GET_AFFINITY_MASK 133
-#define B_PTRACE_EXT_OPTS 134
+#define B_PTRACE_CLONE_BEGIN 134
#define B_PTRACE_STOP_FOR_OPT 135
#define B_UNSUPPORTED 136
#define B_STORE_ARGS 137
@@ -91,37 +91,31 @@ extern "C" {
#define B_SIGNAL_RETURN 139
#define B_UNWIND_NTV_SYSC_FLAG 140
#define B_EXIT_AS_SIG 141
-#define B_PTRACE_GETEVENTMSG 142
+#define B_HELPER_WAITID 142
#define B_IKE_SYSCALL 192
-/* B_PTRACE_EXT_OPTS subcommands */
-#define B_PTRACE_EXT_OPTS_SET 1
-#define B_PTRACE_EXT_OPTS_GET 2
-#define B_PTRACE_EXT_OPTS_EVT 3
-#define B_PTRACE_DETACH 4
-
+#ifndef _ASM
/*
* Support for Linux PTRACE_SETOPTIONS handling.
*/
-#define LX_PTRACE_O_TRACESYSGOOD 0x0001
-#define LX_PTRACE_O_TRACEFORK 0x0002
-#define LX_PTRACE_O_TRACEVFORK 0x0004
-#define LX_PTRACE_O_TRACECLONE 0x0008
-#define LX_PTRACE_O_TRACEEXEC 0x0010
-#define LX_PTRACE_O_TRACEVFORKDONE 0x0020
-#define LX_PTRACE_O_TRACEEXIT 0x0040
-#define LX_PTRACE_O_TRACESECCOMP 0x0080
-/*
- * lx emulation-specific flag to indicate this is a child process being stopped
- * due to one of the PTRACE_SETOPTIONS above.
- */
-#define EMUL_PTRACE_O_CHILD 0x8000
-/*
- * lx emulation-specific flag to determine via B_PTRACE_EXT_OPTS_GET if a
- * process is being traced because of one of the PTRACE_SETOPTIONS above.
- */
-#define EMUL_PTRACE_IS_TRACED 0x8000
+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
@@ -132,6 +126,17 @@ extern "C" {
#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
@@ -154,6 +159,8 @@ extern "C" {
#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 */
@@ -255,10 +262,6 @@ typedef struct lx_proc_data {
uintptr_t l_traceflag; /* address of 32-bit tracing flag */
pid_t l_ppid; /* pid of originating parent proc */
uint64_t l_ptrace; /* process being observed with ptrace */
- uint_t l_ptrace_opts; /* process's extended ptrace options */
- uint_t l_ptrace_event; /* extended ptrace option trap event */
- uint_t l_ptrace_is_traced; /* set if traced due to ptrace setoptions */
- ulong_t l_ptrace_eventmsg; /* extended ptrace event msg */
lx_elf_data_t l_elf_data; /* ELF data for linux executable */
int l_signal; /* signal to deliver to parent when this */
/* thread group dies */
@@ -280,10 +283,70 @@ typedef ulong_t lx_affmask_t[LX_AFF_ULONGS];
#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_state {
+ LX_PTRACE_SYSCALL = 0x01,
+ LX_PTRACE_EXITING = 0x02,
+ LX_PTRACE_STOPPING = 0x04,
+ LX_PTRACE_INHERIT = 0x08,
+ LX_PTRACE_STOPPED = 0x10,
+ LX_PTRACE_PARENT_WAIT = 0x20,
+ LX_PTRACE_CLDPEND = 0x40,
+ LX_PTRACE_CLONING = 0x80
+} lx_ptrace_state_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;
+
/*
* lx-specific data in the klwp_t
*/
-typedef struct lx_lwp_data {
+struct lx_lwp_data {
uint_t br_ntv_syscall; /* 1 = syscall from native libc */
uint_t br_lwp_flags; /* misc. flags */
klwp_t *br_lwp; /* back pointer to container lwp */
@@ -317,8 +380,26 @@ typedef struct lx_lwp_data {
void *br_scall_args;
int br_args_size; /* size in bytes of br_scall_args */
- uint_t br_ptrace; /* ptrace is active for this LWP */
-} lx_lwp_data_t;
+ boolean_t br_waitid_emulate;
+ int br_waitid_flags;
+
+ lx_ptrace_state_t br_ptrace_flags; /* ptrace state 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 */
+
+ uint_t br_ptrace_event;
+ ulong_t br_ptrace_eventmsg;
+};
/*
* Upper limit on br_args_size, low because this value can persist until
@@ -336,8 +417,13 @@ typedef struct lx_zone_data {
#define ttolxlwp(t) ((struct lx_lwp_data *)ttolwpbrand(t))
#define lwptolxlwp(l) ((struct lx_lwp_data *)lwptolwpbrand(l))
-#define ttolxproc(t) ((struct lx_proc_data *)(t)->t_procp->p_brand_data)
-#define ptolxproc(p) ((struct lx_proc_data *)(p)->p_brand_data)
+#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)
+
/* Macro for converting to system call arguments. */
#define LX_ARGS(scall) ((struct lx_##scall##_args *)\
(ttolxlwp(curthread)->br_scall_args))
diff --git a/usr/src/uts/common/brand/lx/sys/lx_misc.h b/usr/src/uts/common/brand/lx/sys/lx_misc.h
index 56b5bb4047..7b77789c56 100644
--- a/usr/src/uts/common/brand/lx/sys/lx_misc.h
+++ b/usr/src/uts/common/brand/lx/sys/lx_misc.h
@@ -46,6 +46,20 @@ extern boolean_t lx_wait_filter(proc_t *, proc_t *);
extern void lx_ifname_convert(char *, 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_ptrace_kernel(int, pid_t, uintptr_t, uintptr_t);
+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);
+extern int lx_ptrace_set_clone_inherit(int, boolean_t);
+extern int lx_sigcld_repost(proc_t *, sigqueue_t *);
+extern int lx_issig_stop(proc_t *, klwp_t *);
+
#endif
#ifdef __cplusplus
diff --git a/usr/src/uts/common/brand/lx/syscall/lx_clone.c b/usr/src/uts/common/brand/lx/syscall/lx_clone.c
index 949db3a73b..d73c5f100b 100644
--- a/usr/src/uts/common/brand/lx/syscall/lx_clone.c
+++ b/usr/src/uts/common/brand/lx/syscall/lx_clone.c
@@ -21,7 +21,7 @@
/*
* Copyright 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
- * Copyright 2014 Joyent, Inc. All rights reserved.
+ * Copyright 2015 Joyent, Inc.
*/
#include <sys/types.h>
@@ -32,25 +32,10 @@
#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>
-#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_PARENT 0x00008000
-#define LX_CLONE_THREAD 0x00010000
-#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_CHILD_SETTID 0x01000000
-
/*
* 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
diff --git a/usr/src/uts/common/disp/thread.c b/usr/src/uts/common/disp/thread.c
index cfc4c99f64..ae6c5eef16 100644
--- a/usr/src/uts/common/disp/thread.c
+++ b/usr/src/uts/common/disp/thread.c
@@ -87,7 +87,7 @@ struct kmem_cache *turnstile_cache; /* cache of free turnstiles */
* allthreads is only for use by kmem_readers. All kernel loops can use
* the current thread as a start/end point.
*/
-static kthread_t *allthreads = &t0; /* circular list of all threads */
+kthread_t *allthreads = &t0; /* circular list of all threads */
static kcondvar_t reaper_cv; /* synchronization var */
kthread_t *thread_deathrow; /* circular list of reapable threads */
diff --git a/usr/src/uts/common/fs/lookup.c b/usr/src/uts/common/fs/lookup.c
index 6819509d00..55ffb94805 100644
--- a/usr/src/uts/common/fs/lookup.c
+++ b/usr/src/uts/common/fs/lookup.c
@@ -20,6 +20,7 @@
*/
/*
+ * Copyright 2015 Nexenta Systems, Inc. All rights reserved.
* Copyright (c) 1988, 2010, Oracle and/or its affiliates. All rights reserved.
*/
@@ -217,7 +218,6 @@ lookuppnvp(
cred_t *cr) /* user's credential */
{
vnode_t *cvp; /* current component vp */
- vnode_t *tvp; /* addressable temp ptr */
char component[MAXNAMELEN]; /* buffer for component (incl null) */
int error;
int nlink;
@@ -373,7 +373,7 @@ checkforroot:
/*
* Perform a lookup in the current directory.
*/
- error = VOP_LOOKUP(vp, component, &tvp, pnp, lookup_flags,
+ error = VOP_LOOKUP(vp, component, &cvp, pnp, lookup_flags,
rootvp, cr, NULL, NULL, pp);
/*
@@ -391,10 +391,9 @@ checkforroot:
* directory inside NFS FS.
*/
if ((error == EACCES) && retry_with_kcred)
- error = VOP_LOOKUP(vp, component, &tvp, pnp, lookup_flags,
+ error = VOP_LOOKUP(vp, component, &cvp, pnp, lookup_flags,
rootvp, zone_kcred(), NULL, NULL, pp);
- cvp = tvp;
if (error) {
cvp = NULL;
/*
@@ -440,20 +439,8 @@ checkforroot:
* be atomic!)
*/
if (vn_mountedvfs(cvp) != NULL) {
- tvp = cvp;
- if ((error = traverse(&tvp)) != 0) {
- /*
- * It is required to assign cvp here, because
- * traverse() will return a held vnode which
- * may different than the vnode that was passed
- * in (even in the error case). If traverse()
- * changes the vnode it releases the original,
- * and holds the new one.
- */
- cvp = tvp;
+ if ((error = traverse(&cvp)) != 0)
goto bad;
- }
- cvp = tvp;
}
/*
diff --git a/usr/src/uts/common/fs/nfs/nfs4_srv.c b/usr/src/uts/common/fs/nfs/nfs4_srv.c
index 127d9e3f29..fe1a10b966 100644
--- a/usr/src/uts/common/fs/nfs/nfs4_srv.c
+++ b/usr/src/uts/common/fs/nfs/nfs4_srv.c
@@ -18,10 +18,11 @@
*
* CDDL HEADER END
*/
+
/*
+ * Copyright 2015 Nexenta Systems, Inc. All rights reserved.
* Copyright (c) 2003, 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.
*/
/*
@@ -869,7 +870,7 @@ static nfsstat4
do_rfs4_op_secinfo(struct compound_state *cs, char *nm, SECINFO4res *resp)
{
int error, different_export = 0;
- vnode_t *dvp, *vp, *tvp;
+ vnode_t *dvp, *vp;
struct exportinfo *exi = NULL;
fid_t fid;
uint_t count, i;
@@ -950,14 +951,12 @@ do_rfs4_op_secinfo(struct compound_state *cs, char *nm, SECINFO4res *resp)
* If it's a mountpoint, then traverse it.
*/
if (vn_ismntpt(vp)) {
- tvp = vp;
- if ((error = traverse(&tvp)) != 0) {
+ if ((error = traverse(&vp)) != 0) {
VN_RELE(vp);
return (puterrno4(error));
}
/* remember that we had to traverse mountpoint */
did_traverse = TRUE;
- vp = tvp;
different_export = 1;
} else if (vp->v_vfsp != dvp->v_vfsp) {
/*
@@ -2610,7 +2609,7 @@ do_rfs4_op_lookup(char *nm, struct svc_req *req, struct compound_state *cs)
{
int error;
int different_export = 0;
- vnode_t *vp, *tvp, *pre_tvp = NULL, *oldvp = NULL;
+ vnode_t *vp, *pre_tvp = NULL, *oldvp = NULL;
struct exportinfo *exi = NULL, *pre_exi = NULL;
nfsstat4 stat;
fid_t fid;
@@ -2708,13 +2707,11 @@ do_rfs4_op_lookup(char *nm, struct svc_req *req, struct compound_state *cs)
* need pre_tvp below if checkexport4 fails
*/
VN_HOLD(pre_tvp);
- tvp = vp;
- if ((error = traverse(&tvp)) != 0) {
+ if ((error = traverse(&vp)) != 0) {
VN_RELE(vp);
VN_RELE(pre_tvp);
return (puterrno4(error));
}
- vp = tvp;
different_export = 1;
} else if (vp->v_vfsp != cs->vp->v_vfsp) {
/*
diff --git a/usr/src/uts/common/fs/nfs/nfs4_srv_readdir.c b/usr/src/uts/common/fs/nfs/nfs4_srv_readdir.c
index 3069a98835..276d3b4f19 100644
--- a/usr/src/uts/common/fs/nfs/nfs4_srv_readdir.c
+++ b/usr/src/uts/common/fs/nfs/nfs4_srv_readdir.c
@@ -18,6 +18,11 @@
*
* CDDL HEADER END
*/
+
+/*
+ * Copyright 2015 Nexenta Systems, Inc. All rights reserved.
+ */
+
/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
@@ -149,6 +154,7 @@ nfs4_readdir_getvp(vnode_t *dvp, char *d_name, vnode_t **vpp,
VN_HOLD(pre_tvp);
if ((error = traverse(&vp)) != 0) {
+ VN_RELE(vp);
VN_RELE(pre_tvp);
return (error);
}
diff --git a/usr/src/uts/common/fs/proc/prcontrol.c b/usr/src/uts/common/fs/proc/prcontrol.c
index a5679a8afb..7e99d23b97 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);
}
diff --git a/usr/src/uts/common/fs/proc/prsubr.c b/usr/src/uts/common/fs/proc/prsubr.c
index 7801fd0ac8..284bf8cb88 100644
--- a/usr/src/uts/common/fs/proc/prsubr.c
+++ b/usr/src/uts/common/fs/proc/prsubr.c
@@ -21,7 +21,7 @@
/*
* Copyright (c) 1989, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright (c) 2013, Joyent, Inc. All rights reserved.
+ * Copyright 2015, Joyent, Inc.
*/
/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
@@ -201,6 +201,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.
diff --git a/usr/src/uts/common/fs/smbsrv/smb_common_open.c b/usr/src/uts/common/fs/smbsrv/smb_common_open.c
index 3fa43d43cb..5eaa5865c6 100644
--- a/usr/src/uts/common/fs/smbsrv/smb_common_open.c
+++ b/usr/src/uts/common/fs/smbsrv/smb_common_open.c
@@ -820,8 +820,8 @@ smb_open_subr(smb_request_t *sr)
status = NT_STATUS_SUCCESS;
- of = smb_ofile_open(sr->tid_tree, node, sr->smb_pid, op, SMB_FTYPE_DISK,
- uniq_fid, &err);
+ of = smb_ofile_open(sr, node, sr->smb_pid, op, SMB_FTYPE_DISK, uniq_fid,
+ &err);
if (of == NULL) {
smbsr_error(sr, err.status, err.errcls, err.errcode);
status = err.status;
diff --git a/usr/src/uts/common/fs/smbsrv/smb_delete.c b/usr/src/uts/common/fs/smbsrv/smb_delete.c
index 4930f741ef..14eff73896 100644
--- a/usr/src/uts/common/fs/smbsrv/smb_delete.c
+++ b/usr/src/uts/common/fs/smbsrv/smb_delete.c
@@ -297,7 +297,7 @@ smb_delete_multiple_files(smb_request_t *sr, smb_error_t *err)
if (odid == 0)
return (-1);
- if ((od = smb_tree_lookup_odir(sr->tid_tree, odid)) == NULL)
+ if ((od = smb_tree_lookup_odir(sr, odid)) == NULL)
return (-1);
for (;;) {
diff --git a/usr/src/uts/common/fs/smbsrv/smb_dispatch.c b/usr/src/uts/common/fs/smbsrv/smb_dispatch.c
index 1afcf18b28..9b1fed6f9a 100644
--- a/usr/src/uts/common/fs/smbsrv/smb_dispatch.c
+++ b/usr/src/uts/common/fs/smbsrv/smb_dispatch.c
@@ -20,8 +20,8 @@
*/
/*
- * Copyright 2011 Nexenta Systems, Inc. All rights reserved.
* Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2012 Nexenta Systems, Inc. All rights reserved.
*/
/*
@@ -694,16 +694,13 @@ andx_more:
}
sr->user_cr = smb_user_getcred(sr->uid_user);
-
- if (!(sdd->sdt_flags & SDDF_SUPPRESS_TID) &&
- (sr->tid_tree == NULL)) {
- sr->tid_tree = smb_user_lookup_tree(
- sr->uid_user, sr->smb_tid);
- if (sr->tid_tree == NULL) {
- smbsr_error(sr, 0, ERRSRV, ERRinvnid);
- smbsr_cleanup(sr);
- goto report_error;
- }
+ }
+ if (!(sdd->sdt_flags & SDDF_SUPPRESS_TID) && (sr->tid_tree == NULL)) {
+ sr->tid_tree = smb_session_lookup_tree(session, sr->smb_tid);
+ if (sr->tid_tree == NULL) {
+ smbsr_error(sr, 0, ERRSRV, ERRinvnid);
+ smbsr_cleanup(sr);
+ goto report_error;
}
}
@@ -1116,8 +1113,7 @@ void
smbsr_lookup_file(smb_request_t *sr)
{
if (sr->fid_ofile == NULL)
- sr->fid_ofile = smb_ofile_lookup_by_fid(sr->tid_tree,
- sr->smb_fid);
+ sr->fid_ofile = smb_ofile_lookup_by_fid(sr, sr->smb_fid);
}
static int
diff --git a/usr/src/uts/common/fs/smbsrv/smb_find.c b/usr/src/uts/common/fs/smbsrv/smb_find.c
index 1dae4e8cb5..eecbeff4df 100644
--- a/usr/src/uts/common/fs/smbsrv/smb_find.c
+++ b/usr/src/uts/common/fs/smbsrv/smb_find.c
@@ -306,7 +306,7 @@ smb_com_search(smb_request_t *sr)
}
}
- od = smb_tree_lookup_odir(sr->tid_tree, odid);
+ od = smb_tree_lookup_odir(sr, odid);
if (od == NULL) {
smbsr_error(sr, NT_STATUS_INVALID_HANDLE,
ERRDOS, ERROR_INVALID_HANDLE);
@@ -452,7 +452,7 @@ smb_com_find(smb_request_t *sr)
}
}
- od = smb_tree_lookup_odir(sr->tid_tree, odid);
+ od = smb_tree_lookup_odir(sr, odid);
if (od == NULL) {
smbsr_error(sr, NT_STATUS_INVALID_HANDLE,
ERRDOS, ERROR_INVALID_HANDLE);
@@ -575,7 +575,7 @@ smb_com_find_close(smb_request_t *sr)
return (SDRC_ERROR);
}
- od = smb_tree_lookup_odir(sr->tid_tree, odid);
+ od = smb_tree_lookup_odir(sr, odid);
if (od == NULL) {
smbsr_error(sr, NT_STATUS_INVALID_HANDLE,
ERRDOS, ERROR_INVALID_HANDLE);
@@ -649,7 +649,7 @@ smb_com_find_unique(struct smb_request *sr)
odid = smb_odir_open(sr, pn->pn_path, sattr, 0);
if (odid == 0)
return (SDRC_ERROR);
- od = smb_tree_lookup_odir(sr->tid_tree, odid);
+ od = smb_tree_lookup_odir(sr, odid);
if (od == NULL)
return (SDRC_ERROR);
diff --git a/usr/src/uts/common/fs/smbsrv/smb_fsops.c b/usr/src/uts/common/fs/smbsrv/smb_fsops.c
index 2f4545e966..c64313fdbf 100644
--- a/usr/src/uts/common/fs/smbsrv/smb_fsops.c
+++ b/usr/src/uts/common/fs/smbsrv/smb_fsops.c
@@ -805,7 +805,7 @@ smb_fsop_remove_streams(smb_request_t *sr, cred_t *cr, smb_node_t *fnode)
return (-1);
}
- if ((od = smb_tree_lookup_odir(sr->tid_tree, odid)) == NULL) {
+ if ((od = smb_tree_lookup_odir(sr, odid)) == NULL) {
smbsr_errno(sr, ENOENT);
return (-1);
}
diff --git a/usr/src/uts/common/fs/smbsrv/smb_nt_create_andx.c b/usr/src/uts/common/fs/smbsrv/smb_nt_create_andx.c
index c77c175fc1..037c1373b5 100644
--- a/usr/src/uts/common/fs/smbsrv/smb_nt_create_andx.c
+++ b/usr/src/uts/common/fs/smbsrv/smb_nt_create_andx.c
@@ -264,8 +264,7 @@ smb_com_nt_create_andx(struct smb_request *sr)
if (op->rootdirfid == 0) {
op->fqi.fq_dnode = sr->tid_tree->t_snode;
} else {
- op->dir = smb_ofile_lookup_by_fid(sr->tid_tree,
- (uint16_t)op->rootdirfid);
+ op->dir = smb_ofile_lookup_by_fid(sr, (uint16_t)op->rootdirfid);
if (op->dir == NULL) {
smbsr_error(sr, NT_STATUS_INVALID_HANDLE,
ERRDOS, ERRbadfid);
diff --git a/usr/src/uts/common/fs/smbsrv/smb_nt_transact_create.c b/usr/src/uts/common/fs/smbsrv/smb_nt_transact_create.c
index fcc12f2fc8..dcfa469617 100644
--- a/usr/src/uts/common/fs/smbsrv/smb_nt_transact_create.c
+++ b/usr/src/uts/common/fs/smbsrv/smb_nt_transact_create.c
@@ -173,8 +173,7 @@ smb_nt_transact_create(smb_request_t *sr, smb_xa_t *xa)
if (op->rootdirfid == 0) {
op->fqi.fq_dnode = sr->tid_tree->t_snode;
} else {
- op->dir = smb_ofile_lookup_by_fid(sr->tid_tree,
- (uint16_t)op->rootdirfid);
+ op->dir = smb_ofile_lookup_by_fid(sr, (uint16_t)op->rootdirfid);
if (op->dir == NULL) {
smbsr_error(sr, NT_STATUS_INVALID_HANDLE,
ERRDOS, ERRbadfid);
diff --git a/usr/src/uts/common/fs/smbsrv/smb_odir.c b/usr/src/uts/common/fs/smbsrv/smb_odir.c
index b8435d191a..16fffa6692 100644
--- a/usr/src/uts/common/fs/smbsrv/smb_odir.c
+++ b/usr/src/uts/common/fs/smbsrv/smb_odir.c
@@ -39,15 +39,15 @@
* +-------------------+ +-------------------+ +-------------------+
* | SESSION |<----->| SESSION |......| SESSION |
* +-------------------+ +-------------------+ +-------------------+
- * |
- * |
- * v
- * +-------------------+ +-------------------+ +-------------------+
- * | USER |<----->| USER |......| USER |
- * +-------------------+ +-------------------+ +-------------------+
- * |
- * |
- * v
+ * | |
+ * | |
+ * | v
+ * | +-------------------+ +-------------------+ +-------------------+
+ * | | USER |<--->| USER |...| USER |
+ * | +-------------------+ +-------------------+ +-------------------+
+ * |
+ * |
+ * v
* +-------------------+ +-------------------+ +-------------------+
* | TREE |<----->| TREE |......| TREE |
* +-------------------+ +-------------------+ +-------------------+
@@ -153,7 +153,7 @@
* and add it into the tree's list of odirs.
* Return an identifier (odid) uniquely identifying the created odir.
*
- * smb_odir_t *odir = smb_tree_lookup_odir(odid)
+ * smb_odir_t *odir = smb_tree_lookup_odir(..., odid)
* Find the odir corresponding to the specified odid in the tree's
* list of odirs. Place a hold on the odir.
*
@@ -312,9 +312,9 @@ smb_odir_open(smb_request_t *sr, char *path, uint16_t sattr, uint32_t flags)
}
if (flags & SMB_ODIR_OPENF_BACKUP_INTENT)
- cr = smb_user_getprivcred(tree->t_user);
+ cr = smb_user_getprivcred(sr->uid_user);
else
- cr = tree->t_user->u_cred;
+ cr = sr->uid_user->u_cred;
odid = smb_odir_create(sr, dnode, pattern, sattr, cr);
smb_node_release(dnode);
@@ -888,6 +888,12 @@ smb_odir_create(smb_request_t *sr, smb_node_t *dnode,
od->d_opened_by_pid = sr->smb_pid;
od->d_session = tree->t_session;
od->d_cred = cr;
+ /*
+ * grab a ref for od->d_user
+ * released in smb_odir_delete()
+ */
+ smb_user_hold_internal(sr->uid_user);
+ od->d_user = sr->uid_user;
od->d_tree = tree;
od->d_dnode = dnode;
smb_node_ref(dnode);
@@ -947,6 +953,7 @@ smb_odir_delete(void *arg)
od->d_magic = 0;
smb_node_release(od->d_dnode);
+ smb_user_release(od->d_user);
mutex_destroy(&od->d_mutex);
kmem_cache_free(od->d_tree->t_server->si_cache_odir, od);
}
diff --git a/usr/src/uts/common/fs/smbsrv/smb_ofile.c b/usr/src/uts/common/fs/smbsrv/smb_ofile.c
index 8987da2950..ee45f13c8b 100644
--- a/usr/src/uts/common/fs/smbsrv/smb_ofile.c
+++ b/usr/src/uts/common/fs/smbsrv/smb_ofile.c
@@ -39,15 +39,15 @@
* +-------------------+ +-------------------+ +-------------------+
* | SESSION |<----->| SESSION |......| SESSION |
* +-------------------+ +-------------------+ +-------------------+
- * |
- * |
- * v
- * +-------------------+ +-------------------+ +-------------------+
- * | USER |<----->| USER |......| USER |
- * +-------------------+ +-------------------+ +-------------------+
- * |
- * |
- * v
+ * | |
+ * | |
+ * | v
+ * | +-------------------+ +-------------------+ +-------------------+
+ * | | USER |<--->| USER |...| USER |
+ * | +-------------------+ +-------------------+ +-------------------+
+ * |
+ * |
+ * v
* +-------------------+ +-------------------+ +-------------------+
* | TREE |<----->| TREE |......| TREE |
* +-------------------+ +-------------------+ +-------------------+
@@ -175,7 +175,7 @@ static void smb_ofile_netinfo_fini(smb_netfileinfo_t *);
*/
smb_ofile_t *
smb_ofile_open(
- smb_tree_t *tree,
+ smb_request_t *sr,
smb_node_t *node,
uint16_t pid,
struct open_param *op,
@@ -183,10 +183,13 @@ smb_ofile_open(
uint32_t uniqid,
smb_error_t *err)
{
+ smb_tree_t *tree = sr->tid_tree;
smb_ofile_t *of;
uint16_t fid;
smb_attr_t attr;
int rc;
+ enum errstates { EMPTY, FIDALLOC, CRHELD, MUTEXINIT };
+ enum errstates state = EMPTY;
if (smb_idpool_alloc(&tree->t_fid_pool, &fid)) {
err->status = NT_STATUS_TOO_MANY_OPENED_FILES;
@@ -194,6 +197,7 @@ smb_ofile_open(
err->errcode = ERROR_TOO_MANY_OPEN_FILES;
return (NULL);
}
+ state = FIDALLOC;
of = kmem_cache_alloc(tree->t_server->si_cache_ofile, KM_SLEEP);
bzero(of, sizeof (smb_ofile_t));
@@ -206,16 +210,23 @@ smb_ofile_open(
of->f_share_access = op->share_access;
of->f_create_options = op->create_options;
of->f_cr = (op->create_options & FILE_OPEN_FOR_BACKUP_INTENT) ?
- smb_user_getprivcred(tree->t_user) : tree->t_user->u_cred;
+ smb_user_getprivcred(sr->uid_user) : sr->uid_user->u_cred;
crhold(of->f_cr);
+ state = CRHELD;
of->f_ftype = ftype;
of->f_server = tree->t_server;
- of->f_session = tree->t_user->u_session;
- of->f_user = tree->t_user;
+ of->f_session = tree->t_session;
+ /*
+ * grab a ref for of->f_user
+ * released in smb_ofile_delete()
+ */
+ smb_user_hold_internal(sr->uid_user);
+ of->f_user = sr->uid_user;
of->f_tree = tree;
of->f_node = node;
mutex_init(&of->f_mutex, NULL, MUTEX_DEFAULT, NULL);
+ state = MUTEXINIT;
of->f_state = SMB_OFILE_STATE_OPEN;
if (ftype == SMB_FTYPE_MESG_PIPE) {
@@ -232,15 +243,10 @@ smb_ofile_open(
attr.sa_mask = SMB_AT_UID | SMB_AT_DOSATTR;
rc = smb_node_getattr(NULL, node, of->f_cr, NULL, &attr);
if (rc != 0) {
- of->f_magic = 0;
- mutex_destroy(&of->f_mutex);
- crfree(of->f_cr);
- smb_idpool_free(&tree->t_fid_pool, of->f_fid);
- kmem_cache_free(tree->t_server->si_cache_ofile, of);
err->status = NT_STATUS_INTERNAL_ERROR;
err->errcls = ERRDOS;
err->errcode = ERROR_INTERNAL_ERROR;
- return (NULL);
+ goto errout;
}
if (crgetuid(of->f_cr) == attr.sa_vattr.va_uid) {
/*
@@ -254,16 +260,10 @@ smb_ofile_open(
of->f_mode =
smb_fsop_amask_to_omode(of->f_granted_access);
if (smb_fsop_open(node, of->f_mode, of->f_cr) != 0) {
- of->f_magic = 0;
- mutex_destroy(&of->f_mutex);
- crfree(of->f_cr);
- smb_idpool_free(&tree->t_fid_pool, of->f_fid);
- kmem_cache_free(tree->t_server->si_cache_ofile,
- of);
err->status = NT_STATUS_ACCESS_DENIED;
err->errcls = ERRDOS;
err->errcode = ERROR_ACCESS_DENIED;
- return (NULL);
+ goto errout;
}
}
@@ -290,6 +290,25 @@ smb_ofile_open(
atomic_inc_32(&tree->t_open_files);
atomic_inc_32(&of->f_session->s_file_cnt);
return (of);
+
+errout:
+ switch (state) {
+ case MUTEXINIT:
+ mutex_destroy(&of->f_mutex);
+ smb_user_release(of->f_user);
+ /*FALLTHROUGH*/
+ case CRHELD:
+ crfree(of->f_cr);
+ of->f_magic = 0;
+ kmem_cache_free(tree->t_server->si_cache_ofile, of);
+ /*FALLTHROUGH*/
+ case FIDALLOC:
+ smb_idpool_free(&tree->t_fid_pool, fid);
+ /*FALLTHROUGH*/
+ case EMPTY:
+ break;
+ }
+ return (NULL);
}
/*
@@ -601,9 +620,10 @@ smb_ofile_request_complete(smb_ofile_t *of)
*/
smb_ofile_t *
smb_ofile_lookup_by_fid(
- smb_tree_t *tree,
+ smb_request_t *sr,
uint16_t fid)
{
+ smb_tree_t *tree = sr->tid_tree;
smb_llist_t *of_list;
smb_ofile_t *of;
@@ -616,19 +636,32 @@ smb_ofile_lookup_by_fid(
while (of) {
ASSERT(of->f_magic == SMB_OFILE_MAGIC);
ASSERT(of->f_tree == tree);
- if (of->f_fid == fid) {
- mutex_enter(&of->f_mutex);
- if (of->f_state != SMB_OFILE_STATE_OPEN) {
- mutex_exit(&of->f_mutex);
- smb_llist_exit(of_list);
- return (NULL);
- }
- of->f_refcnt++;
- mutex_exit(&of->f_mutex);
+ if (of->f_fid == fid)
break;
- }
of = smb_llist_next(of_list, of);
}
+ if (of == NULL)
+ goto out;
+
+ /*
+ * Only allow use of a given FID with the same UID that
+ * was used to open it. MS-CIFS 3.3.5.14
+ */
+ if (of->f_user != sr->uid_user) {
+ of = NULL;
+ goto out;
+ }
+
+ mutex_enter(&of->f_mutex);
+ if (of->f_state != SMB_OFILE_STATE_OPEN) {
+ mutex_exit(&of->f_mutex);
+ of = NULL;
+ goto out;
+ }
+ of->f_refcnt++;
+ mutex_exit(&of->f_mutex);
+
+out:
smb_llist_exit(of_list);
return (of);
}
@@ -921,6 +954,7 @@ smb_ofile_delete(void *arg)
of->f_magic = (uint32_t)~SMB_OFILE_MAGIC;
mutex_destroy(&of->f_mutex);
crfree(of->f_cr);
+ smb_user_release(of->f_user);
kmem_cache_free(of->f_tree->t_server->si_cache_ofile, of);
}
diff --git a/usr/src/uts/common/fs/smbsrv/smb_opipe.c b/usr/src/uts/common/fs/smbsrv/smb_opipe.c
index bb178f3952..90cb25aaa0 100644
--- a/usr/src/uts/common/fs/smbsrv/smb_opipe.c
+++ b/usr/src/uts/common/fs/smbsrv/smb_opipe.c
@@ -130,8 +130,8 @@ smb_opipe_open(smb_request_t *sr)
op->create_options = 0;
- of = smb_ofile_open(sr->tid_tree, NULL, sr->smb_pid, op,
- SMB_FTYPE_MESG_PIPE, SMB_UNIQ_FID(), &err);
+ of = smb_ofile_open(sr, NULL, sr->smb_pid, op, SMB_FTYPE_MESG_PIPE,
+ SMB_UNIQ_FID(), &err);
if (of == NULL)
return (err.status);
diff --git a/usr/src/uts/common/fs/smbsrv/smb_process_exit.c b/usr/src/uts/common/fs/smbsrv/smb_process_exit.c
index b8c835cd57..2839ca2807 100644
--- a/usr/src/uts/common/fs/smbsrv/smb_process_exit.c
+++ b/usr/src/uts/common/fs/smbsrv/smb_process_exit.c
@@ -85,11 +85,11 @@ smb_com_process_exit(smb_request_t *sr)
* to be the only thing that sends this request these days and
* it doesn't provide a TID.
*/
- sr->tid_tree = smb_user_lookup_tree(sr->uid_user, sr->smb_tid);
+ sr->tid_tree = smb_session_lookup_tree(sr->session, sr->smb_tid);
if (sr->tid_tree != NULL)
smb_tree_close_pid(sr->tid_tree, sr->smb_pid);
else
- smb_user_close_pid(sr->uid_user, sr->smb_pid);
+ smb_session_close_pid(sr->session, sr->smb_pid);
rc = smbsr_encode_empty_result(sr);
return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
diff --git a/usr/src/uts/common/fs/smbsrv/smb_query_fileinfo.c b/usr/src/uts/common/fs/smbsrv/smb_query_fileinfo.c
index bef69e7f61..70ac2e7b24 100644
--- a/usr/src/uts/common/fs/smbsrv/smb_query_fileinfo.c
+++ b/usr/src/uts/common/fs/smbsrv/smb_query_fileinfo.c
@@ -662,7 +662,7 @@ smb_encode_stream_info(smb_request_t *sr, smb_xa_t *xa, smb_queryinfo_t *qinfo)
odid = smb_odir_openat(sr, fnode);
if (odid != 0)
- od = smb_tree_lookup_odir(sr->tid_tree, odid);
+ od = smb_tree_lookup_odir(sr, odid);
if (od != NULL)
rc = smb_odir_read_streaminfo(sr, od, sinfo, &eos);
diff --git a/usr/src/uts/common/fs/smbsrv/smb_server.c b/usr/src/uts/common/fs/smbsrv/smb_server.c
index 3654744569..8687d42b18 100644
--- a/usr/src/uts/common/fs/smbsrv/smb_server.c
+++ b/usr/src/uts/common/fs/smbsrv/smb_server.c
@@ -240,7 +240,8 @@ static void smb_event_cancel(smb_server_t *, uint32_t);
static uint32_t smb_event_alloc_txid(void);
static void smb_server_disconnect_share(smb_llist_t *, const char *);
-static void smb_server_enum_private(smb_llist_t *, smb_svcenum_t *);
+static void smb_server_enum_users(smb_llist_t *, smb_svcenum_t *);
+static void smb_server_enum_trees(smb_llist_t *, smb_svcenum_t *);
static int smb_server_session_disconnect(smb_llist_t *, const char *,
const char *);
static int smb_server_fclose(smb_llist_t *, uint32_t);
@@ -833,15 +834,6 @@ smb_server_enum(smb_ioc_svcenum_t *ioc)
smb_server_t *sv;
int rc;
- switch (svcenum->se_type) {
- case SMB_SVCENUM_TYPE_USER:
- case SMB_SVCENUM_TYPE_TREE:
- case SMB_SVCENUM_TYPE_FILE:
- break;
- default:
- return (EINVAL);
- }
-
if ((rc = smb_server_lookup(&sv)) != 0)
return (rc);
@@ -849,11 +841,26 @@ smb_server_enum(smb_ioc_svcenum_t *ioc)
svcenum->se_bused = 0;
svcenum->se_nitems = 0;
- smb_server_enum_private(&sv->sv_nbt_daemon.ld_session_list, svcenum);
- smb_server_enum_private(&sv->sv_tcp_daemon.ld_session_list, svcenum);
+ switch (svcenum->se_type) {
+ case SMB_SVCENUM_TYPE_USER:
+ smb_server_enum_users(&sv->sv_nbt_daemon.ld_session_list,
+ svcenum);
+ smb_server_enum_users(&sv->sv_tcp_daemon.ld_session_list,
+ svcenum);
+ break;
+ case SMB_SVCENUM_TYPE_TREE:
+ case SMB_SVCENUM_TYPE_FILE:
+ smb_server_enum_trees(&sv->sv_nbt_daemon.ld_session_list,
+ svcenum);
+ smb_server_enum_trees(&sv->sv_tcp_daemon.ld_session_list,
+ svcenum);
+ break;
+ default:
+ rc = EINVAL;
+ }
smb_server_release(sv);
- return (0);
+ return (rc);
}
/*
@@ -1694,7 +1701,7 @@ smb_server_release(smb_server_t *sv)
* Enumerate the users associated with a session list.
*/
static void
-smb_server_enum_private(smb_llist_t *ll, smb_svcenum_t *svcenum)
+smb_server_enum_users(smb_llist_t *ll, smb_svcenum_t *svcenum)
{
smb_session_t *sn;
smb_llist_t *ulist;
@@ -1714,6 +1721,8 @@ smb_server_enum_private(smb_llist_t *ll, smb_svcenum_t *svcenum)
if (smb_user_hold(user)) {
rc = smb_user_enum(user, svcenum);
smb_user_release(user);
+ if (rc != 0)
+ break;
}
user = smb_llist_next(ulist, user);
@@ -1731,6 +1740,48 @@ smb_server_enum_private(smb_llist_t *ll, smb_svcenum_t *svcenum)
}
/*
+ * Enumerate the trees/files associated with a session list.
+ */
+static void
+smb_server_enum_trees(smb_llist_t *ll, smb_svcenum_t *svcenum)
+{
+ smb_session_t *sn;
+ smb_llist_t *tlist;
+ smb_tree_t *tree;
+ int rc = 0;
+
+ smb_llist_enter(ll, RW_READER);
+ sn = smb_llist_head(ll);
+
+ while (sn != NULL) {
+ SMB_SESSION_VALID(sn);
+ tlist = &sn->s_tree_list;
+ smb_llist_enter(tlist, RW_READER);
+ tree = smb_llist_head(tlist);
+
+ while (tree != NULL) {
+ if (smb_tree_hold(tree)) {
+ rc = smb_tree_enum(tree, svcenum);
+ smb_tree_release(tree);
+ if (rc != 0)
+ break;
+ }
+
+ tree = smb_llist_next(tlist, tree);
+ }
+
+ smb_llist_exit(tlist);
+
+ if (rc != 0)
+ break;
+
+ sn = smb_llist_next(ll, sn);
+ }
+
+ smb_llist_exit(ll);
+}
+
+/*
* Disconnect sessions associated with the specified client and username.
* Empty strings are treated as wildcards.
*/
@@ -1796,8 +1847,8 @@ static int
smb_server_fclose(smb_llist_t *ll, uint32_t uniqid)
{
smb_session_t *sn;
- smb_llist_t *ulist;
- smb_user_t *user;
+ smb_llist_t *tlist;
+ smb_tree_t *tree;
int rc = ENOENT;
smb_llist_enter(ll, RW_READER);
@@ -1805,20 +1856,20 @@ smb_server_fclose(smb_llist_t *ll, uint32_t uniqid)
while ((sn != NULL) && (rc == ENOENT)) {
SMB_SESSION_VALID(sn);
- ulist = &sn->s_user_list;
- smb_llist_enter(ulist, RW_READER);
- user = smb_llist_head(ulist);
-
- while ((user != NULL) && (rc == ENOENT)) {
- if (smb_user_hold(user)) {
- rc = smb_user_fclose(user, uniqid);
- smb_user_release(user);
+ tlist = &sn->s_tree_list;
+ smb_llist_enter(tlist, RW_READER);
+ tree = smb_llist_head(tlist);
+
+ while ((tree != NULL) && (rc == ENOENT)) {
+ if (smb_tree_hold(tree)) {
+ rc = smb_tree_fclose(tree, uniqid);
+ smb_tree_release(tree);
}
- user = smb_llist_next(ulist, user);
+ tree = smb_llist_next(tlist, tree);
}
- smb_llist_exit(ulist);
+ smb_llist_exit(tlist);
sn = smb_llist_next(ll, sn);
}
diff --git a/usr/src/uts/common/fs/smbsrv/smb_session.c b/usr/src/uts/common/fs/smbsrv/smb_session.c
index 0fdac10ca6..b8284b372f 100644
--- a/usr/src/uts/common/fs/smbsrv/smb_session.c
+++ b/usr/src/uts/common/fs/smbsrv/smb_session.c
@@ -43,6 +43,7 @@ static int smb_session_message(smb_session_t *);
static int smb_session_xprt_puthdr(smb_session_t *, smb_xprt_t *,
uint8_t *, size_t);
static smb_user_t *smb_session_lookup_user(smb_session_t *, char *, char *);
+static smb_tree_t *smb_session_get_tree(smb_session_t *, smb_tree_t *);
static void smb_session_logoff(smb_session_t *);
static void smb_request_init_command_mbuf(smb_request_t *sr);
void dump_smb_inaddr(smb_inaddr_t *ipaddr);
@@ -624,6 +625,11 @@ smb_session_create(ksocket_t new_so, uint16_t port, smb_server_t *sv,
kmem_cache_free(sv->si_cache_session, session);
return (NULL);
}
+ if (smb_idpool_constructor(&session->s_tid_pool)) {
+ smb_idpool_destructor(&session->s_uid_pool);
+ kmem_cache_free(sv->si_cache_session, session);
+ return (NULL);
+ }
now = ddi_get_lbolt64();
@@ -642,6 +648,9 @@ smb_session_create(ksocket_t new_so, uint16_t port, smb_server_t *sv,
smb_llist_constructor(&session->s_user_list, sizeof (smb_user_t),
offsetof(smb_user_t, u_lnd));
+ smb_llist_constructor(&session->s_tree_list, sizeof (smb_tree_t),
+ offsetof(smb_tree_t, t_lnd));
+
smb_llist_constructor(&session->s_xa_list, sizeof (smb_xa_t),
offsetof(smb_xa_t, xa_lnd));
@@ -719,6 +728,7 @@ smb_session_delete(smb_session_t *session)
list_destroy(&session->s_oplock_brkreqs);
smb_slist_destructor(&session->s_req_list);
+ smb_llist_destructor(&session->s_tree_list);
smb_llist_destructor(&session->s_user_list);
smb_llist_destructor(&session->s_xa_list);
@@ -726,6 +736,7 @@ smb_session_delete(smb_session_t *session)
ASSERT(session->s_file_cnt == 0);
ASSERT(session->s_dir_cnt == 0);
+ smb_idpool_destructor(&session->s_tid_pool);
smb_idpool_destructor(&session->s_uid_pool);
if (session->sock != NULL) {
if (session->s_local_port == IPPORT_NETBIOS_SSN)
@@ -928,45 +939,306 @@ smb_session_post_user(smb_session_t *session, smb_user_t *user)
}
/*
- * Logoff all users associated with the specified session.
+ * Find a tree by tree-id.
*/
-static void
-smb_session_logoff(smb_session_t *session)
+smb_tree_t *
+smb_session_lookup_tree(
+ smb_session_t *session,
+ uint16_t tid)
+
{
- smb_user_t *user;
+ smb_tree_t *tree;
SMB_SESSION_VALID(session);
- smb_llist_enter(&session->s_user_list, RW_READER);
+ smb_llist_enter(&session->s_tree_list, RW_READER);
+ tree = smb_llist_head(&session->s_tree_list);
- user = smb_llist_head(&session->s_user_list);
- while (user) {
- SMB_USER_VALID(user);
- ASSERT(user->u_session == session);
+ while (tree) {
+ ASSERT3U(tree->t_magic, ==, SMB_TREE_MAGIC);
+ ASSERT(tree->t_session == session);
- if (smb_user_hold(user)) {
- smb_user_logoff(user);
- smb_user_release(user);
+ if (tree->t_tid == tid) {
+ if (smb_tree_hold(tree)) {
+ smb_llist_exit(&session->s_tree_list);
+ return (tree);
+ } else {
+ smb_llist_exit(&session->s_tree_list);
+ return (NULL);
+ }
}
- user = smb_llist_next(&session->s_user_list, user);
+ tree = smb_llist_next(&session->s_tree_list, tree);
}
- smb_llist_exit(&session->s_user_list);
+ smb_llist_exit(&session->s_tree_list);
+ return (NULL);
+}
+
+/*
+ * Find the first connected tree that matches the specified sharename.
+ * If the specified tree is NULL the search starts from the beginning of
+ * the user's tree list. If a tree is provided the search starts just
+ * after that tree.
+ */
+smb_tree_t *
+smb_session_lookup_share(
+ smb_session_t *session,
+ const char *sharename,
+ smb_tree_t *tree)
+{
+ SMB_SESSION_VALID(session);
+ ASSERT(sharename);
+
+ smb_llist_enter(&session->s_tree_list, RW_READER);
+
+ if (tree) {
+ ASSERT3U(tree->t_magic, ==, SMB_TREE_MAGIC);
+ ASSERT(tree->t_session == session);
+ tree = smb_llist_next(&session->s_tree_list, tree);
+ } else {
+ tree = smb_llist_head(&session->s_tree_list);
+ }
+
+ while (tree) {
+ ASSERT3U(tree->t_magic, ==, SMB_TREE_MAGIC);
+ ASSERT(tree->t_session == session);
+ if (smb_strcasecmp(tree->t_sharename, sharename, 0) == 0) {
+ if (smb_tree_hold(tree)) {
+ smb_llist_exit(&session->s_tree_list);
+ return (tree);
+ }
+ }
+ tree = smb_llist_next(&session->s_tree_list, tree);
+ }
+
+ smb_llist_exit(&session->s_tree_list);
+ return (NULL);
+}
+
+/*
+ * Find the first connected tree that matches the specified volume name.
+ * If the specified tree is NULL the search starts from the beginning of
+ * the user's tree list. If a tree is provided the search starts just
+ * after that tree.
+ */
+smb_tree_t *
+smb_session_lookup_volume(
+ smb_session_t *session,
+ const char *name,
+ smb_tree_t *tree)
+{
+ SMB_SESSION_VALID(session);
+ ASSERT(name);
+
+ smb_llist_enter(&session->s_tree_list, RW_READER);
+
+ if (tree) {
+ ASSERT3U(tree->t_magic, ==, SMB_TREE_MAGIC);
+ ASSERT(tree->t_session == session);
+ tree = smb_llist_next(&session->s_tree_list, tree);
+ } else {
+ tree = smb_llist_head(&session->s_tree_list);
+ }
+
+ while (tree) {
+ ASSERT3U(tree->t_magic, ==, SMB_TREE_MAGIC);
+ ASSERT(tree->t_session == session);
+
+ if (smb_strcasecmp(tree->t_volume, name, 0) == 0) {
+ if (smb_tree_hold(tree)) {
+ smb_llist_exit(&session->s_tree_list);
+ return (tree);
+ }
+ }
+
+ tree = smb_llist_next(&session->s_tree_list, tree);
+ }
+
+ smb_llist_exit(&session->s_tree_list);
+ return (NULL);
+}
+
+/*
+ * Disconnect all trees that match the specified client process-id.
+ */
+void
+smb_session_close_pid(
+ smb_session_t *session,
+ uint16_t pid)
+{
+ smb_tree_t *tree;
+
+ SMB_SESSION_VALID(session);
+
+ tree = smb_session_get_tree(session, NULL);
+ while (tree) {
+ smb_tree_t *next;
+ ASSERT3U(tree->t_magic, ==, SMB_TREE_MAGIC);
+ ASSERT(tree->t_session == session);
+ smb_tree_close_pid(tree, pid);
+ next = smb_session_get_tree(session, tree);
+ smb_tree_release(tree);
+ tree = next;
+ }
+}
+
+static void
+smb_session_tree_dtor(void *t)
+{
+ smb_tree_t *tree = (smb_tree_t *)t;
+
+ smb_tree_disconnect(tree, B_TRUE);
+ /* release the ref acquired during the traversal loop */
+ smb_tree_release(tree);
}
+
/*
- * Disconnect any trees associated with the specified share.
- * Iterate through the users on this session and tell each user
- * to disconnect from the share.
+ * Disconnect all trees that this user has connected.
*/
void
-smb_session_disconnect_share(smb_session_t *session, const char *sharename)
+smb_session_disconnect_owned_trees(
+ smb_session_t *session,
+ smb_user_t *owner)
+{
+ smb_tree_t *tree;
+ smb_llist_t *tree_list = &session->s_tree_list;
+
+ SMB_SESSION_VALID(session);
+ SMB_USER_VALID(owner);
+
+ smb_llist_enter(tree_list, RW_READER);
+
+ tree = smb_llist_head(tree_list);
+ while (tree) {
+ if ((tree->t_owner == owner) &&
+ smb_tree_hold(tree)) {
+ /*
+ * smb_tree_hold() succeeded, hence we are in state
+ * SMB_TREE_STATE_CONNECTED; schedule this tree
+ * for asynchronous disconnect, which will fire
+ * after we drop the llist traversal lock.
+ */
+ smb_llist_post(tree_list, tree, smb_session_tree_dtor);
+ }
+ tree = smb_llist_next(tree_list, tree);
+ }
+
+ /* drop the lock and flush the dtor queue */
+ smb_llist_exit(tree_list);
+}
+
+/*
+ * Disconnect all trees that this user has connected.
+ */
+void
+smb_session_disconnect_trees(
+ smb_session_t *session)
+{
+ smb_tree_t *tree;
+
+ SMB_SESSION_VALID(session);
+
+ tree = smb_session_get_tree(session, NULL);
+ while (tree) {
+ ASSERT3U(tree->t_magic, ==, SMB_TREE_MAGIC);
+ ASSERT(tree->t_session == session);
+ smb_tree_disconnect(tree, B_TRUE);
+ smb_tree_release(tree);
+ tree = smb_session_get_tree(session, NULL);
+ }
+}
+
+/*
+ * Disconnect all trees that match the specified share name.
+ */
+void
+smb_session_disconnect_share(
+ smb_session_t *session,
+ const char *sharename)
+{
+ smb_tree_t *tree;
+ smb_tree_t *next;
+
+ SMB_SESSION_VALID(session);
+
+ tree = smb_session_lookup_share(session, sharename, NULL);
+ while (tree) {
+ ASSERT3U(tree->t_magic, ==, SMB_TREE_MAGIC);
+ ASSERT(tree->t_session == session);
+ smb_session_cancel_requests(session, tree, NULL);
+ smb_tree_disconnect(tree, B_TRUE);
+ next = smb_session_lookup_share(session, sharename, tree);
+ smb_tree_release(tree);
+ tree = next;
+ }
+}
+
+void
+smb_session_post_tree(smb_session_t *session, smb_tree_t *tree)
+{
+ SMB_SESSION_VALID(session);
+ SMB_TREE_VALID(tree);
+ ASSERT0(tree->t_refcnt);
+ ASSERT(tree->t_state == SMB_TREE_STATE_DISCONNECTED);
+ ASSERT(tree->t_session == session);
+
+ smb_llist_post(&session->s_tree_list, tree, smb_tree_dealloc);
+}
+
+/*
+ * Get the next connected tree in the list. A reference is taken on
+ * the tree, which can be released later with smb_tree_release().
+ *
+ * If the specified tree is NULL the search starts from the beginning of
+ * the tree list. If a tree is provided the search starts just after
+ * that tree.
+ *
+ * Returns NULL if there are no connected trees in the list.
+ */
+static smb_tree_t *
+smb_session_get_tree(
+ smb_session_t *session,
+ smb_tree_t *tree)
+{
+ smb_llist_t *tree_list;
+
+ SMB_SESSION_VALID(session);
+ tree_list = &session->s_tree_list;
+
+ smb_llist_enter(tree_list, RW_READER);
+
+ if (tree) {
+ ASSERT3U(tree->t_magic, ==, SMB_TREE_MAGIC);
+ tree = smb_llist_next(tree_list, tree);
+ } else {
+ tree = smb_llist_head(tree_list);
+ }
+
+ while (tree) {
+ if (smb_tree_hold(tree))
+ break;
+
+ tree = smb_llist_next(tree_list, tree);
+ }
+
+ smb_llist_exit(tree_list);
+ return (tree);
+}
+
+/*
+ * Logoff all users associated with the specified session.
+ */
+static void
+smb_session_logoff(smb_session_t *session)
{
smb_user_t *user;
SMB_SESSION_VALID(session);
+ smb_session_disconnect_trees(session);
+
smb_llist_enter(&session->s_user_list, RW_READER);
user = smb_llist_head(&session->s_user_list);
@@ -975,7 +1247,7 @@ smb_session_disconnect_share(smb_session_t *session, const char *sharename)
ASSERT(user->u_session == session);
if (smb_user_hold(user)) {
- smb_user_disconnect_share(user, sharename);
+ smb_user_logoff(user);
smb_user_release(user);
}
diff --git a/usr/src/uts/common/fs/smbsrv/smb_trans2_find.c b/usr/src/uts/common/fs/smbsrv/smb_trans2_find.c
index 037b2a3b36..d0d60cea5d 100644
--- a/usr/src/uts/common/fs/smbsrv/smb_trans2_find.c
+++ b/usr/src/uts/common/fs/smbsrv/smb_trans2_find.c
@@ -332,7 +332,7 @@ smb_com_trans2_find_first2(smb_request_t *sr, smb_xa_t *xa)
return (SDRC_ERROR);
}
- od = smb_tree_lookup_odir(sr->tid_tree, odid);
+ od = smb_tree_lookup_odir(sr, odid);
if (od == NULL)
return (SDRC_ERROR);
@@ -463,7 +463,7 @@ smb_com_trans2_find_next2(smb_request_t *sr, smb_xa_t *xa)
if (args.fa_maxdata == 0)
return (SDRC_ERROR);
- od = smb_tree_lookup_odir(sr->tid_tree, odid);
+ od = smb_tree_lookup_odir(sr, odid);
if (od == NULL) {
smbsr_error(sr, NT_STATUS_INVALID_HANDLE,
ERRDOS, ERROR_INVALID_HANDLE);
@@ -943,7 +943,7 @@ smb_com_find_close2(smb_request_t *sr)
if (smbsr_decode_vwv(sr, "w", &odid) != 0)
return (SDRC_ERROR);
- od = smb_tree_lookup_odir(sr->tid_tree, odid);
+ od = smb_tree_lookup_odir(sr, odid);
if (od == NULL) {
smbsr_error(sr, NT_STATUS_INVALID_HANDLE,
ERRDOS, ERROR_INVALID_HANDLE);
diff --git a/usr/src/uts/common/fs/smbsrv/smb_tree.c b/usr/src/uts/common/fs/smbsrv/smb_tree.c
index 13adc2d803..b225c67623 100644
--- a/usr/src/uts/common/fs/smbsrv/smb_tree.c
+++ b/usr/src/uts/common/fs/smbsrv/smb_tree.c
@@ -21,7 +21,7 @@
/*
* Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright 2011 Nexenta Systems, Inc. All rights reserved.
+ * Copyright 2012 Nexenta Systems, Inc. All rights reserved.
*/
/*
@@ -40,15 +40,15 @@
* +-------------------+ +-------------------+ +-------------------+
* | SESSION |<----->| SESSION |......| SESSION |
* +-------------------+ +-------------------+ +-------------------+
- * |
- * |
- * v
- * +-------------------+ +-------------------+ +-------------------+
- * | USER |<----->| USER |......| USER |
- * +-------------------+ +-------------------+ +-------------------+
- * |
- * |
- * v
+ * | |
+ * | |
+ * | v
+ * | +-------------------+ +-------------------+ +-------------------+
+ * | | USER |<--->| USER |...| USER |
+ * | +-------------------+ +-------------------+ +-------------------+
+ * |
+ * |
+ * v
* +-------------------+ +-------------------+ +-------------------+
* | TREE |<----->| TREE |......| TREE |
* +-------------------+ +-------------------+ +-------------------+
@@ -175,7 +175,7 @@ static smb_tree_t *smb_tree_connect_core(smb_request_t *);
static smb_tree_t *smb_tree_connect_disk(smb_request_t *, const char *);
static smb_tree_t *smb_tree_connect_printq(smb_request_t *, const char *);
static smb_tree_t *smb_tree_connect_ipc(smb_request_t *, const char *);
-static smb_tree_t *smb_tree_alloc(smb_user_t *, const smb_kshare_t *,
+static smb_tree_t *smb_tree_alloc(smb_request_t *, const smb_kshare_t *,
smb_node_t *, uint32_t, uint32_t);
static boolean_t smb_tree_is_connected_locked(smb_tree_t *);
static boolean_t smb_tree_is_disconnected(smb_tree_t *);
@@ -269,6 +269,7 @@ smb_tree_connect_core(smb_request_t *sr)
}
smb_kshare_release(si);
+
return (tree);
}
@@ -361,7 +362,7 @@ smb_tree_release(
smb_llist_flush(&tree->t_odir_list);
if (smb_tree_is_disconnected(tree) && (tree->t_refcnt == 0))
- smb_user_post_tree(tree->t_user, tree);
+ smb_session_post_tree(tree->t_session, tree);
mutex_exit(&tree->t_mutex);
}
@@ -428,7 +429,7 @@ smb_tree_enum(smb_tree_t *tree, smb_svcenum_t *svcenum)
{
smb_ofile_t *of;
smb_ofile_t *next;
- int rc;
+ int rc = 0;
ASSERT(tree);
ASSERT(tree->t_magic == SMB_TREE_MAGIC);
@@ -712,8 +713,7 @@ smb_tree_connect_disk(smb_request_t *sr, const char *sharename)
if (!smb_shortnames)
sr->arg.tcon.optional_support |= SMB_UNIQUE_FILE_NAME;
- tree = smb_tree_alloc(user, si, snode, access,
- sr->sr_cfg->skc_execflags);
+ tree = smb_tree_alloc(sr, si, snode, access, sr->sr_cfg->skc_execflags);
smb_node_release(snode);
@@ -805,8 +805,7 @@ smb_tree_connect_printq(smb_request_t *sr, const char *sharename)
sr->sr_tcon.optional_support = SMB_SUPPORT_SEARCH_BITS;
- tree = smb_tree_alloc(user, si, snode, access,
- sr->sr_cfg->skc_execflags);
+ tree = smb_tree_alloc(sr, si, snode, access, sr->sr_cfg->skc_execflags);
smb_node_release(snode);
@@ -846,7 +845,7 @@ smb_tree_connect_ipc(smb_request_t *sr, const char *name)
sr->sr_tcon.optional_support = SMB_SUPPORT_SEARCH_BITS;
- tree = smb_tree_alloc(user, si, NULL, ACE_ALL_PERMS, 0);
+ tree = smb_tree_alloc(sr, si, NULL, ACE_ALL_PERMS, 0);
if (tree == NULL) {
smb_tree_log(sr, name, "access denied");
smbsr_error(sr, NT_STATUS_ACCESS_DENIED, ERRSRV, ERRaccess);
@@ -859,41 +858,45 @@ smb_tree_connect_ipc(smb_request_t *sr, const char *name)
* Allocate a tree.
*/
static smb_tree_t *
-smb_tree_alloc(smb_user_t *user, const smb_kshare_t *si, smb_node_t *snode,
- uint32_t access, uint32_t execflags)
+smb_tree_alloc(smb_request_t *sr, const smb_kshare_t *si,
+ smb_node_t *snode, uint32_t access, uint32_t execflags)
{
+ smb_session_t *session = sr->session;
smb_tree_t *tree;
uint32_t stype = si->shr_type;
uint16_t tid;
- if (smb_idpool_alloc(&user->u_tid_pool, &tid))
+ if (smb_idpool_alloc(&session->s_tid_pool, &tid))
return (NULL);
- tree = kmem_cache_alloc(user->u_server->si_cache_tree, KM_SLEEP);
+ tree = kmem_cache_alloc(session->s_server->si_cache_tree, KM_SLEEP);
bzero(tree, sizeof (smb_tree_t));
- tree->t_user = user;
- tree->t_session = user->u_session;
- tree->t_server = user->u_server;
+ tree->t_session = session;
+ tree->t_server = session->s_server;
+
+ /* grab a ref for tree->t_owner */
+ smb_user_hold_internal(sr->uid_user);
+ tree->t_owner = sr->uid_user;
if (STYPE_ISDSK(stype) || STYPE_ISPRN(stype)) {
if (smb_tree_getattr(si, snode, tree) != 0) {
- smb_idpool_free(&user->u_tid_pool, tid);
- kmem_cache_free(user->u_server->si_cache_tree, tree);
+ smb_idpool_free(&session->s_tid_pool, tid);
+ kmem_cache_free(session->s_server->si_cache_tree, tree);
return (NULL);
}
}
if (smb_idpool_constructor(&tree->t_fid_pool)) {
- smb_idpool_free(&user->u_tid_pool, tid);
- kmem_cache_free(user->u_server->si_cache_tree, tree);
+ smb_idpool_free(&session->s_tid_pool, tid);
+ kmem_cache_free(session->s_server->si_cache_tree, tree);
return (NULL);
}
if (smb_idpool_constructor(&tree->t_odid_pool)) {
smb_idpool_destructor(&tree->t_fid_pool);
- smb_idpool_free(&user->u_tid_pool, tid);
- kmem_cache_free(user->u_server->si_cache_tree, tree);
+ smb_idpool_free(&session->s_tid_pool, tid);
+ kmem_cache_free(session->s_server->si_cache_tree, tree);
return (NULL);
}
@@ -929,11 +932,11 @@ smb_tree_alloc(smb_user_t *user, const smb_kshare_t *si, smb_node_t *snode,
tree->t_acltype = smb_fsop_acltype(snode);
}
- smb_llist_enter(&user->u_tree_list, RW_WRITER);
- smb_llist_insert_head(&user->u_tree_list, tree);
- smb_llist_exit(&user->u_tree_list);
- atomic_inc_32(&user->u_session->s_tree_cnt);
- smb_server_inc_trees(user->u_server);
+ smb_llist_enter(&session->s_tree_list, RW_WRITER);
+ smb_llist_insert_head(&session->s_tree_list, tree);
+ smb_llist_exit(&session->s_tree_list);
+ atomic_inc_32(&session->s_tree_cnt);
+ smb_server_inc_trees(session->s_server);
return (tree);
}
@@ -947,19 +950,19 @@ smb_tree_alloc(smb_user_t *user, const smb_kshare_t *si, smb_node_t *snode,
void
smb_tree_dealloc(void *arg)
{
- smb_user_t *user;
+ smb_session_t *session;
smb_tree_t *tree = (smb_tree_t *)arg;
SMB_TREE_VALID(tree);
ASSERT(tree->t_state == SMB_TREE_STATE_DISCONNECTED);
ASSERT(tree->t_refcnt == 0);
- user = tree->t_user;
- smb_llist_enter(&user->u_tree_list, RW_WRITER);
- smb_llist_remove(&user->u_tree_list, tree);
- smb_idpool_free(&user->u_tid_pool, tree->t_tid);
- atomic_dec_32(&tree->t_session->s_tree_cnt);
- smb_llist_exit(&user->u_tree_list);
+ session = tree->t_session;
+ smb_llist_enter(&session->s_tree_list, RW_WRITER);
+ smb_llist_remove(&session->s_tree_list, tree);
+ smb_idpool_free(&session->s_tid_pool, tree->t_tid);
+ atomic_dec_32(&session->s_tree_cnt);
+ smb_llist_exit(&session->s_tree_list);
mutex_enter(&tree->t_mutex);
mutex_exit(&tree->t_mutex);
@@ -974,6 +977,10 @@ smb_tree_dealloc(void *arg)
smb_llist_destructor(&tree->t_odir_list);
smb_idpool_destructor(&tree->t_fid_pool);
smb_idpool_destructor(&tree->t_odid_pool);
+
+ SMB_USER_VALID(tree->t_owner);
+ smb_user_release(tree->t_owner);
+
kmem_cache_free(tree->t_server->si_cache_tree, tree);
}
@@ -1234,27 +1241,38 @@ smb_tree_log(smb_request_t *sr, const char *sharename, const char *fmt, ...)
* Returns NULL if odir not found or a hold cannot be obtained.
*/
smb_odir_t *
-smb_tree_lookup_odir(smb_tree_t *tree, uint16_t odid)
+smb_tree_lookup_odir(smb_request_t *sr, uint16_t odid)
{
smb_odir_t *od;
smb_llist_t *od_list;
+ smb_tree_t *tree = sr->tid_tree;
- ASSERT(tree);
ASSERT(tree->t_magic == SMB_TREE_MAGIC);
od_list = &tree->t_odir_list;
- smb_llist_enter(od_list, RW_READER);
+ smb_llist_enter(od_list, RW_READER);
od = smb_llist_head(od_list);
while (od) {
- if (od->d_odid == odid) {
- if (!smb_odir_hold(od))
- od = NULL;
+ if (od->d_odid == odid)
break;
- }
od = smb_llist_next(od_list, od);
}
+ if (od == NULL)
+ goto out;
+
+ /*
+ * Only allow use of a given Search ID with the same UID that
+ * was used to create it. MS-CIFS 3.3.5.14
+ */
+ if (od->d_user != sr->uid_user) {
+ od = NULL;
+ goto out;
+ }
+ if (!smb_odir_hold(od))
+ od = NULL;
+out:
smb_llist_exit(od_list);
return (od);
}
@@ -1377,15 +1395,16 @@ smb_tree_close_odirs(smb_tree_t *tree, uint16_t pid)
}
static void
-smb_tree_set_execinfo(smb_tree_t *tree, smb_shr_execinfo_t *exec, int exec_type)
+smb_tree_set_execinfo(smb_tree_t *tree, smb_shr_execinfo_t *exec,
+ int exec_type)
{
exec->e_sharename = tree->t_sharename;
- exec->e_winname = tree->t_user->u_name;
- exec->e_userdom = tree->t_user->u_domain;
+ exec->e_winname = tree->t_owner->u_name;
+ exec->e_userdom = tree->t_owner->u_domain;
exec->e_srv_ipaddr = tree->t_session->local_ipaddr;
exec->e_cli_ipaddr = tree->t_session->ipaddr;
exec->e_cli_netbiosname = tree->t_session->workstation;
- exec->e_uid = crgetuid(tree->t_user->u_cred);
+ exec->e_uid = crgetuid(tree->t_owner->u_cred);
exec->e_type = exec_type;
}
@@ -1438,6 +1457,26 @@ smb_tree_netinfo_encode(smb_tree_t *tree, uint8_t *buf, size_t buflen,
return (rc);
}
+static void
+smb_tree_netinfo_username(smb_tree_t *tree, char **namestr, uint32_t *namelen)
+{
+ smb_user_t *user = tree->t_owner;
+
+ /*
+ * u_domain_len and u_name_len include the '\0' in their
+ * lengths, hence the sum of the two lengths gives us room
+ * for both the '\\' and '\0' chars.
+ */
+ ASSERT(namestr);
+ ASSERT(namelen);
+ ASSERT(user->u_domain_len > 0);
+ ASSERT(user->u_name_len > 0);
+ *namelen = user->u_domain_len + user->u_name_len;
+ *namestr = kmem_alloc(*namelen, KM_SLEEP);
+ (void) snprintf(*namestr, *namelen, "%s\\%s", user->u_domain,
+ user->u_name);
+}
+
/*
* Note: ci_numusers should be the number of users connected to
* the share rather than the number of references on the tree but
@@ -1446,8 +1485,6 @@ smb_tree_netinfo_encode(smb_tree_t *tree, uint8_t *buf, size_t buflen,
static void
smb_tree_netinfo_init(smb_tree_t *tree, smb_netconnectinfo_t *info)
{
- smb_user_t *user;
-
ASSERT(tree);
info->ci_id = tree->t_tid;
@@ -1459,13 +1496,7 @@ smb_tree_netinfo_init(smb_tree_t *tree, smb_netconnectinfo_t *info)
info->ci_sharelen = strlen(tree->t_sharename) + 1;
info->ci_share = smb_mem_strdup(tree->t_sharename);
- user = tree->t_user;
- ASSERT(user);
-
- info->ci_namelen = user->u_domain_len + user->u_name_len + 2;
- info->ci_username = kmem_alloc(info->ci_namelen, KM_SLEEP);
- (void) snprintf(info->ci_username, info->ci_namelen, "%s\\%s",
- user->u_domain, user->u_name);
+ smb_tree_netinfo_username(tree, &info->ci_username, &info->ci_namelen);
}
static void
diff --git a/usr/src/uts/common/fs/smbsrv/smb_tree_connect.c b/usr/src/uts/common/fs/smbsrv/smb_tree_connect.c
index 1ce9720f5d..19b857e834 100644
--- a/usr/src/uts/common/fs/smbsrv/smb_tree_connect.c
+++ b/usr/src/uts/common/fs/smbsrv/smb_tree_connect.c
@@ -20,6 +20,7 @@
*/
/*
* Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2012 Nexenta Systems, Inc. All rights reserved.
*/
#include <smbsrv/smb_kproto.h>
@@ -362,8 +363,7 @@ smb_sdrc_t
smb_pre_tree_disconnect(smb_request_t *sr)
{
sr->uid_user = smb_session_lookup_uid(sr->session, sr->smb_uid);
- if (sr->uid_user != NULL)
- sr->tid_tree = smb_user_lookup_tree(sr->uid_user, sr->smb_tid);
+ sr->tid_tree = smb_session_lookup_tree(sr->session, sr->smb_tid);
DTRACE_SMB_1(op__TreeDisconnect__start, smb_request_t *, sr);
return (SDRC_SUCCESS);
diff --git a/usr/src/uts/common/fs/smbsrv/smb_user.c b/usr/src/uts/common/fs/smbsrv/smb_user.c
index cc3fde7f38..09eaba699c 100644
--- a/usr/src/uts/common/fs/smbsrv/smb_user.c
+++ b/usr/src/uts/common/fs/smbsrv/smb_user.c
@@ -20,6 +20,7 @@
*/
/*
* Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2012 Nexenta Systems, Inc. All rights reserved.
*/
/*
@@ -38,15 +39,15 @@
* +-------------------+ +-------------------+ +-------------------+
* | SESSION |<----->| SESSION |......| SESSION |
* +-------------------+ +-------------------+ +-------------------+
- * |
- * |
- * v
- * +-------------------+ +-------------------+ +-------------------+
- * | USER |<----->| USER |......| USER |
- * +-------------------+ +-------------------+ +-------------------+
- * |
- * |
- * v
+ * | |
+ * | |
+ * | v
+ * | +-------------------+ +-------------------+ +-------------------+
+ * | | USER |<--->| USER |...| USER |
+ * | +-------------------+ +-------------------+ +-------------------+
+ * |
+ * |
+ * v
* +-------------------+ +-------------------+ +-------------------+
* | TREE |<----->| TREE |......| TREE |
* +-------------------+ +-------------------+ +-------------------+
@@ -170,7 +171,6 @@
static boolean_t smb_user_is_logged_in(smb_user_t *);
static int smb_user_enum_private(smb_user_t *, smb_svcenum_t *);
-static smb_tree_t *smb_user_get_tree(smb_llist_t *, smb_tree_t *);
static void smb_user_setcred(smb_user_t *, cred_t *, uint32_t);
static void smb_user_nonauth_logon(uint32_t);
static void smb_user_auth_logoff(uint32_t);
@@ -210,20 +210,15 @@ smb_user_login(
user->u_audit_sid = audit_sid;
if (!smb_idpool_alloc(&session->s_uid_pool, &user->u_uid)) {
- if (!smb_idpool_constructor(&user->u_tid_pool)) {
- smb_llist_constructor(&user->u_tree_list,
- sizeof (smb_tree_t), offsetof(smb_tree_t, t_lnd));
- mutex_init(&user->u_mutex, NULL, MUTEX_DEFAULT, NULL);
- smb_user_setcred(user, cr, privileges);
- user->u_state = SMB_USER_STATE_LOGGED_IN;
- user->u_magic = SMB_USER_MAGIC;
- smb_llist_enter(&session->s_user_list, RW_WRITER);
- smb_llist_insert_tail(&session->s_user_list, user);
- smb_llist_exit(&session->s_user_list);
- smb_server_inc_users(session->s_server);
- return (user);
- }
- smb_idpool_free(&session->s_uid_pool, user->u_uid);
+ mutex_init(&user->u_mutex, NULL, MUTEX_DEFAULT, NULL);
+ smb_user_setcred(user, cr, privileges);
+ user->u_state = SMB_USER_STATE_LOGGED_IN;
+ user->u_magic = SMB_USER_MAGIC;
+ smb_llist_enter(&session->s_user_list, RW_WRITER);
+ smb_llist_insert_tail(&session->s_user_list, user);
+ smb_llist_exit(&session->s_user_list);
+ smb_server_inc_users(session->s_server);
+ return (user);
}
smb_mem_free(user->u_name);
smb_mem_free(user->u_domain);
@@ -279,10 +274,7 @@ smb_user_logoff(
*/
user->u_state = SMB_USER_STATE_LOGGING_OFF;
mutex_exit(&user->u_mutex);
- /*
- * All the trees hanging off of this user are disconnected.
- */
- smb_user_disconnect_trees(user);
+ smb_session_disconnect_owned_trees(user->u_session, user);
smb_user_auth_logoff(user->u_audit_sid);
mutex_enter(&user->u_mutex);
user->u_state = SMB_USER_STATE_LOGGED_OFF;
@@ -301,13 +293,13 @@ smb_user_logoff(
}
/*
- * Take a reference on a user.
+ * Take a reference on a user. Do not return a reference unless the user is in
+ * the logged-in state.
*/
boolean_t
smb_user_hold(smb_user_t *user)
{
- ASSERT(user);
- ASSERT(user->u_magic == SMB_USER_MAGIC);
+ SMB_USER_VALID(user);
mutex_enter(&user->u_mutex);
@@ -322,6 +314,19 @@ smb_user_hold(smb_user_t *user)
}
/*
+ * Unconditionally take a reference on a user.
+ */
+void
+smb_user_hold_internal(smb_user_t *user)
+{
+ SMB_USER_VALID(user);
+
+ mutex_enter(&user->u_mutex);
+ user->u_refcnt++;
+ mutex_exit(&user->u_mutex);
+}
+
+/*
* Release a reference on a user. If the reference count falls to
* zero and the user has logged off, post the object for deletion.
* Object deletion is deferred to avoid modifying a list while an
@@ -337,9 +342,6 @@ smb_user_release(
ASSERT(user->u_refcnt);
user->u_refcnt--;
- /* flush the tree list's delete queue */
- smb_llist_flush(&user->u_tree_list);
-
switch (user->u_state) {
case SMB_USER_STATE_LOGGED_OFF:
if (user->u_refcnt == 0)
@@ -357,248 +359,6 @@ smb_user_release(
mutex_exit(&user->u_mutex);
}
-void
-smb_user_post_tree(smb_user_t *user, smb_tree_t *tree)
-{
- SMB_USER_VALID(user);
- SMB_TREE_VALID(tree);
- ASSERT(tree->t_refcnt == 0);
- ASSERT(tree->t_state == SMB_TREE_STATE_DISCONNECTED);
- ASSERT(tree->t_user == user);
-
- smb_llist_post(&user->u_tree_list, tree, smb_tree_dealloc);
-}
-
-
-/*
- * Find a tree by tree-id.
- */
-smb_tree_t *
-smb_user_lookup_tree(
- smb_user_t *user,
- uint16_t tid)
-
-{
- smb_tree_t *tree;
-
- ASSERT(user);
- ASSERT(user->u_magic == SMB_USER_MAGIC);
-
- smb_llist_enter(&user->u_tree_list, RW_READER);
- tree = smb_llist_head(&user->u_tree_list);
-
- while (tree) {
- ASSERT(tree->t_magic == SMB_TREE_MAGIC);
- ASSERT(tree->t_user == user);
-
- if (tree->t_tid == tid) {
- if (smb_tree_hold(tree)) {
- smb_llist_exit(&user->u_tree_list);
- return (tree);
- } else {
- smb_llist_exit(&user->u_tree_list);
- return (NULL);
- }
- }
-
- tree = smb_llist_next(&user->u_tree_list, tree);
- }
-
- smb_llist_exit(&user->u_tree_list);
- return (NULL);
-}
-
-/*
- * Find the first connected tree that matches the specified sharename.
- * If the specified tree is NULL the search starts from the beginning of
- * the user's tree list. If a tree is provided the search starts just
- * after that tree.
- */
-smb_tree_t *
-smb_user_lookup_share(
- smb_user_t *user,
- const char *sharename,
- smb_tree_t *tree)
-{
- ASSERT(user);
- ASSERT(user->u_magic == SMB_USER_MAGIC);
- ASSERT(sharename);
-
- smb_llist_enter(&user->u_tree_list, RW_READER);
-
- if (tree) {
- ASSERT(tree->t_magic == SMB_TREE_MAGIC);
- ASSERT(tree->t_user == user);
- tree = smb_llist_next(&user->u_tree_list, tree);
- } else {
- tree = smb_llist_head(&user->u_tree_list);
- }
-
- while (tree) {
- ASSERT(tree->t_magic == SMB_TREE_MAGIC);
- ASSERT(tree->t_user == user);
- if (smb_strcasecmp(tree->t_sharename, sharename, 0) == 0) {
- if (smb_tree_hold(tree)) {
- smb_llist_exit(&user->u_tree_list);
- return (tree);
- }
- }
- tree = smb_llist_next(&user->u_tree_list, tree);
- }
-
- smb_llist_exit(&user->u_tree_list);
- return (NULL);
-}
-
-/*
- * Find the first connected tree that matches the specified volume name.
- * If the specified tree is NULL the search starts from the beginning of
- * the user's tree list. If a tree is provided the search starts just
- * after that tree.
- */
-smb_tree_t *
-smb_user_lookup_volume(
- smb_user_t *user,
- const char *name,
- smb_tree_t *tree)
-{
- ASSERT(user);
- ASSERT(user->u_magic == SMB_USER_MAGIC);
- ASSERT(name);
-
- smb_llist_enter(&user->u_tree_list, RW_READER);
-
- if (tree) {
- ASSERT(tree->t_magic == SMB_TREE_MAGIC);
- ASSERT(tree->t_user == user);
- tree = smb_llist_next(&user->u_tree_list, tree);
- } else {
- tree = smb_llist_head(&user->u_tree_list);
- }
-
- while (tree) {
- ASSERT(tree->t_magic == SMB_TREE_MAGIC);
- ASSERT(tree->t_user == user);
-
- if (smb_strcasecmp(tree->t_volume, name, 0) == 0) {
- if (smb_tree_hold(tree)) {
- smb_llist_exit(&user->u_tree_list);
- return (tree);
- }
- }
-
- tree = smb_llist_next(&user->u_tree_list, tree);
- }
-
- smb_llist_exit(&user->u_tree_list);
- return (NULL);
-}
-
-/*
- * Disconnect all trees that match the specified client process-id.
- */
-void
-smb_user_close_pid(
- smb_user_t *user,
- uint16_t pid)
-{
- smb_tree_t *tree;
-
- ASSERT(user);
- ASSERT(user->u_magic == SMB_USER_MAGIC);
-
- tree = smb_user_get_tree(&user->u_tree_list, NULL);
- while (tree) {
- smb_tree_t *next;
- ASSERT(tree->t_user == user);
- smb_tree_close_pid(tree, pid);
- next = smb_user_get_tree(&user->u_tree_list, tree);
- smb_tree_release(tree);
- tree = next;
- }
-}
-
-/*
- * Disconnect all trees that this user has connected.
- */
-void
-smb_user_disconnect_trees(
- smb_user_t *user)
-{
- smb_tree_t *tree;
-
- ASSERT(user);
- ASSERT(user->u_magic == SMB_USER_MAGIC);
-
- tree = smb_user_get_tree(&user->u_tree_list, NULL);
- while (tree) {
- ASSERT(tree->t_user == user);
- smb_tree_disconnect(tree, B_TRUE);
- smb_tree_release(tree);
- tree = smb_user_get_tree(&user->u_tree_list, NULL);
- }
-}
-
-/*
- * Disconnect all trees that match the specified share name.
- */
-void
-smb_user_disconnect_share(
- smb_user_t *user,
- const char *sharename)
-{
- smb_tree_t *tree;
- smb_tree_t *next;
-
- ASSERT(user);
- ASSERT(user->u_magic == SMB_USER_MAGIC);
- ASSERT(user->u_refcnt);
-
- tree = smb_user_lookup_share(user, sharename, NULL);
- while (tree) {
- ASSERT(tree->t_magic == SMB_TREE_MAGIC);
- smb_session_cancel_requests(user->u_session, tree, NULL);
- smb_tree_disconnect(tree, B_TRUE);
- next = smb_user_lookup_share(user, sharename, tree);
- smb_tree_release(tree);
- tree = next;
- }
-}
-
-/*
- * Close a file by its unique id.
- */
-int
-smb_user_fclose(smb_user_t *user, uint32_t uniqid)
-{
- smb_llist_t *tree_list;
- smb_tree_t *tree;
- int rc = ENOENT;
-
- ASSERT(user);
- ASSERT(user->u_magic == SMB_USER_MAGIC);
-
- tree_list = &user->u_tree_list;
- ASSERT(tree_list);
-
- smb_llist_enter(tree_list, RW_READER);
- tree = smb_llist_head(tree_list);
-
- while ((tree != NULL) && (rc == ENOENT)) {
- ASSERT(tree->t_user == user);
-
- if (smb_tree_hold(tree)) {
- rc = smb_tree_fclose(tree, uniqid);
- smb_tree_release(tree);
- }
-
- tree = smb_llist_next(tree_list, tree);
- }
-
- smb_llist_exit(tree_list);
- return (rc);
-}
-
/*
* Determine whether or not the user is an administrator.
* Members of the administrators group have administrative rights.
@@ -688,9 +448,7 @@ smb_user_namecmp(smb_user_t *user, const char *name)
int
smb_user_enum(smb_user_t *user, smb_svcenum_t *svcenum)
{
- smb_tree_t *tree;
- smb_tree_t *next;
- int rc;
+ int rc = 0;
ASSERT(user);
ASSERT(user->u_magic == SMB_USER_MAGIC);
@@ -698,21 +456,6 @@ smb_user_enum(smb_user_t *user, smb_svcenum_t *svcenum)
if (svcenum->se_type == SMB_SVCENUM_TYPE_USER)
return (smb_user_enum_private(user, svcenum));
- tree = smb_user_get_tree(&user->u_tree_list, NULL);
- while (tree) {
- ASSERT(tree->t_user == user);
-
- rc = smb_tree_enum(tree, svcenum);
- if (rc != 0) {
- smb_tree_release(tree);
- break;
- }
-
- next = smb_user_get_tree(&user->u_tree_list, tree);
- smb_tree_release(tree);
- tree = next;
- }
-
return (rc);
}
@@ -769,8 +512,6 @@ smb_user_delete(void *arg)
user->u_magic = (uint32_t)~SMB_USER_MAGIC;
mutex_destroy(&user->u_mutex);
- smb_llist_destructor(&user->u_tree_list);
- smb_idpool_destructor(&user->u_tid_pool);
if (user->u_cred)
crfree(user->u_cred);
if (user->u_privcred)
@@ -780,43 +521,6 @@ smb_user_delete(void *arg)
kmem_cache_free(user->u_server->si_cache_user, user);
}
-/*
- * Get the next connected tree in the list. A reference is taken on
- * the tree, which can be released later with smb_tree_release().
- *
- * If the specified tree is NULL the search starts from the beginning of
- * the tree list. If a tree is provided the search starts just after
- * that tree.
- *
- * Returns NULL if there are no connected trees in the list.
- */
-static smb_tree_t *
-smb_user_get_tree(
- smb_llist_t *tree_list,
- smb_tree_t *tree)
-{
- ASSERT(tree_list);
-
- smb_llist_enter(tree_list, RW_READER);
-
- if (tree) {
- ASSERT(tree->t_magic == SMB_TREE_MAGIC);
- tree = smb_llist_next(tree_list, tree);
- } else {
- tree = smb_llist_head(tree_list);
- }
-
- while (tree) {
- if (smb_tree_hold(tree))
- break;
-
- tree = smb_llist_next(tree_list, tree);
- }
-
- smb_llist_exit(tree_list);
- return (tree);
-}
-
cred_t *
smb_user_getcred(smb_user_t *user)
{
diff --git a/usr/src/uts/common/fs/zfs/dsl_dataset.c b/usr/src/uts/common/fs/zfs/dsl_dataset.c
index ba9c766c65..b4ab4ec3fd 100644
--- a/usr/src/uts/common/fs/zfs/dsl_dataset.c
+++ b/usr/src/uts/common/fs/zfs/dsl_dataset.c
@@ -363,8 +363,19 @@ dsl_dataset_snap_remove(dsl_dataset_t *ds, const char *name, dmu_tx_t *tx,
boolean_t
dsl_dataset_try_add_ref(dsl_pool_t *dp, dsl_dataset_t *ds, void *tag)
{
- return (dmu_buf_try_add_ref(ds->ds_dbuf, dp->dp_meta_objset,
- ds->ds_object, DMU_BONUS_BLKID, tag));
+ dmu_buf_t *dbuf = ds->ds_dbuf;
+ boolean_t result = B_FALSE;
+
+ if (dbuf != NULL && dmu_buf_try_add_ref(dbuf, dp->dp_meta_objset,
+ ds->ds_object, DMU_BONUS_BLKID, tag)) {
+
+ if (ds == dmu_buf_get_user(dbuf))
+ result = B_TRUE;
+ else
+ dmu_buf_rele(dbuf, tag);
+ }
+
+ return (result);
}
int
diff --git a/usr/src/uts/common/os/exit.c b/usr/src/uts/common/os/exit.c
index 6a27544201..02844cef07 100644
--- a/usr/src/uts/common/os/exit.c
+++ b/usr/src/uts/common/os/exit.c
@@ -400,14 +400,36 @@ proc_exit(int why, int what)
if (z->zone_boot_err == 0 &&
zone_status_get(z) < ZONE_IS_SHUTTING_DOWN &&
zone_status_get(global_zone) < ZONE_IS_SHUTTING_DOWN) {
- if (z->zone_restart_init == B_TRUE) {
- if (restart_init(what, why) == 0)
- return (0);
- }
- z->zone_init_status = wstat(why, what);
- (void) zone_kadmin(A_SHUTDOWN, AD_HALT, NULL,
- zone_kcred());
+ /*
+ * If the init process should be restarted, the
+ * "zone_restart_init" member will be set. Some init
+ * programs in branded zones do not tolerate a restart
+ * in the traditional manner; setting the
+ * "zone_reboot_on_init_exit" member will cause the
+ * entire zone to be rebooted instead. If neither of
+ * these flags is set the zone will shut down.
+ */
+ if (z->zone_reboot_on_init_exit == B_TRUE &&
+ z->zone_restart_init == B_TRUE) {
+ /*
+ * Trigger a zone reboot and continue
+ * with exit processing.
+ */
+ z->zone_init_status = wstat(why, what);
+ (void) zone_kadmin(A_REBOOT, 0, NULL,
+ zone_kcred());
+
+ } else {
+ if (z->zone_restart_init == B_TRUE) {
+ if (restart_init(what, why) == 0)
+ return (0);
+ }
+
+ z->zone_init_status = wstat(why, what);
+ (void) zone_kadmin(A_SHUTDOWN, AD_HALT, NULL,
+ zone_kcred());
+ }
}
/*
@@ -995,10 +1017,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
@@ -1047,10 +1068,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;
+
+ /*
+ * 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;
- proc_gone = 0;
+ 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;
@@ -1107,7 +1155,6 @@ 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;
@@ -1186,11 +1233,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) {
@@ -1209,7 +1257,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)) {
diff --git a/usr/src/uts/common/os/logsubr.c b/usr/src/uts/common/os/logsubr.c
index 86e9045887..6a603c8982 100644
--- a/usr/src/uts/common/os/logsubr.c
+++ b/usr/src/uts/common/os/logsubr.c
@@ -22,7 +22,7 @@
/*
* Copyright (c) 2013 Gary Mills
* Copyright (c) 1998, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright (c) 2014, Joyent, Inc. All rights reserved.
+ * Copyright (c) 2015, Joyent, Inc.
*/
#include <sys/types.h>
@@ -250,7 +250,7 @@ log_init(void)
*/
printf("\rSunOS Release %s Version %s %u-bit\n",
utsname.release, utsname.version, NBBY * (uint_t)sizeof (void *));
- printf("Copyright (c) 2010-2014, Joyent Inc. All rights reserved.\n");
+ printf("Copyright (c) 2010-2015, Joyent Inc. All rights reserved.\n");
#ifdef DEBUG
printf("DEBUG enabled\n");
#endif
diff --git a/usr/src/uts/common/os/sig.c b/usr/src/uts/common/os/sig.c
index b117bf3584..ae643c280e 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 2014, Joyent, Inc. All rights reserved.
+ * Copyright 2015, Joyent, Inc.
*/
/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
@@ -194,7 +194,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 */
@@ -608,6 +608,21 @@ issig_forreal(void)
}
/*
+ * Allow the brand the chance to alter (or suppress) delivery
+ * of this signal.
+ */
+ if (PROC_IS_BRANDED(p) && BROP(p)->b_issig_stop != NULL) {
+ /*
+ * The brand hook will return 0 if it would like
+ * us to drive on, or -1 if we should restart
+ * the loop to check other conditions.
+ */
+ if (BROP(p)->b_issig_stop(p, lwp) != 0) {
+ continue;
+ }
+ }
+
+ /*
* 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
@@ -939,6 +954,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;
/*
@@ -1050,7 +1075,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
@@ -1156,6 +1181,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;
@@ -1177,7 +1209,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) {
@@ -1795,6 +1827,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);
diff --git a/usr/src/uts/common/os/zone.c b/usr/src/uts/common/os/zone.c
index 285aeac032..347a90a022 100644
--- a/usr/src/uts/common/os/zone.c
+++ b/usr/src/uts/common/os/zone.c
@@ -2624,6 +2624,7 @@ zone_init(void)
zone0.zone_ntasks = 1;
mutex_exit(&p0.p_lock);
zone0.zone_restart_init = B_TRUE;
+ zone0.zone_reboot_on_init_exit = B_FALSE;
zone0.zone_init_status = -1;
zone0.zone_brand = &native_brand;
rctl_prealloc_destroy(gp);
@@ -4669,8 +4670,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) {
@@ -4819,6 +4821,7 @@ zone_create(const char *zone_name, const char *zone_root,
zone->zone_ncpus = 0;
zone->zone_ncpus_online = 0;
zone->zone_restart_init = B_TRUE;
+ zone->zone_reboot_on_init_exit = B_FALSE;
zone->zone_init_status = -1;
zone->zone_brand = &native_brand;
zone->zone_initname = NULL;
@@ -5045,8 +5048,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) {
diff --git a/usr/src/uts/common/smbsrv/smb_kproto.h b/usr/src/uts/common/smbsrv/smb_kproto.h
index f2de265176..ad7402fd80 100644
--- a/usr/src/uts/common/smbsrv/smb_kproto.h
+++ b/usr/src/uts/common/smbsrv/smb_kproto.h
@@ -521,6 +521,15 @@ void smb_session_disconnect_from_share(smb_llist_t *, char *);
smb_user_t *smb_session_dup_user(smb_session_t *, char *, char *);
smb_user_t *smb_session_lookup_uid(smb_session_t *, uint16_t);
void smb_session_post_user(smb_session_t *, smb_user_t *);
+void smb_session_post_tree(smb_session_t *, smb_tree_t *);
+smb_tree_t *smb_session_lookup_tree(smb_session_t *, uint16_t);
+smb_tree_t *smb_session_lookup_share(smb_session_t *, const char *,
+ smb_tree_t *);
+smb_tree_t *smb_session_lookup_volume(smb_session_t *, const char *,
+ smb_tree_t *);
+void smb_session_close_pid(smb_session_t *, uint16_t);
+void smb_session_disconnect_owned_trees(smb_session_t *, smb_user_t *);
+void smb_session_disconnect_trees(smb_session_t *);
void smb_session_disconnect_share(smb_session_t *, const char *);
void smb_session_getclient(smb_session_t *, char *, size_t);
boolean_t smb_session_isclient(smb_session_t *, const char *);
@@ -539,10 +548,10 @@ void smb_request_free(smb_request_t *);
/*
* ofile functions (file smb_ofile.c)
*/
-smb_ofile_t *smb_ofile_lookup_by_fid(smb_tree_t *, uint16_t);
+smb_ofile_t *smb_ofile_lookup_by_fid(smb_request_t *, uint16_t);
smb_ofile_t *smb_ofile_lookup_by_uniqid(smb_tree_t *, uint32_t);
boolean_t smb_ofile_disallow_fclose(smb_ofile_t *);
-smb_ofile_t *smb_ofile_open(smb_tree_t *, smb_node_t *, uint16_t,
+smb_ofile_t *smb_ofile_open(smb_request_t *, smb_node_t *, uint16_t,
smb_arg_open_t *, uint16_t, uint32_t, smb_error_t *);
void smb_ofile_close(smb_ofile_t *, int32_t);
void smb_ofile_delete(void *);
@@ -603,18 +612,11 @@ smb_user_t *smb_user_login(smb_session_t *, cred_t *,
smb_user_t *smb_user_dup(smb_user_t *);
void smb_user_logoff(smb_user_t *);
void smb_user_delete(void *);
-void smb_user_post_tree(smb_user_t *, smb_tree_t *);
-smb_tree_t *smb_user_lookup_tree(smb_user_t *, uint16_t);
-smb_tree_t *smb_user_lookup_share(smb_user_t *, const char *, smb_tree_t *);
-smb_tree_t *smb_user_lookup_volume(smb_user_t *, const char *, smb_tree_t *);
boolean_t smb_user_is_admin(smb_user_t *);
boolean_t smb_user_namecmp(smb_user_t *, const char *);
int smb_user_enum(smb_user_t *, smb_svcenum_t *);
-void smb_user_close_pid(smb_user_t *, uint16_t);
-void smb_user_disconnect_trees(smb_user_t *user);
-void smb_user_disconnect_share(smb_user_t *, const char *);
-int smb_user_fclose(smb_user_t *, uint32_t);
boolean_t smb_user_hold(smb_user_t *);
+void smb_user_hold_internal(smb_user_t *);
void smb_user_release(smb_user_t *);
cred_t *smb_user_getcred(smb_user_t *);
cred_t *smb_user_getprivcred(smb_user_t *);
@@ -637,7 +639,7 @@ int smb_tree_enum(smb_tree_t *, smb_svcenum_t *);
int smb_tree_fclose(smb_tree_t *, uint32_t);
boolean_t smb_tree_hold(smb_tree_t *);
void smb_tree_release(smb_tree_t *);
-smb_odir_t *smb_tree_lookup_odir(smb_tree_t *, uint16_t);
+smb_odir_t *smb_tree_lookup_odir(smb_request_t *, uint16_t);
boolean_t smb_tree_is_connected(smb_tree_t *);
#define SMB_TREE_GET_TID(tree) ((tree)->t_tid)
diff --git a/usr/src/uts/common/smbsrv/smb_ktypes.h b/usr/src/uts/common/smbsrv/smb_ktypes.h
index 493e7130a7..2c5d102f62 100644
--- a/usr/src/uts/common/smbsrv/smb_ktypes.h
+++ b/usr/src/uts/common/smbsrv/smb_ktypes.h
@@ -908,7 +908,9 @@ typedef struct smb_session {
smb_slist_t s_req_list;
smb_llist_t s_xa_list;
smb_llist_t s_user_list;
+ smb_llist_t s_tree_list;
smb_idpool_t s_uid_pool;
+ smb_idpool_t s_tid_pool;
smb_txlst_t s_txlst;
volatile uint32_t s_tree_cnt;
@@ -975,9 +977,6 @@ typedef struct smb_user {
cred_t *u_cred;
cred_t *u_privcred;
- smb_llist_t u_tree_list;
- smb_idpool_t u_tid_pool;
-
uint32_t u_refcnt;
uint32_t u_flags;
uint32_t u_privileges;
@@ -1028,7 +1027,11 @@ typedef struct smb_tree {
struct smb_server *t_server;
smb_session_t *t_session;
- smb_user_t *t_user;
+ /*
+ * user whose uid was in the tree connect message
+ * ("owner" in MS-CIFS parlance, see section 2.2.1.6 definition of FID)
+ */
+ smb_user_t *t_owner;
smb_node_t *t_snode;
smb_llist_t t_ofile_list;
@@ -1259,6 +1262,7 @@ typedef struct smb_odir {
list_node_t d_lnd;
smb_odir_state_t d_state;
smb_session_t *d_session;
+ smb_user_t *d_user;
smb_tree_t *d_tree;
smb_node_t *d_dnode;
cred_t *d_cred;
diff --git a/usr/src/uts/common/sys/brand.h b/usr/src/uts/common/sys/brand.h
index 3486ae864d..b3abada863 100644
--- a/usr/src/uts/common/sys/brand.h
+++ b/usr/src/uts/common/sys/brand.h
@@ -21,7 +21,7 @@
/*
* Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright (c) 2014, Joyent, Inc. All rights reserved.
+ * Copyright 2015, Joyent, Inc.
*/
#ifndef _SYS_BRAND_H
@@ -132,6 +132,11 @@ struct brand_ops {
boolean_t (*b_native_exec)(uint8_t, const char **);
void (*b_ptrace_exectrap)(proc_t *);
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 *);
};
/*
diff --git a/usr/src/uts/common/sys/procfs.h b/usr/src/uts/common/sys/procfs.h
index f592fd9dcf..501af712ef 100644
--- a/usr/src/uts/common/sys/procfs.h
+++ b/usr/src/uts/common/sys/procfs.h
@@ -25,6 +25,7 @@
*/
/*
* Copyright 2012 DEY Storage Systems, Inc. All rights reserved.
+ * Copyright 2015, Joyent, Inc.
*/
#ifndef _SYS_PROCFS_H
@@ -233,6 +234,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
diff --git a/usr/src/uts/common/sys/thread.h b/usr/src/uts/common/sys/thread.h
index 9f2e166fea..41ea2331df 100644
--- a/usr/src/uts/common/sys/thread.h
+++ b/usr/src/uts/common/sys/thread.h
@@ -419,8 +419,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)
/*
@@ -448,6 +449,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/zone.h b/usr/src/uts/common/sys/zone.h
index 7ab9377e16..a5d1610842 100644
--- a/usr/src/uts/common/sys/zone.h
+++ b/usr/src/uts/common/sys/zone.h
@@ -594,6 +594,7 @@ typedef struct zone {
tsol_mlp_list_t zone_mlps; /* MLPs on zone-private addresses */
boolean_t zone_restart_init; /* Restart init if it dies? */
+ boolean_t zone_reboot_on_init_exit; /* Reboot if init dies? */
struct brand *zone_brand; /* zone's brand */
void *zone_brand_data; /* store brand specific data */
id_t zone_defaultcid; /* dflt scheduling class id */
diff --git a/usr/src/uts/intel/Makefile.files b/usr/src/uts/intel/Makefile.files
index a4d6b7e309..0f058f262d 100644
--- a/usr/src/uts/intel/Makefile.files
+++ b/usr/src/uts/intel/Makefile.files
@@ -21,7 +21,7 @@
#
# Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
-# Copyright (c) 2014, Joyent, Inc. All rights reserved.
+# Copyright 2015, Joyent, Inc.
#
#
@@ -289,6 +289,7 @@ LX_BRAND_OBJS = \
lx_modify_ldt.o \
lx_pid.o \
lx_pipe.o \
+ lx_ptrace.o \
lx_rw.o \
lx_sched.o \
lx_signum.o \
diff --git a/usr/src/uts/intel/lx_proc/Makefile b/usr/src/uts/intel/lx_proc/Makefile
index 0d3d5511bf..980c60bff2 100644
--- a/usr/src/uts/intel/lx_proc/Makefile
+++ b/usr/src/uts/intel/lx_proc/Makefile
@@ -24,7 +24,7 @@
# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
-# Copyright 2014 Joyent, Inc. All rights reserved.
+# Copyright 2015 Joyent, Inc.
#
# This makefile drives the production of the lxproc file system
# kernel module.
@@ -73,6 +73,7 @@ CFLAGS += $(CCVERBOSE)
# Depends on procfs and lx_brand
#
LDFLAGS += -dy -Nfs/procfs -Nbrand/lx_brand -Ndrv/inotify -Ndrv/ip
+LDFLAGS += -Nfs/sockfs
#
# For now, disable these lint checks; maintainers should endeavor