sysinfo/procfs_user_exe.cThis 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 2024, 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.
|
/* 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); }
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.