59

I need a C/C++ API that allows me to list the running processes on a Linux system, and list the files each process has open.

I do not want to end up reading the /proc/ file system directly.

Can anyone think of a way to do this?

John Topley
  • 113,588
  • 46
  • 195
  • 237
Thomi
  • 11,647
  • 13
  • 72
  • 110
  • 6
    The utility lsof does that. It is open-source, read the code and see how it works (it must use /proc, though) – 0x6adb015 Jun 02 '09 at 14:11

9 Answers9

47

http://procps.sourceforge.net/

http://procps.cvs.sourceforge.net/viewvc/procps/procps/proc/readproc.c?view=markup

Is the source of ps and other process tools. They do indeed use proc (indicating it is probably the conventional and best way). Their source is quite readable. The file

/procps-3.2.8/proc/readproc.c

May be useful. Also a useful suggestion as posted by ephemient is linking to the API provided by libproc, which should be available in your repo (or already installed I would say) but you will need the "-dev" variation for the headers and what-not.

Good Luck

Josh Crozier
  • 233,099
  • 56
  • 391
  • 304
Aiden Bell
  • 28,212
  • 4
  • 75
  • 119
25

If you do not want to read from '/proc. Then you can consider writing a Kernel module which will implement your own system call. And your system call should be written so that it can obtain the list of current processes, such as:

/* ProcessList.c 
    Robert Love Chapter 3
    */
    #include < linux/kernel.h >
    #include < linux/sched.h >
    #include < linux/module.h >

    int init_module(void) {
        struct task_struct *task;
        for_each_process(task) {
              printk("%s [%d]\n",task->comm , task->pid);
        }
        return 0;
    }
   
    void cleanup_module(void) {
        printk(KERN_INFO "Cleaning Up.\n");
    }

The code above is taken from my article here at http://linuxgazette.net/133/saha.html.Once you have your own system call, you can call it from your user space program.

