ProcMgrProcInfoArray *
ProcMgr_ListProcesses(void)
{
   ProcMgrProcInfoArray *procList = NULL;
   ProcMgrProcInfo processInfo;
   Bool failed = TRUE;
   DIR *dir;
   struct dirent *ent;

   procList = Util_SafeCalloc(1, sizeof *procList);
   ProcMgrProcInfoArray_Init(procList, 0);
   processInfo.procOwner = NULL;
   processInfo.procCmd = NULL;

   dir = opendir("/proc");
   if (NULL == dir) {
      Warning("ProcMgr_ListProcesses unable to open /proc\n");
      goto exit;
   }

   while (TRUE) {
      struct passwd *pwd;
      char tempPath[MAXPATHLEN];
      psinfo_t procInfo;
      size_t strLen = 0;
      size_t numRead = 0;
      FileIODescriptor psInfoFd;
      FileIOResult res;

      errno = 0;
      FileIO_Invalidate(&psInfoFd);

      ent = readdir(dir);
      if (ent == NULL) {
         if (errno == 0) {
            break;
         } else {
            goto exit;
         }
      }

      if (Str_Snprintf(tempPath,
                       sizeof tempPath,
                       "/proc/%s/psinfo",
                       ent->d_name) == -1) {
         Debug("Process id '%s' too large\n", ent->d_name);
         continue;
      }
      res = FileIO_Open(&psInfoFd,
                        tempPath,
                        FILEIO_OPEN_ACCESS_READ,
                        FILEIO_OPEN);
      if (res != FILEIO_SUCCESS) {
         if ((res == FILEIO_FILE_NOT_FOUND) ||
             (res == FILEIO_NO_PERMISSION)) {
            continue;
         } else {
            goto exit;
         }
      }

      res = FileIO_Read(&psInfoFd, &procInfo, sizeof procInfo, &numRead);
      FileIO_Close(&psInfoFd);
      if (res != FILEIO_SUCCESS) {
         goto exit;
      }

      processInfo.procStartTime = procInfo.pr_start.tv_sec;

      /*
       * Command line strings in procInfo.pr_psargs are truncated to PRARGZ
       * bytes. In this case we extract the arguments from the /proc/<pid>/as
       * file. Since most command line strings are expected to fit within
       * PRARGSZ bytes, we avoid calling
       * ExtractCommandLineFromAddressSpaceFile for every process.
       */
      if (strlen(procInfo.pr_psargs) + 1 == PRARGSZ) {
         char *tmp;

         tmp = ExtractCommandLineFromAddressSpaceFile(&procInfo);
         if (tmp != NULL) {
            processInfo.procCmd = Unicode_Alloc(tmp, STRING_ENCODING_DEFAULT);
            free(tmp);
         } else {
            processInfo.procCmd = Unicode_Alloc(procInfo.pr_psargs,
                                                STRING_ENCODING_DEFAULT);
         }
      } else {
         processInfo.procCmd = Unicode_Alloc(procInfo.pr_psargs,
                                             STRING_ENCODING_DEFAULT);
      }

      /*
       * Store the pid in dynbuf.
       */
      processInfo.procId = procInfo.pr_pid;

      /*
       * Store the owner of the process.
       */
      pwd = getpwuid(procInfo.pr_uid);
      processInfo.procOwner = (NULL == pwd)
                              ? Str_SafeAsprintf(&strLen, "%d", (int) procInfo.pr_uid)
                              : Unicode_Alloc(pwd->pw_name, STRING_ENCODING_DEFAULT);

      /*
       * Store the process info into a list buffer.
       */
      if (!ProcMgrProcInfoArray_Push(procList, processInfo)) {
         Warning("%s: failed to expand DynArray - out of memory\n",
                 __FUNCTION__);
         goto exit;
      }
      processInfo.procCmd = NULL;
      processInfo.procOwner = NULL;
   } // while (TRUE)

   if (0 < ProcMgrProcInfoArray_Count(procList)) {
      failed = FALSE;
   }

exit:
   closedir(dir);

   free(processInfo.procOwner);
   free(processInfo.procCmd);

   if (failed) {
      ProcMgr_FreeProcList(procList);
      procList = NULL;
   }

   return procList;
}
ProcMgrProcInfoArray *
ProcMgr_ListProcesses(void)
{
   ProcMgrProcInfoArray *procList = NULL;
   ProcMgrProcInfo processInfo;
   Bool failed = TRUE;
   DIR *dir;
   struct dirent *ent;

   procList = Util_SafeCalloc(1, sizeof *procList);
   ProcMgrProcInfoArray_Init(procList, 0);
   processInfo.procOwner = NULL;
   processInfo.procCmdLine = NULL;
   processInfo.procCmdName = NULL;

   dir = opendir("/proc");
   if (NULL == dir) {
      Warning("ProcMgr_ListProcesses unable to open /proc\n");
      goto exit;
   }

   while (TRUE) {
      char *tmp;
      char *cmdNameBegin = NULL;
      char *cmdNameEnd = NULL;
      struct passwd *pwd;
      char tempPath[MAXPATHLEN];
      psinfo_t procInfo;
      size_t strLen = 0;
      size_t numRead = 0;
      FileIODescriptor psInfoFd;
      FileIOResult res;
      Bool cmdNameLookup = TRUE;

      errno = 0;
      FileIO_Invalidate(&psInfoFd);

      ent = readdir(dir);
      if (ent == NULL) {
         if (errno == 0) {
            break;
         } else {
            goto exit;
         }
      }

      if (Str_Snprintf(tempPath,
                       sizeof tempPath,
                       "/proc/%s/psinfo",
                       ent->d_name) == -1) {
         Debug("Process id '%s' too large\n", ent->d_name);
         continue;
      }
      res = FileIO_Open(&psInfoFd,
                        tempPath,
                        FILEIO_OPEN_ACCESS_READ,
                        FILEIO_OPEN);
      if (res != FILEIO_SUCCESS) {
         if ((res == FILEIO_FILE_NOT_FOUND) ||
             (res == FILEIO_NO_PERMISSION)) {
            continue;
         } else {
            goto exit;
         }
      }

      res = FileIO_Read(&psInfoFd, &procInfo, sizeof procInfo, &numRead);
      FileIO_Close(&psInfoFd);
      if (res != FILEIO_SUCCESS) {
         goto exit;
      }

      processInfo.procStartTime = procInfo.pr_start.tv_sec;

      /*
       * If the command name in the ps info struct is strictly less than the
       * maximum allowed size, then we can save it right now. Else we shall
       * need to try and parse it from the entire command line, to avoid
       * saving a truncated command name.
       */
      if (strlen(procInfo.pr_fname) + 1 < sizeof procInfo.pr_fname) {
         processInfo.procCmdName = Unicode_Alloc(procInfo.pr_fname,
                                                 STRING_ENCODING_DEFAULT);
         cmdNameLookup = FALSE;
      }

      /*
       * The logic below is this:
       * 1. If we are looking for the explicit command name, we need to
       *    extract the arguments from the /proc/<pid>/as file and save argv[0].
       * 2. If we are not looking for the explicit command name, but the command
       *    line in the ps info struct is not strictly less than the maximum
       *    allowed size, we still need to extract the arguments from the
       *    /proc/<pid>/as file, to avoid saving truncated comand line.
       * 3. Else we can save the command line directly from the ps info struct.
       */
      if (cmdNameLookup) {
         tmp = ExtractCommandLineFromAddressSpaceFile(&procInfo, &processInfo.procCmdName);
      } else if (strlen(procInfo.pr_psargs) + 1 >= sizeof procInfo.pr_psargs) {
         tmp = ExtractCommandLineFromAddressSpaceFile(&procInfo, NULL);
      } else {
         tmp = NULL;
      }

      if (tmp != NULL) {
         processInfo.procCmdLine = Unicode_Alloc(tmp, STRING_ENCODING_DEFAULT);
         cmdNameLookup = FALSE;
      } else {
         /*
          * We had some issues reading procfs, mostly due to lack of
          * permissions for certain system owned precesses. So let's resort to
          * what the procinfo structure provides as a last resort.
          */
         processInfo.procCmdLine = Unicode_Alloc(procInfo.pr_psargs,
                                                 STRING_ENCODING_DEFAULT);

         if (cmdNameLookup) {
            /*
             * Now let's try and get the best effort command name from the entire
             * command line. The method below does not take care of spaces in folder
             * names and executable file names. This is the best we can do at this
             * point, considering that spaces are not common in either file or
             * folder names in Solaris, specially when owned by the system.
             */
            char *tmp2 = Unicode_Alloc(procInfo.pr_psargs, STRING_ENCODING_DEFAULT);
            cmdNameBegin = tmp2;
            /*
             * Assuming the command name to end at the first blank space.
             */
            cmdNameEnd = cmdNameBegin;
            while ('\0' != *cmdNameEnd) {
               if ('/' == *cmdNameEnd) {
                  cmdNameBegin = cmdNameEnd + 1;
               }
               if (' ' == *cmdNameEnd) {
                  break;
               }
               cmdNameEnd++;
            }
            *cmdNameEnd = '\0';
            processInfo.procCmdName = Str_SafeAsprintf(NULL, "%s", cmdNameBegin);
            free(tmp2);
         }
      }
      free(tmp);
      tmp = NULL;

      /*
       * Store the pid.
       */
      processInfo.procId = procInfo.pr_pid;

      /*
       * Store the owner of the process.
       */
      pwd = getpwuid(procInfo.pr_uid);
      processInfo.procOwner = (NULL == pwd)
                              ? Str_SafeAsprintf(&strLen, "%d", (int) procInfo.pr_uid)
                              : Unicode_Alloc(pwd->pw_name, STRING_ENCODING_DEFAULT);

      /*
       * Store the process info into a list buffer.
       */
      if (!ProcMgrProcInfoArray_Push(procList, processInfo)) {
         Warning("%s: failed to expand DynArray - out of memory\n",
                 __FUNCTION__);
         goto exit;
      }
      processInfo.procCmdName = NULL;
      processInfo.procCmdLine = NULL;
      processInfo.procOwner = NULL;
   } // while (TRUE)

   if (0 < ProcMgrProcInfoArray_Count(procList)) {
      failed = FALSE;
   }

exit:
   closedir(dir);

   free(processInfo.procOwner);
   free(processInfo.procCmdLine);
   free(processInfo.procCmdName);

   if (failed) {
      ProcMgr_FreeProcList(procList);
      procList = NULL;
   }

   return procList;
}