Bug 515732 - Solaris support for TCF Processes and SysMonitor services

* Turn off ENABLE_AIO by default (SIGEV_THREAD doesnt work on Solaris 8)
* Must be compiled with -pthreads
* SysMonitor: Process Children (LWP) not yet implemented
* SysMonitor: Getting environment and cmdline > 80 chars not implemented
* Largefile support turned off since incompatible with /proc

Change-Id: I402953c992afc42ead92d71df2d18996267a581c
Signed-off-by: Martin Oberhuber <martin.oberhuber@windriver.com>
diff --git a/agent/Makefile.inc b/agent/Makefile.inc
index 3e338ce..8304cfe 100644
--- a/agent/Makefile.inc
+++ b/agent/Makefile.inc
@@ -71,6 +71,11 @@
   RANLIB = ranlib $@
 endif
 
+ifeq ($(OPSYS),SunOS)
+  LIBS = -lpthread -lsocket -lnsl -lrt
+  RANLIB = ranlib $@
+endif
+
 ifeq ($(OPSYS),GNU/Linux)
   ifeq ($(NO_SSL),)
     LIBS = -lpthread -lssl -lcrypto -lrt
@@ -87,7 +92,10 @@
 endif
 
 ifneq ($(OPSYS),Windows)
-  OPTS += -D_FILE_OFFSET_BITS=64
+  # Solaris: The procfs used in sysmon.c only works without largefile support
+  ifneq ($(OPSYS),SunOS)
+    OPTS += -D_FILE_OFFSET_BITS=64
+  endif
   OPTS += -Wall
   ifneq ($(CC),g++)
     OPTS += -Wmissing-prototypes
diff --git a/agent/tcf/config.h b/agent/tcf/config.h
index c5f6b8e..ca23ce0 100644
--- a/agent/tcf/config.h
+++ b/agent/tcf/config.h
@@ -259,6 +259,10 @@
 #  elif defined(__linux__)
 /* Linux implementation of POSIX AIO found to be inefficient */
 #    define ENABLE_AIO          0
+#  elif defined(__sun__)
+/* Solaris has _POSIX_ASYNCHRONOUS_IO, but SIGEV_THREAD does not seem to return */
+/* It should work from Solaris Express 6/06 and newer but fails if compiled on Solaris 8 */
+#    define ENABLE_AIO          0
 #  elif TARGET_SYMBIAN
 /* Symbian impl (OpenC) not desired either */
 #    define ENABLE_AIO          0
diff --git a/agent/tcf/framework/mdep-inet.h b/agent/tcf/framework/mdep-inet.h
index 9d4459b..1786ac9 100644
--- a/agent/tcf/framework/mdep-inet.h
+++ b/agent/tcf/framework/mdep-inet.h
@@ -152,6 +152,10 @@
 #include <netinet/tcp.h>
 #include <arpa/inet.h>
 #include <net/if.h>
+#if defined (__sun__)
+#include <sys/ioctl.h>
+#include <sys/sockio.h>
+#endif
 
 #define loc_freeaddrinfo freeaddrinfo
 #define loc_getaddrinfo getaddrinfo
diff --git a/agent/tcf/framework/mdep.c b/agent/tcf/framework/mdep.c
index 977fcaa..cf5771e 100644
--- a/agent/tcf/framework/mdep.c
+++ b/agent/tcf/framework/mdep.c
@@ -1077,7 +1077,7 @@
     return strdup(buf);
 }
 
