Edgar Huckert
 

How to kill an external process from a program

The problem

I have used external processes (wave players, MP3 players etc.) often in my programs. Normally the external processes are started via the system() API call (or similar calls depending on the library you use).

If we weren't in a program we could easily stop these process by using GUI applications like system monitors (Windows, Linux) or using CLI commands like "kill -9" (Linux) or "taskkill /pid" (Windows). The problem is: if we have started an external process via a system() call we get no process ID (PID) as the return code. But normally we have a process name or even a complete path.

This is not a revolutionary solution - but I have seen so many requests for such a solution on the Internet that I decided to publish it here.

The solution

The solution (see the C Code below) uses the process table available under Linux ("ps ax" command) or Windows ("tasklist" command). It parses this table to find the PID and kills then the process.

Features

The main features of my solution are:

Caveats

Do not confuse threads and processes - this is a solution for processes!

Under Linux a sudo command may be necessary to start this program. The call of method stopProcessID() must be parameterized depending on your needs. Two very simple examples are given in the main program. You must compile and link this program with either option -DWIN32 or -DLINUX. This source code should normally be included in a foreign program. To test it you can compile it with option -DSTANDALONE. Two sample compile/link commands are given in the comments at the top of the source code.

The program uses a file to store the output of the process list command. It can therefor not be started from CDROM or DVD. A better solution might be to use a "memory mapped file" for output - but this makes the program more complex ans less portable.

The source code

You may download the source code here.

// module stopExternalProcess.c
// EH 03-2020  - written by Dr.Edgar Huckert
// This version has no dependencies on external libraries!

// compile under Windows (using MinGW gcc):
// gcc -DWIN32 -DSTANDALONE -o stopExternalProcess.exe stopExternalProcess.c
//         under Linux (GNU gcc):
// gcc -DLINUX -DSTANDALONE -o stopExternalProcess stopExternalProcess.c        

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#ifdef WIN32
#define COL_PROCNAME 0
#define COL_PROCID   1
#else
#include <fcntl.h>
#include <unistd.h>
#define COL_PROCNAME 4
#define COL_PROCID   0
#endif

// ---------------------------------------------------------------------
// Parse a string and return the desired field content
// This is part of a tokenizer - leading or trailing 
// white space is skipped
// Returns: the extracted string (zero terminated)
//           May return an empty string (never NULL)
// Warning: this is not reentrant (static ret buffer!)
char * getFieldNoWsp(const char *buf, 
                  int no_field,   // from 0 on
                  char sep)         
{
  static char retField[256];
  int    n, nof, retPos;
  char   lastCh = 0;
  //
  nof         = 0;
  retField[0] = 0;
  retPos      = 0;
  //
  n = 0;
  while (buf[n] != 0)
  {
    if ((lastCh & 0xff) <= ' ')
    {
      if ((buf[n] & 0xff) <= ' ')
      {
        // collapse sequences of white space
        n++;
        continue;
      }
    }
    if (buf[n] == sep)
    {
      nof++;
      if (nof > no_field) 
        goto zurueck;
    }
    else
    {
      if (nof == no_field)
      {
        // we are in the given field
        int i;
        for (i = n; buf[i] != 0; i++)
        {
          if ((buf[i] == sep) && (retPos > 0))
            goto zurueck;
          if ((buf[i] & 0xff) <= ' ')
          {
            // ignore white space
            if (retPos == 0)
                continue;
          }
          retField[retPos++] = buf[i];
          retField[retPos]   = 0;
          if (retPos >= (sizeof(retField) - 1))
            goto zurueck;
        }   // end for i ...
        goto zurueck;
      }
    }   // end else
    lastCh = buf[n];
    n++;
  }   // end while ...
  //
  zurueck:
  //printf("getFieldNoWsp() buf=%s no_field=%d retField=%s\n",
  //        buf, no_field, retField);
  return retField;
}   // end getFieldNoWsp()

// ----------------------------------------------------------
// get the process ID from the given file
// The file is the result of command
//   tasklist (Windows/cmd) or "ps ax" (Linux)
int getProcessID(const char *fn,
                 const char *procName)
{
  int      locPid = -1;
  char     buf[512];
  char     *pField = NULL;
  //
  FILE *ifd = fopen(fn, "r");
  if (ifd == NULL)
    goto zurueck;
  while (1)
  {
    if (fgets(buf, sizeof(buf)-1, ifd) == NULL)
      break;
    // ignore empty lines
    if (strlen(buf) <= 3)
      continue;
    //printf("process list line=%s\n", buf);
    // col. #1 is the process name
    pField = getFieldNoWsp(buf, COL_PROCNAME, ' ');
    //printf("COL_PROCNAME field=%s\n", pField);
    if (pField[0] == 0)
      break;
    if (strstr(pField, procName) != NULL)
    {
      // col. #2 is the process ID
      pField = getFieldNoWsp(buf, COL_PROCID, ' ');
      //printf("COL_PROCID field=%s\n", pField);
      if (pField[0] == 0)
        break;
      locPid = atoi(pField);
      break;
    }
  }   // end while (1)
  //
  zurueck:
  if (ifd != NULL)
    fclose(ifd);
  return locPid;
}   // end getProcessID()

// ----------------------------------------------------------------
// Stops the current song: stop an external process
int stopExtProcess(const char *stopCmd,    // "kill -9" or similar
                   const char *pProcName)  // "aplay" or mpg123" etc...
{
  int ret = 0;
  int locRet = 0;
  char cmd[256];
  int mpgPid = -1;
  //
  // produce a file with the current task list
  #ifdef WIN32
  locRet = system("tasklist >aux1.txt");
  #else
  locRet = system("ps ax >aux1.txt");
  #endif
  if (locRet < 0)
  {
    ret = -2;
    goto zurueck;
  }
  // get and keep the process ID of the player
  mpgPid = getProcessID("aux1.txt", pProcName);
  if (mpgPid < 0)
  {
    ret = -3;
    goto zurueck;
  }
  // stopCmd should be something like "taskkill /pid" or "kill -9"
  if (mpgPid >= 0)
  {
    strcpy(cmd, stopCmd);
    sprintf(cmd, "%s %u", stopCmd, mpgPid);
    //
    int locRet = system(cmd);
    //
    if (locRet < 0)
      ret = -4;
    else
      ret = 0;
  }
  else
    ret = -5;
  //
  zurueck:
  return ret;
}   // end stopExtProcess()

// -------------------------------------------------
#ifdef STANDALONE
// this is just a very simple test frame
int main(int argc, char *argv[])
{
  int ret = 0;
  //
  #ifdef WIN32
  // for Windows....
  // the process whose name is given(here mpg123) must have been
  // started in a separate cmd-window
  ret = stopExtProcess("taskkill /F /pid", "mpg123");
  #else
  // Linux: aplay plays WAVE files (extension .wav)
  // The aplay command may be started in the background (append &) or
  // in a separate black window
  // This program should be started with "sudo" - then no password is
  // required for the "kill" command
  ret = stopExtProcess("sudo kill -9", "aplay");
  #endif
  //
  return ret;
}
#endif

Copyright for all images, texts and software on this page: Dr. E. Huckert

Contact

If you want to contact me: this is my
mail address