Kyrol
  • 3,475
  • 7
  • 34
  • 46
  • 14
    Making your program dependent on a custom kernel module is much dirtier than reading /proc/. I wouldn't do this without a really good reason... – aksommerville Oct 02 '11 at 12:07
  • Shouldn't be there any locking? – Roman Byshko Jan 29 '12 at 20:26
  • 2
    Writing new system calls is generally bad idea. The worse that you _can_ do get running processes list without it. Writing new system call should have _really_ good reason. [Linux kernel development book](http://www.linuxjournal.com/content/book-excerpt-linux-kernel-development-3rd-edition) describes it briefly. – gumik May 29 '12 at 08:37
  • 20
    Come on guys, this is fun stuff! Let's live a little :-) – Freedom_Ben May 25 '13 at 04:21
  • @aksommerville For sure. If you're only writing a program for your own use though, I'd say that's a really good reason. – flarn2006 Mar 25 '21 at 01:34
9

Here you go (C/C++):

You could have found it here: http://ubuntuforums.org/showthread.php?t=657097

Essentially, what it does is loop through all numeric folders in /proc/<pid>, and then it does a readlink on /proc/<pid>/exe, or if you want the command-line-arguments cat /proc/<pid>/cmdline

The file-descriptors open by the process are in /proc/<pid>/fd/<descriptor>, and you get the file name by doing a readlink on each symlink, e.g. readlink /proc/<pid>/fd/<descriptor>. fd can be a device, such as /dev/null, a socket, or a file, and potentially more.

#include <unistd.h>

ssize_t readlink(const char *path, char *buf, size_t bufsiz);
On success, readlink() returns the number of bytes placed in buf.
On error, -1 is returned and errno is set to indicate the error.

This is, by the way, the same that readproc.c does (or at least did).
Of course, hopefully they did it without buffer overflow possiblity.

#ifndef __cplusplus
    #define _GNU_SOURCE
#endif

#include <unistd.h>
#include <dirent.h>
#include <sys/types.h> // for opendir(), readdir(), closedir()
#include <sys/stat.h> // for stat()

#ifdef __cplusplus
    #include <iostream>
    #include <cstdlib>
    #include <cstring>
    #include <cstdarg>
#else
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <stdarg.h>
#endif


#define PROC_DIRECTORY "/proc/"
#define CASE_SENSITIVE    1
#define CASE_INSENSITIVE  0
#define EXACT_MATCH       1
#define INEXACT_MATCH     0


int IsNumeric(const char* ccharptr_CharacterList)
{
    for ( ; *ccharptr_CharacterList; ccharptr_CharacterList++)
        if (*ccharptr_CharacterList < '0' || *ccharptr_CharacterList > '9')
            return 0; // false
    return 1; // true
}


int strcmp_Wrapper(const char *s1, const char *s2, int intCaseSensitive)
{
    if (intCaseSensitive)
        return !strcmp(s1, s2);
    else
        return !strcasecmp(s1, s2);
}

int strstr_Wrapper(const char* haystack, const char* needle, int intCaseSensitive)
{
    if (intCaseSensitive)
        return (int) strstr(haystack, needle);
    else
        return (int) strcasestr(haystack, needle);
}


#ifdef __cplusplus
pid_t GetPIDbyName(const char* cchrptr_ProcessName, int intCaseSensitiveness, int intExactMatch)
#else
pid_t GetPIDbyName_implements(const char* cchrptr_ProcessName, int intCaseSensitiveness, int intExactMatch)
#endif
{
    char chrarry_CommandLinePath[100]  ;
    char chrarry_NameOfProcess[300]  ;
    char* chrptr_StringToCompare = NULL ;
    pid_t pid_ProcessIdentifier = (pid_t) -1 ;
    struct dirent* de_DirEntity = NULL ;
    DIR* dir_proc = NULL ;

    int (*CompareFunction) (const char*, const char*, int) ;

    if (intExactMatch)
        CompareFunction = &strcmp_Wrapper;
    else
        CompareFunction = &strstr_Wrapper;


    dir_proc = opendir(PROC_DIRECTORY) ;
    if (dir_proc == NULL)
    {
        perror("Couldn't open the " PROC_DIRECTORY " directory") ;
        return (pid_t) -2 ;
    }

    // Loop while not NULL
    while ( (de_DirEntity = readdir(dir_proc)) )
    {
        if (de_DirEntity->d_type == DT_DIR)
        {
            if (IsNumeric(de_DirEntity->d_name))
            {
                strcpy(chrarry_CommandLinePath, PROC_DIRECTORY) ;
                strcat(chrarry_CommandLinePath, de_DirEntity->d_name) ;
                strcat(chrarry_CommandLinePath, "/cmdline") ;
                FILE* fd_CmdLineFile = fopen (chrarry_CommandLinePath, "rt") ;  // open the file for reading text
                if (fd_CmdLineFile)
                {
                    fscanf(fd_CmdLineFile, "%s", chrarry_NameOfProcess) ; // read from /proc/<NR>/cmdline
                    fclose(fd_CmdLineFile);  // close the file prior to exiting the routine

                    if (strrchr(chrarry_NameOfProcess, '/'))
                        chrptr_StringToCompare = strrchr(chrarry_NameOfProcess, '/') +1 ;
                    else
                        chrptr_StringToCompare = chrarry_NameOfProcess ;

                    //printf("Process name: %s\n", chrarry_NameOfProcess);
                    //printf("Pure Process name: %s\n", chrptr_StringToCompare );

                    if ( CompareFunction(chrptr_StringToCompare, cchrptr_ProcessName, intCaseSensitiveness) )
                    {
                        pid_ProcessIdentifier = (pid_t) atoi(de_DirEntity->d_name) ;
                        closedir(dir_proc) ;
                        return pid_ProcessIdentifier ;
                    }
                }
            }
        }
    }
    closedir(dir_proc) ;
    return pid_ProcessIdentifier ;
}

#ifdef __cplusplus
    pid_t GetPIDbyName(const char* cchrptr_ProcessName)
    {
        return GetPIDbyName(cchrptr_ProcessName, CASE_INSENSITIVE, EXACT_MATCH) ;
    }
#else
    // C cannot overload functions - fixed
    pid_t GetPIDbyName_Wrapper(const char* cchrptr_ProcessName, ... )
    {
        int intTempArgument ;
        int intInputArguments[2] ;
        // intInputArguments[0] = 0 ;
        // intInputArguments[1] = 0 ;
        memset(intInputArguments, 0, sizeof(intInputArguments) ) ;
        int intInputIndex ;
        va_list argptr;

        va_start( argptr, cchrptr_ProcessName );
            for (intInputIndex = 0;  (intTempArgument = va_arg( argptr, int )) != 15; ++intInputIndex)
            {
                intInputArguments[intInputIndex] = intTempArgument ;
            }
        va_end( argptr );
        return GetPIDbyName_implements(cchrptr_ProcessName, intInputArguments[0], intInputArguments[1]);
    }

    #define GetPIDbyName(ProcessName,...) GetPIDbyName_Wrapper(ProcessName, ##__VA_ARGS__, (int) 15)

#endif

int main()
{
    pid_t pid = GetPIDbyName("bash") ; // If -1 = not found, if -2 = proc fs access error
    printf("PID %d\n", pid);
    return EXIT_SUCCESS ;
}
Stefan Steiger
  • 78,642
  • 66
  • 377
  • 442
  • 1
    Weird looking typecasts in `strstr_Wrapper()`. `return (strstr(haystack, needle) != NULL)` probably better. – Craig McQueen Aug 26 '13 at 05:26
  • Essentially, loop through all numeric folders in /proc/, then do readlink /proc//exe, or cat /proc//cmdline – Stefan Steiger Dec 06 '13 at 08:02
  • It's possible to get a stack overflow of `chrarry_NameOfProcess[300]`. This code isn't so good. – Craig McQueen Mar 17 '14 at 23:45
  • @Craig McQueen: If you have that long process names, increase the buffer size, or better limit fscanf to the length of the buffer... – Stefan Steiger Jul 18 '14 at 07:57
  • Better yet, use C++ file stream I/O that avoids the buffer overflow issues, and avoids the [complexity of using `fscanf()` in a way that avoids buffer overflows and truncation](http://stackoverflow.com/q/1621394/60075). – Craig McQueen Jul 20 '14 at 23:46
  • @Thomas: Most of the awefulness comes from C not supporting function overloading. If you're willing to sacrifice that, it's a lot less aweful. The other awefullness is security. But the code was really just written for an aimbot on my machine ;) If you don't like the variable naming, join the club, and change them in your copy. – Stefan Steiger Aug 20 '19 at 13:18
  • @Craig McQueen: Yea, but then you need C++ - and C++ is a horrible language *quote Linus Torvalds* ;) http://harmful.cat-v.org/software/c++/linus – Stefan Steiger Aug 20 '19 at 13:24
