(root)/
util-linux-2.39/
lib/
procfs.c
       1  /*
       2   * No copyright is claimed.  This code is in the public domain; do with
       3   * it what you wish.
       4   *
       5   * Written by Karel Zak <kzak@redhat.com>
       6   */
       7  #include <ctype.h>
       8  #include <unistd.h>
       9  #include <errno.h>
      10  
      11  #ifdef HAVE_SYS_VFS_H
      12  # include <sys/vfs.h>
      13  # include "statfs_magic.h"
      14  #endif
      15  
      16  #include "c.h"
      17  #include "pathnames.h"
      18  #include "procfs.h"
      19  #include "fileutils.h"
      20  #include "all-io.h"
      21  #include "debug.h"
      22  #include "strutils.h"
      23  
      24  static void procfs_process_deinit_path(struct path_cxt *pc);
      25  
      26  /*
      27   * Debug stuff (based on include/debug.h)
      28   */
      29  static UL_DEBUG_DEFINE_MASK(ulprocfs);
      30  UL_DEBUG_DEFINE_MASKNAMES(ulprocfs) = UL_DEBUG_EMPTY_MASKNAMES;
      31  
      32  #define ULPROCFS_DEBUG_INIT	(1 << 1)
      33  #define ULPROCFS_DEBUG_CXT	(1 << 2)
      34  
      35  #define DBG(m, x)       __UL_DBG(ulprocfs, ULPROCFS_DEBUG_, m, x)
      36  #define ON_DBG(m, x)    __UL_DBG_CALL(ulprocfs, ULPROCFS_DEBUG_, m, x)
      37  
      38  #define UL_DEBUG_CURRENT_MASK	UL_DEBUG_MASK(ulprocfs)
      39  #include "debugobj.h"
      40  
      41  void ul_procfs_init_debug(void)
      42  {
      43  	if (ulprocfs_debug_mask)
      44  		return;
      45  	__UL_INIT_DEBUG_FROM_ENV(ulprocfs, ULPROCFS_DEBUG_, 0, ULPROCFS_DEBUG);
      46  }
      47  
      48  struct path_cxt *ul_new_procfs_path(pid_t pid, const char *prefix)
      49  {
      50  	struct path_cxt *pc = ul_new_path(NULL);
      51  
      52  	if (!pc)
      53  		return NULL;
      54  	if (prefix)
      55  		ul_path_set_prefix(pc, prefix);
      56  
      57  	if (procfs_process_init_path(pc, pid) != 0) {
      58  		ul_unref_path(pc);
      59  		return NULL;
      60  	}
      61  
      62  	DBG(CXT, ul_debugobj(pc, "alloc"));
      63  	return pc;
      64  }
      65  
      66  /*
      67   * procfs_blkdev_* is procfs extension to ul_path_* API to read info about process.
      68   *
      69   * The function is possible to call in loop and without sysfs_procfs_deinit_path().
      70   * The procfs_process_deinit_path() is automatically called by ul_unref_path().
      71   *
      72   */
      73  int procfs_process_init_path(struct path_cxt *pc, pid_t pid)
      74  {
      75  	struct procfs_process *prc;
      76  	int rc;
      77  	char buf[sizeof(_PATH_PROC) + sizeof(stringify_value(UINT32_MAX)) + 2];
      78  
      79  	/* define path to pid stuff */
      80  	snprintf(buf, sizeof(buf), _PATH_PROC "/%zu", (size_t) pid);
      81  	rc = ul_path_set_dir(pc, buf);
      82  	if (rc)
      83  		return rc;
      84  
      85  	/* make sure path exists */
      86  	rc = ul_path_get_dirfd(pc);
      87  	if (rc < 0)
      88  		return rc;
      89  
      90  	/* initialize procfs specific stuff */
      91  	prc = ul_path_get_dialect(pc);
      92  	if (!prc) {
      93  		DBG(CXT, ul_debugobj(pc, "alloc new procfs handler"));
      94  		prc = calloc(1, sizeof(struct procfs_process));
      95  		if (!prc)
      96  			return -ENOMEM;
      97  
      98  		ul_path_set_dialect(pc, prc, procfs_process_deinit_path);
      99  	}
     100  
     101  	DBG(CXT, ul_debugobj(pc, "init procfs stuff"));
     102  
     103  	prc->pid = pid;
     104  	return 0;
     105  }
     106  
     107  static void procfs_process_deinit_path(struct path_cxt *pc)
     108  {
     109  	struct procfs_process *prc;
     110  
     111  	if (!pc)
     112  		return;
     113  
     114  	DBG(CXT, ul_debugobj(pc, "deinit"));
     115  
     116  	prc = ul_path_get_dialect(pc);
     117  	if (!prc)
     118  		return;
     119  
     120  	free(prc);
     121  	ul_path_set_dialect(pc, NULL, NULL);
     122  }
     123  
     124  static ssize_t read_procfs_file(int fd, char *buf, size_t bufsz)
     125  {
     126  	ssize_t sz = 0;
     127  	size_t i;
     128  
     129  	if (fd < 0)
     130  		return -EINVAL;
     131  
     132  	sz = read_all(fd, buf, bufsz);
     133  	if (sz <= 0)
     134  		return sz;
     135  
     136  	for (i = 0; i < (size_t) sz; i++) {
     137  		if (buf[i] == '\0')
     138  			buf[i] = ' ';
     139  	}
     140  	buf[sz - 1] = '\0';
     141  	return sz;
     142  }
     143  
     144  static ssize_t procfs_process_get_data_for(struct path_cxt *pc, char *buf, size_t bufsz,
     145  					    const char *fname)
     146  {
     147  	int fd = ul_path_open(pc, O_RDONLY|O_CLOEXEC, fname);
     148  
     149  	if (fd >= 0) {
     150  		ssize_t sz = read_procfs_file(fd, buf, bufsz);
     151  		close(fd);
     152  		return sz;
     153  	}
     154  	return -errno;
     155  }
     156  
     157  ssize_t procfs_process_get_cmdline(struct path_cxt *pc, char *buf, size_t bufsz)
     158  {
     159  	return procfs_process_get_data_for(pc, buf, bufsz, "cmdline");
     160  }
     161  
     162  ssize_t procfs_process_get_cmdname(struct path_cxt *pc, char *buf, size_t bufsz)
     163  {
     164  	return procfs_process_get_data_for(pc, buf, bufsz, "comm");
     165  }
     166  
     167  ssize_t procfs_process_get_stat(struct path_cxt *pc, char *buf, size_t bufsz)
     168  {
     169  	return procfs_process_get_data_for(pc, buf, bufsz, "stat");
     170  }
     171  
     172  int procfs_process_get_stat_nth(struct path_cxt *pc, int n, uintmax_t *re)
     173  {
     174  	ssize_t rc;
     175  	char *key = NULL, *tok, *p;
     176  	char buf[BUFSIZ];
     177  	int i;
     178  
     179  	if (n == 2 || n == 3)		/* process name and status (strings) */
     180  		return -EINVAL;
     181  
     182  	rc = procfs_process_get_data_for(pc, buf, sizeof(buf), "stat");
     183  	if (rc < 0)
     184  		return rc;
     185  
     186  	for (i = 0, tok = strtok_r(buf, " ", &key); tok;
     187  	     tok = strtok_r(NULL, " ", &key)) {
     188  
     189  		i++;
     190  		if (i == n)
     191  			return ul_strtou64(tok, re, 10);
     192  
     193  		/* skip rest of the process name */
     194  		if (i == 2 && (p = strrchr(key, ')')))
     195  			key = p + 2;
     196  	}
     197  
     198  	return -EINVAL;
     199  }
     200  
     201  int procfs_process_get_uid(struct path_cxt *pc, uid_t *uid)
     202  {
     203  	struct stat sb;
     204  	int rc;
     205  
     206  	if ((rc = ul_path_stat(pc, &sb, 0, NULL)) == 0)
     207  		*uid = sb.st_uid;
     208  	return rc;
     209  }
     210  
     211  /*
     212   * returns the next task TID, the @sub is automatically initialized
     213   * when called first time and closed after last call or you can
     214   * call closedir()* when you need to break the loop.
     215   *
     216   * Returns: <0 on error, 0 on success, >1 done
     217   *
     218   * Example:
     219   *
     220   * pid_t tid;
     221   * DIR *sub = NULL;
     222   * path_cxt *pc = ul_new_procfs_path(123, NULL);
     223   *
     224   * while (procfs_process_next_tid(pc, &sub, &tid) == 0)
     225   *	printf("task: %d", (int) tid);
     226   *
     227   */
     228  int procfs_process_next_tid(struct path_cxt *pc, DIR **sub, pid_t *tid)
     229  {
     230  	struct dirent *d;
     231  
     232  	if (!pc || !sub || !tid)
     233  		return -EINVAL;
     234  
     235  	if (!*sub) {
     236  		*sub = ul_path_opendir(pc, "task");
     237  		if (!*sub)
     238  			return -errno;
     239  	}
     240  
     241  	while ((d = xreaddir(*sub))) {
     242  		if (procfs_dirent_get_pid(d, tid) == 0)
     243  			return 0;
     244  	}
     245  
     246  	closedir(*sub);
     247  	*sub = NULL;
     248  	return 1;
     249  }
     250  
     251  int procfs_process_next_fd(struct path_cxt *pc, DIR **sub, int *fd)
     252  {
     253  	struct dirent *d;
     254  
     255  	if (!pc || !sub || !fd)
     256  		return -EINVAL;
     257  
     258  	if (!*sub) {
     259  		*sub = ul_path_opendir(pc, "fd");
     260  		if (!*sub)
     261  			return -errno;
     262  	}
     263  
     264  	while ((d = xreaddir(*sub))) {
     265  		uint64_t num;
     266  #ifdef _DIRENT_HAVE_D_TYPE
     267  		if (d->d_type != DT_LNK && d->d_type != DT_UNKNOWN)
     268  			continue;
     269  #endif
     270  		if (ul_strtou64(d->d_name, &num, 10) < 0)
     271  			continue;
     272  		*fd = num;
     273  		return 0;
     274  	}
     275  
     276  	closedir(*sub);
     277  	*sub = NULL;
     278  	return 1;
     279  }
     280  
     281  /*
     282   * Simple 'dirent' based stuff for use-cases where procfs_process_* API is overkill
     283   */
     284  
     285  /* stupid, but good enough as a basic filter */
     286  int procfs_dirent_is_process(struct dirent *d)
     287  {
     288  #ifdef _DIRENT_HAVE_D_TYPE
     289  	if (d->d_type != DT_DIR && d->d_type != DT_UNKNOWN)
     290  		return 0;
     291  #endif
     292  	if (!isdigit((unsigned char) *d->d_name))
     293  		return 0;
     294  
     295  	return 1;
     296  }
     297  
     298  int procfs_dirent_get_pid(struct dirent *d, pid_t *pid)
     299  {
     300  	uint64_t num;
     301  
     302  	if (!procfs_dirent_is_process(d))
     303  		return -EINVAL;
     304  
     305  	if (ul_strtou64(d->d_name, &num, 10) < 0)
     306  		return -EINVAL;
     307  
     308  	*pid = (pid_t) num;
     309  	return 0;
     310  }
     311  
     312  int procfs_dirent_get_uid(DIR *procfs, struct dirent *d, uid_t *uid)
     313  {
     314  	struct stat st;
     315  
     316  	if (!procfs_dirent_is_process(d))
     317  		return -EINVAL;
     318  
     319  	if (fstatat(dirfd(procfs), d->d_name, &st, 0))
     320  		return -EINVAL;
     321  
     322  	*uid = st.st_uid;
     323  	return 0;
     324  }
     325  
     326  int procfs_dirent_match_uid(DIR *procfs, struct dirent *d, uid_t uid)
     327  {
     328  	uid_t x;
     329  
     330  	if (procfs_dirent_get_uid(procfs, d, &x) == 0)
     331  		return x == uid;
     332  
     333  	return 0;
     334  }
     335  
     336  /* "name" of process; may be truncated, see prctl(2) and PR_SET_NAME.
     337   * The minimal of the @buf has to be 32 bytes. */
     338  int procfs_dirent_get_name(DIR *procfs, struct dirent *d, char *buf, size_t bufsz)
     339  {
     340  	FILE *f;
     341  	size_t sz;
     342  	char tmp[1024], *p, *end = NULL;
     343  
     344  	if (bufsz < 32)
     345  		return -EINVAL;
     346  	if (!procfs_dirent_is_process(d))
     347  		return -EINVAL;
     348  
     349  	snprintf(tmp, sizeof(tmp), "%s/stat", d->d_name);
     350  	f = fopen_at(dirfd(procfs), tmp, O_CLOEXEC|O_RDONLY, "r");
     351  	if (!f)
     352  		return -errno;
     353  
     354  	p = fgets(tmp, sizeof(tmp), f);
     355  	fclose(f);
     356  	if (!p)
     357  		return -errno;
     358  
     359  	/* skip PID */
     360  	while (*p && *p != '(')
     361  		p++;
     362  
     363  	/* skip extra '(' */
     364  	while (*p && *p == '(')
     365  		p++;
     366  
     367  	end = p;
     368  	while (*end && *end != ')')
     369  		end++;
     370  
     371  	sz = end - p;
     372  	if (sz >= bufsz)
     373  		sz = bufsz - 1;
     374  
     375  	memcpy(buf, p, sz);
     376  	buf[sz] = '\0';
     377  
     378  	return 0;
     379  }
     380  
     381  int procfs_dirent_match_name(DIR *procfs, struct dirent *d, const char *name)
     382  {
     383  	char buf[33];
     384  
     385  	if (procfs_dirent_get_name(procfs, d, buf, sizeof(buf)) == 0)
     386  		return strcmp(name, buf) == 0;
     387  
     388  	return 0;
     389  }
     390  
     391  #ifdef HAVE_SYS_VFS_H
     392  /* checks if fd is file in a procfs;
     393   * returns 1 if true, 0 if false or couldn't determine */
     394  int fd_is_procfs(int fd)
     395  {
     396  	struct statfs st;
     397  	int ret;
     398  
     399  	do {
     400  		errno = 0;
     401  		ret = fstatfs(fd, &st);
     402  
     403  		if (ret < 0) {
     404  			if (errno != EINTR && errno != EAGAIN)
     405  				return 0;
     406  			xusleep(250000);
     407  		}
     408  	} while (ret != 0);
     409  
     410  	return st.f_type == STATFS_PROC_MAGIC;
     411  	return 0;
     412  }
     413  #else
     414  int fd_is_procfs(int fd __attribute__((__unused__)))
     415  {
     416  	return 0;
     417  }
     418  #endif
     419  
     420  static char *strdup_procfs_file(pid_t pid, const char *name)
     421  {
     422  	char buf[BUFSIZ];
     423  	char *re = NULL;
     424  	int fd;
     425  
     426  	snprintf(buf, sizeof(buf), _PATH_PROC "/%d/%s", (int) pid, name);
     427  	fd = open(buf, O_CLOEXEC|O_RDONLY);
     428  	if (fd < 0)
     429  		return NULL;
     430  
     431  	if (read_procfs_file(fd, buf, sizeof(buf)) > 0)
     432  		re = strdup(buf);
     433  	close(fd);
     434  	return re;
     435  }
     436  
     437  char *pid_get_cmdname(pid_t pid)
     438  {
     439  	return strdup_procfs_file(pid, "comm");
     440  }
     441  
     442  char *pid_get_cmdline(pid_t pid)
     443  {
     444  	return strdup_procfs_file(pid, "cmdline");
     445  }
     446  
     447  #ifdef TEST_PROGRAM_PROCFS
     448  
     449  static int test_tasks(int argc, char *argv[], const char *prefix)
     450  {
     451  	DIR *sub = NULL;
     452  	struct path_cxt *pc;
     453  	pid_t tid = 0, pid;
     454  
     455  	if (argc != 2)
     456  		return EXIT_FAILURE;
     457  
     458  	pid = strtol(argv[1], (char **) NULL, 10);
     459  	printf("PID=%d, TIDs:", pid);
     460  
     461  	pc = ul_new_procfs_path(pid, prefix);
     462  	if (!pc)
     463  		err(EXIT_FAILURE, "alloc procfs handler failed");
     464  
     465  	while (procfs_process_next_tid(pc, &sub, &tid) == 0)
     466  		printf(" %d", tid);
     467  
     468  	printf("\n");
     469          ul_unref_path(pc);
     470  	return EXIT_SUCCESS;
     471  }
     472  
     473  static int test_fds(int argc, char *argv[], const char *prefix)
     474  {
     475  	DIR *sub = NULL;
     476  	struct path_cxt *pc;
     477  	pid_t pid;
     478  	int fd = -1;
     479  
     480  	if (argc != 2)
     481  		return EXIT_FAILURE;
     482  
     483  	pid = strtol(argv[1], (char **) NULL, 10);
     484  	printf("PID=%d, FDs:", pid);
     485  
     486  	pc = ul_new_procfs_path(pid, prefix);
     487  	if (!pc)
     488  		err(EXIT_FAILURE, "alloc procfs handler failed");
     489  
     490  	while (procfs_process_next_fd(pc, &sub, &fd) == 0)
     491  		printf(" %d", fd);
     492  
     493  	fputc('\n', stdout);
     494          ul_unref_path(pc);
     495  	return EXIT_SUCCESS;
     496  }
     497  
     498  static int test_processes(int argc, char *argv[])
     499  {
     500  	DIR *dir;
     501  	struct dirent *d;
     502  	char *name = NULL;
     503  	uid_t uid = (uid_t) -1;
     504  	char buf[128];
     505  
     506  	if (argc >= 3 && strcmp(argv[1], "--name") == 0)
     507  		name = argv[2];
     508  	if (argc >= 3 && strcmp(argv[1], "--uid") == 0)
     509  		uid = (uid_t) atol(argv[2]);
     510  
     511  	dir = opendir(_PATH_PROC);
     512  	if (!dir)
     513  		err(EXIT_FAILURE, "cannot open proc");
     514  
     515  	while ((d = xreaddir(dir))) {
     516  		pid_t pid = 0;
     517  
     518  		if (procfs_dirent_get_pid(d, &pid) != 0)
     519  			continue;
     520  		if (name && !procfs_dirent_match_name(dir, d, name))
     521  			continue;
     522  		if (uid != (uid_t) -1 && !procfs_dirent_match_uid(dir, d, uid))
     523  			continue;
     524  		procfs_dirent_get_name(dir, d, buf, sizeof(buf));
     525  		printf(" %d [%s]", pid, buf);
     526  	}
     527  
     528  	fputc('\n', stdout);
     529  	closedir(dir);
     530  	return EXIT_SUCCESS;
     531  }
     532  
     533  static int test_one_process(int argc, char *argv[], const char *prefix)
     534  {
     535  	pid_t pid;
     536  	struct path_cxt *pc;
     537  	char buf[BUFSIZ];
     538  	uid_t uid = (uid_t) -1;
     539  
     540  	if (argc != 2)
     541  		return EXIT_FAILURE;
     542  	pid = strtol(argv[1], (char **) NULL, 10);
     543  
     544  	pc = ul_new_procfs_path(pid, prefix);
     545  	if (!pc)
     546  		err(EXIT_FAILURE, "cannot alloc procfs handler");
     547  
     548  	printf("%d\n", (int) pid);
     549  
     550  	procfs_process_get_uid(pc, &uid);
     551  	printf("   UID: %zu\n", (size_t) uid);
     552  
     553  	procfs_process_get_cmdline(pc, buf, sizeof(buf));
     554  	printf("   CMDLINE: '%s'\n", buf);
     555  
     556  	procfs_process_get_cmdname(pc, buf, sizeof(buf));
     557  	printf("   COMM: '%s'\n", buf);
     558  
     559  	ul_unref_path(pc);
     560  	return EXIT_SUCCESS;
     561  }
     562  
     563  static int test_isprocfs(int argc, char *argv[])
     564  {
     565  	const char *name = argc > 1 ? argv[1] : "/proc";
     566  	int fd = open(name, O_RDONLY);
     567  	int is = 0;
     568  
     569  	if (fd >= 0) {
     570  		is = fd_is_procfs(fd);
     571  		close(fd);
     572  	} else
     573  		err(EXIT_FAILURE, "cannot open %s", name);
     574  
     575  	printf("%s: %s procfs\n", name, is ? "is" : "is NOT");
     576  	return is ? EXIT_SUCCESS : EXIT_FAILURE;
     577  }
     578  
     579  static int test_process_stat_nth(int argc, char *argv[], const char *prefix)
     580  {
     581  	pid_t pid;
     582  	struct path_cxt *pc;
     583  	uintmax_t num = 0;
     584  	int n, ret;
     585  
     586  	if (argc != 3)
     587  		return EXIT_FAILURE;
     588  	pid = strtol(argv[1], (char **) NULL, 10);
     589  	n = strtol(argv[2], (char **) NULL, 10);
     590  
     591  	pc = ul_new_procfs_path(pid, prefix);
     592  	if (!pc)
     593  		err(EXIT_FAILURE, "cannot alloc procfs handler");
     594  
     595  	ret = procfs_process_get_stat_nth(pc, n, &num);
     596  	if (ret)
     597  		errx(EXIT_FAILURE, "read %dth number failed: %s", n, strerror(-ret));
     598  
     599  	printf("%d: %dth %ju\n", (int) pid, n, num);
     600  	ul_unref_path(pc);
     601  	return EXIT_SUCCESS;
     602  }
     603  
     604  int main(int argc, char *argv[])
     605  {
     606  	const char *prefix = NULL;
     607  
     608  	if (argc > 2 && strcmp(argv[1], "--prefix") == 0) {
     609  		prefix = argv[2];
     610  		argc -= 2;
     611  		argv += 2;
     612  	}
     613  
     614  	if (argc < 2) {
     615  		fprintf(stderr, "usage: %1$s [--prefix <prefix>] --tasks <pid>\n"
     616  				"       %1$s [--prefix <prefix>] --fds <pid>\n"
     617  				"       %1$s --is-procfs [<dir>]\n"
     618  				"       %1$s --processes [--name <name>] [--uid <uid>]\n"
     619  				"       %1$s [--prefix <prefix>] --one <pid>\n"
     620  				"       %1$s [--prefix <prefix>] --stat-nth <pid> <n>\n",
     621  				program_invocation_short_name);
     622  		return EXIT_FAILURE;
     623  	}
     624  
     625  	if (strcmp(argv[1], "--tasks") == 0)
     626  		return test_tasks(argc - 1, argv + 1, prefix);
     627  	if (strcmp(argv[1], "--fds") == 0)
     628  		return test_fds(argc - 1, argv + 1, prefix);
     629  	if (strcmp(argv[1], "--processes") == 0)
     630  		return test_processes(argc - 1, argv + 1);
     631  	if (strcmp(argv[1], "--is-procfs") == 0)
     632  		return test_isprocfs(argc - 1, argv + 1);
     633  	if (strcmp(argv[1], "--one") == 0)
     634  		return test_one_process(argc - 1, argv + 1, prefix);
     635  	if (strcmp(argv[1], "--stat-nth") == 0)
     636  		return test_process_stat_nth(argc - 1, argv + 1, prefix);
     637  
     638  	return EXIT_FAILURE;
     639  }
     640  #endif /* TEST_PROGRAM_PROCUTILS */