title: Count the running process in AIX by it's name in C
body: <p>Operating system: <strong>IBM AIX 5.3</strong></p>
  
  <p>Compiler: <strong>xlc</strong></p>
  
  <p>Hello Everyone</p>
  
  <p>I have a project that using C to process some files using multi-processes. The number of sub-processes are mainly depends on the the number of incoming files and the number of current running processes. I need a reliable method to count on how many sub-processes are running at background. </p>
  
  <p>By comparing the efficiency, directly reading <code>/proc</code> directory may have better performance than invoking popen() to execute <code>$ ps -ef | grep blah</code>.</p>
  
  <p>I wrote a function to read psinfo in <code>/proc/pid/psinfo</code> and compare the arugments. 
  Pseudo code is as follow:</p>
  
  <pre><code>int count = 0;
  dp = opendir("/proc");
  while (readdir_r(...))
  {
      if (dir is not a process)
          return -1;
      if (dir's owner is not current user)
          return -2;
      if (failed to open "/proc/[pid]/psinfo")
          return -3;
      if (failed to read "/proc/[pid]/psinfo")
          return -4;
      if (process's name matches the given pname)
          count += 1;
  }
  return count;
  </code></pre>
  
  <p>The function generally runs perfectly at single call. However, it returns -2 or -3 or even wrong counts when embedded in while loop. </p>
  
  <p>The function failed to read the attribute of <code>/proc/pid</code> randomly. It tells <code>No such file or directory</code>.</p>
  
  <p>There is also a small chance to get wrong counts at the end. There seems to be an extra process with certain pid but disappeared when printing the current processes using ps.</p>
  
  <p>I think there is any change were made when reading from sub-directory rapidly after parent directory were being listed.</p>
  
  <p><strong>Is there any thing I made wrong or is there any method to avoid the race condition?</strong></p>
  
  <p>Come extra information about psinfo in AIX
  <a href="http://www-01.ibm.com/support/knowledgecenter/ssw_aix_53/com.ibm.aix.files/doc/aixfiles/proc.htm%23files-proc?lang=en[233]" rel="nofollow">http://www-01.ibm.com/support/knowledgecenter/ssw_aix_53/com.ibm.aix.files/doc/aixfiles/proc.htm%23files-proc?lang=en[233]</a></p>
  
  <p>Here is the full source code:</p>
  
  <pre class="lang-c prettyprint-override"><code>#include &lt;stdio.h&gt;
  #include &lt;stdlib.h&gt;
  #include &lt;string.h&gt;
  #include &lt;dirent.h&gt;
  #include &lt;sys/procfs.h&gt;
  #include &lt;sys/types.h&gt;
  #include &lt;sys/stat.h&gt;
  #include &lt;fcntl.h&gt;
  #include &lt;unistd.h&gt;
  #include &lt;errno.h&gt;
  
  int countProcess(char *pname)
  {
      DIR                 *dir;
      int                 fd;
      int                 pid;
      int                 uid;
      int                 pcounter = 0;
      struct psinfo       pinfo;
      struct dirent       entry;
      struct dirent       *result;
      struct stat         fsstat;
      char                path[256];
      char                process_path[256];
  
      /* Open the /proc directory */
      if ((dir = opendir("/proc")) == NULL)
      {
          return -1;
      }
  
      /* Retrieve the current user id */
      uid = getuid();
  
      /* Walk through the /proc dir */
      for (readdir_r(dir, &amp;entry, &amp;result); result != NULL; readdir_r(dir, &amp;entry, &amp;result))
      {
          /* See if this is a process, e.g., the dirname is a number
             If not, then start off again
           */
          if ((pid = atoi(entry.d_name)) == 0)
          {
              continue;
          }
  
          /* Get the attributes of process dir */
          snprintf(process_path, sizeof(process_path), "/proc/%s", entry.d_name);
          if (stat(process_path, &amp;fsstat) == -1)
          {
              closedir(dir);
              return -2;
          }
  
  
          /* Verify if the process runs by current user
             If not, then start off again
           */
          if (fsstat.st_uid != uid)
          {
              continue;
          }
  
          /* Open and read from psinfo file */
          snprintf(path, sizeof(path), "/proc/%s/psinfo", entry.d_name);
  
          if ((fd = open(path, O_RDONLY)) &lt; 0)
          {
              close(fd);
              closedir(dir);
              return -3;
          }
  
          if (read(fd, &amp;pinfo, sizeof(pinfo)) &lt; 0)
          {
              close(fd);
              closedir(dir);
              return -4;
          }
  
          /* String comparison, if same, increase the counter */
          if (!strcmp(pinfo.pr_psargs, pname))
          {
              pcounter++;
          }
          close(fd);
  
      }
  
      /* returns counter */
      closedir(dir);
      return pcounter;
  }
  </code></pre>
  
  <hr>
  
  <h1>Update 13/Jan/2015</h1>
  
  <p>Thanks to CoreyStup. The race condition can be bypassed by getprocs() function provided in procinfo.h</p>
  
  <p>Here is the code for the solution</p>
  
  <pre class="lang-c prettyprint-override"><code>#include &lt;stdio.h&gt;
  #include &lt;unistd.h&gt;
  #include &lt;procinfo.h&gt;
  #include &lt;sys/types.h&gt;
  
  int countProcess(const char *pname)
  {
      struct procsinfo            pinfo;
      pid_t                       pid = 0;
      uid_t                       uid;
      char                        args[256];
      int                         index;
      int                         pcounter = 0;
  
      memset(args, 0, sizeof(args));
      uid = getuid();
  
      /* Get procsinfo from internal API */
      while (0 &lt; getprocs(&amp;pinfo, (int)sizeof(struct procsinfo), NULL, 0, &amp;pid, 1))
      {
          /* Skip the process that doesn't belong to current user */
          if (pinfo.pi_uid != uid)
          {
              continue;
          }
  
          /* Get process arguments */
          if (getargs(&amp;pinfo, sizeof(struct procsinfo), args, sizeof(args)) != 0)
          {
              return -1;
          }
  
          /* getargs returns the args list seperated by 0, we need to use space to replace 0 */
          for (index = 0; index &lt; 256 - 1 &amp;&amp; !(args[index] == 0 &amp;&amp; args[index + 1] == 0); index++)
          {
              if (args[index] == 0)
              {
                  args[index] = ' ';
              }
          }
          if (!strncmp(args, pname, strlen(pname)))
          {
              pcounter++;
          }
  
      }
      return pcounter;
  }
  </code></pre>