--- old/usr/src/cmd/ptools/ptree/ptree.c	Wed Oct 16 05:58:48 2019
+++ new/usr/src/cmd/ptools/ptree/ptree.c	Wed Oct 16 05:58:48 2019
@@ -1,4 +1,4 @@
-/*
+ /*
  * CDDL HEADER START
  *
  * The contents of this file are subject to the terms of the
@@ -21,6 +21,7 @@
 /*
  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
+ * Copyright 2019 Joyent, Inc.
  */
 
 /*
@@ -27,8 +28,6 @@
  * ptree -- print family tree of processes
  */
 
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-
 #include <assert.h>
 #include <stdio.h>
 #include <string.h>
@@ -48,6 +47,7 @@
 #include <sys/ctfs.h>
 #include <libcontract_priv.h>
 #include <sys/stat.h>
+#include <stdbool.h>
 
 #define	FAKEDPID0(p)	(p->pid == 0 && p->psargs[0] == '\0')
 
@@ -61,6 +61,7 @@
 	pid_t	sid;
 	zoneid_t zoneid;
 	ctid_t	ctid;
+	char *svc_fmri;
 	timestruc_t start;
 	char	psargs[PRARGSZ];
 	struct ps *pp;		/* parent */
@@ -81,16 +82,22 @@
 
 static	int	aflag = 0;
 static	int	cflag = 0;
+static	int	sflag = 0;
 static	int	zflag = 0;
 static	zoneid_t zoneid;
+static	char *match_svc;
+static	char *match_inst;
 static	int	columns = 80;
 
-static void markprocs(ps_t *p);
-static int printone(ps_t *p, int level);
+static bool match_proc(ps_t *);
+static void markprocs(ps_t *);
+static int printone(ps_t *, int);
 static void insertchild(ps_t *, ps_t *);
-static void prsort(ps_t *p);
-static void printsubtree(ps_t *p, int level);
-static zoneid_t getzone(char *arg);
+static void prsort(ps_t *);
+static void printsubtree(ps_t *, int);
+static void p_get_svc_fmri(ps_t *, ct_stathdl_t);
+static char *parse_svc(const char *, char **);
+static zoneid_t getzone(const char *);
 static ps_t *fakepid0(void);
 
 int
@@ -117,7 +124,7 @@
 		command++;
 
 	/* options */
-	while ((opt = getopt(argc, argv, "acz:")) != EOF) {
+	while ((opt = getopt(argc, argv, "acs:z:")) != EOF) {
 		switch (opt) {
 		case 'a':		/* include children of process 0 */
 			aflag = 1;
@@ -125,6 +132,10 @@
 		case 'c':		/* display contract ownership */
 			aflag = cflag = 1;
 			break;
+		case 's':
+			sflag = 1;
+			match_svc = parse_svc(optarg, &match_inst);
+			break;
 		case 'z':		/* only processes in given zone */
 			zflag = 1;
 			zoneid = getzone(optarg);
@@ -140,7 +151,7 @@
 
 	if (errflg) {
 		(void) fprintf(stderr,
-		    "usage:\t%s [-ac] [-z zone] [ {pid|user} ... ]\n",
+		    "usage:\t%s [-ac] [-s svc] [-z zone] [ {pid|user} ... ]\n",
 		    command);
 		(void) fprintf(stderr,
 		    "  (show process trees)\n");
@@ -149,8 +160,10 @@
 		(void) fprintf(stderr,
 		    "  -a : include children of process 0\n");
 		(void) fprintf(stderr,
-		    "  -c : show contract ownership\n");
+		    "  -c : show contracts\n");
 		(void) fprintf(stderr,
+		    "  -s : print only processes with given service FMRI\n");
+		(void) fprintf(stderr,
 		    "  -z : print only processes in given zone\n");
 		return (2);
 	}
@@ -221,8 +234,8 @@
 				return (1);
 			}
 		}
