/* * Copyright (c) 2008-2010 Mike Massonnet * Copyright (c) 2006 Johannes Zellner * * 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. */ #include #include #include #include #include #include #include #include "task-manager.h" static gushort _cpu_count = 0; gboolean get_memory_usage (guint64 *memory_total, guint64 *memory_free, guint64 *memory_cache, guint64 *memory_buffers, guint64 *swap_total, guint64 *swap_free) { FILE *file; gchar buffer[1024]; gchar *filename = "/proc/meminfo"; gushort found = 0; if ((file = fopen (filename, "r")) == NULL) return FALSE; while (found < 6 && fgets (buffer, 1024, file) != NULL) { found += sscanf (buffer, "MemTotal:\t%u kB", memory_total); found += sscanf (buffer, "MemFree:\t%u kB", memory_free); found += sscanf (buffer, "Cached:\t%u kB", memory_cache); found += sscanf (buffer, "Buffers:\t%u kB", memory_buffers); found += sscanf (buffer, "SwapTotal:\t%u kB", swap_total); found += sscanf (buffer, "SwapFree:\t%u kB", swap_free); } fclose (file); *memory_total *= 1024; *memory_free *= 1024; *memory_cache *= 1024; *memory_buffers *= 1024; *swap_total *= 1024; *swap_free *= 1024; return TRUE; } gboolean get_cpu_usage (gushort *cpu_count, gfloat *cpu_user, gfloat *cpu_system) { FILE *file; gchar *filename = "/proc/stat"; gchar buffer[1024]; static gulong cur_jiffies_user = 0, old_jiffies_user = 0; static gulong cur_jiffies_system = 0, old_jiffies_system = 0; static gulong cur_jiffies = 0, old_jiffies = 0; gulong user, user_nice, system, idle; if ((file = fopen (filename, "r")) == NULL) return FALSE; fgets (buffer, 1024, file); sscanf (buffer, "cpu\t%u %u %u %u", &user, &user_nice, &system, &idle); if (_cpu_count == 0) { _cpu_count = 0; while (fgets (buffer, 1024, file) != NULL) { if (buffer[0] != 'c' && buffer[1] != 'p' && buffer[2] != 'u') break; _cpu_count += 1; } if (_cpu_count == 0) _cpu_count += 1; } fclose (file); old_jiffies_user = cur_jiffies_user; old_jiffies_system = cur_jiffies_system; old_jiffies = cur_jiffies; cur_jiffies_user = user + user_nice; cur_jiffies_system = system; cur_jiffies = cur_jiffies_user + cur_jiffies_system + idle; *cpu_user = *cpu_system = 0.0; if (cur_jiffies > old_jiffies) { *cpu_user = (cur_jiffies_user - old_jiffies_user) * 100 / (gdouble)(cur_jiffies - old_jiffies); *cpu_system = (cur_jiffies_system - old_jiffies_system) * 100 / (gdouble)(cur_jiffies - old_jiffies); } *cpu_count = _cpu_count; return TRUE; } static inline int get_pagesize () { static int pagesize = 0; if (pagesize == 0) { pagesize = sysconf (_SC_PAGESIZE); if (pagesize == 0) pagesize = 4096; } return pagesize; } static gboolean get_task_cmdline (Task *task) { FILE *file; gchar filename[96]; gint i; gchar c; snprintf (filename, 96, "/proc/%i/cmdline", task->pid); if ((file = fopen (filename, "r")) == NULL) return FALSE; /* Read full command byte per byte until EOF */ for (i = 0; (c = fgetc (file)) != EOF && i < sizeof (task->cmdline) - 1; i++) task->cmdline[i] = (c == '\0') ? ' ' : c; task->cmdline[i] = '\0'; if (task->cmdline[i-1] == ' ') task->cmdline[i-1] = '\0'; fclose (file); /* Kernel processes don't have a cmdline nor an exec path */ if (i == 0) { size_t len = strlen (task->name); g_strlcpy (&task->cmdline[1], task->name, len + 1); task->cmdline[0] = '['; task->cmdline[len+1] = ']'; task->cmdline[len+2] = '\0'; } return TRUE; } static void get_cpu_percent (guint pid, gulong jiffies_user, gfloat *cpu_user, gulong jiffies_system, gfloat *cpu_system) { static GHashTable *hash_cpu_user = NULL; static GHashTable *hash_cpu_system = NULL; gulong jiffies_user_old, jiffies_system_old; if (hash_cpu_user == NULL) { hash_cpu_user = g_hash_table_new (NULL, NULL); hash_cpu_system = g_hash_table_new (NULL, NULL); } jiffies_user_old = GPOINTER_TO_UINT (g_hash_table_lookup (hash_cpu_user, GUINT_TO_POINTER (pid))); jiffies_system_old = GPOINTER_TO_UINT (g_hash_table_lookup (hash_cpu_system, GUINT_TO_POINTER (pid))); g_hash_table_insert (hash_cpu_user, GUINT_TO_POINTER (pid), GUINT_TO_POINTER (jiffies_user)); g_hash_table_insert (hash_cpu_system, GUINT_TO_POINTER (pid), GUINT_TO_POINTER (jiffies_system)); if (jiffies_user < jiffies_user_old || jiffies_system < jiffies_system_old) return; if (_cpu_count > 0) { *cpu_user = (jiffies_user_old > 0) ? (jiffies_user - jiffies_user_old) / (gfloat)_cpu_count : 0; *cpu_system = (jiffies_system_old > 0) ? (jiffies_system - jiffies_system_old) / (gfloat)_cpu_count : 0; } else { *cpu_user = *cpu_system = 0; } } static gboolean get_task_details (guint pid, Task *task) { FILE *file; gchar filename[96]; gchar buffer[1024]; snprintf (filename, 96, "/proc/%d/stat", pid); if ((file = fopen (filename, "r")) == NULL) return FALSE; fgets (buffer, 1024, file); fclose (file); /* Scanning the short process name is unreliable with scanf when it contains * spaces, retrieve it manually and fill the buffer */ { gchar *p1, *po, *p2; guint i = 0; p1 = po = g_strstr_len (buffer, -1, "("); p2 = g_strrstr (buffer, ")"); while (po <= p2) { if (po > p1 && po < p2) { task->name[i++] = *po; task->name[i] = '\0'; } *po = 'x'; po++; } } /* Parse the stat file */ { gchar dummy[256]; gint idummy; gulong jiffies_user, jiffies_system; struct passwd *pw; struct stat sstat; sscanf(buffer, "%i %255s %1s %i %i %i %i %i %255s %255s %255s %255s %255s %i %i %i %i %i %i %i %i %i %i %i %255s %255s %255s %i %255s %255s %255s %255s %255s %255s %255s %255s %255s %255s %i %255s %255s", &task->pid, // processid dummy, // processname task->state, // processstate &task->ppid, // parentid &idummy, // processs groupid &idummy, // session id &idummy, // tty id &idummy, // tpgid the process group ID of the process running on tty of the process dummy, // flags dummy, // minflt minor faults the process has maid dummy, // cminflt dummy, // majflt dummy, // cmajflt &jiffies_user, // utime the number of jiffies that this process has scheduled in user mode &jiffies_system,// stime " system mode &idummy, // cutime " waited for children in user mode &idummy, // cstime " system mode &idummy, // priority (nice value + fifteen) &task->prio, // nice range from 19 to -19 &idummy, // hardcoded 0 &idummy, // itrealvalue time in jiffies to next SIGALRM send to this process &idummy, // starttime jiffies the process startet after system boot &task->vsz, // vsize in bytes &task->rss, // rss (number of pages in real memory) dummy, // rlim limit in bytes for rss dummy, // startcode dummy, // endcode &idummy, // startstack dummy, // kstkesp value of esp (stack pointer) dummy, // kstkeip value of EIP (instruction pointer) dummy, // signal. bitmap of pending signals dummy, // blocked: bitmap of blocked signals dummy, // sigignore: bitmap of ignored signals dummy, // sigcatch: bitmap of catched signals dummy, // wchan dummy, // nswap dummy, // cnswap dummy, // exit_signal &idummy, // CPU number last executed on dummy, dummy ); task->rss *= get_pagesize (); get_cpu_percent (task->pid, jiffies_user, &task->cpu_user, jiffies_system, &task->cpu_system); stat (filename, &sstat); pw = getpwuid (sstat.st_uid); task->uid = sstat.st_uid; g_strlcpy (task->uid_name, (pw != NULL) ? pw->pw_name : "nobody", sizeof (task->uid_name)); } /* Read the full command line */ if (!get_task_cmdline (task)) return FALSE; return TRUE; } gboolean get_task_list (GArray *task_list) { GDir *dir; const gchar *name; guint pid; Task task = { 0 }; if ((dir = g_dir_open ("/proc", 0, NULL)) == NULL) return FALSE; while ((name = g_dir_read_name(dir)) != NULL) { if ((pid = (guint)g_ascii_strtoull (name, NULL, 0)) > 0) { if (get_task_details (pid, &task)) { g_array_append_val (task_list, task); } } } g_dir_close (dir); return TRUE; } gboolean pid_is_sleeping (guint pid) { FILE *file; gchar filename[96]; gchar buffer[1024]; gchar state[2]; snprintf (filename, 96, "/proc/%i/status", pid); if ((file = fopen (filename, "r")) == NULL) return FALSE; while (fgets (buffer, 1024, file) != NULL) { if (sscanf (buffer, "State:\t%1s", state) > 0) break; } fclose (file); return (state[0] == 'T') ? TRUE : FALSE; }