Files
s390-tools/mon_tools/mon_procd.c
Sertonix 10d72caac0 Add missing includes
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>
2025-12-11 15:46:05 +01:00

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;
}