mirror of
https://github.com/ibm-s390-linux/s390-tools.git
synced 2026-08-05 02:14:52 +00:00
include/lib/util_sys.h: missing dev_t ziomon/ziomon_msg_tools.h: missing time_t zkey/keystore.c: missing open mon_tools/mon_procd.c: missing gettimeofday Fixes some build failures on musl libc Github-ID: https://github.com/ibm-s390-linux/s390-tools/pull/193 Signed-off-by: Sertonix <sertonix@posteo.net> Reviewed-by: Steffen Eiden <seiden@linux.ibm.com> Reviewed-by: Ingo Franzki <ifranzki@linux.ibm.com> Reviewed-by: Jan Höppner <hoeppner@linux.ibm.com> Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
1068 lines
26 KiB
C
1068 lines
26 KiB
C
/*
|
|
* mon_procd - Write process data to the z/VM monitor stream
|
|
*
|
|
* Copyright IBM Corp. 2007, 2017
|
|
*
|
|
* s390-tools is free software; you can redistribute it and/or modify
|
|
* it under the terms of the MIT license. See LICENSE for details.
|
|
*/
|
|
|
|
#include <ctype.h>
|
|
#include <dirent.h>
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <getopt.h>
|
|
#include <grp.h>
|
|
#include <linux/types.h>
|
|
#include <pwd.h>
|
|
#include <signal.h>
|
|
#include <stdarg.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <sys/dir.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/statvfs.h>
|
|
#include <sys/vfs.h>
|
|
#include <syslog.h>
|
|
#include <sys/time.h>
|
|
#include <unistd.h>
|
|
#include <utmp.h>
|
|
|
|
#include "mon_procd.h"
|
|
|
|
struct name_lens_t {
|
|
__u16 ruser_len;
|
|
__u16 euser_len;
|
|
__u16 egroup_len;
|
|
__u16 wchan_len;
|
|
__u16 cmd_len;
|
|
__u16 cmdline_len;
|
|
};
|
|
|
|
static int num_cpus;
|
|
static int curr_small_max, curr_big_max;
|
|
static int prev_small_max, prev_big_max;
|
|
static unsigned int pg_to_kb_shift, sort_tbl_size;
|
|
static float e_time;
|
|
static struct timeval prev_time, curr_time;
|
|
static struct cpudata_t cpudata;
|
|
static struct proc_sum_t proc_sum;
|
|
static struct task_sort_t *prev_sort_tbl, *curr_sort_tbl;
|
|
static struct name_lens_t name_lens;
|
|
|
|
static int attach;
|
|
static int mw_dev;
|
|
static char *temp;
|
|
static char fname[32];
|
|
static char buf[BUF_SIZE];
|
|
static char mon_record[MAX_REC_LEN];
|
|
static long sample_interval = 60;
|
|
|
|
static const char *pid_file = "/run/mon_procd.pid";
|
|
|
|
/*
|
|
* Clean up when SIGTERM or SIGINT received
|
|
*/
|
|
void stop_procd(int UNUSED(a))
|
|
{
|
|
remove(pid_file);
|
|
exit(0);
|
|
}
|
|
|
|
/*
|
|
* Set up for handling SIGTERM or SIGINT
|
|
*/
|
|
static void procd_handle_signals(void)
|
|
{
|
|
struct sigaction act;
|
|
|
|
act.sa_flags = 0;
|
|
sigemptyset( &act.sa_mask );
|
|
|
|
act.sa_handler = stop_procd;
|
|
if (sigaction(SIGTERM, &act, NULL) < 0) {
|
|
fprintf(stderr, "sigaction( SIGTERM, ... ) failed - "
|
|
"reason %s\n", strerror( errno ) );
|
|
exit(1);
|
|
}
|
|
act.sa_handler = stop_procd;
|
|
if (sigaction(SIGINT, &act, NULL) < 0) {
|
|
fprintf(stderr, "sigaction( SIGINT, ... ) failed - "
|
|
"reason %s\n", strerror( errno ) );
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Open /dev/monwriter
|
|
*/
|
|
static void procd_open_monwriter(void)
|
|
{
|
|
mw_dev = open("/dev/monwriter", O_EXCL | O_RDWR);
|
|
if (mw_dev == -1) {
|
|
printf("cannot open /dev/monwriter: %s\n", strerror(errno));
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Store daemon's pid so it can be stopped
|
|
*/
|
|
static int store_pid(void)
|
|
{
|
|
FILE *f = fopen(pid_file, "w");
|
|
|
|
if (!f) {
|
|
syslog(LOG_ERR, "cannot open pid file %s: %s", pid_file,
|
|
strerror(errno));
|
|
return -1;
|
|
}
|
|
fprintf(f, "%d\n", getpid());
|
|
fclose(f);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Stop sampling of any buffers that are not longer needed
|
|
*/
|
|
static void stop_unused(int curr_max, int prev_max)
|
|
{
|
|
struct monwrite_hdr *mw_hdrp;
|
|
int i;
|
|
|
|
mw_hdrp = (struct monwrite_hdr *)mon_record;
|
|
mw_hdrp->applid = PROCD_APPLID;
|
|
mw_hdrp->record_num = TASK_FLAG;
|
|
mw_hdrp->hdrlen = sizeof(struct monwrite_hdr);
|
|
mw_hdrp->mon_function = MONWRITE_STOP_INTERVAL;
|
|
mw_hdrp->datalen = 0;
|
|
for (i = 0; i < prev_max - curr_max; i += 2) {
|
|
mw_hdrp->mod_level = curr_max + i;
|
|
if (write(mw_dev, mw_hdrp, sizeof(struct monwrite_hdr)) == -1)
|
|
syslog(LOG_ERR, "write error on STOP: %s\n",
|
|
strerror(errno));
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Write process data to monitor stream
|
|
*/
|
|
static void procd_write_ent(void *entry, int size, char flag)
|
|
{
|
|
struct monwrite_hdr *mw_hdrp;
|
|
struct procd_hdr *mw_pchdrp;
|
|
|
|
char *mw_tmpp;
|
|
char *mw_bufp;
|
|
int write_len;
|
|
|
|
mw_bufp = mon_record;
|
|
mw_hdrp = (struct monwrite_hdr *)mw_bufp;
|
|
write_len = size + sizeof(struct monwrite_hdr)
|
|
+ sizeof(struct procd_hdr);
|
|
/* fill in monwrite_hdr */
|
|
if (flag == TASK_FLAG) {
|
|
if (write_len <= SMALL_MON_RECORD_LEN) {
|
|
write_len = SMALL_MON_RECORD_LEN;
|
|
mw_hdrp->mod_level = curr_small_max;
|
|
curr_small_max += 2;
|
|
} else {
|
|
write_len = MAX_REC_LEN;
|
|
mw_hdrp->mod_level = curr_big_max;
|
|
curr_big_max += 2;
|
|
}
|
|
} else {
|
|
mw_hdrp->mod_level = 0;
|
|
}
|
|
mw_hdrp->datalen = write_len - sizeof(struct monwrite_hdr);
|
|
mw_hdrp->applid = PROCD_APPLID;
|
|
mw_hdrp->record_num = flag;
|
|
mw_hdrp->hdrlen = sizeof(struct monwrite_hdr);
|
|
mw_hdrp->mon_function = MONWRITE_START_INTERVAL;
|
|
|
|
/* fill in procd_hdr */
|
|
mw_tmpp = mw_bufp + sizeof(struct monwrite_hdr);
|
|
mw_pchdrp = (struct procd_hdr *)mw_tmpp;
|
|
mw_pchdrp->time_stamp = (__u64) curr_time.tv_sec;
|
|
mw_pchdrp->data_len = (__u16) size;
|
|
mw_pchdrp->data_offset = (__u16) sizeof(struct procd_hdr);
|
|
|
|
/* fill in entry information */
|
|
mw_tmpp += sizeof(struct procd_hdr);
|
|
if (flag == SUM_FLAG)
|
|
memcpy(mw_tmpp, entry, size);
|
|
else
|
|
memcpy(mw_tmpp, entry, sizeof(struct task_t));
|
|
|
|
if (write(mw_dev, mw_bufp, write_len) == -1)
|
|
syslog(LOG_ERR, "write error: %s\n", strerror(errno));
|
|
}
|
|
|
|
/*
|
|
* Open and read a file into a buffer, terminated with buf[size/num] = '\0'
|
|
*/
|
|
static int read_file(char *fname, char *buf, int size)
|
|
{
|
|
int num, fp;
|
|
|
|
fp = open(fname, O_RDONLY);
|
|
if (!fp)
|
|
return -1;
|
|
|
|
num = read(fp, buf, size);
|
|
if (num < 0) {
|
|
close(fp);
|
|
return -1;
|
|
}
|
|
buf[num] = '\0';
|
|
|
|
close(fp);
|
|
return num;
|
|
}
|
|
|
|
/*
|
|
* Get uptime
|
|
*/
|
|
static void read_uptime(void)
|
|
{
|
|
double uptm;
|
|
|
|
if (read_file("/proc/uptime", buf, sizeof(buf) - 1) <= 0)
|
|
return;
|
|
|
|
if (sscanf(buf, "%lf ", &uptm) < 1) {
|
|
syslog(LOG_ERR, "bad data in %s \n", fname);
|
|
return;
|
|
}
|
|
proc_sum.uptime = (__u64)uptm;
|
|
}
|
|
|
|
/*
|
|
* Get number of users
|
|
*/
|
|
static void count_users(void)
|
|
{
|
|
struct utmp *temp;
|
|
|
|
proc_sum.users = 0;
|
|
setutent();
|
|
while ((temp = getutent())) {
|
|
if (temp->ut_type == USER_PROCESS)
|
|
proc_sum.users++;
|
|
}
|
|
endutent();
|
|
}
|
|
|
|
/*
|
|
* Get load averages
|
|
*/
|
|
static void read_loadavg(void)
|
|
{
|
|
int ret_num;
|
|
|
|
if (read_file("/proc/loadavg", buf, sizeof(buf) - 1) <= 0)
|
|
return;
|
|
|
|
ret_num = sscanf(buf, "%s %s %s", proc_sum.loadavg_1,
|
|
proc_sum.loadavg_5, proc_sum.loadavg_15);
|
|
|
|
if (ret_num < 3)
|
|
syslog(LOG_ERR, "bad data in %s \n", fname);
|
|
}
|
|
|
|
/*
|
|
* Calculate the state percentages for a CPU
|
|
*/
|
|
static void cal_cpu(struct cpudata_t *cpudt, struct cpu_t *cpu)
|
|
{
|
|
__u64 total_time;
|
|
|
|
total_time = cpudt->usr - cpudt->usr_prev;
|
|
total_time += cpudt->sys - cpudt->sys_prev;
|
|
total_time += cpudt->nice - cpudt->nice_prev;
|
|
if (cpudt->idle - cpudt->idle_prev > 0)
|
|
total_time += cpudt->idle - cpudt->idle_prev;
|
|
total_time += cpudt->iowt - cpudt->iowt_prev;
|
|
total_time += cpudt->irq - cpudt->irq_prev;
|
|
total_time += cpudt->sirq - cpudt->sirq_prev;
|
|
total_time += cpudt->steal - cpudt->steal_prev;
|
|
|
|
if (total_time < 1)
|
|
total_time = 1;
|
|
|
|
cpu->num_cpus = cpudt->id;
|
|
cpu->puser = (__u16)((cpudt->usr - cpudt->usr_prev) * 10000
|
|
/ total_time);
|
|
cpu->psystem = (__u16)((cpudt->sys - cpudt->sys_prev) * 10000
|
|
/ total_time);
|
|
cpu->pnice = (__u16)((cpudt->nice - cpudt->nice_prev) * 10000
|
|
/ total_time);
|
|
if (cpudt->idle - cpudt->idle_prev > 0)
|
|
cpu->pidle = (__u16)((cpudt->idle - cpudt->idle_prev) * 10000
|
|
/ total_time);
|
|
else
|
|
cpu->pidle = 0;
|
|
cpu->piowait = (__u16)((cpudt->iowt - cpudt->iowt_prev) * 10000
|
|
/ total_time);
|
|
cpu->pirq = (__u16)((cpudt->irq - cpudt->irq_prev) * 10000
|
|
/ total_time);
|
|
cpu->psoftirq = (__u16)((cpudt->sirq - cpudt->sirq_prev) * 10000
|
|
/ total_time);
|
|
cpu->psteal = (__u16)((cpudt->steal - cpudt->steal_prev) * 10000
|
|
/ total_time);
|
|
|
|
cpudt->usr_prev = cpudt->usr;
|
|
cpudt->sys_prev = cpudt->sys;
|
|
cpudt->nice_prev = cpudt->nice;
|
|
cpudt->idle_prev = cpudt->idle;
|
|
cpudt->iowt_prev = cpudt->iowt;
|
|
cpudt->irq_prev = cpudt->irq;
|
|
cpudt->sirq_prev = cpudt->sirq;
|
|
cpudt->steal_prev = cpudt->steal;
|
|
}
|
|
|
|
/*
|
|
* Get CPU summary information
|
|
*/
|
|
static void read_cpu(void)
|
|
{
|
|
unsigned long long u, n, s, i, w, x, y, z;
|
|
|
|
if (read_file("/proc/stat", buf, sizeof(buf) - 1) <= 0)
|
|
return;
|
|
|
|
u = n = s = i = w = x = y = z = 0;
|
|
temp = strstr(buf, "cpu");
|
|
if (temp)
|
|
sscanf(temp, "cpu %Lu %Lu %Lu %Lu %Lu %Lu %Lu %Lu",
|
|
&u, &n, &s, &i, &w, &x, &y, &z);
|
|
else
|
|
syslog(LOG_ERR, "no cpu in /proc/stat\n");
|
|
cpudata.usr = (__u64)u;
|
|
cpudata.nice = (__u64)n;
|
|
cpudata.sys = (__u64)s;
|
|
cpudata.idle = (__u64)i;
|
|
cpudata.iowt = (__u64)w;
|
|
cpudata.irq = (__u64)x;
|
|
cpudata.sirq = (__u64)y;
|
|
cpudata.steal = (__u64)z;
|
|
cpudata.id = (__u32)num_cpus;
|
|
cal_cpu(&cpudata, &proc_sum.cpu);
|
|
}
|
|
|
|
/*
|
|
* Get memory information
|
|
*/
|
|
static void read_mem(void)
|
|
{
|
|
unsigned long long mtotal, mfree, mbuf;
|
|
unsigned long long stotal, sfree, scached;
|
|
|
|
if (read_file("/proc/meminfo", buf, sizeof(buf) - 1) <= 0)
|
|
return;
|
|
|
|
mtotal = mfree = mbuf = stotal = sfree = scached = 0;
|
|
temp = strstr(buf, "MemTotal");
|
|
if (temp)
|
|
sscanf(temp, "MemTotal: %Lu kB", &mtotal);
|
|
else
|
|
syslog(LOG_ERR, "no MemTotal in /proc/meminfo\n");
|
|
temp = strstr(buf, "MemFree");
|
|
if (temp)
|
|
sscanf(temp, "MemFree: %Lu kB", &mfree);
|
|
else
|
|
syslog(LOG_ERR, "no MemFree in /proc/meminfo\n");
|
|
temp = strstr(buf, "Buffers");
|
|
if (temp)
|
|
sscanf(temp, "Buffers: %Lu kB", &mbuf);
|
|
else
|
|
syslog(LOG_ERR, "no Buffers in /proc/meminfo\n");
|
|
temp = strstr(buf, "SwapTotal");
|
|
if (temp)
|
|
sscanf(temp, "SwapTotal: %Lu kB", &stotal);
|
|
else
|
|
syslog(LOG_ERR, "no SwapTotal in /proc/meminfo\n");
|
|
temp = strstr(buf, "SwapFree");
|
|
if (temp)
|
|
sscanf(temp, "SwapFree: %Lu kB", &sfree);
|
|
else
|
|
syslog(LOG_ERR, "no SwapFree in /proc/meminfo\n");
|
|
temp = strstr(buf, "Cached");
|
|
if (temp)
|
|
sscanf(temp, "Cached: %Lu kB", &scached);
|
|
else
|
|
syslog(LOG_ERR, "no Cached in /proc/meminfo\n");
|
|
|
|
proc_sum.mem.total = (__u64)mtotal;
|
|
proc_sum.mem.free = (__u64)mfree;
|
|
proc_sum.mem.buffers = (__u64)mbuf;
|
|
proc_sum.swap.total = (__u64)stotal;
|
|
proc_sum.swap.free = (__u64)sfree;
|
|
proc_sum.swap.cached = (__u64)scached;
|
|
proc_sum.mem.used = proc_sum.mem.total - proc_sum.mem.free;
|
|
proc_sum.swap.used = proc_sum.swap.total - proc_sum.swap.free;
|
|
}
|
|
|
|
/*
|
|
* Get virtual memory information
|
|
*/
|
|
static void read_vmem(void)
|
|
{
|
|
unsigned long long pgin, pgout, swpin, swpout;
|
|
|
|
if (read_file("/proc/vmstat", buf, sizeof(buf) - 1) <= 0)
|
|
return;
|
|
|
|
pgin = pgout = swpin = swpout = 0;
|
|
temp = strstr(buf, "pgpgin");
|
|
if (temp)
|
|
sscanf(temp, "pgpgin %Lu", &pgin);
|
|
else
|
|
syslog(LOG_ERR, "no pgpgin in /proc/vmstat\n");
|
|
temp = strstr(buf, "pgpgout");
|
|
if (temp)
|
|
sscanf(temp, "pgpgout %Lu", &pgout);
|
|
else
|
|
syslog(LOG_ERR, "no pgpgout in /proc/vmstat\n");
|
|
temp = strstr(buf, "pswpin");
|
|
if (temp)
|
|
sscanf(temp, "pswpin %Lu", &swpin);
|
|
else
|
|
syslog(LOG_ERR, "no pswpin in /proc/vmstat\n");
|
|
temp = strstr(buf, "pswpout");
|
|
if (temp)
|
|
sscanf(temp, "pswpout %Lu", &swpout);
|
|
else
|
|
syslog(LOG_ERR, "no pswpout in /proc/vmstat\n");
|
|
|
|
proc_sum.mem.pgpgin = (__u64)(pgin << pg_to_kb_shift);
|
|
proc_sum.mem.pgpgout = (__u64)(pgout << pg_to_kb_shift);
|
|
proc_sum.swap.pswpin = (__u64)swpin;
|
|
proc_sum.swap.pswpout = (__u64)swpout;
|
|
}
|
|
|
|
/*
|
|
* Get process summary information
|
|
*/
|
|
static void read_summary(void)
|
|
{
|
|
read_uptime();
|
|
count_users();
|
|
read_loadavg();
|
|
read_cpu();
|
|
read_mem();
|
|
read_vmem();
|
|
}
|
|
|
|
/*
|
|
* Get memory information for a task
|
|
*/
|
|
static int read_statm(struct task_t *task)
|
|
{
|
|
long size, res, sh, trs, lrs, drs, dt;
|
|
|
|
snprintf(fname, sizeof(fname), "/proc/%u/statm", task->pid);
|
|
if (read_file(fname, buf, sizeof(buf) - 1) == -1)
|
|
return 0;
|
|
|
|
sscanf(buf, "%ld %ld %ld %ld %ld %ld %ld",
|
|
&size, &res, &sh, &trs, &lrs, &drs, &dt);
|
|
task->size = (__u64)(size << pg_to_kb_shift);
|
|
task->resident = (__u64)(res << pg_to_kb_shift);
|
|
task->swap = task->size - task->resident;
|
|
task->share = (__u64)(sh << pg_to_kb_shift);
|
|
task->trs = (__u64)(trs << pg_to_kb_shift);
|
|
task->drs = (__u64)(drs << pg_to_kb_shift);
|
|
task->dt = (__u64)dt;
|
|
|
|
task->pmem = (__u16)(task->resident * 10000 / proc_sum.mem.total);
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
* Get status information for a task from /proc/.../status
|
|
*/
|
|
static int read_status(struct task_t *task)
|
|
{
|
|
int ruid, euid, egid;
|
|
char *lenp, *namep;
|
|
struct passwd *pwd;
|
|
struct group *grp;
|
|
|
|
snprintf(fname, sizeof(fname), "/proc/%u/status", task->pid);
|
|
if (read_file(fname, buf, sizeof(buf) - 1) == -1)
|
|
return 0;
|
|
|
|
ruid = euid = egid = 0;
|
|
temp = strstr(buf, "Uid");
|
|
if (temp)
|
|
sscanf(temp, "Uid: %d %d", &ruid, &euid);
|
|
else
|
|
syslog(LOG_ERR, "no Uid in /proc/%u/status\n", task->pid);
|
|
temp = strstr(buf, "Gid");
|
|
if (temp)
|
|
sscanf(temp, "Gid: %*d %d", &egid);
|
|
else
|
|
syslog(LOG_ERR, "no Gid in /proc/%u/status\n", task->pid);
|
|
task->euid = (__u16)euid;
|
|
|
|
lenp = mon_record + sizeof(struct monwrite_hdr);
|
|
lenp += sizeof(struct procd_hdr);
|
|
lenp += sizeof(struct task_t);
|
|
namep = lenp + sizeof(__u16);
|
|
pwd = getpwuid(ruid);
|
|
if (!pwd)
|
|
name_lens.ruser_len = sprintf(namep, "%u", ruid);
|
|
else {
|
|
name_lens.ruser_len = strlen(pwd->pw_name);
|
|
if (name_lens.ruser_len > MAX_NAME_LEN)
|
|
name_lens.ruser_len = MAX_NAME_LEN;
|
|
memcpy(namep, pwd->pw_name, name_lens.ruser_len);
|
|
}
|
|
memcpy(lenp, &name_lens.ruser_len, sizeof(__u16));
|
|
|
|
lenp = namep + name_lens.ruser_len;
|
|
namep = lenp + sizeof(__u16);
|
|
pwd = getpwuid(task->euid);
|
|
if (!pwd)
|
|
name_lens.euser_len = sprintf(namep, "%u", task->euid);
|
|
else {
|
|
name_lens.euser_len = strlen(pwd->pw_name);
|
|
if (name_lens.euser_len > MAX_NAME_LEN)
|
|
name_lens.euser_len = MAX_NAME_LEN;
|
|
memcpy(namep, pwd->pw_name, name_lens.euser_len);
|
|
}
|
|
memcpy(lenp, &name_lens.euser_len, sizeof(__u16));
|
|
|
|
lenp = namep + name_lens.euser_len;
|
|
namep = lenp + sizeof(__u16);
|
|
grp = getgrgid(egid);
|
|
if (!grp)
|
|
name_lens.egroup_len = sprintf(namep, "%u", egid);
|
|
else {
|
|
name_lens.egroup_len = strlen(grp->gr_name);
|
|
if (name_lens.egroup_len > MAX_NAME_LEN)
|
|
name_lens.egroup_len = MAX_NAME_LEN;
|
|
memcpy(namep, grp->gr_name, name_lens.egroup_len);
|
|
}
|
|
memcpy(lenp, &name_lens.egroup_len, sizeof(__u16));
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
* Calculate percentage of CPU used by a task since last sampling
|
|
*/
|
|
static void cal_task_pcpu(struct task_t *task, const unsigned long long tics)
|
|
{
|
|
unsigned int i, size;
|
|
__u64 etics;
|
|
|
|
if (proc_sum.task.total + 1 >= sort_tbl_size) {
|
|
sort_tbl_size = sort_tbl_size * 5 / 4 + 100;
|
|
size = sort_tbl_size * sizeof(struct task_sort_t);
|
|
curr_sort_tbl = realloc(curr_sort_tbl, size);
|
|
if (!curr_sort_tbl) {
|
|
fprintf(stderr, "Allocating memory failed - "
|
|
"reason %s\n", strerror(errno));
|
|
exit(1);
|
|
}
|
|
prev_sort_tbl = realloc(prev_sort_tbl, size);
|
|
if (!prev_sort_tbl) {
|
|
fprintf(stderr, "Allocating memory failed - "
|
|
"reason %s\n", strerror(errno));
|
|
exit(1);
|
|
}
|
|
}
|
|
curr_sort_tbl[proc_sum.task.total].pid = task->pid;
|
|
curr_sort_tbl[proc_sum.task.total].tics = (__u64)tics;
|
|
|
|
etics = (__u64)tics;
|
|
for (i = 0; i < sort_tbl_size; i++) {
|
|
if (prev_sort_tbl[i].pid == task->pid) {
|
|
etics -= prev_sort_tbl[i].tics;
|
|
break;
|
|
}
|
|
}
|
|
task->pcpu = (__u16)((etics * 10000 / Hertz) / (e_time * num_cpus));
|
|
if (task->pcpu > 9999)
|
|
task->pcpu = 9999;
|
|
}
|
|
|
|
/*
|
|
* Get status information for a task from /proc/.../stat
|
|
*/
|
|
static int read_stat(struct task_t *task)
|
|
{
|
|
unsigned long long maj_flt = 0, utime = 0, stime = 0, cutime = 0,
|
|
cstime = 0;
|
|
unsigned long flags = 0, pri = 0, nice = 0;
|
|
char *cmd_start, *cmd_end, *cmdlenp, *cmdp;
|
|
int ppid = 0, tty = 0, proc = 0, rc;
|
|
|
|
snprintf(fname, sizeof(fname), "/proc/%u/stat", task->pid);
|
|
if (read_file(fname, buf, sizeof(buf) - 1) == -1)
|
|
return 0;
|
|
|
|
cmd_start = strchr(buf, '(') + 1;
|
|
cmd_end = strrchr(cmd_start, ')');
|
|
name_lens.cmd_len = cmd_end - cmd_start;
|
|
cmdlenp = mon_record + sizeof(struct monwrite_hdr);
|
|
cmdlenp += sizeof(struct procd_hdr);
|
|
cmdlenp += sizeof(struct task_t);
|
|
cmdlenp += sizeof(__u16) + name_lens.ruser_len;
|
|
cmdlenp += sizeof(__u16) + name_lens.euser_len;
|
|
cmdlenp += sizeof(__u16) + name_lens.egroup_len;
|
|
cmdlenp += sizeof(__u16) + name_lens.wchan_len;
|
|
|
|
if (name_lens.cmd_len <= 0)
|
|
name_lens.cmd_len = 0;
|
|
else {
|
|
cmdp = cmdlenp + sizeof(__u16);
|
|
if (name_lens.cmd_len > MAX_NAME_LEN)
|
|
name_lens.cmd_len = MAX_NAME_LEN;
|
|
memcpy(cmdp, cmd_start, name_lens.cmd_len);
|
|
}
|
|
memcpy(cmdlenp, &name_lens.cmd_len, sizeof(__u16));
|
|
|
|
cmd_end += 2;
|
|
rc = sscanf(cmd_end,
|
|
"%c %d %*d %*d %d %*d "
|
|
"%lu %*s %*s %Lu %*s "
|
|
"%Lu %Lu %Lu %Lu "
|
|
"%ld %ld "
|
|
"%*d %*s "
|
|
"%*s %*s %*s "
|
|
"%*s %*s %*s %*s %*s %*s "
|
|
"%*s %*s %*s %*s "
|
|
"%*s %*s %*s "
|
|
"%*d %d "
|
|
"%*s %*s",
|
|
&task->state, &ppid, &tty,
|
|
&flags, &maj_flt,
|
|
&utime, &stime, &cutime, &cstime,
|
|
&pri, &nice,
|
|
&proc);
|
|
if (rc != 12)
|
|
syslog(LOG_ERR, "bad data in %s \n", fname);
|
|
task->ppid = (__u32)ppid;
|
|
task->tty = (__u16)tty;
|
|
task->flags = (__u32)flags;
|
|
task->maj_flt = (__u64)maj_flt;
|
|
task->priority = (__s16)pri;
|
|
task->nice = (__s16)nice;
|
|
task->processor = (__u32)proc;
|
|
task->total_time = (__u64)((utime + stime) * 100 / Hertz);
|
|
task->ctotal_time = (__u64)((utime + stime + cutime + cstime) * 100
|
|
/ Hertz);
|
|
cal_task_pcpu(task, utime + stime);
|
|
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
* Get the sleeping in function of a task
|
|
*/
|
|
static int read_wchan(struct task_t *task)
|
|
{
|
|
int num;
|
|
char *wchanlenp, *wchanp;
|
|
|
|
snprintf(fname, sizeof(fname), "/proc/%u/wchan", task->pid);
|
|
num = read_file(fname, buf, sizeof(buf) -1);
|
|
|
|
if (num < 0)
|
|
return 0;
|
|
|
|
wchanlenp = mon_record + sizeof(struct monwrite_hdr);
|
|
wchanlenp += sizeof(struct procd_hdr);
|
|
wchanlenp += sizeof(struct task_t);
|
|
wchanlenp += sizeof(__u16) + name_lens.ruser_len;
|
|
wchanlenp += sizeof(__u16) + name_lens.euser_len;
|
|
wchanlenp += sizeof(__u16) + name_lens.egroup_len;
|
|
wchanp = wchanlenp + sizeof(__u16);
|
|
|
|
name_lens.wchan_len = num;
|
|
if (num == 0) {
|
|
memcpy(wchanlenp, &name_lens.wchan_len, sizeof(__u16));
|
|
return 1;
|
|
}
|
|
buf[num] = '\0';
|
|
|
|
if (buf[0] == '0' && buf[1] == '\0') {
|
|
memcpy(wchanlenp, &name_lens.wchan_len, sizeof(__u16));
|
|
wchanp[0] = '-';
|
|
return 1;
|
|
}
|
|
|
|
if (buf[0] == 's' && !strncmp(buf, "sys_", 4)) {
|
|
name_lens.wchan_len -= 4;
|
|
if (name_lens.wchan_len > MAX_NAME_LEN)
|
|
name_lens.wchan_len = MAX_NAME_LEN;
|
|
memcpy(wchanlenp, &name_lens.wchan_len, sizeof(__u16));
|
|
memcpy(wchanp, buf + 4, name_lens.wchan_len);
|
|
return 1;
|
|
}
|
|
|
|
if (buf[0] == 'd' && !strncmp(buf, "do_", 3)) {
|
|
name_lens.wchan_len -= 3;
|
|
if (name_lens.wchan_len > MAX_NAME_LEN)
|
|
name_lens.wchan_len = MAX_NAME_LEN;
|
|
memcpy(wchanlenp, &name_lens.wchan_len, sizeof(__u16));
|
|
memcpy(wchanp, buf + 3, name_lens.wchan_len);
|
|
return 1;
|
|
}
|
|
|
|
temp = buf;
|
|
while (*temp == '_') {
|
|
temp++;
|
|
name_lens.wchan_len--;
|
|
}
|
|
if (name_lens.wchan_len > MAX_NAME_LEN)
|
|
name_lens.wchan_len = MAX_NAME_LEN;
|
|
memcpy(wchanlenp, &name_lens.wchan_len, sizeof(__u16));
|
|
memcpy(wchanp, temp, name_lens.wchan_len);
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
* Get command line of a task
|
|
*/
|
|
static int read_cmdline(struct task_t *task)
|
|
{
|
|
int i, num;
|
|
char *cmdlnlenp, *cmdlinep;
|
|
|
|
snprintf(fname, sizeof(fname), "/proc/%u/cmdline", task->pid);
|
|
num = read_file(fname, buf, sizeof(buf) - 1);
|
|
if (num == -1)
|
|
return 0;
|
|
for (i = 0; i < num; i++) {
|
|
if (buf[i] < ' ' || buf[i] > '~')
|
|
buf[i] = ' ';
|
|
}
|
|
name_lens.cmdline_len = num;
|
|
cmdlnlenp = mon_record + sizeof(struct monwrite_hdr);
|
|
cmdlnlenp += sizeof(struct procd_hdr);
|
|
cmdlnlenp += sizeof(struct task_t);
|
|
cmdlnlenp += sizeof(__u16) + name_lens.ruser_len;
|
|
cmdlnlenp += sizeof(__u16) + name_lens.euser_len;
|
|
cmdlnlenp += sizeof(__u16) + name_lens.egroup_len;
|
|
cmdlnlenp += sizeof(__u16) + name_lens.wchan_len;
|
|
cmdlnlenp += sizeof(__u16) + name_lens.cmd_len;
|
|
cmdlinep = cmdlnlenp + sizeof(__u16);
|
|
|
|
memcpy(cmdlnlenp, &name_lens.cmdline_len, sizeof(__u16));
|
|
if (name_lens.cmdline_len > 0)
|
|
memcpy(cmdlinep, buf, name_lens.cmdline_len);
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
* Usage sorting help function
|
|
*/
|
|
static int sort_usage(const void *et1, const void *et2)
|
|
{
|
|
return ((struct task_sort_t *)et2)->cpu_mem_usage
|
|
- ((struct task_sort_t *)et1)->cpu_mem_usage;
|
|
}
|
|
|
|
/*
|
|
* Adjust task state counters
|
|
*/
|
|
static void task_count(char oldst, char newst)
|
|
{
|
|
if (oldst == newst)
|
|
return;
|
|
|
|
switch (oldst) {
|
|
case 'R':
|
|
proc_sum.task.running--;
|
|
break;
|
|
case 'S':
|
|
case 'D':
|
|
proc_sum.task.sleeping--;
|
|
break;
|
|
case 'T':
|
|
proc_sum.task.stopped--;
|
|
break;
|
|
case 'Z':
|
|
proc_sum.task.zombie--;
|
|
break;
|
|
}
|
|
switch (newst) {
|
|
case 'R':
|
|
proc_sum.task.running++;
|
|
break;
|
|
case 'S':
|
|
case 'D':
|
|
proc_sum.task.sleeping++;
|
|
break;
|
|
case 'T':
|
|
proc_sum.task.stopped++;
|
|
break;
|
|
case 'Z':
|
|
proc_sum.task.zombie++;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Read and calculate memory and cpu usages of a task
|
|
*/
|
|
static void task_usage(struct task_t *task)
|
|
{
|
|
long res;
|
|
unsigned long long utime, stime;
|
|
|
|
snprintf(fname, sizeof(fname), "/proc/%u/statm", task->pid);
|
|
if (read_file(fname, buf, sizeof(buf) - 1) == -1)
|
|
return;
|
|
sscanf(buf, "%*s %ld %*s %*s %*s %*s %*s", &res);
|
|
task->resident = (__u64)(res << pg_to_kb_shift);
|
|
task->pmem = (__u16)(task->resident * 10000 / proc_sum.mem.total);
|
|
|
|
snprintf(fname, sizeof(fname), "/proc/%u/stat", task->pid);
|
|
if (read_file(fname, buf, sizeof(buf) - 1) == -1)
|
|
return;
|
|
sscanf(buf,
|
|
"%*s %*s "
|
|
"%c %*s %*s %*s %*s %*s "
|
|
"%*s %*s %*s %*s %*s "
|
|
"%Lu %Lu ",
|
|
&task->state,
|
|
&utime, &stime);
|
|
cal_task_pcpu(task, utime + stime);
|
|
|
|
curr_sort_tbl[proc_sum.task.total].cpu_mem_usage = task->pcpu +
|
|
task->pmem;
|
|
curr_sort_tbl[proc_sum.task.total].state = task->state;
|
|
task_count('\0', task->state);
|
|
proc_sum.task.total++;
|
|
}
|
|
|
|
/*
|
|
* read tasks information and write to monitor stream
|
|
*/
|
|
static void read_tasks(void)
|
|
{
|
|
int size;
|
|
unsigned int i = 0, j = 0;
|
|
DIR *proc_dir;
|
|
struct direct *entry;
|
|
struct task_t task;
|
|
|
|
proc_dir = opendir("/proc");
|
|
if (!proc_dir) {
|
|
syslog(LOG_ERR, "failed /proc open: %s ", strerror(errno));
|
|
return;
|
|
}
|
|
|
|
while ((entry = readdir(proc_dir))) {
|
|
if (!isdigit(entry->d_name[0]) || !atoi(entry->d_name))
|
|
continue;
|
|
memset(&task, 0, sizeof(struct task_t));
|
|
task.pid = atoi(entry->d_name);
|
|
task_usage(&task);
|
|
}
|
|
closedir(proc_dir);
|
|
|
|
if (proc_sum.task.total > MAX_TASK_REC)
|
|
qsort(curr_sort_tbl, proc_sum.task.total,
|
|
sizeof(struct task_sort_t), sort_usage);
|
|
|
|
/* only write up to top 100 processes data to monitor stream */
|
|
while ((i < proc_sum.task.total) && (j < MAX_TASK_REC)) {
|
|
memset(&task, 0, sizeof(struct task_t));
|
|
memset(mon_record, 0, sizeof(mon_record));
|
|
task.pid = curr_sort_tbl[i].pid;
|
|
if (read_statm(&task) && read_status(&task) &&
|
|
read_wchan(&task) && read_stat(&task) &&
|
|
read_cmdline(&task)) {
|
|
task_count(curr_sort_tbl[i].state, task.state);
|
|
size = sizeof(struct task_t);
|
|
size += sizeof(struct name_lens_t);
|
|
size += name_lens.ruser_len + name_lens.euser_len;
|
|
size += name_lens.egroup_len + name_lens.wchan_len;
|
|
size += name_lens.cmd_len + name_lens.cmdline_len;
|
|
procd_write_ent(&task, size, TASK_FLAG);
|
|
j++;
|
|
} else
|
|
task_count(curr_sort_tbl[i].state, '\0');
|
|
i++;
|
|
}
|
|
proc_sum.task.total -= i - j;
|
|
}
|
|
|
|
/*
|
|
* Run as background process
|
|
*/
|
|
static void procd_daemonize(void)
|
|
{
|
|
int pipe_fds[2], startup_rc = 1;
|
|
pid_t pid;
|
|
|
|
if (pipe(pipe_fds) == -1) {
|
|
syslog(LOG_ERR, "pipe error: %s\n", strerror(errno));
|
|
exit(1);
|
|
}
|
|
|
|
/* Fork off the parent process */
|
|
pid = fork();
|
|
if (pid < 0) {
|
|
syslog(LOG_ERR, "fork error: %s\n", strerror(errno));
|
|
exit(1);
|
|
}
|
|
if (pid > 0) {
|
|
/* Wait for startup return code from daemon */
|
|
if (read(pipe_fds[0], &startup_rc, sizeof(startup_rc)) == -1)
|
|
syslog(LOG_ERR, "pipe read error: %s\n", strerror(errno));
|
|
/* With startup_rc == 0, pid file was written at this point */
|
|
exit(startup_rc);
|
|
}
|
|
|
|
/* Change the file mode mask */
|
|
umask(0);
|
|
|
|
/* Catch SIGINT and SIGTERM to clean up pid file on exit */
|
|
procd_handle_signals();
|
|
|
|
/* Create a new SID for the child process */
|
|
if (setsid() < 0) {
|
|
syslog(LOG_ERR, "setsid error: %s\n", strerror(errno));
|
|
goto notify_parent;
|
|
}
|
|
|
|
/* Change the current working directory */
|
|
if (chdir("/") < 0) {
|
|
syslog(LOG_ERR, "chdir error: %s\n", strerror(errno));
|
|
goto notify_parent;
|
|
}
|
|
|
|
/* Close out the standard file descriptors */
|
|
close(STDIN_FILENO);
|
|
close(STDOUT_FILENO);
|
|
close(STDERR_FILENO);
|
|
|
|
/* Store daemon pid */
|
|
if (store_pid() < 0)
|
|
goto notify_parent;
|
|
startup_rc = 0;
|
|
|
|
notify_parent:
|
|
/* Inform waiting parent about startup return code */
|
|
if (write(pipe_fds[1], &startup_rc, sizeof(startup_rc)) == -1) {
|
|
syslog(LOG_ERR, "pipe write error: %s\n", strerror(errno));
|
|
exit(1);
|
|
}
|
|
if (startup_rc != 0)
|
|
exit(startup_rc);
|
|
}
|
|
|
|
static int procd_do_work(void)
|
|
{
|
|
int pgsize;
|
|
struct timezone tz;
|
|
struct task_sort_t *tmp;
|
|
|
|
prev_small_max = 0;
|
|
prev_big_max = 1;
|
|
num_cpus = sysconf(_SC_NPROCESSORS_ONLN);
|
|
if (num_cpus < 1)
|
|
num_cpus = 1;
|
|
|
|
pg_to_kb_shift = 0;
|
|
pgsize = getpagesize();
|
|
while (pgsize > 1024) {
|
|
pgsize >>= 1;
|
|
pg_to_kb_shift++;
|
|
}
|
|
|
|
syslog(LOG_INFO, "procd sample interval: %lu\n", sample_interval);
|
|
while (1) {
|
|
gettimeofday(&curr_time, &tz);
|
|
e_time = (curr_time.tv_sec - prev_time.tv_sec) +
|
|
(float)(curr_time.tv_usec - prev_time.tv_usec)
|
|
/ 1000000.0;
|
|
memset(&proc_sum, 0, sizeof(struct proc_sum_t));
|
|
tmp = prev_sort_tbl;
|
|
prev_sort_tbl = curr_sort_tbl;
|
|
curr_sort_tbl = tmp;
|
|
curr_small_max = 0;
|
|
curr_big_max = 1;
|
|
read_summary();
|
|
read_tasks();
|
|
memset(mon_record, 0, sizeof(mon_record));
|
|
procd_write_ent(&proc_sum, sizeof(struct proc_sum_t), SUM_FLAG);
|
|
|
|
if (curr_small_max < prev_small_max)
|
|
stop_unused(curr_small_max, prev_small_max);
|
|
if (curr_big_max < prev_big_max)
|
|
stop_unused(curr_big_max, prev_big_max);
|
|
|
|
prev_small_max = curr_small_max;
|
|
prev_big_max = curr_big_max;
|
|
prev_time.tv_sec = curr_time.tv_sec;
|
|
prev_time.tv_usec = curr_time.tv_usec;
|
|
sleep(sample_interval);
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
Parse options
|
|
*/
|
|
static int parse_options(int argc, char **argv)
|
|
{
|
|
int opt;
|
|
|
|
do {
|
|
opt = getopt_long(argc, argv, opt_string, options, NULL);
|
|
switch (opt) {
|
|
case -1:
|
|
/* Reached end of parameter list. */
|
|
break;
|
|
case 'h':
|
|
printf("%s", help_text);
|
|
exit(0);
|
|
case 'v':
|
|
printf( "mon_procd: version %s\n", RELEASE_STRING);
|
|
printf("Copyright IBM Corp. 2007, 2017\n");
|
|
exit(0);
|
|
case 'a':
|
|
attach = 1;
|
|
break;
|
|
case 'i':
|
|
sample_interval = strtol(optarg, NULL, 10);
|
|
if (sample_interval <= 0) {
|
|
fprintf(stderr, "Error: Invalid interval "
|
|
"(needs to be greater than 0)\n");
|
|
return(1);
|
|
}
|
|
break;
|
|
default:
|
|
fprintf(stderr, "Try ' --help' for more"
|
|
" information.\n");
|
|
return(1);
|
|
}
|
|
} while (opt != -1);
|
|
return(0);
|
|
}
|
|
|
|
int main(int argc, char **argv)
|
|
{
|
|
int rc;
|
|
|
|
attach = 0;
|
|
rc = parse_options(argc, argv);
|
|
if (rc > 0)
|
|
return rc;
|
|
procd_open_monwriter();
|
|
openlog("mon_procd", 0, LOG_DAEMON);
|
|
if (!attach)
|
|
procd_daemonize();
|
|
rc = procd_do_work();
|
|
close(mw_dev);
|
|
return rc;
|
|
}
|