/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * Copyright (C) 2006 William Jon McCann * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * */ #include "config.h" #include #include #include #include #include #include #include #define DEV_ENCODE(M,m) ( \ ( (M&0xfff) << 8) | ( (m&0xfff00) << 12) | (m&0xff) \ ) #include #include #define _STRUCTURED_PROC 1 #include #define NO_TTY_VALUE DEV_ENCODE(-1,-1) #include "proc.h" /* adapted from procps */ struct _proc_stat_t { int pid; int ppid; /* stat,status pid of parent process */ char state; /* stat,status single-char code for process state (S=sleeping) */ char cmd[16]; /* stat,status basename of executable file in call to exec(2) */ unsigned long long utime; /* stat user-mode CPU time accumulated by process */ unsigned long long stime; /* stat kernel-mode CPU time accumulated by process */ unsigned long long cutime; /* stat cumulative utime of process and reaped children */ unsigned long long cstime; /* stat cumulative stime of process and reaped children */ unsigned long long start_time; /* stat start time of process -- seconds since 1-1-70 */ unsigned long start_code; /* stat address of beginning of code segment */ unsigned long end_code; /* stat address of end of code segment */ unsigned long start_stack; /* stat address of the bottom of stack for the process */ unsigned long kstk_esp; /* stat kernel stack pointer */ unsigned long kstk_eip; /* stat kernel instruction pointer */ unsigned long wchan; /* stat (special) address of kernel wait channel proc is sleeping in */ long priority; /* stat kernel scheduling priority */ long nice; /* stat standard unix nice level of process */ long rss; /* stat resident set size from /proc/#/stat (pages) */ long alarm; /* stat ? */ unsigned long rtprio; /* stat real-time priority */ unsigned long sched; /* stat scheduling class */ unsigned long vsize; /* stat number of pages of virtual memory ... */ unsigned long rss_rlim; /* stat resident set size limit? */ unsigned long flags; /* stat kernel flags for the process */ unsigned long min_flt; /* stat number of minor page faults since process start */ unsigned long maj_flt; /* stat number of major page faults since process start */ unsigned long cmin_flt; /* stat cumulative min_flt of process and child processes */ unsigned long cmaj_flt; /* stat cumulative maj_flt of process and child processes */ int pgrp; /* stat process group id */ int session; /* stat session id */ int nlwp; /* stat number of threads, or 0 if no clue */ int tty; /* stat full device number of controlling terminal */ int tpgid; /* stat terminal process group id */ int exit_signal; /* stat might not be SIGCHLD */ int processor; /* stat current (or most recent?) CPU */ uintptr_t penv; /* stat address of initial environment vector */ char tty_text[16]; /* stat device name */ }; pid_t proc_stat_get_ppid (proc_stat_t *stat) { g_return_val_if_fail (stat != NULL, -1); return stat->ppid; } char * proc_stat_get_cmd (proc_stat_t *stat) { g_return_val_if_fail (stat != NULL, NULL); return g_strdup (stat->cmd); } /* adapted from procps */ char * proc_stat_get_tty (proc_stat_t *stat) { guint dev; char *tty; guint dev_maj; guint dev_min; pid_t pid; g_return_val_if_fail (stat != NULL, NULL); return stat->tty_text; } /* return 1 if it works, or 0 for failure */ static gboolean stat2proc (pid_t pid, proc_stat_t *P) { struct psinfo p; char buf[32]; int num; int fd; int tty_maj; int tty_min; snprintf (buf, sizeof buf, "/proc/%d/psinfo", pid); if ((fd = open (buf, O_RDONLY, 0) ) == -1 ) { return FALSE; } num = read (fd, &p, sizeof p); close (fd); if (num != sizeof p) { return FALSE; } num = PRFNSZ; if (num >= sizeof P->cmd) { num = sizeof P->cmd - 1; } memcpy (P->cmd, p.pr_fname, num); /* p.pr_fname or p.pr_lwp.pr_name */ P->cmd[num] = '\0'; P->pid = p.pr_pid; P->ppid = p.pr_ppid; P->pgrp = p.pr_pgid; P->session = p.pr_sid; P->rss = p.pr_rssize; P->vsize = p.pr_size; P->start_time = p.pr_start.tv_sec; P->wchan = p.pr_lwp.pr_wchan; P->state = p.pr_lwp.pr_sname; P->nice = p.pr_lwp.pr_nice; P->priority = p.pr_lwp.pr_pri; /* or pr_oldpri */ P->penv = p.pr_envp; /* we like it Linux-encoded :-) */ tty_maj = major (p.pr_ttydev); tty_min = minor (p.pr_ttydev); P->tty = DEV_ENCODE (tty_maj,tty_min); snprintf (P->tty_text, sizeof P->tty_text, "%3d,%-3d", tty_maj, tty_min); if (tty_maj == 24) { snprintf (P->tty_text, sizeof P->tty_text, "pts/%-3u", tty_min); } if (P->tty == NO_TTY_VALUE) { memcpy (P->tty_text, " ? ", 8); } if (P->tty == DEV_ENCODE(0,0)) { memcpy (P->tty_text, "console", 8); } if (P->pid != pid) { return FALSE; } return TRUE; } gboolean proc_stat_new_for_pid (pid_t pid, proc_stat_t **stat, GError **error) { char *path; char *contents; gsize length; gboolean res; GError *local_error; proc_stat_t *proc; g_return_val_if_fail (pid > 1, FALSE); if (stat == NULL) { return FALSE; } proc = g_new0 (proc_stat_t, 1); proc->pid = pid; res = stat2proc (pid, proc); if (res) { *stat = proc; } else { g_propagate_error (error, local_error); *stat = NULL; } return res; } void proc_stat_free (proc_stat_t *stat) { g_free (stat); } GHashTable * proc_pid_get_env_hash (pid_t pid) { GHashTable *hash; gboolean res; proc_stat_t *stat; char *env[400]; char buf[BUFSIZ]; int fd; int i; res = proc_stat_new_for_pid (pid, &stat, NULL); if (! res) { goto out; } hash = NULL; hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); pread (fd, env, 400, stat->penv); i = 0; while (env[i] != NULL && i < 400) { char **vals; pread (fd, buf, BUFSIZ, env[i++]); vals = g_strsplit (buf, "=", 2); if (vals != NULL) { g_hash_table_insert (hash, vals[0], vals[1]); } } out: return hash; } char * proc_pid_get_env (pid_t pid, const char *var) { GHashTable *hash; char *val; hash = proc_pid_get_env_hash (pid); val = g_hash_table_lookup (hash, var); return val; } uid_t proc_pid_get_uid (pid_t pid) { struct stat st; char *path; int uid; int res; g_return_val_if_fail (pid > 1, 0); uid = -1; path = g_strdup_printf ("/proc/%u", (guint)pid); res = stat (path, &st); g_free (path); if (res == 0) { uid = st.st_uid; } return uid; } pid_t proc_pid_get_ppid (pid_t pid) { int ppid; gboolean res; proc_stat_t *stat; g_return_val_if_fail (pid > 1, 0); ppid = -1; res = proc_stat_new_for_pid (pid, &stat, NULL); if (! res) { goto out; } ppid = proc_stat_get_ppid (stat); proc_stat_free (stat); out: return ppid; }