-#elif defined(__FreeBSD__) || defined(__NetBSD__) || defined(__APPLE__)
+#elif defined(__FreeBSD__) || defined(__NetBSD__) || defined(__APPLE__) || defined(__sun__)
 
 char * canonicalize_file_name(const char * path) {
     char buf[PATH_MAX];
diff --git a/agent/tcf/framework/mdep.h b/agent/tcf/framework/mdep.h
index eeb8c2c..fec94d7 100644
--- a/agent/tcf/framework/mdep.h
+++ b/agent/tcf/framework/mdep.h
@@ -281,10 +281,14 @@
 #include <sys/socket.h>
 #include <limits.h>
 #include <inttypes.h>
+#if defined(__sun__)
+#include <string.h>
+#include <sys/stropts.h>
+#endif
 
 #define O_BINARY 0
 
-#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__APPLE__)
+#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__APPLE__) || defined(__sun__)
 #  define O_LARGEFILE 0
 extern char ** environ;
 extern char * canonicalize_file_name(const char * path);
diff --git a/agent/tcf/framework/trace.c b/agent/tcf/framework/trace.c
index ffcb608..cc16307 100644
--- a/agent/tcf/framework/trace.c
+++ b/agent/tcf/framework/trace.c
@@ -73,6 +73,7 @@
 #if defined(_WIN32) || defined(__CYGWIN__)
 #elif defined(_WRS_KERNEL)
 #elif defined(__SYMBIAN32__)
+#elif defined(__sun__)
 #else
         vsyslog(LOG_MAKEPRI(LOG_DAEMON, LOG_INFO), fmt, ap);
 #endif
diff --git a/agent/tcf/services/processes.c b/agent/tcf/services/processes.c
index 0f9c1f8..32915f9 100644
--- a/agent/tcf/services/processes.c
+++ b/agent/tcf/services/processes.c
@@ -1422,7 +1422,13 @@
         int fd_tty_out = -1;
         char * tty_slave_name = NULL;
 
+#if defined(__sun__)
+        /* Solaris: See STREAMS-based Pseudo-Terminal Subsystem */
+        /* http://docs.oracle.com/cd/E18752_01/html/816-4855/termsub15-44781.html */
+        fd_tty_master = open("/dev/ptmx", O_RDWR);
+#else
         fd_tty_master = posix_openpt(O_RDWR|O_NOCTTY);
+#endif
         if (fd_tty_master < 0 || grantpt(fd_tty_master) < 0 || unlockpt(fd_tty_master) < 0) err = errno;
         if (!err && (tty_slave_name = ptsname(fd_tty_master)) == NULL) err = EINVAL;
         if (!err && (fd_tty_slave = open(tty_slave_name, O_RDWR | O_NOCTTY)) < 0) err = errno;
@@ -1442,10 +1448,15 @@
 
                 if (!err && setsid() < 0) err = errno;;
                 if (!err && (fd = open(tty_slave_name, O_RDWR)) < 0) err = errno;
+#if defined(__sun__)
+                if (!err && (ioctl(fd, I_PUSH, "ptem")) < 0) err = errno;
+                if (!err && (ioctl(fd, I_PUSH, "ldterm")) < 0) err = errno;
+#endif
                 while (!err && fd < 3) {
                     int fd0 = fd;
                     if ((fd = dup(fd)) < 0 || close(fd0)) err = errno;
                 }
+
 #if defined(TIOCSCTTY)
                 if (!err && (ioctl(fd, TIOCSCTTY, NULL)) < 0) err = errno;
 #endif
diff --git a/agent/tcf/services/sysmon.c b/agent/tcf/services/sysmon.c
index 2935e4d..501b52b 100644
--- a/agent/tcf/services/sysmon.c
+++ b/agent/tcf/services/sysmon.c
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2007, 2015 Wind River Systems, Inc. and others.
+ * Copyright (c) 2007, 2017 Wind River Systems, Inc. and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * and Eclipse Distribution License v1.0 which accompany this distribution.
@@ -1006,7 +1006,11 @@
 #include <unistd.h>
 #include <pwd.h>
 #include <grp.h>
+#if defined(__sun__)
+#include <procfs.h>
+#else
 #include <linux/param.h>
+#endif
 
 #define BUF_EOF (-1)
 
@@ -1056,6 +1060,20 @@
     write_stream(out, ']');
 }
 
