Print this page
11691 ptree could show service FMRIs
Reviewed by: Jerry Jelinek <jerry.jelinek@joyent.com>
Reviewed by: Jason King <jason.king@joyent.com>
Reviewed by: Andy Fiddaman <andy@omniosce.org>

@@ -1,6 +1,6 @@
-/*
+ /*
  * CDDL HEADER START
  *
  * The contents of this file are subject to the terms of the
  * Common Development and Distribution License (the "License").
  * You may not use this file except in compliance with the License.

@@ -19,18 +19,17 @@
  * CDDL HEADER END
  */
 /*
  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
+ * Copyright 2019 Joyent, Inc.
  */
 
 /*
  * ptree -- print family tree of processes
  */
 
-#pragma ident   "%Z%%M% %I%     %E% SMI"
-
 #include <assert.h>
 #include <stdio.h>
 #include <string.h>
 #include <errno.h>
 #include <fcntl.h>

@@ -46,10 +45,11 @@
 #include <libcontract.h>
 #include <sys/contract.h>
 #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')
 
 typedef struct ps {
         int     done;

@@ -59,10 +59,11 @@
         pid_t   ppid;
         pid_t   pgrp;
         pid_t   sid;
         zoneid_t zoneid;
         ctid_t  ctid;
+        char *svc_fmri;
         timestruc_t start;
         char    psargs[PRARGSZ];
         struct ps *pp;          /* parent */
         struct ps *sp;          /* sibling */
         struct ps *cp;          /* child */

@@ -79,20 +80,26 @@
 
 static  char    *command;
 
 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
 main(int argc, char **argv)
 {

@@ -115,18 +122,22 @@
                 command = argv[0];
         else
                 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;
                         break;
                 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);
                         break;
                 default:

@@ -138,21 +149,23 @@
         argc -= optind;
         argv += optind;
 
         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");
                 (void) fprintf(stderr,
                     "  list can include process-ids and user names\n");
                 (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);
         }
 
         /*

@@ -219,12 +232,12 @@
                         if ((ps = realloc(ps, psize*sizeof (ps_t *))) == NULL) {
                                 perror("realloc()");
                                 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;
                 p->done = 0;
                 p->uid = info.pr_uid;

@@ -247,10 +260,14 @@
                             sizeof (p->psargs));
                 p->psargs[sizeof (p->psargs)-1] = '\0';
                 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)
                         proc1 = p;
         }

@@ -333,12 +350,11 @@
                                 if (p->pid != 0)
                                         for (p = p->pp; p != NULL &&
                                             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;
                         }
                 }

@@ -366,12 +382,13 @@
                 if (p->pid >= 0) {
                         (void) printf("%*.*s%-*d %.*s\n", indent, indent, " ",
                             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);
         }
         return (0);
 }

@@ -396,31 +413,65 @@
         cp->pp = pp;
         cp->sp = sp;
         *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;
 
         for (n = 0; n < nctps; n++)
                 if (ctps[n]->ctid == ctid) {
                         insertchild(ctps[n], p);
                         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)
                         ctsize = 20;
                 if ((ctps = realloc(ctps, ctsize * sizeof (ps_t *))) == NULL) {

@@ -435,14 +486,18 @@
         }
         ctps[nctps++] = pp;
 
         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>.
          * If the -c and -z zonename flags are used together, then
          * we need to adjust the zoneid in the contract's ps_t as

@@ -508,15 +563,55 @@
                 level++;
         for (p = p->cp; p != NULL; p = p->sp)
                 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);
 }
 
 /*

@@ -527,12 +622,12 @@
 fakepid0(void)
 {
         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));
 
         /* First build all partial process trees. */

@@ -554,15 +649,46 @@
         return (p0);
 }
 
 /* convert string containing zone name or id to a numeric id */
 static zoneid_t
-getzone(char *arg)
+getzone(const char *arg)
 {
         zoneid_t zoneid;
 
         if (zone_get_id(arg, &zoneid) != 0) {
                 (void) fprintf(stderr, "%s: unknown zone: %s\n", command, arg);
                 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);
 }