netstar pushed a commit to branch master. http://git.enlightenment.org/apps/evisum.git/commit/?id=a6e45b02b7edb075342f5db063dd4182d122c718
commit a6e45b02b7edb075342f5db063dd4182d122c718 Author: Alastair Poole <nets...@gmail.com> Date: Mon May 17 16:30:30 2021 +0100 wip: wip wip wip wip wip wip wip wip? --- src/bin/next/filesystems.c | 129 ++++ src/bin/next/filesystems.h | 28 + src/bin/next/machine.h | 2 +- src/bin/next/meson.build | 4 + src/bin/next/process.c | 1499 ++++++++++++++++++++++++++++++++++++++++++++ src/bin/next/process.h | 119 ++++ 6 files changed, 1780 insertions(+), 1 deletion(-) diff --git a/src/bin/next/filesystems.c b/src/bin/next/filesystems.c new file mode 100644 index 0000000..35b6499 --- /dev/null +++ b/src/bin/next/filesystems.c @@ -0,0 +1,129 @@ +#include "filesystems.h" + +#include <stdio.h> +#include <limits.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#if defined(__linux__) + #include <sys/vfs.h> +#endif + +#if defined(__APPLE__) && defined(__MACH__) +# define __MacOS__ +#endif + +#if defined (__MacOS__) || defined(__FreeBSD__) || defined(__DragonFly__) +# include <sys/types.h> +# include <sys/sysctl.h> +# include <sys/param.h> +# include <sys/ucred.h> +# include <sys/mount.h> +#endif + +#if defined(__OpenBSD__) || defined(__NetBSD__) +# include <sys/param.h> +# include <sys/sysctl.h> +# include <sys/mount.h> +#endif + +Eina_List * +file_system_info_all_get(void) +{ + Eina_List *list = NULL; +# if defined(__linux__) + FILE *f; + char *dev, *mount, *type_name, *cp, *end; + struct statfs stats; + char buf[4096]; + + f = fopen("/proc/mounts", "r"); + if (!f) return NULL; + + while ((fgets(buf, sizeof(buf), f)) != NULL) + { + mount = strchr(buf, ' ') + 1; + if (!mount) continue; + dev = strndup(buf, mount - buf); + cp = strchr(mount, ' '); + if (!cp) continue; + end = cp; + *end = '\0'; + cp++; + end = strchr(cp, ' '); + if (!end) continue; + type_name = strndup(cp, end - cp); + + if ((strstr(cp, "nodev")) || (statfs(mount, &stats) < 0) || + (stats.f_blocks == 0 && stats.f_bfree == 0)) + { + free(dev); + free(type_name); + continue; + } + + File_System *fs = calloc(1, sizeof(File_System)); + fs->mount = strdup(mount); + fs->path = dev; + fs->type_name = type_name; + fs->usage.total = stats.f_bsize * stats.f_blocks; + fs->usage.used = fs->usage.total - (stats.f_bsize * stats.f_bfree); + list = eina_list_append(list, fs); + } + fclose(f); +# else + struct statfs *mounts; + int i, count; + + count = getmntinfo(&mounts, MNT_WAIT); + for (i = 0; i < count; i++) + { + File_System *fs = calloc(1, sizeof(File_System)); + fs->mount = strdup(mounts[i].f_mntonname); + fs->path = strdup(mounts[i].f_mntfromname); +#if defined(__OpenBSD__) +#else + fs->type = mounts[i].f_type; +#endif + fs->type_name = strdup(mounts[i].f_fstypename); + fs->usage.total = mounts[i].f_bsize * mounts[i].f_blocks; + fs->usage.used = fs->usage.total - (mounts[i].f_bsize * mounts[i].f_bfree); + + list = eina_list_append(list, fs); + } +# endif + return list; +} + +void +file_system_info_free(File_System *fs) +{ + free(fs->mount); + free(fs->path); + if (fs->type_name) + free(fs->type_name); + free(fs); +} + +Eina_Bool +file_system_in_use(const char *name) +{ + Eina_List *list; + File_System *fs; + Eina_Bool mounted = 0; + + if (!name) return 0; + + list = file_system_info_all_get(); + EINA_LIST_FREE(list, fs) + { + if (fs->type_name && (!strcasecmp(fs->type_name, name))) + mounted = 1; + + file_system_info_free(fs); + } + + return mounted; +} + diff --git a/src/bin/next/filesystems.h b/src/bin/next/filesystems.h new file mode 100644 index 0000000..897c10b --- /dev/null +++ b/src/bin/next/filesystems.h @@ -0,0 +1,28 @@ +#ifndef __FILESYSTEMS_H__ +#define __FILESYSTEMS_H__ + +#include <Eina.h> + +typedef struct { + unsigned long long total; + unsigned long long used; +} _Usage; + +typedef struct _File_System { + char *path; + char *mount; + char *type_name; + unsigned int type; + _Usage usage; +} File_System; + +Eina_List * +file_system_info_all_get(void); + +void +file_system_info_free(File_System *fs); + +Eina_Bool +file_system_in_use(const char *name); + +#endif diff --git a/src/bin/next/machine.h b/src/bin/next/machine.h index 597ad58..d43166a 100644 --- a/src/bin/next/machine.h +++ b/src/bin/next/machine.h @@ -136,7 +136,7 @@ int cores_frequency(void); int -core_id_frequency(int d); +core_id_frequency(int id); int core_id_temperature(int id); diff --git a/src/bin/next/meson.build b/src/bin/next/meson.build index 6c0b60f..c0db5f0 100644 --- a/src/bin/next/meson.build +++ b/src/bin/next/meson.build @@ -1,4 +1,8 @@ src_watch += files([ 'machine.c', 'machine.h', + 'process.c', + 'process.h', + 'filesystems.c', + 'filesystems.h', ]) diff --git a/src/bin/next/process.c b/src/bin/next/process.c new file mode 100644 index 0000000..4d0ec14 --- /dev/null +++ b/src/bin/next/process.c @@ -0,0 +1,1499 @@ +#if defined(__MACH__) && defined(__APPLE__) +# define __MacOS__ +#endif + +#include "ui/gettext.h" +#define _(STR) gettext(STR) + +#if defined(__MacOS__) || defined(__FreeBSD__) || defined(__DragonFly__) || defined(__OpenBSD__) +# include <sys/types.h> +# include <sys/sysctl.h> +# include <sys/user.h> +# include <sys/proc.h> +# include <libgen.h> +#endif + +#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__OpenBSD__) +# include <libgen.h> +# include <unistd.h> +# include <fcntl.h> +# include <kvm.h> +# include <limits.h> +# include <sys/proc.h> +# include <sys/param.h> +# include <sys/resource.h> +#endif + +#if defined(__FreeBSD__) +# define _WANT_FILE +# include <sys/file.h> +# include <sys/filedesc.h> +#endif + +#if defined(__MacOS__) +# include <libproc.h> +# include <sys/proc_info.h> +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <ctype.h> +#include <unistd.h> +#include <limits.h> + +#include "process.h" +#include <Eina.h> +#include <Ecore.h> +#include <Ecore_File.h> + +#if defined(__linux__) && !defined(PF_KTHREAD) +# define PF_KTHREAD 0x00200000 +#endif + +#include "macros.h" + +static Eina_Bool _show_kthreads = 1; + +void +proc_info_kthreads_show_set(Eina_Bool enabled) +{ + _show_kthreads = enabled; +} + +Eina_Bool +proc_info_kthreads_show_get(void) +{ + return _show_kthreads; +} + +static const char * _states[128]; + +static void +_states_init(void) +{ +#if defined(__linux__) + _states['D'] = _("dsleep"); + _states['I'] = _("idle"); + _states['R'] = _("running"); + _states['S'] = _("sleeping"); + _states['T'] = _("stopped"); + _states['X'] = _("dead"); + _states['Z'] = _("zombie"); +#else + _states[SIDL] = _("idle"); + _states[SRUN] = _("running"); + _states[SSLEEP] = _("sleeping"); + _states[SSTOP] = _("stopped"); +#if !defined(__MacOS__) +#if !defined(__OpenBSD__) + _states[SWAIT] = _("wait"); + _states[SLOCK] = _("lock"); + _states[SZOMB] = _("zombie"); +#endif +#if defined(__OpenBSD__) + _states[SDEAD] = _("zombie"); + _states[SONPROC] = _("running"); +#endif +#endif +#endif +} + +static const char * +_process_state_name(char state) +{ + static int init = 0; + if (!init) + { + _states_init(); + init = 1; + } + return _states[toupper(state)]; +} + +#if defined(__linux__) + +static unsigned long +_parse_line(const char *line) +{ + char *p, *tok; + + p = strchr(line, ':') + 1; + while (isspace(*p)) + p++; + tok = strtok(p, " "); + + return atol(tok); +} + +static void +_mem_size(Proc_Info *proc) +{ + FILE *f; + char buf[4096]; + unsigned int dummy, size, shared, resident, data, text; + static int pagesize = 0; + + if (!pagesize) pagesize = getpagesize(); + + snprintf(buf, sizeof(buf), "/proc/%d/statm", proc->pid); + f = fopen(buf, "r"); + if (!f) return; + + if (fgets(buf, sizeof(buf), f)) + { + if (sscanf(buf, "%u %u %u %u %u %u %u", + &size, &resident, &shared, &text, + &dummy, &data, &dummy) == 7) + { + proc->mem_rss = MEMSIZE(resident) * MEMSIZE(pagesize); + proc->mem_shared = MEMSIZE(shared) * MEMSIZE(pagesize); + proc->mem_size = proc->mem_rss - proc->mem_shared; + proc->mem_virt = MEMSIZE(size) * MEMSIZE(pagesize); + } + } + + fclose(f); +} + +static void +_cmd_args(Proc_Info *p, char *name, size_t len) +{ + char buf[8192]; + int pid = p->pid; + + snprintf(buf, sizeof(buf), "/proc/%d/exe", pid); + char *link = ecore_file_readlink(buf); + if (link) + { + snprintf(name, len, "%s", ecore_file_file_get(link)); + free(link); + } + + snprintf(buf, sizeof(buf), "/proc/%d/cmdline", pid); + FILE *f = fopen(buf, "r"); + if (f) + { + if (fgets(buf, sizeof(buf), f)) + { + Eina_Strbuf *b = eina_strbuf_new(); + const char *n; + + if (ecore_file_exists(buf)) + snprintf(name, len, "%s", ecore_file_file_get(buf)); + + n = buf; + while (n && *n && (*n + 1)) + { + eina_strbuf_append(b, n); + n = strchr(n, '\0') + 1; + if (n && *n && (*n + 1)) eina_strbuf_append(b, " "); + } + p->arguments = eina_strbuf_release(b); + } + fclose(f); + } + + char *end = strchr(name, ' '); + if (end) *end = '\0'; + + p->command = strdup(name); +} + +static int +_uid(int pid) +{ + FILE *f; + char buf[4096]; + int uid = 0; + + snprintf(buf, sizeof(buf),"/proc/%d/status", pid); + f = fopen(buf, "r"); + if (!f) return -1; + + while ((fgets(buf, sizeof(buf), f)) != NULL) + { + if (!strncmp(buf, "Uid:", 4)) + { + uid = _parse_line(buf); + break; + } + } + + fclose(f); + + return uid; +} + +static int64_t +_boot_time(void) +{ + FILE *f; + int64_t boot_time; + char buf[4096]; + double uptime = 0.0; + + f = fopen("/proc/uptime", "r"); + if (!f) return 0; + + if (fgets(buf, sizeof(buf), f)) + sscanf(buf, "%lf", &uptime); + else boot_time = 0; + + fclose(f); + + if (uptime > 0.0) + boot_time = time(NULL) - (time_t) uptime; + + return boot_time; +} + +typedef struct +{ + int pid, ppid, utime, stime, cutime, cstime; + int psr, pri, nice, numthreads; + long long int start_time, run_time; + char state; + unsigned int mem_rss, flags; + unsigned long mem_virt; + char name[1024]; +} Stat; + +static Eina_Bool +_stat(const char *path, Stat *st) +{ + FILE *f; + char line[4096]; + char name[1024]; + int dummy, len = 0; + static long tck = 0; + static int64_t boot_time = 0; + + if (!boot_time) boot_time = _boot_time(); + + memset(st, 0, sizeof(Stat)); + + f = fopen(path, "r"); + if (!f) return 0; + + if (fgets(line, sizeof(line), f)) + { + + len = sscanf(line, "%d %s %c %d %d %d %d %d %u %u %u %u %u %d %d %d" + " %d %d %d %u %u %lld %lu %u %u %u %u %u %u %u %d %d %d %d %u" + " %d %d %d %d %d %d %d %d %d", &dummy, name, + &st->state, &st->ppid, &dummy, &dummy, &dummy, &dummy, &st->flags, + &dummy, &dummy, &dummy, &dummy, &st->utime, &st->stime, &st->cutime, + &st->cstime, &st->pri, &st->nice, &st->numthreads, &dummy, &st->start_time, + &st->mem_virt, &st->mem_rss, &dummy, &dummy, &dummy, &dummy, &dummy, + &dummy, &dummy, &dummy, &dummy, &dummy, &dummy, &dummy, + &dummy, &dummy, &st->psr, &dummy, &dummy, &dummy, &dummy, &dummy); + } + fclose(f); + + if (len != 44) return 0; + + len = strlen(name); + if (len) + { + name[len-1] = '\0'; + snprintf(st->name, sizeof(st->name), "%s", &name[1]); + } + + if (!tck) tck = sysconf(_SC_CLK_TCK); + + st->start_time /= tck; + st->start_time += boot_time; + st->run_time = (st->utime + st->stime) / tck; + + return 1; +} + +static int +_n_files(Proc_Info *p) +{ + char buf[256]; + Eina_List *files; + char *f; + + snprintf(buf, sizeof(buf), "/proc/%d/fd", p->pid); + + files = ecore_file_ls(buf); + EINA_LIST_FREE(files, f) + { + p->numfiles++; + free(f); + } + return p->numfiles; +} + +static Eina_List * +_process_list_linux_get(void) +{ + Eina_List *files, *list; + const char *state; + char *n; + char buf[4096]; + Stat st; + + list = NULL; + + files = ecore_file_ls("/proc"); + EINA_LIST_FREE(files, n) + { + int pid = atoi(n); + free(n); + + if (!pid) continue; + + snprintf(buf, sizeof(buf), "/proc/%d/stat", pid); + if (!_stat(buf, &st)) + continue; + + if (st.flags & PF_KTHREAD && !proc_info_kthreads_show_get()) + continue; + + Proc_Info *p = calloc(1, sizeof(Proc_Info)); + if (!p) return NULL; + + p->pid = pid; + p->ppid = st.ppid; + p->uid = _uid(pid); + p->cpu_id = st.psr; + p->start = st.start_time; + p->run_time = st.run_time; + state = _process_state_name(st.state); + snprintf(p->state, sizeof(p->state), "%s", state); + p->cpu_time = st.utime + st.stime; + p->nice = st.nice; + p->priority = st.pri; + p->numthreads = st.numthreads; + p->numfiles = _n_files(p); + if (st.flags & PF_KTHREAD) + p->is_kernel = 1; + _mem_size(p); + _cmd_args(p, st.name, sizeof(st.name)); + + list = eina_list_append(list, p); + } + + return list; +} + +static void +_proc_thread_info(Proc_Info *p) +{ + Eina_List *files; + const char *state; + char *n; + char buf[4096]; + Stat st; + + snprintf(buf, sizeof(buf), "/proc/%d/task", p->pid); + files = ecore_file_ls(buf); + EINA_LIST_FREE(files, n) + { + int tid = atoi(n); + free(n); + snprintf(buf, sizeof(buf), "/proc/%d/task/%d/stat", p->pid, tid); + if (!_stat(buf, &st)) + continue; + + Proc_Info *t = calloc(1, sizeof(Proc_Info)); + if (!t) continue; + t->cpu_id = st.psr; + state = _process_state_name(st.state); + snprintf(t->state, sizeof(t->state), "%s", state); + t->cpu_time = st.utime + st.stime; + t->nice = st.nice; + t->priority = st.pri; + t->numthreads = st.numthreads; + t->mem_virt = st.mem_virt; + t->mem_rss = st.mem_rss; + + t->tid = tid; + t->thread_name = strdup(st.name); + + p->threads = eina_list_append(p->threads, t); + } +} + +Proc_Info * +proc_info_by_pid(int pid) +{ + const char *state; + Stat st; + char buf[4096]; + + snprintf(buf, sizeof(buf), "/proc/%d/stat", pid); + if (!_stat(buf, &st)) + return NULL; + + Proc_Info *p = calloc(1, sizeof(Proc_Info)); + if (!p) return NULL; + + p->pid = pid; + p->ppid = st.ppid; + p->uid = _uid(pid); + p->cpu_id = st.psr; + p->start = st.start_time; + p->run_time = st.run_time; + state = _process_state_name(st.state); + snprintf(p->state, sizeof(p->state), "%s", state); + p->cpu_time = st.utime + st.stime; + p->priority = st.pri; + p->nice = st.nice; + p->numthreads = st.numthreads; + p->numfiles = _n_files(p); + if (st.flags & PF_KTHREAD) p->is_kernel = 1; + _mem_size(p); + _cmd_args(p, st.name, sizeof(st.name)); + + _proc_thread_info(p); + + return p; +} + +#endif + +#if defined(__OpenBSD__) + +static void +_proc_get(Proc_Info *p, struct kinfo_proc *kp) +{ + static int pagesize = 0; + const char *state; + + if (!pagesize) pagesize = getpagesize(); + + p->pid = kp->p_pid; + p->ppid = kp->p_ppid; + p->uid = kp->p_uid; + p->cpu_id = kp->p_cpuid; + p->start = kp->p_ustart_sec; + p->run_time = kp->p_uutime_sec + kp->p_ustime_sec + + (kp->p_uutime_usec / 1000000) + (kp->p_ustime_usec / 1000000); + + state = _process_state_name(kp->p_stat); + snprintf(p->state, sizeof(p->state), "%s", state); + snprintf(p->wchan, sizeof(p->wchan), "%s", kp->p_wmesg); + p->cpu_time = kp->p_uticks + kp->p_sticks + kp->p_iticks; + p->mem_virt = p->mem_size = (MEMSIZE(kp->p_vm_tsize) * MEMSIZE(pagesize)) + + (MEMSIZE(kp->p_vm_dsize) * MEMSIZE(pagesize)) + (MEMSIZE(kp->p_vm_ssize) * MEMSIZE(pagesize)); + p->mem_rss = MEMSIZE(kp->p_vm_rssize) * MEMSIZE(pagesize); + p->priority = kp->p_priority - PZERO; + p->nice = kp->p_nice - NZERO; + p->tid = kp->p_tid; +} + +static void +_kvm_get(Proc_Info *p, kvm_t *kern, struct kinfo_proc *kp) +{ + char **args; + char name[4096]; + + if ((args = kvm_getargv(kern, kp, sizeof(name)-1))) + { + Eina_Strbuf *buf = eina_strbuf_new(); + for (int i = 0; args[i]; i++) + { + eina_strbuf_append(buf, args[i]); + if (args[i + 1]) + eina_strbuf_append(buf, " "); + } + p->arguments = eina_strbuf_string_steal(buf); + eina_strbuf_free(buf); + + if (args[0] && ecore_file_exists(args[0])) + p->command = strdup(ecore_file_file_get(args[0])); + } + struct kinfo_file *kf; + int n; + + if ((kf = kvm_getfiles(kern, KERN_FILE_BYPID, -1, sizeof(struct kinfo_file), &n))) + { + for (int i = 0; i < n; i++) + { + if (kf[i].p_pid == kp->p_pid) + { + if (kf[i].fd_fd >= 0) p->numfiles++; + } + } + } + + if (!p->command) + p->command = strdup(kp->p_comm); +} + +Proc_Info * +proc_info_by_pid(int pid) +{ + struct kinfo_proc *kp, *kpt; + kvm_t *kern; + char errbuf[_POSIX2_LINE_MAX]; + int count, pid_count; + + kern = kvm_openfiles(NULL, NULL, NULL, KVM_NO_FILES, errbuf); + if (!kern) return NULL; + + kp = kvm_getprocs(kern, KERN_PROC_PID, pid, sizeof(*kp), &count); + if (!kp) return NULL; + + if (count == 0) return NULL; + + Proc_Info *p = calloc(1, sizeof(Proc_Info)); + if (!p) return NULL; + + _proc_get(p, kp); + _kvm_get(p, kern, kp); + + kp = kvm_getprocs(kern, KERN_PROC_SHOW_THREADS, 0, sizeof(*kp), &pid_count); + + for (int i = 0; i < pid_count; i++) + { + if (kp[i].p_pid != p->pid) continue; + + kpt = &kp[i]; + + if (kpt->p_tid <= 0) continue; + + Proc_Info *t = calloc(1, sizeof(Proc_Info)); + if (!t) continue; + + _proc_get(t, kpt); + + t->tid = kpt->p_tid; + t->thread_name = strdup(kpt->p_comm); + + p->threads = eina_list_append(p->threads, t); + } + + p->numthreads = eina_list_count(p->threads); + + kvm_close(kern); + + return p; +} + +static Eina_List * +_process_list_openbsd_get(void) +{ + struct kinfo_proc *kps, *kp; + Proc_Info *p; + char errbuf[4096]; + kvm_t *kern; + int pid_count; + Eina_List *list = NULL; + + kern = kvm_openfiles(NULL, NULL, NULL, KVM_NO_FILES, errbuf); + if (!kern) return NULL; + + kps = kvm_getprocs(kern, KERN_PROC_ALL, 0, sizeof(*kps), &pid_count); + if (!kps) return NULL; + + for (int i = 0; i < pid_count; i++) + { + p = calloc(1, sizeof(Proc_Info)); + if (!p) return NULL; + + kp = &kps[i]; + + Proc_Info *p = proc_info_by_pid(kp->p_pid); + if (p) + list = eina_list_append(list, p); + } + + kvm_close(kern); + + return list; +} + +#endif + +#if defined(__MacOS__) +static void +_cmd_get(Proc_Info *p, int pid) +{ + char *cp, *args, **argv; + int mib[3], argmax, argc; + size_t size; + + mib[0] = CTL_KERN; + mib[1] = KERN_ARGMAX; + + size = sizeof(argmax); + + if (sysctl(mib, 2, &argmax, &size, NULL, 0) == -1) return; + + mib[0] = CTL_KERN; + mib[1] = KERN_PROCARGS2; + mib[2] = pid; + + size = (size_t) argmax; + args = malloc(argmax); + if (!args) return; + + /* See libtop.c (top) for the origin of this comment, which is necessary as + * there is little other documentation...thanks Apple. + * + * Make a sysctl() call to get the raw argument space of the process. + * The layout is documented in start.s, which is part of the Csu + * project. In summary, it looks like: + * + * /---------------\ 0x00000000 + * : : + * : : + * |---------------| + * | argc | + * |---------------| + * | arg[0] | + * |---------------| + * : : + * : : + * |---------------| + * | arg[argc - 1] | + * |---------------| + * | 0 | + * |---------------| + * | env[0] | + * |---------------| + * : : + * : : + * |---------------| + * | env[n] | + * |---------------| + * | 0 | + * |---------------| <-- Beginning of data returned by sysctl() is here. + * | argc | + * |---------------| + * | exec_path | + * |:::::::::::::::| + * | | + * | String area. | + * | | + * |---------------| <-- Top of stack. + * : : + * : : + * \---------------/ 0xffffffff + */ + + if (sysctl(mib, 3, args, &size, NULL, 0) == -1) return; + + memcpy(&argc, args, sizeof(argc)); + cp = args + sizeof(argc); + + /* Skip exec path */ + for (;cp < &args[size]; cp++) + { + if (*cp == '\0') break; + } + + if (cp == &args[size]) return; + + /* Skip any padded NULLs. */ + for (;cp < &args[size]; cp++) + { + if (*cp == '\0') break; + } + + if (cp == &args[size]) return; + + argv = malloc(1 + argc * sizeof(char *)); + if (!argv) return; + + int i = 0; + argv[i] = cp; + + for (cp = args + sizeof(int); cp < &args[size] && i < argc; cp++) + { + if (*cp == '\0') + { + while (*cp == '\0') cp++; + argv[i++] = cp; + } + } + + if (i == 0) i++; + + argv[i] = NULL; + + p->command = strdup(basename(argv[0])); + + Eina_Strbuf *buf = eina_strbuf_new(); + + for (i = 0; i < argc; i++) + eina_strbuf_append_printf(buf, "%s ", argv[i]); + + if (argc > 0) + p->arguments = eina_strbuf_release(buf); + else + eina_strbuf_free(buf); + + free(args); + free(argv); +} + +static Proc_Info * +_proc_pidinfo(size_t pid) +{ + const char *state; + struct proc_taskallinfo taskinfo; + int size = proc_pidinfo(pid, PROC_PIDTASKALLINFO, 0, &taskinfo, sizeof(taskinfo)); + if (size != sizeof(taskinfo)) return NULL; + + Proc_Info *p = calloc(1, sizeof(Proc_Info)); + if (!p) return NULL; + + p->pid = pid; + p->ppid = taskinfo.pbsd.pbi_ppid; + p->uid = taskinfo.pbsd.pbi_uid; + p->cpu_id = -1; + p->cpu_time = taskinfo.ptinfo.pti_total_user + + taskinfo.ptinfo.pti_total_system; + p->cpu_time /= 10000000; + p->start = taskinfo.pbsd.pbi_start_tvsec; + state = _process_state_name(taskinfo.pbsd.pbi_status); + snprintf(p->state, sizeof(p->state), "%s", state); + p->mem_size = p->mem_virt = taskinfo.ptinfo.pti_virtual_size; + p->mem_rss = taskinfo.ptinfo.pti_resident_size; + p->priority = taskinfo.ptinfo.pti_priority; + p->nice = taskinfo.pbsd.pbi_nice; + p->numthreads = taskinfo.ptinfo.pti_threadnum; + _cmd_get(p, pid); + + return p; +} + +static Eina_List * +_process_list_macos_fallback_get(void) +{ + Eina_List *list = NULL; + + for (int i = 1; i <= PID_MAX; i++) + { + Proc_Info *p = _proc_pidinfo(i); + if (p) + list = eina_list_append(list, p); + } + + return list; +} + +static Eina_List * +_process_list_macos_get(void) +{ + Eina_List *list = NULL; + pid_t *pids = NULL; + int size, pid_count; + + size = proc_listpids(PROC_ALL_PIDS, 0, NULL, 0); + if (size == -1) + return _process_list_macos_fallback_get(); + + pids = malloc(size * sizeof(pid_t)); + if (!pids) return NULL; + + size = proc_listpids(PROC_ALL_PIDS, 0, pids, size * sizeof(pid_t)); + if (size == -1) + { + free(pids); + return _process_list_macos_fallback_get(); + } + + pid_count = size / sizeof(pid_t); + for (int i = 0; i < pid_count; i++) + { + pid_t pid = pids[i]; + Proc_Info *p = _proc_pidinfo(pid); + if (p) + list = eina_list_append(list, p); + } + + free(pids); + + return list; +} + +Proc_Info * +proc_info_by_pid(int pid) +{ + const char *state; + struct proc_taskallinfo taskinfo; + struct proc_workqueueinfo workqueue; + size_t size; + + size = proc_pidinfo(pid, PROC_PIDTASKALLINFO, 0, &taskinfo, sizeof(taskinfo)); + if (size != sizeof(taskinfo)) + return NULL; + + size = proc_pidinfo(pid, PROC_PIDWORKQUEUEINFO, 0, &workqueue, sizeof(workqueue)); + if (size != sizeof(workqueue)) + memset(&workqueue, 0, sizeof(struct proc_workqueueinfo)); + + Proc_Info *p = calloc(1, sizeof(Proc_Info)); + if (!p) return NULL; + + p->pid = pid; + p->uid = taskinfo.pbsd.pbi_uid; + p->ppid = taskinfo.pbsd.pbi_ppid; + p->cpu_id = workqueue.pwq_nthreads; + p->cpu_time = taskinfo.ptinfo.pti_total_user + + taskinfo.ptinfo.pti_total_system; + p->cpu_time /= 10000000; + p->start = taskinfo.pbsd.pbi_start_tvsec; + state = _process_state_name(taskinfo.pbsd.pbi_status); + snprintf(p->state, sizeof(p->state), "%s", state); + p->mem_size = p->mem_virt = taskinfo.ptinfo.pti_virtual_size; + p->mem_rss = taskinfo.ptinfo.pti_resident_size; + p->priority = taskinfo.ptinfo.pti_priority; + p->nice = taskinfo.pbsd.pbi_nice; + p->numthreads = taskinfo.ptinfo.pti_threadnum; + _cmd_get(p, pid); + + return p; +} + +#endif + +#if defined(__FreeBSD__) || defined(__DragonFly__) + +static int +_pid_max(void) +{ + size_t len; + static int pid_max = 0; + + if (pid_max != 0) return pid_max; + + len = sizeof(pid_max); + if (sysctlbyname("kern.pid_max", &pid_max, &len, NULL, 0) == -1) + { +#if defined(__FreeBSD__) + pid_max = 99999; +#elif defined(__DragonFly__) + pid_max = 999999; +#else + pid_max = PID_MAX; +#endif + } + + return pid_max; +} + +static void +_kvm_get(Proc_Info *p, struct kinfo_proc *kp) +{ + kvm_t * kern; + char name[4096]; + Eina_Bool have_command = 0; + + kern = kvm_open(NULL, "/dev/mem", NULL, O_RDONLY, "kvm_open"); + if (!kern) goto nokvm; + char **args; + if ((args = kvm_getargv(kern, kp, sizeof(name)-1)) && (args[0])) + { + char *base = strdup(args[0]); + if (base) + { + char *spc = strchr(base, ' '); + if (spc) *spc = '\0'; + + if (ecore_file_exists(base)) + { + snprintf(name, sizeof(name), "%s", basename(base)); + have_command = 1; + } + free(base); + } + Eina_Strbuf *buf = eina_strbuf_new(); + for (int i = 0; args[i] != NULL; i++) + { + eina_strbuf_append(buf, args[i]); + if (args[i + 1]) + eina_strbuf_append(buf, " "); + } + p->arguments = eina_strbuf_string_steal(buf); + eina_strbuf_free(buf); + } + + struct filedesc filed; + struct fdescenttbl *fdt; + unsigned int n; + + if (!kvm_read(kern, (unsigned long)kp->ki_fd, &filed, sizeof(filed))) + goto kvmerror; + + if (!kvm_read(kern, (unsigned long)filed.fd_files, &n, sizeof(n))) + goto kvmerror; + + unsigned int size = sizeof(*fdt) + n * sizeof(struct filedescent); + fdt = malloc(size); + if (fdt) + { + if (kvm_read(kern, (unsigned long)filed.fd_files, fdt, size)) + { + for (int i = 0; i < n; i++) + { + if (!fdt->fdt_ofiles[i].fde_file) continue; + p->numfiles++; + } + } + free(fdt); + } + +kvmerror: + if (kern) + kvm_close(kern); +nokvm: + if (!have_command) + snprintf(name, sizeof(name), "%s", kp->ki_comm); + + p->command = strdup(name); +} + +static Proc_Info * +_proc_thread_info(struct kinfo_proc *kp, Eina_Bool is_thread) +{ + struct rusage *usage; + const char *state; + Proc_Info *p; + static int pagesize = 0; + + if (!pagesize) pagesize = getpagesize(); + + p = calloc(1, sizeof(Proc_Info)); + if (!p) return NULL; + + p->pid = kp->ki_pid; + p->ppid = kp->ki_ppid; + p->uid = kp->ki_uid; + + if (!is_thread) + _kvm_get(p, kp); + + p->cpu_id = kp->ki_oncpu; + if (p->cpu_id == -1) + p->cpu_id = kp->ki_lastcpu; + + usage = &kp->ki_rusage; + + p->cpu_time = ((usage->ru_utime.tv_sec * 1000000) + usage->ru_utime.tv_usec + + (usage->ru_stime.tv_sec * 1000000) + usage->ru_stime.tv_usec) / 10000; + p->run_time = (kp->ki_runtime + 500000) / 1000000; + state = _process_state_name(kp->ki_stat); + snprintf(p->state, sizeof(p->state), "%s", state); + snprintf(p->wchan, sizeof(p->wchan), "%s", kp->ki_wmesg); + p->mem_virt = kp->ki_size; + p->mem_rss = MEMSIZE(kp->ki_rssize) * MEMSIZE(pagesize); + p->start = kp->ki_start.tv_sec; + p->mem_size = p->mem_virt; + p->nice = kp->ki_nice - NZERO; + p->priority = kp->ki_pri.pri_level - PZERO; + p->numthreads = kp->ki_numthreads; + + p->tid = kp->ki_tid; + p->thread_name = strdup(kp->ki_tdname); + if (kp->ki_flag & P_KPROC) p->is_kernel = 1; + + return p; +} + +static Eina_List * +_process_list_freebsd_fallback_get(void) +{ + Eina_List *list; + struct kinfo_proc kp; + int mib[4]; + size_t len; + static int pid_max; + + pid_max = _pid_max(); + + list = NULL; + + len = sizeof(int); + if (sysctlnametomib("kern.proc.pid", mib, &len) == -1) + return NULL; + + for (int i = 1; i <= pid_max; i++) + { + mib[3] = i; + len = sizeof(kp); + if (sysctl(mib, 4, &kp, &len, NULL, 0) == -1) + continue; + + if (kp.ki_flag & P_KPROC && !proc_info_kthreads_show_get()) + continue; + + Proc_Info *p = _proc_thread_info(&kp, 0); + if (p) + list = eina_list_append(list, p); + } + + return list; +} + +static Eina_List * +_process_list_freebsd_get(void) +{ + kvm_t *kern; + Eina_List *list = NULL; + struct kinfo_proc *kps, *kp; + char errbuf[_POSIX2_LINE_MAX]; + int pid_count; + + kern = kvm_openfiles(NULL, NULL, NULL, O_RDONLY, errbuf); + if (!kern) + return _process_list_freebsd_fallback_get(); + + kps = kvm_getprocs(kern, KERN_PROC_PROC, 0, &pid_count); + if (!kps) + { + kvm_close(kern); + return _process_list_freebsd_fallback_get(); + } + + for (int i = 0; i < pid_count; i++) + { + if (kps[i].ki_flag & P_KPROC && !proc_info_kthreads_show_get()) + continue; + + kp = &kps[i]; + + Proc_Info *p = _proc_thread_info(kp, 0); + if (p) + list = eina_list_append(list, p); + } + + kvm_close(kern); + + return list; +} + +static Proc_Info * +_proc_info_by_pid_fallback(int pid) +{ + struct kinfo_proc kp; + int mib[4]; + size_t len; + + len = sizeof(int); + if (sysctlnametomib("kern.proc.pid", mib, &len) == -1) + return NULL; + + mib[3] = pid; + + len = sizeof(kp); + if (sysctl(mib, 4, &kp, &len, NULL, 0) == -1) + return NULL; + + Proc_Info *p = _proc_thread_info(&kp, 0); + + return p; +} + +Proc_Info * +proc_info_by_pid(int pid) +{ + kvm_t *kern; + struct kinfo_proc *kps, *kp; + char errbuf[_POSIX2_LINE_MAX]; + int pid_count; + + kern = kvm_openfiles(NULL, NULL, NULL, O_RDONLY, errbuf); + if (!kern) + return _proc_info_by_pid_fallback(pid); + + kps = kvm_getprocs(kern, KERN_PROC_ALL, 0, &pid_count); + if (!kps) + { + kvm_close(kern); + return _proc_info_by_pid_fallback(pid); + } + + Proc_Info *p = NULL; + + // XXX: run_time does not include interrupts. + + for (int i = 0; i < pid_count; i++) + { + if (kps[i].ki_flag & P_KPROC && !proc_info_kthreads_show_get()) + continue; + if (kps[i].ki_pid != pid) + continue; + + kp = &kps[i]; + Proc_Info *t = _proc_thread_info(kp, 1); + if (!p) + { + p = _proc_thread_info(kp, 0); + p->cpu_time = 0; + } + + p->cpu_time += t->cpu_time; + p->threads = eina_list_append(p->threads, t); + } + + kvm_close(kern); + + if (!p) return _proc_info_by_pid_fallback(pid); + + return p; +} +#endif + +void +proc_info_free(Proc_Info *proc) +{ + Proc_Info *t; + + if (!proc) return; + + EINA_LIST_FREE(proc->threads, t) + proc_info_free(t); + + if (proc->command) + free(proc->command); + if (proc->arguments) + free(proc->arguments); + if (proc->thread_name) + free(proc->thread_name); + + free(proc); +} + +Eina_List * +proc_info_all_get(void) +{ + Eina_List *processes; + +#if defined(__linux__) + processes = _process_list_linux_get(); +#elif defined(__FreeBSD__) || defined(__DragonFly__) + processes = _process_list_freebsd_get(); +#elif defined(__MacOS__) + processes = _process_list_macos_get(); +#elif defined(__OpenBSD__) + processes = _process_list_openbsd_get(); +#else + processes = NULL; +#endif + + return processes; +} + +static Eina_Bool +_child_add(Eina_List *parents, Proc_Info *child) +{ + Eina_List *l; + Proc_Info *parent; + + EINA_LIST_FOREACH(parents, l, parent) + { + if (parent->pid == child->ppid) + { + parent->children = eina_list_append(parent->children, child); + return 1; + } + } + + return 0; +} + +Eina_List * +proc_info_all_children_get() +{ + Proc_Info *proc; + Eina_List *l; + Eina_List *procs; + + procs = proc_info_all_get(); + + EINA_LIST_FOREACH(procs, l, proc) + { + int ok =_child_add(procs, proc); + (void) ok; + } + + return procs; +} + +Eina_List * +_append_wanted(Eina_List *wanted, Eina_List *tree) +{ + Eina_List *l; + Proc_Info *parent; + + EINA_LIST_FOREACH(tree, l, parent) + { + wanted = eina_list_append(wanted, parent); + if (parent->children) + wanted = _append_wanted(wanted, parent->children); + } + return wanted; +} + +Eina_List * +proc_info_pid_children_get(pid_t pid) +{ + Proc_Info *proc; + Eina_List *l, *procs, *wanted = NULL; + + procs = proc_info_all_children_get(); + + EINA_LIST_FOREACH(procs, l, proc) + { + if (!wanted && proc->pid == pid) + { + wanted = eina_list_append(wanted, proc); + if (proc->children) + wanted = _append_wanted(wanted, proc->children); + } + } + + EINA_LIST_FREE(procs, proc) + { + if (!eina_list_data_find(wanted, proc)) + { + proc_info_free(proc); + } + } + + return wanted; +} + +void +proc_info_all_children_free(Eina_List *pstree) +{ + Proc_Info *parent, *child; + + EINA_LIST_FREE(pstree, parent) + { + EINA_LIST_FREE(parent->children, child) + proc_info_pid_children_free(child); + proc_info_free(parent); + } +} + +void +proc_info_pid_children_free(Proc_Info *proc) +{ + Proc_Info *child; + + EINA_LIST_FREE(proc->children, child) + proc_info_free(child); + + proc_info_free(proc); +} + +int +proc_sort_by_pid(const void *p1, const void *p2) +{ + const Proc_Info *inf1, *inf2; + + inf1 = p1; inf2 = p2; + + return inf1->pid - inf2->pid; +} + +int +proc_sort_by_uid(const void *p1, const void *p2) +{ + const Proc_Info *inf1, *inf2; + + inf1 = p1; inf2 = p2; + + return inf1->uid - inf2->uid; +} + +int +proc_sort_by_nice(const void *p1, const void *p2) +{ + const Proc_Info *inf1, *inf2; + + inf1 = p1; inf2 = p2; + + return inf1->nice - inf2->nice; +} + +int +proc_sort_by_pri(const void *p1, const void *p2) +{ + const Proc_Info *inf1, *inf2; + + inf1 = p1; inf2 = p2; + + return inf1->priority - inf2->priority; +} + +int +proc_sort_by_cpu(const void *p1, const void *p2) +{ + const Proc_Info *inf1, *inf2; + + inf1 = p1; inf2 = p2; + + return inf1->cpu_id - inf2->cpu_id; +} + +int +proc_sort_by_threads(const void *p1, const void *p2) +{ + const Proc_Info *inf1, *inf2; + + inf1 = p1; inf2 = p2; + + return inf1->numthreads - inf2->numthreads; +} + +int +proc_sort_by_files(const void *p1, const void *p2) +{ + const Proc_Info *inf1, *inf2; + + inf1 = p1; inf2 = p2; + + return inf1->numfiles - inf2->numfiles; +} + +int +proc_sort_by_size(const void *p1, const void *p2) +{ + const Proc_Info *inf1, *inf2; + int64_t size1, size2; + + inf1 = p1; inf2 = p2; + + size1 = inf1->mem_size; + size2 = inf2->mem_size; + + if (size1 > size2) + return 1; + if (size1 < size2) + return -1; + + return 0; +} + +int +proc_sort_by_virt(const void *p1, const void *p2) +{ + const Proc_Info *inf1, *inf2; + int64_t size1, size2; + + inf1 = p1; inf2 = p2; + + size1 = inf1->mem_virt; + size2 = inf2->mem_virt; + + if (size1 > size2) + return 1; + if (size1 < size2) + return -1; + + return 0; +} + +int +proc_sort_by_rss(const void *p1, const void *p2) +{ + const Proc_Info *inf1, *inf2; + int64_t size1, size2; + + inf1 = p1; inf2 = p2; + + size1 = inf1->mem_rss; + size2 = inf2->mem_rss; + + if (size1 > size2) + return 1; + if (size1 < size2) + return -1; + + return 0; +} + +int +proc_sort_by_shared(const void *p1, const void *p2) +{ + const Proc_Info *inf1, *inf2; + int64_t size1, size2; + + inf1 = p1; inf2 = p2; + + size1 = inf1->mem_shared; + size2 = inf2->mem_shared; + + if (size1 > size2) + return 1; + if (size1 < size2) + return -1; + + return 0; +} + +int +proc_sort_by_time(const void *p1, const void *p2) +{ + const Proc_Info *inf1, *inf2; + int64_t t1, t2; + + inf1 = p1; inf2 = p2; + + t1 = inf1->run_time; + t2 = inf2->run_time; + + if (t1 > t2) + return 1; + if (t1 < t2) + return -1; + + return 0; +} + +int +proc_sort_by_cpu_usage(const void *p1, const void *p2) +{ + const Proc_Info *inf1, *inf2; + double one, two; + + inf1 = p1; inf2 = p2; + + one = inf1->cpu_usage; + two = inf2->cpu_usage; + + if (one > two) + return 1; + else if (one < two) + return -1; + else return 0; +} + +int +proc_sort_by_cmd(const void *p1, const void *p2) +{ + const Proc_Info *inf1, *inf2; + + inf1 = p1; inf2 = p2; + + return strcasecmp(inf1->command, inf2->command); +} + +int +proc_sort_by_state(const void *p1, const void *p2) +{ + const Proc_Info *inf1, *inf2; + + inf1 = p1; inf2 = p2; + + return strcmp(inf1->state, inf2->state); +} + +int +proc_sort_by_age(const void *p1, const void *p2) +{ + const Proc_Info *c1 = p1, *c2 = p2; + + return c1->start - c2->start; +} + diff --git a/src/bin/next/process.h b/src/bin/next/process.h new file mode 100644 index 0000000..9f3f795 --- /dev/null +++ b/src/bin/next/process.h @@ -0,0 +1,119 @@ +#ifndef __PROC_H__ +#define __PROC_H__ + +#include <Eina.h> +#include <stdint.h> +#include <unistd.h> + +#if !defined(PID_MAX) +# define PID_MAX 99999 +#endif + +typedef struct _Proc_Info +{ + pid_t pid; + pid_t ppid; + uid_t uid; + int8_t nice; + int8_t priority; + int cpu_id; + int32_t numthreads; + int64_t cpu_time; + double cpu_usage; + int64_t run_time; + int64_t start; + + uint64_t mem_size; + uint64_t mem_virt; + uint64_t mem_rss; + uint64_t mem_shared; + + char *command; + char *arguments; + char state[32]; + char wchan[32]; + + Eina_List *fds; + int numfiles; + + short is_kernel; + int tid; + char *thread_name; + + Eina_List *threads; + Eina_List *children; +} Proc_Info; + +Eina_List * +proc_info_all_get(void); + +Proc_Info * +proc_info_by_pid(int pid); + +void +proc_info_free(Proc_Info *proc); + +void +proc_info_kthreads_show_set(Eina_Bool enabled); + +Eina_Bool +proc_info_kthreads_show_get(void); + +Eina_List * +proc_info_all_children_get(void); + +Eina_List * +proc_info_pid_children_get(pid_t pid); + +void +proc_info_pid_children_free(Proc_Info *procs); + +int +proc_sort_by_pid(const void *p1, const void *p2); + +int +proc_sort_by_uid(const void *p1, const void *p2); + +int +proc_sort_by_nice(const void *p1, const void *p2); + +int +proc_sort_by_pri(const void *p1, const void *p2); + +int +proc_sort_by_cpu(const void *p1, const void *p2); + +int +proc_sort_by_threads(const void *p1, const void *p2); + +int +proc_sort_by_files(const void *p1, const void *p2); + +int +proc_sort_by_size(const void *p1, const void *p2); + +int +proc_sort_by_virt(const void *p1, const void *p2); + +int +proc_sort_by_rss(const void *p1, const void *p2); + +int +proc_sort_by_shared(const void *p1, const void *p2); + +int +proc_sort_by_time(const void *p1, const void *p2); + +int +proc_sort_by_cpu_usage(const void *p1, const void *p2); + +int +proc_sort_by_cmd(const void *p1, const void *p2); + +int +proc_sort_by_state(const void *p1, const void *p2); + +int +proc_sort_by_age(const void *p1, const void *p2); + +#endif --