+#if defined(__sun__)
+#define PROC_CWD_PATH "path/cwd"
+#define PROC_ROOT_PATH "path/root"
+#define PROC_EXE_PATH "path/a.out"
+#define PROC_PSINFO_STAT "psinfo"
+#define JIFFIES_TO_MSEC_FACTOR (1)
+#else
+#define PROC_CWD_PATH "cwd"
+#define PROC_ROOT_PATH "root"
+#define PROC_EXE_PATH "exe"
+#define PROC_PSINFO_STAT "stat"
+#define JIFFIES_TO_MSEC_FACTOR (1000/HZ)
+#endif
+
 static void write_context(OutputStream * out, char * id, char * parent_id, char * dir) {
     char fnm[FILE_PATH_SIZE + 1];
     int sz;
@@ -1064,7 +1082,7 @@
     write_stream(out, '{');
 
     if (chdir(dir) >= 0) {
-        if ((sz = readlink("cwd", fnm, FILE_PATH_SIZE)) > 0) {
+        if ((sz = readlink(PROC_CWD_PATH, fnm, FILE_PATH_SIZE)) > 0) {
             fnm[sz] = 0;
             json_write_string(out, "CWD");
             write_stream(out, ':');
@@ -1072,7 +1090,7 @@
             write_stream(out, ',');
         }
 
-        if ((sz = readlink("root", fnm, FILE_PATH_SIZE)) > 0) {
+        if ((sz = readlink(PROC_ROOT_PATH, fnm, FILE_PATH_SIZE)) > 0) {
             fnm[sz] = 0;
             json_write_string(out, "Root");
             write_stream(out, ':');
@@ -1080,7 +1098,7 @@
             write_stream(out, ',');
         }
 
-        if ((sz = readlink("exe", fnm, FILE_PATH_SIZE)) > 0) {
+        if ((sz = readlink(PROC_EXE_PATH, fnm, FILE_PATH_SIZE)) > 0) {
             fnm[sz] = 0;
             json_write_string(out, "Exe");
             write_stream(out, ':');
@@ -1104,7 +1122,17 @@
             write_stream(out, ',');
         }
 
-        f = open("stat", O_RDONLY);
+        f = open(PROC_PSINFO_STAT, O_RDONLY);
+#if defined(__sun__)
+    } else {
+        /* On Solaris, "chdir /proc/<pid>" is only allowed for the owner, */
+        /* but psinfo is readable for all. So, try this as fallback.      */
+        snprintf(fnm, FILE_PATH_SIZE, "/%s/%s", dir, PROC_PSINFO_STAT);
+        fnm[FILE_PATH_SIZE]=0;
+        f = open(fnm, O_RDONLY);
+    }
+    {
+#endif
         if (f >= 0) {
             struct stat st;
             if (fstat(f, &st) == 0) {
@@ -1207,6 +1235,63 @@
                 unsigned long rt_priority=0;/* Real-time scheduling priority (see sched_setscheduler(2)). */
                 unsigned long policy = 0;   /* Scheduling policy (see sched_setscheduler(2)). */
 
+#if defined(__sun__)
+                psinfo_t *psinfo = (psinfo_t *) &buf;
+                pid = psinfo->pr_pid;
+                strncpy(comm, psinfo->pr_fname, PRFNSZ);
+                comm[PRFNSZ] = 0;
+                state = psinfo->pr_lwp.pr_sname;
+                ppid = psinfo->pr_ppid;
+                pgrp = psinfo->pr_pgid;
+                session = psinfo->pr_sid;
+                /* 
+                 * Solaris: A number of fields either have no equivalent on Solaris,
+                 * or they are only available from additional files in /proc. See
+                 * http://docs.oracle.com/cd/E19253-01/816-5174/6mbb98ui2/index.html
+                 * - pstatus_t would provide System CPU Time
+                 * - prusage_t would provide page faults
+                 * 
+                 * For an initial port, we don't care about this extra information,
+                 * and focus on the psinfo_t structure only to save some performance.
+                 * Respective flags are still kept here as reference -- they will
+                 * be sent to the client with initialized default values.
+                 */
+                //tty_nr = psinfo->pr_ttydev;
+                //tpgid =
+                //flags = 
+                //minflt =
+                //cminflt =
+                //majflt =
+                //cmajflt =
+                utime = psinfo->pr_time.tv_sec * 1000  + psinfo->pr_time.tv_nsec / 1000;
+                //stime = 
+                cutime = psinfo->pr_ctime.tv_sec * 1000 + psinfo->pr_ctime.tv_nsec / 1000;
+                //cstime =
+                priority = psinfo->pr_lwp.pr_pri;
+                nice = psinfo->pr_lwp.pr_nice;
+                //dummy =
+                //itrealvalue =
+                starttime = psinfo->pr_start.tv_sec * 1000 + psinfo->pr_start.tv_nsec / 1000;
+                vsize = psinfo->pr_size * 1024;
+                rss = psinfo->pr_rssize * 1024;
+                //rlim =
+                //startcode =
+                //endcode =
+                //startstack =
+                //kstkesp =
+                //kstkeip =
+                //signal =
+                //blocked =
+                //sigignore =
+                //sigcatch =
+                //wchan =
+                //nswap =
+                //cnswap =
+                //exit_signal =
+                processor = psinfo->pr_lwp.pr_onpro;
+                //rt_priority =
+                //policy =
+#else
                 pid = (int)strtol(str, &str, 10);
                 while (*str == ' ') str++;
 
@@ -1226,7 +1311,7 @@
                     &priority, &nice, &dummy, &itrealvalue, &starttime, &vsize, &rss, &rlim,
                     &startcode, &endcode, &startstack, &kstkesp, &kstkeip, &signal, &blocked, &sigignore, &sigcatch,
                     &wchan, &nswap, &cnswap, &exit_signal, &processor, &rt_priority, &policy);
-
+#endif
                 json_write_string(out, "PID");
                 write_stream(out, ':');
                 json_write_long(out, pid);
@@ -1302,22 +1387,22 @@
 
                 json_write_string(out, "UTime");
                 write_stream(out, ':');
-                json_write_uint64(out, (uint64_t)utime * 1000 / HZ);
+                json_write_uint64(out, (uint64_t)utime * JIFFIES_TO_MSEC_FACTOR);
                 write_stream(out, ',');
 
                 json_write_string(out, "STime");
                 write_stream(out, ':');
-                json_write_uint64(out, (uint64_t)stime * 1000 / HZ);
+                json_write_uint64(out, (uint64_t)stime * JIFFIES_TO_MSEC_FACTOR);
                 write_stream(out, ',');
 
                 json_write_string(out, "CUTime");
                 write_stream(out, ':');
-                json_write_uint64(out, (uint64_t)cutime * 1000 / HZ);
+                json_write_uint64(out, (uint64_t)cutime * JIFFIES_TO_MSEC_FACTOR);
                 write_stream(out, ',');
 
                 json_write_string(out, "CSTime");
                 write_stream(out, ':');
-                json_write_uint64(out, (uint64_t)cstime * 1000 / HZ);
+                json_write_uint64(out, (uint64_t)cstime * JIFFIES_TO_MSEC_FACTOR);
                 write_stream(out, ',');
 
                 json_write_string(out, "Priority");
@@ -1335,13 +1420,13 @@
                 if (itrealvalue != 0) {
                     json_write_string(out, "ITRealValue");
                     write_stream(out, ':');
-                    json_write_int64(out, (int64_t)itrealvalue * 1000 / HZ);
+                    json_write_int64(out, (int64_t)itrealvalue * JIFFIES_TO_MSEC_FACTOR);
                     write_stream(out, ',');
                 }
 
                 json_write_string(out, "StartTime");
                 write_stream(out, ':');
-                json_write_uint64(out, (uint64_t)starttime * 1000 / HZ);
+                json_write_uint64(out, (uint64_t)starttime * JIFFIES_TO_MSEC_FACTOR);
                 write_stream(out, ',');
 
                 json_write_string(out, "VSize");
@@ -1478,6 +1563,7 @@
     if (pid != 0) {
         struct stat st;
         if (parent != 0) {
+            /* Solaris: Not implemented, we don't care about LWP process children */
             snprintf(dir, sizeof(dir), "/proc/%d/task/%d", parent, pid);
         }
         else {
@@ -1514,7 +1600,13 @@
     write_stringz(&c->out, token);
 
     pid = id2pid(id, &parent);
-
+    
+#if defined(__sun__)
+    /* Solaris: Process Children (LWP) support is not implemented at this time.  */
+    /* Therefore, we always return null for children, without actually checking. */
+    if (pid != 0) parent = pid;
+#endif
+    
     if (parent != 0) {
         write_errno(&c->out, 0);
         write_stringz(&c->out, "null");
@@ -1523,7 +1615,10 @@
         DIR * proc = NULL;
         char dir[FILE_PATH_SIZE];
         if (pid == 0) strcpy(dir, "/proc");
-        else snprintf(dir, sizeof(dir), "/proc/%d/task", pid);
+        else {
+            /* Solaris: LWP is not implemented */
+            snprintf(dir, sizeof(dir), "/proc/%d/task", pid);
+        }
         proc = opendir(dir);
         if (proc == NULL) {
             int err = errno;
@@ -1589,11 +1684,43 @@
         err = ERR_INV_CONTEXT;
     }
 
+#if defined(__sun__)
+    /* Solaris only saves the first 80 characters of the command-line in psinfo.
+     * Getting the full untruncated command-line requires access to process memory:
+     * http://praveen.kumar.in/2010/02/24/getting-untruncated-command-line-options-passed-to-a-solaris-process/
+     * 
+     * We don't care about these details and just send the first 80 characters, like ps does.
+     */
+    if (err == 0) {
+        snprintf(dir, sizeof(dir), "/proc/%d/psinfo", pid);
+        if ((f = open(dir, O_RDONLY)) < 0) err = errno == ENOENT ? ESRCH : errno;
+    }
+    write_errno(&c->out, err);
+    if (err == 0) {
+        psinfo_t psinfo;
+        ssize_t bytes_read;
+        bytes_read = read(f, &psinfo, sizeof(psinfo_t));
+        if (bytes_read == sizeof(psinfo_t)) {
+            write_stream(&c->out, '[');
+            /* We don't care about tokenizing the command. */
+            /* Solaris seems to ensure that there is no " character in the command. */
+            write_stream(&c->out, '"');
+            write_stringz(&c->out, psinfo.pr_psargs);
+            write_stream(&c->out, '"');
+            write_stream(&c->out, ']');
+            write_stream(&c->out, 0);
+        } else {
+            write_stringz(&c->out, "null");
+        }
+        close(f);
+    }
+    else {
+        write_stringz(&c->out, "null");
+    }
+#else
     if (err == 0 && chdir(dir) < 0) err = errno;
     if (err == 0 && (f = open("cmdline", O_RDONLY)) < 0) err = errno;
-
     write_errno(&c->out, err);
-
     if (err == 0) {
         write_string_array(&c->out, f);
         close(f);
@@ -1602,7 +1729,7 @@
     else {
         write_stringz(&c->out, "null");
     }
-
+#endif
     write_stream(&c->out, MARKER_EOM);
 }
 
@@ -1621,6 +1748,12 @@
     write_stringz(&c->out, "R");
     write_stringz(&c->out, token);
 
+#if defined(__sun__)
+    /* Solaris: Accessing the environment would require accessing process memory. */
+    /* This is not implemented at this time -> Just return null for any context.  */
+    write_errno(&c->out, ENOENT);
+    write_stringz(&c->out, "null");
+#else
     pid = id2pid(id, &parent);
     if (pid != 0 && parent == 0) {
         struct stat st;
@@ -1645,7 +1778,7 @@
     else {
         write_stringz(&c->out, "null");
     }
-
+#endif
     write_stream(&c->out, MARKER_EOM);
 }
 #endif