#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <assert.h>
#include <ctype.h>
#include <unistd.h>
#include <libproc.h>
#include "preferences.h"
#include "statistic.h"
#include "top.h"
static struct {
int mode;
int sort_by;
int secondary_sort;
bool sort_ascending;
bool secondary_sort_ascending;
int sleep_seconds;
bool frameworks;
int frameworks_interval;
char *user;
uid_t uid;
int samples;
int nprocs;
bool have_pid;
pid_t pid;
bool logging_mode;
bool have_ncols;
int ncols;
bool show_swap;
bool mmr;
bool delta_forced_mmr;
struct {
int total;
int array[STATISTIC_TOTAL];
} display_stats;
const char *signal_string;
int signal_number;
} prefs = {
.mode = STATMODE_NON_EVENT,
.sort_by = STATISTIC_PID,
.secondary_sort = STATISTIC_PID,
.sort_ascending = false,
.secondary_sort_ascending = false,
.sleep_seconds = 1,
.frameworks = true,
.frameworks_interval = 10,
.user = NULL,
.uid = 0,
.samples = -1,
.nprocs = -1,
.have_pid = false,
.pid = 0,
.logging_mode = false,
.have_ncols = false,
.ncols = -1,
.show_swap = false,
.mmr = true,
.delta_forced_mmr = false
};
static struct {
const char *string;
int e;
} stat_map[] = {
{"pid", STATISTIC_PID},
{"command", STATISTIC_COMMAND},
{"cpu", STATISTIC_CPU},
{"csw", STATISTIC_CSW},
{"time", STATISTIC_TIME},
{"threads", STATISTIC_THREADS},
{"th", STATISTIC_THREADS},
{"ports", STATISTIC_PORTS},
{"prt", STATISTIC_PORTS},
{"mregion", STATISTIC_MREGION},
{"mreg", STATISTIC_MREGION},
{"mregs", STATISTIC_MREGION},
{"reg", STATISTIC_MREGION},
{"rprvt", STATISTIC_RPRVT},
{"rshrd", STATISTIC_RSHRD},
{"rsize", STATISTIC_RSIZE},
{"vsize", STATISTIC_VSIZE},
{"vprvt", STATISTIC_VPRVT},
{"pgrp", STATISTIC_PGRP},
{"ppid", STATISTIC_PPID},
{"state", STATISTIC_PSTATE},
{"pstate", STATISTIC_PSTATE},
{"uid", STATISTIC_UID},
{"wq", STATISTIC_WORKQUEUE},
{"#wq", STATISTIC_WORKQUEUE},
{"workqueue", STATISTIC_WORKQUEUE},
{"faults", STATISTIC_FAULTS},
{"fault", STATISTIC_FAULTS},
{"cow", STATISTIC_COW_FAULTS},
{"cow_faults", STATISTIC_COW_FAULTS},
{"user", STATISTIC_USER},
{"username", STATISTIC_USER},
{"msgsent", STATISTIC_MESSAGES_SENT},
{"msgrecv", STATISTIC_MESSAGES_RECEIVED},
{"sysbsd", STATISTIC_SYSBSD},
{"sysmach", STATISTIC_SYSMACH},
{"pageins", STATISTIC_PAGEINS},
{NULL, 0}
};
static struct {
const char *string;
int value;
} signal_map[] = {
{"HUP", SIGHUP},
{"INT", SIGINT},
{"QUIT", SIGQUIT},
{"ILL", SIGILL},
{"TRAP", SIGTRAP},
{"ABRT", SIGABRT},
{"IOT", SIGIOT},
{"EMT", SIGEMT},
{"FPE", SIGFPE},
{"KILL", SIGKILL},
{"BUS", SIGBUS},
{"SEGV", SIGSEGV},
{"SYS", SIGSYS},
{"PIPE", SIGPIPE},
{"ALRM", SIGALRM},
{"TERM", SIGTERM},
{"URG", SIGURG},
{"STOP", SIGSTOP},
{"TSTP", SIGTSTP},
{"CONT", SIGCONT},
{"CHLD", SIGCHLD},
{"TTIN", SIGTTIN},
{"TTOU", SIGTTOU},
{"IO", SIGIO},
{"XCPU", SIGXCPU},
{"XFSZ", SIGXFSZ},
{"VTALRM", SIGVTALRM},
{"PROF", SIGPROF},
{"WINCH", SIGWINCH},
{"INFO", SIGINFO},
{"USR1", SIGUSR1},
{"USR2", SIGUSR2},
{NULL, -1}
};
void top_prefs_init(void) {
int i;
prefs.display_stats.total = 0;
#define SPREF(e) do { \
assert(prefs.display_stats.total < STATISTIC_TOTAL); \
prefs.display_stats.array[prefs.display_stats.total] = e; \
prefs.display_stats.total++; \
} while(0)
SPREF(STATISTIC_PID);
SPREF(STATISTIC_COMMAND);
SPREF(STATISTIC_CPU);
SPREF(STATISTIC_TIME);
SPREF(STATISTIC_THREADS);
#ifdef PROC_PIDWORKQUEUEINFO
SPREF(STATISTIC_WORKQUEUE);
#endif
SPREF(STATISTIC_PORTS);
SPREF(STATISTIC_MREGION);
SPREF(STATISTIC_RPRVT);
SPREF(STATISTIC_RSHRD);
SPREF(STATISTIC_RSIZE);
SPREF(STATISTIC_VPRVT);
SPREF(STATISTIC_VSIZE);
SPREF(STATISTIC_PGRP);
SPREF(STATISTIC_PPID);
SPREF(STATISTIC_PSTATE);
SPREF(STATISTIC_UID);
SPREF(STATISTIC_FAULTS);
SPREF(STATISTIC_COW_FAULTS);
SPREF(STATISTIC_MESSAGES_SENT);
SPREF(STATISTIC_MESSAGES_RECEIVED);
SPREF(STATISTIC_SYSBSD);
SPREF(STATISTIC_SYSMACH);
SPREF(STATISTIC_CSW);
SPREF(STATISTIC_PAGEINS);
SPREF(STATISTIC_USER);
#undef SPREF
for(i = 0; signal_map[i].string; ++i) {
if(SIGTERM == signal_map[i].value) {
prefs.signal_string = signal_map[i].string;
prefs.signal_number = SIGTERM;
break;
}
}
}
static struct {
const char *string;
int e;
} mode_map[] = {
{"a", STATMODE_ACCUM},
{"d", STATMODE_DELTA},
{"e", STATMODE_EVENT},
{"n", STATMODE_NON_EVENT},
{NULL, 0}
};
bool top_prefs_set_mode(const char *mode) {
int i;
for(i = 0; mode_map[i].string; ++i) {
if(!strcmp(mode, mode_map[i].string)) {
prefs.mode = mode_map[i].e;
if(STATMODE_DELTA == prefs.mode) {
if(top_prefs_get_mmr()) {
top_prefs_set_mmr(false);
prefs.delta_forced_mmr = true;
}
} else {
if(prefs.delta_forced_mmr) {
top_prefs_set_mmr(true);
prefs.delta_forced_mmr = false;
}
}
return false;
}
}
return true;
}
int top_prefs_get_mode(void) {
return prefs.mode;
}
const char *top_prefs_get_mode_string(void) {
int i;
for(i = 0; mode_map[i].string; ++i) {
if(prefs.mode == mode_map[i].e)
return mode_map[i].string;
}
return NULL;
}
void top_prefs_set_sleep(int seconds) {
prefs.sleep_seconds = seconds;
}
int top_prefs_get_sleep(void) {
return prefs.sleep_seconds;
}
static bool find_sort(const char *sortkey, int *e, bool *ascending) {
int i;
if(ascending) {
if('+' == sortkey[0]) {
*ascending = true;
++sortkey;
} else if('-' == sortkey[0]) {
*ascending = false;
++sortkey;
}
}
for(i = 0; stat_map[i].string; ++i) {
if(!strcasecmp(sortkey, stat_map[i].string)) {
*e = stat_map[i].e;
return true;
}
}
return false;
}
bool top_prefs_set_sort(const char *sortkey) {
bool ascending;
int e;
ascending = top_prefs_get_ascending();
if(find_sort(sortkey, &e, &ascending)) {
top_prefs_set_ascending(ascending);
prefs.sort_by = e;
return false;
}
return true;
}
int top_prefs_get_sort(void) {
return prefs.sort_by;
}
bool top_prefs_set_secondary_sort(const char *sortkey) {
bool ascending;
int e;
ascending = top_prefs_get_secondary_ascending();
if(find_sort(sortkey, &e, &ascending)) {
top_prefs_set_secondary_ascending(ascending);
prefs.secondary_sort = e;
return false;
}
return true;
}
int top_prefs_get_secondary_sort(void) {
return prefs.secondary_sort;
}
static const char *enum_value_to_sort_string(int e) {
int i;
for(i = 0; stat_map[i].string; ++i) {
if(e == stat_map[i].e) {
return stat_map[i].string;
}
}
fprintf(stderr, "Invalid enum value: %d in %s!\n", e, __func__);
abort();
}
const char *top_prefs_get_sort_string(void) {
return enum_value_to_sort_string(prefs.sort_by);
}
const char *top_prefs_get_secondary_sort_string(void) {
return enum_value_to_sort_string(prefs.secondary_sort);
}
void top_prefs_set_ascending(bool flag) {
prefs.sort_ascending = flag;
}
bool top_prefs_get_ascending(void) {
return prefs.sort_ascending;
}
void top_prefs_set_secondary_ascending(bool flag) {
prefs.secondary_sort_ascending = flag;
}
bool top_prefs_get_secondary_ascending(void) {
return prefs.secondary_sort_ascending;
}
void top_prefs_set_frameworks(bool flag) {
prefs.frameworks = flag;
}
bool top_prefs_get_frameworks(void) {
return prefs.frameworks;
}
void top_prefs_set_frameworks_interval(int interval) {
prefs.frameworks_interval = interval;
}
int top_prefs_get_frameworks_interval(void) {
return prefs.frameworks_interval;
}
void top_prefs_set_user(const char *user) {
free(prefs.user);
prefs.user = NULL;
if(strlen(user)) {
prefs.user = strdup(user);
}
}
char *top_prefs_get_user(void) {
return prefs.user;
}
void top_prefs_set_user_uid(uid_t uid) {
prefs.uid = uid;
}
uid_t top_prefs_get_user_uid(void) {
return prefs.uid;
}
static bool find_stat_enum(const char *key, int *e) {
int i;
for(i = 0; stat_map[i].string; ++i) {
if(!strcasecmp(key, stat_map[i].string)) {
*e = stat_map[i].e;
return true;
}
}
return false;
}
bool top_prefs_set_stats(const char *names) {
char key[20];
int key_offset = 0;
const char *np;
int stat_enum_array[STATISTIC_TOTAL];
int stat_enum_array_offset = 0;
int e, i;
for(np = names; *np; ++np) {
if(isspace(*np))
continue;
if(',' == *np) {
key[key_offset++] = '\0';
if(!find_stat_enum(key, &e)) {
fprintf(stderr, "invalid stat: %s\n", key);
return true;
}
if(stat_enum_array_offset >= STATISTIC_TOTAL) {
fprintf(stderr, "too many stats specified.\n");
return true;
}
stat_enum_array[stat_enum_array_offset++] = e;
key_offset = 0;
} else {
key[key_offset++] = *np;
if(key_offset >= (sizeof(key) - 1)) {
fprintf(stderr, "invalid input: longer than any valid stat.\n");
return true;
}
}
}
if(key_offset > 0) {
key[key_offset++] = '\0';
if(!find_stat_enum(key, &e)) {
fprintf(stderr, "invalid stat: %s\n", key);
return true;
}
if(stat_enum_array_offset >= STATISTIC_TOTAL) {
fprintf(stderr, "too many stats specified.\n");
return true;
}
stat_enum_array[stat_enum_array_offset++] = e;
key_offset = 0;
}
if(stat_enum_array_offset <= 0) {
fprintf(stderr, "invalid input: %s\n", names);
return true;
}
for(i = 0; i < stat_enum_array_offset; ++i) {
prefs.display_stats.array[i] = stat_enum_array[i];
}
prefs.display_stats.total = stat_enum_array_offset;
return false;
}
bool top_prefs_get_stats(int *total, int **array) {
*total = prefs.display_stats.total;
*array = prefs.display_stats.array;
return true;
}
int top_prefs_get_samples(void) {
return prefs.samples;
}
void top_prefs_set_samples(int s) {
prefs.samples = s;
}
int top_prefs_get_nprocs(void) {
return prefs.nprocs;
}
void top_prefs_set_nprocs(int n) {
prefs.nprocs = n;
}
void top_prefs_set_pid(pid_t pid) {
prefs.have_pid = true;
prefs.pid = pid;
}
bool top_prefs_get_pid(pid_t *pidptr) {
if(prefs.have_pid) {
*pidptr = prefs.pid;
return true;
}
return false;
}
bool top_prefs_set_signal_string(char *s) {
int i;
for(i = 0; signal_map[i].string; ++i) {
if(!strcasecmp(signal_map[i].string, s)) {
prefs.signal_string = signal_map[i].string;
prefs.signal_number = signal_map[i].value;
return false;
}
}
return true;
}
int top_prefs_get_signal(const char **sptr) {
*sptr = prefs.signal_string;
return prefs.signal_number;
}
void top_prefs_set_logging_mode(bool mode) {
prefs.logging_mode = mode;
}
bool top_prefs_get_logging_mode(void) {
return prefs.logging_mode;
}
void top_prefs_set_ncols(int limit) {
prefs.have_ncols = true;
prefs.ncols = limit;
}
bool top_prefs_get_ncols(int *limit) {
if(prefs.have_ncols) {
*limit = prefs.ncols;
return true;
}
return false;
}
void top_prefs_set_swap(bool show) {
prefs.show_swap = show;
}
bool top_prefs_get_swap(void) {
return prefs.show_swap;
}
void top_prefs_set_mmr(bool mmr) {
prefs.mmr = mmr;
}
bool top_prefs_get_mmr(void) {
return prefs.mmr;
}