blob: f9b9163e06e066bd584071051b3e2124e4ac06d1 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2007 Wind River Systems, Inc. and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Wind River Systems - initial API and implementation
*******************************************************************************/
#include "config.h"
#if SERVICE_SysMonitor
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <assert.h>
#include <sys/types.h>
#include "mdep.h"
#include "sysmon.h"
#include "protocol.h"
#include "json.h"
#include "context.h"
#include "errors.h"
static const char SYS_MON[] = "SysMonitor";
#if defined(WIN32)
# error "SysMonitor service is not supported for Windows"
#elif defined(_WRS_KERNEL)
# error "SysMonitor service is not supported for VxWorks"
#endif
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <dirent.h>
#include <pwd.h>
#include <grp.h>
#include <linux/param.h>
#define BUF_EOF (-1)
static char buf[1024];
static int buf_fd = -1;
static int buf_pos = 0;
static int buf_len = 0;
static int buf_ch = 0;
static void next_ch(void) {
while (buf_len >= 0 && buf_pos >= buf_len) {
buf_pos = 0;
buf_len = read(buf_fd, buf, sizeof(buf));
if (buf_len == 0) buf_len = -1;
}
if (buf_len < 0) {
buf_ch = BUF_EOF;
}
else {
buf_ch = buf[buf_pos++];
}
}
static void first_ch(int fd) {
buf_fd = fd;
buf_pos = 0;
buf_len = 0;
next_ch();
}
static void write_string_array(OutputStream * out, int f) {
int cnt = 0;
first_ch(f);
out->write(out, '[');
while (buf_ch != BUF_EOF && buf_ch != 0) {
if (cnt > 0) out->write(out, ',');
out->write(out, '"');
do {
json_write_char(out, buf_ch);
next_ch();
}
while (buf_ch != BUF_EOF && buf_ch != 0);
next_ch();
out->write(out, '"');
cnt++;
}
out->write(out, ']');
}
static void write_context(OutputStream * out, char * id, char * parent_id, char * dir) {
char fnm[FILE_PATH_SIZE + 1];
int sz;
int f;
out->write(out, '{');
if (chdir(dir) >= 0) {
if ((sz = readlink("cwd", fnm, FILE_PATH_SIZE)) > 0) {
fnm[sz] = 0;
json_write_string(out, "CWD");
out->write(out, ':');
json_write_string(out, fnm);
out->write(out, ',');
}
if ((sz = readlink("root", fnm, FILE_PATH_SIZE)) > 0) {
fnm[sz] = 0;
json_write_string(out, "Root");
out->write(out, ':');
json_write_string(out, fnm);
out->write(out, ',');
}
f = open("stat", O_RDONLY);
if (f >= 0) {
struct_stat st;
if (fstat(f, &st) == 0) {
struct passwd * pwd;
struct group * grp;
json_write_string(out, "UID");
out->write(out, ':');
json_write_long(out, st.st_uid);
out->write(out, ',');
json_write_string(out, "UGID");
out->write(out, ':');
json_write_long(out, st.st_gid);
out->write(out, ',');
pwd = getpwuid(st.st_uid);
if (pwd != NULL) {
json_write_string(out, "UserName");
out->write(out, ':');
json_write_string(out, pwd->pw_name);
out->write(out, ',');
}
grp = getgrgid(st.st_gid);
if (grp != NULL) {
json_write_string(out, "GroupName");
out->write(out, ':');
json_write_string(out, grp->gr_name);
out->write(out, ',');
}
}
memset(buf, 0, sizeof(buf));
if ((sz = read(f, buf, sizeof(buf))) > 0) {
char * str = buf;
int pid = 0; // The process ID.
char * comm = fnm; // The filename of the executable, in parentheses. This is visible
// whether or not the executable is swapped out.
char state = 0; // One character from the string "RSDZTW" where R is running, S is
// sleeping in an interruptible wait, D is waiting in uninterruptible
// disk sleep, Z is zombie, T is traced or stopped (on a signal), and W
// is paging.
int ppid = 0; // The PID of the parent.
int pgrp = 0; // The process group ID of the process.
int session = 0; // The session ID of the process.
int tty_nr = 0; // The tty the process uses.
int tpgid = 0; // The process group ID of the process which currently owns the tty that
// the process is connected to.
unsigned long flags = 0; // The kernel flags word of the process. For bit meanings, see the PF_*
// defines in <linux/sched.h>. Details depend on the kernel version.
unsigned long minflt = 0; // The number of minor faults the process has made which have not
// required loading a memory page from disk.
unsigned long cminflt = 0; // The number of minor faults that the process's waited-for children
// have made.
unsigned long majflt = 0; // The number of major faults the process has made which have required
// loading a memory page from disk.
unsigned long cmajflt = 0; // The number of major faults that the process's waited-for children
// have made.
unsigned long utime = 0; // The number of jiffies that this process has been scheduled in user
// mode.
unsigned long stime = 0; // The number of jiffies that this process has been scheduled in kernel
// mode.
long cutime = 0; // The number of jiffies that this process's waited-for children have
// been scheduled in user mode. (See also times(2).)
long cstime = 0; // The number of jiffies that this process's waited-for children have
// been scheduled in kernel mode.
long priority = 0; // The standard nice value, plus fifteen. The value is never negative
// in the kernel.
long nice = 0; // The nice value ranges from 19 (nicest) to -19 (not nice to others).
long dummy = 0; // This value is hard coded to 0 as a placeholder for a removed field.
long itrealvalue = 0; // The time in jiffies before the next SIGALRM is sent to the process
// due to an interval timer.
unsigned long starttime = 0;// The time in jiffies the process started after system boot.
unsigned long vsize = 0; // Virtual memory size in bytes.
long rss = 0; // Resident Set Size: number of pages the process has in real memory,
// minus 3 for administrative purposes. This is just the pages which
// count towards text, data, or stack space. This does not include
// pages which have not been demand-loaded in, or which are swapped out.
unsigned long rlim = 0; // Current limit in bytes on the rss of the process (usually 4294967295
// on i386).
unsigned long startcode = 0;// The address above which program text can run.
unsigned long endcode = 0; // The address below which program text can run.
unsigned long startstack =0;// The address of the start of the stack.
unsigned long kstkesp = 0; // The current value of esp (stack pointer), as found in the kernel
// stack page for the process.
unsigned long kstkeip = 0; // The current EIP (instruction pointer).
unsigned long signal = 0; // The bitmap of pending signals.
unsigned long blocked = 0; // The bitmap of blocked signals.
unsigned long sigignore = 0;// The bitmap of ignored signals.
unsigned long sigcatch = 0; // The bitmap of caught signals.
unsigned long wchan = 0; // This is the "channel" in which the process is waiting. It is the
// address of a system call, and can be looked up in a namelist if you
// need a textual name. (If you have an up-to-date /etc/psdatabase,
// then try ps -l to see the WCHAN field in action.)
unsigned long nswap = 0; // Number of pages swapped (not maintained).
unsigned long cnswap = 0; // Cumulative nswap for child processes (not maintained).
int exit_signal = 0; // Signal to be sent to parent when we die.
int processor = 0; // CPU number last executed on.
unsigned long rt_priority=0;// Real-time scheduling priority (see sched_setscheduler(2)).
unsigned long policy = 0; // Scheduling policy (see sched_setscheduler(2)).
assert(sz < sizeof(buf));
buf[sz] = 0;
pid = (int)strtol(str, &str, 10);
while (*str == ' ') str++;
if (*str == '(') str++;
sz = 0;
while (*str && *str != ')') comm[sz++] = *str++;
comm[sz] = 0;
if (*str == ')') str++;
while (*str == ' ') str++;
sscanf(str,
"%c %d %d %d %d %d %lu %lu %lu %lu %lu %lu %lu %ld %ld %ld %ld %ld %ld %lu %lu %ld %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %d %d %lu %lu",
&state, &ppid, &pgrp, &session, &tty_nr, &tpgid, &flags,
&minflt, &cminflt, &majflt, &cmajflt, &utime, &stime, &cutime, &cstime,
&priority, &nice, &dummy, &itrealvalue, &starttime, &vsize, &rss, &rlim,
&startcode, &endcode, &startstack, &kstkesp, &kstkeip, &signal, &blocked, &sigignore, &sigcatch,
&wchan, &nswap, &cnswap, &exit_signal, &processor, &rt_priority, &policy);
json_write_string(out, "PID");
out->write(out, ':');
json_write_long(out, pid);
out->write(out, ',');
json_write_string(out, "File");
out->write(out, ':');
json_write_string(out, comm);
out->write(out, ',');
json_write_string(out, "State");
out->write(out, ':');
out->write(out, '"');
json_write_char(out, state);
out->write(out, '"');
out->write(out, ',');
if (ppid > 0) {
json_write_string(out, "PPID");
out->write(out, ':');
json_write_long(out, ppid);
out->write(out, ',');
}
json_write_string(out, "PGRP");
out->write(out, ':');
json_write_long(out, pgrp);
out->write(out, ',');
json_write_string(out, "Session");
out->write(out, ':');
json_write_long(out, session);
out->write(out, ',');
if (tty_nr > 0) {
json_write_string(out, "TTY");
out->write(out, ':');
json_write_long(out, tty_nr);
out->write(out, ',');
}
if (tpgid > 0) {
json_write_string(out, "TGID");
out->write(out, ':');
json_write_long(out, tpgid);
out->write(out, ',');
}
json_write_string(out, "Flags");
out->write(out, ':');
json_write_ulong(out, flags);
out->write(out, ',');
json_write_string(out, "MinFlt");
out->write(out, ':');
json_write_ulong(out, minflt);
out->write(out, ',');
json_write_string(out, "CMinFlt");
out->write(out, ':');
json_write_ulong(out, cminflt);
out->write(out, ',');
json_write_string(out, "MajFlt");
out->write(out, ':');
json_write_ulong(out, majflt);
out->write(out, ',');
json_write_string(out, "CMajFlt");
out->write(out, ':');
json_write_ulong(out, cmajflt);
out->write(out, ',');
json_write_string(out, "UTime");
out->write(out, ':');
json_write_int64(out, (int64)utime * 1000 / HZ);
out->write(out, ',');
json_write_string(out, "STime");
out->write(out, ':');
json_write_int64(out, (int64)stime * 1000 / HZ);
out->write(out, ',');
json_write_string(out, "CUTime");
out->write(out, ':');
json_write_int64(out, (int64)cutime * 1000 / HZ);
out->write(out, ',');
json_write_string(out, "CSTime");
out->write(out, ':');
json_write_int64(out, (int64)cstime * 1000 / HZ);
out->write(out, ',');
json_write_string(out, "Priority");
out->write(out, ':');
json_write_long(out, (long)priority - 15);
out->write(out, ',');
if (nice != 0) {
json_write_string(out, "Nice");
out->write(out, ':');
json_write_long(out, nice);
out->write(out, ',');
}
if (itrealvalue != 0) {
json_write_string(out, "ITRealValue");
out->write(out, ':');
json_write_int64(out, (int64)itrealvalue * 1000 / HZ);
out->write(out, ',');
}
json_write_string(out, "StartTime");
out->write(out, ':');
json_write_int64(out, (int64)starttime * 1000 / HZ);
out->write(out, ',');
json_write_string(out, "VSize");
out->write(out, ':');
json_write_ulong(out, vsize);
out->write(out, ',');
json_write_string(out, "PSize");
out->write(out, ':');
json_write_ulong(out, getpagesize());
out->write(out, ',');
json_write_string(out, "RSS");
out->write(out, ':');
json_write_long(out, rss);
out->write(out, ',');
json_write_string(out, "RLimit");
out->write(out, ':');
json_write_ulong(out, rlim);
out->write(out, ',');
if (startcode != 0) {
json_write_string(out, "CodeStart");
out->write(out, ':');
json_write_ulong(out, startcode);
out->write(out, ',');
}
if (endcode != 0) {
json_write_string(out, "CodeEnd");
out->write(out, ':');
json_write_ulong(out, endcode);
out->write(out, ',');
}
if (startstack != 0) {
json_write_string(out, "StackStart");
out->write(out, ':');
json_write_ulong(out, startstack);
out->write(out, ',');
}
json_write_string(out, "Signals");
out->write(out, ':');
json_write_ulong(out, signal);
out->write(out, ',');
json_write_string(out, "SigBlock");
out->write(out, ':');
json_write_ulong(out, blocked);
out->write(out, ',');
json_write_string(out, "SigIgnore");
out->write(out, ':');
json_write_ulong(out, sigignore);
out->write(out, ',');
json_write_string(out, "SigCatch");
out->write(out, ':');
json_write_ulong(out, sigcatch);
out->write(out, ',');
if (wchan != 0) {
json_write_string(out, "WChan");
out->write(out, ':');
json_write_ulong(out, wchan);
out->write(out, ',');
}
json_write_string(out, "NSwap");
out->write(out, ':');
json_write_ulong(out, nswap);
out->write(out, ',');
json_write_string(out, "CNSwap");
out->write(out, ':');
json_write_ulong(out, cnswap);
out->write(out, ',');
json_write_string(out, "ExitSignal");
out->write(out, ':');
json_write_long(out, exit_signal);
out->write(out, ',');
json_write_string(out, "Processor");
out->write(out, ':');
json_write_long(out, processor);
out->write(out, ',');
json_write_string(out, "RTPriority");
out->write(out, ':');
json_write_ulong(out, rt_priority);
out->write(out, ',');
json_write_string(out, "Policy");
out->write(out, ':');
json_write_ulong(out, policy);
out->write(out, ',');
}
close(f);
}
}
if (parent_id != NULL && parent_id[0] != 0) {
json_write_string(out, "ParentID");
out->write(out, ':');
json_write_string(out, parent_id);
out->write(out, ',');
}
json_write_string(out, "ID");
out->write(out, ':');
json_write_string(out, id);
out->write(out, '}');
}
static void command_get_context(char * token, InputStream * inp, OutputStream * out) {
char id[256];
pid_t pid = 0;
pid_t parent = 0;
int err = 0;
char dir[FILE_PATH_SIZE];
json_read_string(inp, id, sizeof(id));
if (inp->read(inp) != 0) exception(ERR_JSON_SYNTAX);
if (inp->read(inp) != MARKER_EOM) exception(ERR_JSON_SYNTAX);
write_stringz(out, "R");
write_stringz(out, token);
pid = id2pid(id, &parent);
if (pid != 0) {
struct_stat st;
if (parent != 0) {
snprintf(dir, sizeof(dir), "/proc/%d/task/%d", parent, pid);
}
else {
snprintf(dir, sizeof(dir), "/proc/%d", pid);
}
if (lstat(dir, &st) < 0) err = errno;
else if (!S_ISDIR(st.st_mode)) err = ERR_INV_CONTEXT;
}
write_errno(out, err);
if (err == 0 && pid != 0) {
char bf[256];
write_context(out, id, parent == 0 ? NULL : strcpy(bf, pid2id(parent, 0)), dir);
out->write(out, 0);
}
else {
write_stringz(out, "null");
}
out->write(out, MARKER_EOM);
}
static void command_get_children(char * token, InputStream * inp, OutputStream * out) {
char id[256];
DIR * proc = NULL;
char dir[FILE_PATH_SIZE];
pid_t pid = 0;
pid_t parent = 0;
json_read_string(inp, id, sizeof(id));
if (inp->read(inp) != 0) exception(ERR_JSON_SYNTAX);
if (inp->read(inp) != MARKER_EOM) exception(ERR_JSON_SYNTAX);
write_stringz(out, "R");
write_stringz(out, token);
pid = id2pid(id, &parent);
if (pid == 0) strcpy(dir, "/proc");
else snprintf(dir, sizeof(dir), "/proc/%d/task", pid);
if (parent != 0) {
write_errno(out, 0);
write_stringz(out, "null");
}
else {
proc = opendir(dir);
if (proc == NULL) {
write_errno(out, errno);
write_stringz(out, "null");
}
else {
int cnt = 0;
write_errno(out, 0);
out->write(out, '[');
for (;;) {
struct dirent * ent = readdir(proc);
if (ent == NULL) break;
if (ent->d_name[0] >= '1' && ent->d_name[0] <= '9') {
if (cnt > 0) out->write(out, ',');
json_write_string(out, pid2id(atol(ent->d_name), pid));
cnt++;
}
}
out->write(out, ']');
out->write(out, 0);
closedir(proc);
}
}
out->write(out, MARKER_EOM);
}
static void command_get_command_line(char * token, InputStream * inp, OutputStream * out) {
char id[256];
pid_t pid = 0;
pid_t parent = 0;
int err = 0;
char dir[256];
int f;
json_read_string(inp, id, sizeof(id));
if (inp->read(inp) != 0) exception(ERR_JSON_SYNTAX);
if (inp->read(inp) != MARKER_EOM) exception(ERR_JSON_SYNTAX);
write_stringz(out, "R");
write_stringz(out, token);
pid = id2pid(id, &parent);
if (pid != 0 && parent == 0) {
struct_stat st;
snprintf(dir, sizeof(dir), "/proc/%d", pid);
if (lstat(dir, &st) < 0) err = errno;
else if (!S_ISDIR(st.st_mode)) err = ERR_INV_CONTEXT;
}
else {
err = ERR_INV_CONTEXT;
}
if (err == 0 && chdir(dir) < 0) err = errno;
if (err == 0 && (f = open("cmdline", O_RDONLY)) < 0) err = errno;
write_errno(out, err);
if (err == 0) {
write_string_array(out, f);
close(f);
out->write(out, 0);
}
else {
write_stringz(out, "null");
}
out->write(out, MARKER_EOM);
}
static void command_get_environment(char * token, InputStream * inp, OutputStream * out) {
char id[256];
pid_t pid = 0;
pid_t parent = 0;
int err = 0;
char dir[256];
int f;
json_read_string(inp, id, sizeof(id));
if (inp->read(inp) != 0) exception(ERR_JSON_SYNTAX);
if (inp->read(inp) != MARKER_EOM) exception(ERR_JSON_SYNTAX);
write_stringz(out, "R");
write_stringz(out, token);
pid = id2pid(id, &parent);
if (pid != 0 && parent == 0) {
struct_stat st;
snprintf(dir, sizeof(dir), "/proc/%d", pid);
if (lstat(dir, &st) < 0) err = errno;
else if (!S_ISDIR(st.st_mode)) err = ERR_INV_CONTEXT;
}
else {
err = ERR_INV_CONTEXT;
}
if (err == 0 && chdir(dir) < 0) err = errno;
if (err == 0 && (f = open("environ", O_RDONLY)) < 0) err = errno;
write_errno(out, err);
if (err == 0) {
write_string_array(out, f);
close(f);
out->write(out, 0);
}
else {
write_stringz(out, "null");
}
out->write(out, MARKER_EOM);
}
extern void ini_sys_mon_service(void) {
add_command_handler(SYS_MON, "getContext", command_get_context);
add_command_handler(SYS_MON, "getChildren", command_get_children);
add_command_handler(SYS_MON, "getCommandLine", command_get_command_line);
add_command_handler(SYS_MON, "getEnvironment", command_get_environment);
}
#endif