sysinfo/procfs_user_exe.c

This is sysinfo/procfs_user_exe.c, an example to accompany the book, The Linux Programming Interface.

This file is not printed in the book; it is the solution to Exercise 12-1 (page 231).

The source code file is copyright 2022, Michael Kerrisk, and is licensed under the GNU General Public License, version 3.

In the listing below, the names of Linux system calls and C library functions are hyperlinked to manual pages from the Linux man-pages project, and the names of functions implemented in the book are hyperlinked to the implementations of those functions.

 

Download sysinfo/procfs_user_exe.c

  Cover of The Linux Programming Interface

Function list (Bold in this list means a function is not static)

/* procfs_user_exe.c

   Demonstrate the use of /proc/PID files to obtain information about
   processes on a Linux system. In this case, given a username, this
   program scans all /proc/PID/status files to produce a list of all
   processes running under a particular real user ID; for each process,
   the process ID and command that it executes are shown.

   This program is Linux-specific.
*/
#define _GNU_SOURCE
#include <limits.h>
#include <sys/stat.h>
#include <dirent.h>
#include <ctype.h>
#include <stdbool.h>
#include "ugid_functions.h"
#include "tlpi_hdr.h"

#define MAX_LINE 1000
int
main(int argc, char *argv[])
{
    if (argc < 2 || strcmp(argv[1], "--help") == 0)
        usageErr("%s username\n", argv[0]);

    uid_t checkedUid = userIdFromName(argv[1]);
    if (checkedUid == -1)
        cmdLineErr("Bad username: %s\n", argv[1]);

    DIR *dirp = opendir("/proc");
    if (dirp == NULL)
        errExit("opendir");

    /* Scan entries under /proc directory */

    for (;;) {
        errno = 0;              /* To distinguish error from end-of-directory */
        struct dirent *dp = readdir(dirp);
        if (dp == NULL) {
            if (errno != 0)
                errExit("readdir");
            else
                break;
        }

        /* Since we are looking for /proc/PID directories, skip entries
           that are not directories, or don't begin with a digit. */

        if (dp->d_type != DT_DIR || !isdigit((unsigned char) dp->d_name[0]))
            continue;

        char path[PATH_MAX];
        snprintf(path, PATH_MAX, "/proc/%s/status", dp->d_name);

        FILE *fp = fopen(path, "r");
        if (fp == NULL)
            continue;           /* Ignore errors: fopen() might fail if
                                   process has just terminated */

        bool gotName = false;
        bool gotUid = false;
        char line[MAX_LINE], cmd[MAX_LINE];
        uid_t uid;
        while (!gotName || !gotUid) {
            if (fgets(line, MAX_LINE, fp) == NULL)
                break;

            /* The "Name:" line contains the name of the command that
               this process is running */

            if (strncmp(line, "Name:", 5) == 0) {
                char *p;
                for (p = line + 5; *p != '\0' && isspace((unsigned char) *p); )
                    p++;
                strncpy(cmd, p, MAX_LINE - 1);
                cmd[MAX_LINE -1] = '\0';        /* Ensure null-terminated */

                gotName = true;
            }

            /* The "Uid:" line contains the real, effective, saved set-,
               and file-system user IDs */

            if (strncmp(line, "Uid:", 4) == 0) {
                uid = strtol(line + 4, NULL, 10);
                gotUid = true;
            }
        }

        fclose(fp);

        /* If we found a username and a UID, and the UID matches,
           then display the PID and command name */

        if (gotName && gotUid && uid == checkedUid)
            printf("%5s %s", dp->d_name, cmd);
    }

    exit(EXIT_SUCCESS);
}

 

Download sysinfo/procfs_user_exe.c

Note that, in most cases, the programs rendered in these web pages are not free standing: you'll typically also need a few other source files (mostly in the lib/ subdirectory) as well. Generally, it's easier to just download the entire source tarball and build the programs with make(1). By hovering your mouse over the various hyperlinked include files and function calls above, you can see which other source files this file depends on.

Valid XHTML 1.1