8

If you don't do it, then I guess whatever API you will use will end up reading the /proc filesystem. Here are some examples of program doing this:

But unfortunately, that does not constitute an API.

Chin
  • 19,717
  • 37
  • 107
  • 164
shodanex
  • 14,975
  • 11
  • 57
  • 91
  • 7
    Exactly. Reading the /proc filesystem *is* the Linux kernels official mechanism for exporting process information to userspace. Requiring a solution that doesn't use it is basically just showing ignorance about how the system works. It won't lead to better software. – Andy Ross Jan 18 '10 at 18:03
6

PS and every other tool(EXCEPT for Kernel Modules) read from /proc. /proc is a special filesystem created on the fly by the kernel so that user mode processes can read data that will otherwise only be available for the kernel.

The recommended way is therefore, reading from /proc.

You can quickly intuitively look at the /proc filesystem to see how its structured. For every process there is a /proc/pid where pid is the process id number. Inside this folder there are several files which include different data about the current process. If you run

strace ps -aux

you will see how the program ps reads this data from /proc.

Craig McQueen
  • 41,871
  • 30
  • 130
  • 181
daniel
  • 9,732
  • 7
  • 42
  • 57
4

The only way to do this without reading /proc would be to call "ps aux", go through every line, read the second column (the PID) and call lsof -p [PID] with it.

...I'd suggest reading /proc ;)

Lennart Koopmann
  • 20,313
  • 4
  • 26
  • 33
  • 2
    Doing operations on running processes on the box is inherently unportable, so I don't think that's likely to be a problem in practice. Note also that GNU ps has options to control the output format, allowing you to get only the data you want without having to parse the whole line. – Andy Ross Jan 18 '10 at 18:02
  • `ps` reads /proc, so this does not meet the *unjustified constraint* of the question any more than the other answers. – Chris Stratton Mar 18 '14 at 00:26
3

There's a library libprocps from the procps-ng project. On Ubuntu 13.04, if you do strace ps, then you can see that ps uses libprocps.

Craig McQueen
  • 41,871
  • 30
  • 130
  • 181
  • This looks like the way to go now. A pretty standardized way to read the `/proc/###/*` data without having to yourself find out how to read the available fields. – Alexis Wilke Oct 20 '14 at 04:14
1

Reading proc is not too bad. I can't show you in C++, but the following D code should point you in the right direction:

import std.stdio;
import std.string;
import std.file;
import std.regexp;
import std.c.linux.linux;

alias std.string.split explode;

string srex = "^/proc/[0-9]+$";
string trex = "State:[ \t][SR]";
RegExp rex;
RegExp rext;

   string[] scanPidDirs(string target)
   {
      string[] result;

      bool callback(DirEntry* de)
      {
         if (de.isdir)
         {
            if (rex.find(de.name) >= 0)
            {
                string[] a = explode(de.name, "/");
                string pid = a[a.length-1];
                string x = cast(string) std.file.read(de.name ~ "/status");
                int n = rext.find(x);
                if  (n >= 0)
                {
                    x = cast(string) std.file.read(de.name ~ "/cmdline");
                    // This is null terminated
                    if (x.length) x.length = x.length-1;
                    a = explode(x, "/");
                    if (a.length)
                       x = a[a.length-1];
                    else
                       x = "";
                     if  (x == target)
                    {
                        result ~= pid ~ "/" ~x;
                    }
                }
             }
          }
          return true;
      }

      listdir("/proc", &callback);
      return result.dup;
   }

void main(string[] args)
{
    rex= new RegExp(srex);
    rext= new RegExp(trex);
    string[] a = scanPidDirs(args[1]);
    if (!a.length)
    {
        writefln("Not found");
        return;
    }
    writefln("%d matching processes", a.length);
    foreach (s; a)
    {
       string[] p = explode(s, "/");
       int pid = atoi(p[0]);
       writef("Stop %s (%d)? ", s, pid);
       string r = readln();
       if (r == "Y\n" || r == "y\n")
          kill(pid, SIGUSR1);
    }
}
-1

Easy way to fin pid of any process by name

pid_t GetPIDbyName(char* ps_name)
{

    FILE *fp;
    char *cmd=(char*)calloc(1,200);
    sprintf(cmd,"pidof %s",ps_name);
    fp=popen(cmd,"r");
    fread(cmd,1,200,fp);
    fclose(fp);
    return atoi(cmd);
}
tgogos
  • 23,218
  • 20
  • 96
  • 128
Md Muazzam
  • 25
  • 6