-		if ((p = malloc(sizeof (ps_t))) == NULL) {
-			perror("malloc()");
+		if ((p = calloc(1, sizeof (ps_t))) == NULL) {
+			perror("calloc()");
 			return (1);
 		}
 		ps[nps++] = p;
@@ -249,6 +262,10 @@
 		p->pp = NULL;
 		p->sp = NULL;
 		p->cp = NULL;
+
+		if (sflag)
+			p_get_svc_fmri(p, NULL);
+
 		if (p->pid == p->ppid)
 			proc0 = p;
 		if (p->pid == 1)
@@ -335,8 +352,7 @@
 					    p->done != 1 && p->pid != 0;
 					    p = p->pp)
 						if ((p->ppid != 0 || aflag) &&
-						    (!zflag ||
-						    p->zoneid == zoneid))
+						    match_proc(p))
 							p->done = 1;
 				if (uid == (uid_t)-1)
 					break;
@@ -368,8 +384,9 @@
 			    PIDWIDTH, (int)p->pid, n, p->psargs);
 		} else {
 			assert(cflag != 0);
-			(void) printf("%*.*s[process contract %d]\n",
-			    indent, indent, " ", (int)p->ctid);
+			(void) printf("%*.*s[process contract %d: %s]\n",
+			    indent, indent, " ", (int)p->ctid,
+			    p->svc_fmri == NULL ? "?" : p->svc_fmri);
 		}
 		return (1);
 	}
@@ -398,11 +415,50 @@
 	*here = cp;
 }
 
+static ct_stathdl_t
+ct_status_open(ctid_t ctid, struct stat64 *stp)
+{
+	ct_stathdl_t hdl;
+	int fd;
+
+	if ((fd = contract_open(ctid, "process", "status", O_RDONLY)) == -1)
+		return (NULL);
+
+	if (fstat64(fd, stp) == -1 || ct_status_read(fd, CTD_FIXED, &hdl)) {
+		(void) close(fd);
+		return (NULL);
+	}
+
+	(void) close(fd);
+
+	return (hdl);
+}
+
+/*
+ * strdup() failure is OK - better to report something than fail totally.
+ */
 static void
+p_get_svc_fmri(ps_t *p, ct_stathdl_t inhdl)
+{
+	ct_stathdl_t hdl = inhdl;
+	struct stat64 st;
+	char *fmri;
+
+	if (hdl == NULL && (hdl = ct_status_open(p->ctid, &st)) == NULL)
+		return;
+
+	if (ct_pr_status_get_svc_fmri(hdl, &fmri) == 0)
+		p->svc_fmri = strdup(fmri);
+
+	if (inhdl == NULL)
+		ct_status_free(hdl);
+}
+
+static void
 ctsort(ctid_t ctid, ps_t *p)
 {
 	ps_t *pp;
-	int fd, n;
+	int n;
 	ct_stathdl_t hdl;
 	struct stat64 st;
 
@@ -412,13 +468,8 @@
 			return;
 		}
 
-	if ((fd = contract_open(ctid, "process", "status", O_RDONLY)) == -1)
+	if ((hdl = ct_status_open(ctid, &st)) == NULL)
 		return;
-	if (fstat64(fd, &st) == -1 || ct_status_read(fd, CTD_COMMON, &hdl)) {
-		(void) close(fd);
-		return;
-	}
-	(void) close(fd);
 
 	if (nctps >= ctsize) {
 		if ((ctsize *= 2) == 0)
@@ -437,10 +488,14 @@
 
 	pp->pid = -1;
 	pp->ctid = ctid;
+
+	p_get_svc_fmri(pp, hdl);
+
 	pp->start.tv_sec = st.st_ctime;
 	insertchild(pp, p);
 
 	pp->zoneid = ct_status_get_zoneid(hdl);
+
 	/*
 	 * In a zlogin <zonename>, the contract belongs to the
 	 * global zone and the shell opened belongs to <zonename>.
@@ -510,11 +565,51 @@
 		printsubtree(p, level);
 }
 
+/*
+ * Match against the service name (and just the final component), and any
+ * specified instance name.
+ */
+static bool
+match_proc(ps_t *p)
+{
+	bool matched = false;
+	const char *cp;
+	char *p_inst;
+	char *p_svc;
+
+	if (zflag && p->zoneid != zoneid)
+		return (false);
+
+	if (!sflag)
+		return (true);
+
+	if (p->svc_fmri == NULL)
+		return (false);
+
+	p_svc = parse_svc(p->svc_fmri, &p_inst);
+
+	if (strcmp(p_svc, match_svc) != 0 &&
+	    ((cp = strrchr(p_svc, '/')) == NULL ||
+	    strcmp(cp + 1, match_svc) != 0)) {
+		goto out;
+	}
+
+	if (strlen(match_inst) == 0 ||
+	    strcmp(p_inst, match_inst) == 0)
+		matched = true;
+
+out:
+	free(p_svc);
+	free(p_inst);
+	return (matched);
+}
+
 static void
 markprocs(ps_t *p)
 {
-	if (!zflag || p->zoneid == zoneid)
+	if (match_proc(p))
 		p->done = 1;
+
 	for (p = p->cp; p != NULL; p = p->sp)
 		markprocs(p);
 }
@@ -529,8 +624,8 @@
 	ps_t *p0, *p;
 	int n;
 
-	if ((p0 = malloc(sizeof (ps_t))) == NULL) {
-		perror("malloc()");
+	if ((p0 = calloc(1, sizeof (ps_t))) == NULL) {
+		perror("calloc()");
 		exit(1);
 	}
 	(void) memset(p0, '\0', sizeof (ps_t));
@@ -556,7 +651,7 @@
 
 /* convert string containing zone name or id to a numeric id */
 static zoneid_t
-getzone(char *arg)
+getzone(const char *arg)
 {
 	zoneid_t zoneid;
 
@@ -565,4 +660,35 @@
 		exit(1);
 	}
 	return (zoneid);
+}
+
+/* svc:/mysvc:default ->  mysvc, default */
+static char *
+parse_svc(const char *arg, char **instp)
+{
+	const char *p = arg;
+	char *ret;
+	char *cp;
+
+	if (strncmp(p, "svc:/", strlen("svc:/")) == 0)
+		p += strlen("svc:/");
+
+	if ((ret = strdup(p)) == NULL) {
+		perror("strdup()");
+		exit(1);
+	}
+
+	if ((cp = strrchr(ret, ':')) != NULL) {
+		*cp = '\0';
+		cp++;
+	} else {
+		cp = "";
+	}
+
+	if ((*instp = strdup(cp)) == NULL) {
+		perror("strdup()");
+		exit(1);
+	}
+
+	return (ret);
 }
--- old/usr/src/man/man1/ptree.1	Wed Oct 16 05:58:49 2019
+++ new/usr/src/man/man1/ptree.1	Wed Oct 16 05:58:49 2019
@@ -1,27 +1,23 @@
 '\" te
 .\" Copyright (c) 2005, Sun Microsystems, Inc. All Rights Reserved.
+.\" Copyright 2019 Joyent, Inc.
 .\" The contents of this file are subject to the terms of the Common Development and Distribution License (the "License").  You may not use this file except in compliance with the License.
 .\" You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE or http://www.opensolaris.org/os/licensing.  See the License for the specific language governing permissions and limitations under the License.
 .\" When distributing Covered Code, include this CDDL HEADER in each file and include the License file at usr/src/OPENSOLARIS.LICENSE.  If applicable, add the following below this CDDL HEADER, with the fields enclosed by brackets "[]" replaced with your own identifying information: Portions Copyright [yyyy] [name of copyright owner]
-.TH PTREE 1 "Oct 11, 2005"
+.TH PTREE 1 "Sep 3, 2019"
 .SH NAME
 ptree \- print process trees
 .SH SYNOPSIS
-.LP
 .nf
-\fB/usr/bin/ptree\fR [\fB-a\fR] [\fB-c\fR] [\fB-z\fR \fIzone\fR] [\fIpid\fR | \fIuser\fR]...
+\fB/usr/bin/ptree\fR [\fB-a\fR] [\fB-c\fR] [\fB-s\fR \fIsvc\fR] [\fB-z\fR \fIzone\fR] [\fIpid\fR | \fIuser\fR]...
 .fi
 
 .SH DESCRIPTION
-.sp
-.LP
 The \fBptree\fR utility prints the process trees containing the specified
 \fIpid\fRs or \fIuser\fRs, with child processes indented from their respective
 parent processes. An argument of all digits is taken to be a process-ID,
 otherwise it is assumed to be a user login name. The default is all processes.
 .SH OPTIONS
-.sp
-.LP
 The following options are supported:
 .sp
 .ne 2
@@ -38,13 +34,26 @@
 \fB\fB-c\fR\fR
 .ad
 .RS 11n
-Contracts. Print process contract memberships in addition to parent-child
-relationships. See \fBprocess\fR(4). This option implies the \fB-a\fR option.
+Contracts. Print process contract memberships and their associated SMF FMRIs,
+in addition to parent-child relationships. See \fBprocess\fR(4).
+This option implies the \fB-a\fR option.
 .RE
 
 .sp
 .ne 2
 .na
+\fB\fB-s\fR \fIsvc\fR\fR
+.ad
+.RS 11n
+Print only processes with an SMF service FMRI matching the argument. The FMRI
+may be in truncated form (such as 'console-login'). This includes child
+processes even if they are not members of the service contract.
+See \fBprocess\fR(4).
+.RE
+
+.sp
+.ne 2
+.na
 \fB\fB-z\fR \fIzone\fR\fR
 .ad
 .RS 11n
@@ -55,8 +64,6 @@
 .RE
 
 .SH OPERANDS
-.sp
-.LP
 The following operands are supported:
 .sp
 .ne 2
@@ -80,7 +87,6 @@
 .RE
 
 .SH EXAMPLES
-.LP
 \fBExample 1 \fRUsing ptree
 .sp
 .LP
@@ -104,8 +110,6 @@
 .sp
 
 .SH EXIT STATUS
-.sp
-.LP
 The following exit values are returned:
 .sp
 .ne 2
@@ -126,7 +130,6 @@
 .RE
 
 .SH FILES
-.sp
 .ne 2
 .na
 \fB\fB/proc/*\fR\fR
@@ -136,8 +139,6 @@
 .RE
 
 .SH ATTRIBUTES
-.sp
-.LP
 See \fBattributes\fR(5) for descriptions of the following attributes:
 .sp
 
@@ -155,8 +156,6 @@
 .LP
 The human readable output is Unstable. The options are Evolving.
 .SH SEE ALSO
-.sp
-.LP
 \fBgcore\fR(1), \fBldd\fR(1), \fBpargs\fR(1), \fBpgrep\fR(1), \fBpkill\fR(1),
 \fBplimit\fR(1), \fBpmap\fR(1), \fBpreap\fR(1), \fBproc\fR(1), \fBps\fR(1),
 \fBppgsz\fR(1), \fBpwd\fR(1), \fBrlogin\fR(1), \fBtime\fR(1), \fBtruss\fR(1),
--- old/usr/src/uts/common/contract/process.c	Wed Oct 16 05:58:49 2019
+++ new/usr/src/uts/common/contract/process.c	Wed Oct 16 05:58:49 2019
@@ -21,6 +21,7 @@
 /*
  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
+ * Copyright 2019 Joyent, Inc.
  */
 
 #include <sys/mutex.h>
@@ -736,13 +737,11 @@
 	 * if we are in a local zone and svc_fmri was inherited from
 	 * the global zone, we provide fake svc_fmri and svc_ctid
 	 */
-	if (local_svc_zone_enter == 0||
+	if (local_svc_zone_enter == 0 ||
 	    zone->zone_uniqid == GLOBAL_ZONEUNIQID) {
 		if (detail > CTD_COMMON) {
 			VERIFY(nvlist_add_int32(nvl, CTPS_SVC_CTID,
 			    ctp->conp_svc_ctid) == 0);
-		}
-		if (detail == CTD_ALL) {
 			VERIFY(nvlist_add_string(nvl, CTPS_SVC_FMRI,
 			    refstr_value(ctp->conp_svc_fmri)) == 0);
 		}
@@ -750,8 +749,6 @@
 		if (detail > CTD_COMMON) {
 			VERIFY(nvlist_add_int32(nvl, CTPS_SVC_CTID,
 			    local_svc_zone_enter) == 0);
-		}
-		if (detail == CTD_ALL) {
 			VERIFY(nvlist_add_string(nvl, CTPS_SVC_FMRI,
 			    CT_PR_SVC_FMRI_ZONE_ENTER) == 0);
 		}
