1218 lines
28 KiB
C
1218 lines
28 KiB
C
/*
|
|
* $QNXLicenseC:
|
|
* Copyright 2007, QNX Software Systems. All Rights Reserved.
|
|
*
|
|
* You must obtain a written license from and pay applicable license fees to QNX
|
|
* Software Systems before you may reproduce, modify or distribute this software,
|
|
* or any work that includes all or part of this software. Free development
|
|
* licenses are available for evaluation and non-commercial purposes. For more
|
|
* information visit http://licensing.qnx.com or email licensing@qnx.com.
|
|
*
|
|
* This file may contain contributions from others. Please review this entire
|
|
* file for other proprietary rights or license notices, as well as the QNX
|
|
* Development Suite License Guide at http://licensing.qnx.com/license-guide/
|
|
* for other information.
|
|
* $
|
|
*/
|
|
|
|
|
|
|
|
#include "pidin.h"
|
|
|
|
#include <assert.h>
|
|
#include <ctype.h>
|
|
#include <dirent.h>
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <gulliver.h>
|
|
#include <stddef.h>
|
|
#include <stdarg.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <sys/neutrino.h>
|
|
#include <unistd.h>
|
|
#include <sys/netmgr.h>
|
|
#include <sys/netmsg.h>
|
|
#include <sys/sysmgr.h>
|
|
#include <sys/sysmsg.h>
|
|
#include <sys/conf.h>
|
|
#include <sched.h>
|
|
#include <sys/sched_aps.h>
|
|
|
|
|
|
char *opts = "d:klM:n:f:F:ho:p:P:";
|
|
|
|
void dspinfo(pid_t pid, char *pidname, int flags, const char *fmt, const char *mem_format);
|
|
void piddspinfo(int fd, char *pidname, pid_t pid, int flags, const char *fmt, const char *mem_format);
|
|
int find_netdir(char *nodename, char *buf, size_t bufsize, int flag);
|
|
char* tail(char *name);
|
|
|
|
char *node = "";
|
|
|
|
static char *format = "%a %b %N %p %J %B";
|
|
static int exit_on_warning = 0;
|
|
|
|
static struct shorthand {
|
|
char *handle;
|
|
char *format;
|
|
char *mem_format;
|
|
} shorthands[] =
|
|
{
|
|
// these need to be in reverse lexigraphical order for lookup to work
|
|
{
|
|
"users", "%a %N %U %V %W %X %Y %Z"
|
|
}, // users
|
|
{
|
|
"ttimes", "%a %b %N %J %t %y %z"
|
|
}, // ttimes
|
|
{
|
|
"times", "%a %N %L %t %u %v %w %x"
|
|
}, // times
|
|
{
|
|
"timers", "%a %b %N %R"
|
|
}, // pidin timers
|
|
{
|
|
"threads", "%a %N %h %J %B"
|
|
}, // pidin threads
|
|
{
|
|
"syspage", 0, "Page"
|
|
}, // syspage
|
|
{
|
|
"signals", "%a %b %N %S %s"
|
|
}, // signals
|
|
{
|
|
"session","%L %a %P %e %N"
|
|
}, // session
|
|
{
|
|
"sched", "%a %b %N %p %l %H %J",
|
|
}, // scheduling
|
|
{
|
|
"rmasks", "%a %b %N %i"
|
|
}, // runmasks
|
|
{
|
|
"regs", "%a %b %N %r"
|
|
}, // registers
|
|
{
|
|
"rc", 0, "rc"
|
|
}, // remote connections
|
|
{
|
|
"pmem", "%a %b %N %p %J %c %d %m",
|
|
}, // memory (process only)
|
|
{
|
|
"net", 0, "net"
|
|
}, // node info on network
|
|
{
|
|
"memory", "%a %b %N %p %J %c %d %m",
|
|
" %M @%> %? %< %="
|
|
}, // memory
|
|
{
|
|
"irqs", "%a %b %N %Q"
|
|
}, // interrupts
|
|
{
|
|
"channels", "%a %b %N %["
|
|
}, // channels
|
|
{
|
|
"info", 0, "system"
|
|
}, // flags
|
|
{
|
|
"flags", "%a %N %f"
|
|
}, // flags
|
|
{
|
|
"fds", "%a %N %o"
|
|
}, // fd
|
|
{
|
|
"family", "%a %N %L %P %e %G %C"
|
|
}, // family
|
|
{
|
|
"extsched", NULL, "ExtSchedulers"
|
|
}, // ext_schedulers
|
|
{
|
|
"environment", "%a %N %E"
|
|
}, // environment
|
|
{
|
|
"arguments", "%a %A"
|
|
}, // arguments
|
|
{
|
|
0, 0
|
|
}
|
|
};
|
|
|
|
static char *normalize_format(const char *f);
|
|
static struct format *get_format(FILE * fp, const char **f);
|
|
static struct shorthand *lookup_shorthand(const char *f);
|
|
|
|
void dspsys(const char *type, const char *argv);
|
|
|
|
int
|
|
main(int argc, char *argv[])
|
|
{
|
|
int c;
|
|
int p = 1;
|
|
char *pidname = NULL;
|
|
int flags = 0;
|
|
const char *f;
|
|
int loopdelay = 10;
|
|
int loop = 0;
|
|
int pri = 0;
|
|
const char * mem = 0;
|
|
|
|
if(argc == 2) {
|
|
//
|
|
// By long QSSL tradition, the QNX process info utility must
|
|
// have easter eggs.
|
|
//
|
|
if(strcmp(argv[1], "tsk") == 0) {
|
|
printf("Boy, you've been using QNX for a loooong time!\n");
|
|
exit(0);
|
|
}
|
|
if(strcmp(argv[1], "toomuch") == 0) {
|
|
printf("Oh, wow. What OS are you tripping with man?\n");
|
|
exit(0);
|
|
}
|
|
if(strcmp(argv[1], "sin") == 0) {
|
|
printf("Since we're without sin, we're casting the first stone.\n");
|
|
exit(0);
|
|
}
|
|
if(strcmp(argv[1], "english") == 0) {
|
|
printf("Can't even spell pidgin english properly?\n");
|
|
exit(0);
|
|
}
|
|
if(strcmp(argv[1], "pie") == 0) {
|
|
printf("Mmmm, pudding pie. Them's good eating.\n");
|
|
exit(0);
|
|
}
|
|
}
|
|
|
|
while ((c = getopt(argc, argv, opts)) != -1)
|
|
switch (c) {
|
|
case 'd':
|
|
loopdelay = strtoul(optarg, 0, 0);
|
|
break;
|
|
|
|
case 'h':
|
|
execlp("use", "use", argv[0], NULL);
|
|
exit(EXIT_FAILURE);
|
|
break;
|
|
|
|
case 'f':
|
|
format = normalize_format(optarg);
|
|
if(format == NULL) {
|
|
error_exit(1, "\nno memory for format\n");
|
|
}
|
|
break;
|
|
|
|
case 'F':
|
|
format = optarg;
|
|
break;
|
|
|
|
case 'k':
|
|
exit_on_warning = 1;
|
|
break;
|
|
|
|
case 'l':
|
|
loop++;
|
|
break;
|
|
|
|
case 'M':
|
|
mem = optarg;
|
|
break;
|
|
|
|
case 'o':
|
|
pri = strtoul(optarg, 0, 0);
|
|
break;
|
|
|
|
case 'p':
|
|
flags = DONT_RECURSE;
|
|
/*
|
|
* fall through
|
|
*/
|
|
case 'P':
|
|
if(*optarg >= '0' && *optarg <= '9') {
|
|
p = atoi(optarg);
|
|
} else {
|
|
pidname = optarg;
|
|
}
|
|
break;
|
|
|
|
case 'n':
|
|
node = optarg;
|
|
break;
|
|
|
|
case '?':
|
|
//getopt already reported the error
|
|
exit(1);
|
|
|
|
default:
|
|
error_exit(1, "invalid option '%c'\n", c);
|
|
break;
|
|
}
|
|
|
|
if (pri) {
|
|
struct sched_param p;
|
|
p.sched_priority = pri;
|
|
if (SchedSet(0, 0, SCHED_NOCHANGE, &p) == -1)
|
|
error_exit(1, "sched_setprio: %s\n", strerror(errno));
|
|
}
|
|
|
|
do {
|
|
do {
|
|
const char *g;
|
|
const char *mf = 0;
|
|
struct shorthand *s = 0;
|
|
|
|
if (optind < argc &&
|
|
!(s = lookup_shorthand(argv[optind]))) {
|
|
error_exit(1, "%s invalid shorthand\n", argv[optind]);
|
|
}
|
|
|
|
if (!s) {
|
|
f = g = format;
|
|
} else if(s->format) {
|
|
f = g = s->format;
|
|
mf = s->mem_format;
|
|
} else {
|
|
dspsys(s->mem_format, argv[optind]);
|
|
if (loop)
|
|
delay(loopdelay * 100);
|
|
continue;
|
|
}
|
|
if (mem) {
|
|
mf = mem;
|
|
}
|
|
|
|
while (*f) {
|
|
struct format *format;
|
|
|
|
if (!(format = get_format(stdout, &f)))
|
|
break;
|
|
if (format->flags & THREAD_UNIQUE)
|
|
flags |= DO_THREADS;
|
|
if (format->flags & MULTI_LINE)
|
|
continue;
|
|
format_title_string(stdout, format, format->title);
|
|
}
|
|
fputc('\n', stdout);
|
|
|
|
dspinfo(p, pidname, flags, g, mf);
|
|
if (loop)
|
|
delay(loopdelay * 100);
|
|
} while (loop);
|
|
} while (++optind < argc);
|
|
|
|
return EXIT_SUCCESS;
|
|
}
|
|
|
|
/* node = "", local node
|
|
* flag = 0, find the root of "node" (default: /net/node)
|
|
* flag = 1, find the network mountpoint of node (default: /net/node/net)
|
|
*/
|
|
int find_netdir(char *node, char *buf, size_t bufsize, int flag)
|
|
{
|
|
int len, fd;
|
|
|
|
if (*node == 0) {
|
|
*buf = '/';
|
|
*(buf + 1) = 0;
|
|
len = 1;
|
|
if (flag)
|
|
len = netmgr_ndtostr(ND2S_DIR_SHOW | ND2S_NAME_HIDE, ND_LOCAL_NODE, buf, bufsize);
|
|
return len;
|
|
}
|
|
|
|
if (*node == '/') {
|
|
len = snprintf(buf, bufsize, "%s/", node);
|
|
} else {
|
|
if ((len = netmgr_ndtostr(ND2S_DIR_SHOW | ND2S_NAME_HIDE, ND_LOCAL_NODE, buf, bufsize)) == -1)
|
|
return -1;
|
|
len--;
|
|
len += snprintf(&buf[len], bufsize - len, "%s/", node);
|
|
}
|
|
|
|
if (flag) {
|
|
char *netmgr;
|
|
netmgr_ndtostr_t msg;
|
|
|
|
if (!(netmgr = alloca(len + 12)))
|
|
return -1;
|
|
|
|
sprintf(netmgr, "%s/dev/netmgr", buf);
|
|
|
|
if ((fd = open(netmgr, O_RDONLY)) == -1)
|
|
return -1;
|
|
|
|
msg.i.hdr.type = _IO_MSG;
|
|
msg.i.hdr.combine_len = sizeof msg.i;
|
|
msg.i.hdr.mgrid = _IOMGR_NETMGR;
|
|
msg.i.hdr.subtype = _NETMGR_NDTOSTR;
|
|
msg.i.len = bufsize - len;
|
|
msg.i.flags = ND2S_DIR_SHOW | ND2S_NAME_HIDE;
|
|
msg.i.nd = ND_LOCAL_NODE;
|
|
if (MsgSend(fd, &msg.i, sizeof(msg.i), &buf[len], bufsize - len) == -1)
|
|
return -1;
|
|
close(fd);
|
|
len = strlen(buf);
|
|
}
|
|
return len;
|
|
}
|
|
|
|
char* tail(char *name) {
|
|
register char *p;
|
|
|
|
p = name + strlen(name);
|
|
while(p > name && *p != '/' && *p != '\\' ) --p;
|
|
|
|
return( (*p == '/' || *p == '\\') ? p + 1 : p );
|
|
}
|
|
|
|
void
|
|
dsprc(DIR *dp)
|
|
{
|
|
int len, fd, coid, nd;
|
|
struct _server_info qinfo, sinfo;
|
|
char noderoot[PATH_MAX + 1], nodename[20];
|
|
|
|
if ((len = find_netdir(node, noderoot, PATH_MAX, 0)) == -1)
|
|
error_exit(1, "can't find node %s: %s\n", node, strerror(errno));
|
|
|
|
strcat(noderoot, "dev/netmgr");
|
|
if ((fd = open(noderoot, O_RDONLY)) == -1)
|
|
error_exit(1, "can't open %s: %s\n", noderoot, strerror(errno));
|
|
|
|
if (ConnectServerInfo(0, fd, &qinfo) == -1)
|
|
error_exit(1, "can't find server info for %s: %s\n", noderoot, strerror(errno));
|
|
close(fd);
|
|
|
|
printf(" From Pid Arguments\n");
|
|
for (coid = 0; (coid = ConnectServerInfo(qinfo.pid, coid, &sinfo)) >= 0; coid++)
|
|
{
|
|
if (sinfo.chid != -1)
|
|
continue;
|
|
|
|
if ((nd = netmgr_remote_nd(qinfo.nd, sinfo.nd)) == -1 ||
|
|
(len = netmgr_ndtostr(ND2S_DIR_SHOW | ND2S_NAME_SHOW | ND2S_LOCAL_STR | ND2S_QOS_HIDE,
|
|
nd, noderoot, PATH_MAX)) == -1)
|
|
continue;
|
|
|
|
nodename[16] = 0;
|
|
netmgr_ndtostr(ND2S_DIR_HIDE | ND2S_LOCAL_STR, nd, nodename, 15);
|
|
snprintf(&noderoot[len - 1], PATH_MAX - len, "proc/%d/as", sinfo.pid);
|
|
if ((fd = open(noderoot, O_RDWR)) == -1)
|
|
continue;
|
|
|
|
printf("%-15s %-10d ", nodename, sinfo.pid);
|
|
piddspinfo(fd, 0, sinfo.pid, 0, "%A", 0);
|
|
close(fd);
|
|
}
|
|
return;
|
|
}
|
|
|
|
|
|
void dspsched(void)
|
|
{
|
|
struct sched_query query;
|
|
|
|
if (!SchedCtl(SCHED_QUERY_SCHED_EXT, &query, sizeof(query))) {
|
|
switch (query.extsched) {
|
|
case SCHED_EXT_APS: {
|
|
sched_aps_info aps_info;
|
|
sched_aps_partition_info aps_pinfo;
|
|
sched_aps_partition_stats *aps_pstats;
|
|
uint64_t round[2];
|
|
int n;
|
|
char notify[32], security[16];
|
|
APS_INIT_DATA(&aps_info);
|
|
APS_INIT_DATA(&aps_pinfo);
|
|
|
|
|
|
if (SchedCtl(SCHED_APS_QUERY_PARMS, &aps_info, sizeof(aps_info)))
|
|
error_exit(!0, "Unable to query APS - %s\n", strerror(errno));
|
|
if ((aps_pstats = alloca(aps_info.num_partitions * sizeof(sched_aps_partition_stats))) == NULL)
|
|
error_exit(!0, "Unable to query APS statistics - %s\n", strerror(ENOMEM));
|
|
|
|
aps_pstats=(sched_aps_partition_stats*)calloc(aps_info.num_partitions, sizeof(sched_aps_partition_stats));
|
|
if (!aps_pstats) error_exit(!0, "Unable to allocate memory\n", strerror(ENOMEM));
|
|
|
|
APS_INIT_DATA(&aps_pstats[0]);
|
|
aps_pstats->id = APS_SYSTEM_PARTITION_ID;
|
|
if (SchedCtl(SCHED_APS_PARTITION_STATS, aps_pstats, aps_info.num_partitions * sizeof(sched_aps_partition_stats)))
|
|
error_exit(!0, "Unable to query APS statistics - %s\n", strerror(errno));
|
|
round[0] = aps_info.windowsize_cycles / 2 / 100, round[1] = aps_info.cycles_per_ms / 2;
|
|
printf("APS scheduler");
|
|
printf(", %d partitions", aps_info.num_partitions);
|
|
printf(", window %d ms", (int)(aps_info.windowsize_cycles / aps_info.cycles_per_ms));
|
|
if (aps_info.sec_flags == SCHED_APS_SEC_OFF)
|
|
strcpy(security, "off");
|
|
else if (aps_info.sec_flags & SCHED_APS_SEC_PARTITIONS_LOCKED)
|
|
strcpy(security, "LOCKED");
|
|
else if (aps_info.sec_flags == SCHED_APS_SEC_BASIC)
|
|
strcpy(security, "basic");
|
|
else if (aps_info.sec_flags == SCHED_APS_SEC_FLEXIBLE)
|
|
strcpy(security, "flexible");
|
|
else if (aps_info.sec_flags == SCHED_APS_SEC_RECOMMENDED)
|
|
strcpy(security, "recommended");
|
|
else
|
|
sprintf(security, "bits %04X", aps_info.sec_flags);
|
|
printf(", security %s", security);
|
|
printf("\n");
|
|
for (n = 0; n < aps_info.num_partitions; ++n) {
|
|
if (SchedCtl(SCHED_APS_QUERY_PARTITION, (aps_pinfo.id = n, &aps_pinfo), sizeof(aps_pinfo)))
|
|
error_exit(!0, "Unable to query APS partition - %s\n", strerror(errno));
|
|
sprintf(notify, (aps_pinfo.notify_pid != -1) ? "%d/%d" : "", aps_pinfo.notify_pid, aps_pinfo.notify_tid);
|
|
printf("%-*.*s %3d%% (%3d%%) %3dms (%3dms)%c %s\n",
|
|
APS_PARTITION_NAME_LENGTH, APS_PARTITION_NAME_LENGTH, aps_pinfo.name,
|
|
aps_pinfo.budget_percent, (int)((aps_pstats[n].run_time_cycles + round[0]) * 100 / aps_info.windowsize_cycles),
|
|
(int)(aps_pinfo.critical_budget_cycles / aps_info.cycles_per_ms), (int)((aps_pstats[n].critical_time_cycles + round[1]) / aps_info.cycles_per_ms),
|
|
(aps_pstats[n].stats_flags & SCHED_APS_PSTATS_IS_BANKRUPT_NOW) ? '!' : ' ',
|
|
notify);
|
|
}
|
|
} break;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
int confstr_get(int fd, int name, int *value, char *str)
|
|
{
|
|
/* Copied from confstr lib file with added fd allowing
|
|
* retrieval of information from existing connection. */
|
|
sys_conf_t msg;
|
|
iov_t iov[2];
|
|
|
|
msg.i.type = _SYS_CONF;
|
|
msg.i.subtype = _SYS_SUB_GET;
|
|
msg.i.cmd = _CONF_STR;
|
|
msg.i.name = name;
|
|
msg.i.value = *value;
|
|
|
|
SETIOV(iov + 0, &msg.o, sizeof(msg.o));
|
|
SETIOV(iov + 1, str, *value);
|
|
|
|
if (MsgSendvnc(fd, (iov_t *)&msg.i, -sizeof (msg.i), iov, 2) == -1) {
|
|
return(-1);
|
|
}
|
|
*value = msg.o.value;
|
|
|
|
return (msg.o.match);
|
|
}
|
|
|
|
|
|
void
|
|
dspsys(const char *type, const char *argv)
|
|
{
|
|
char buffer[PATH_MAX + 1];
|
|
int fd;
|
|
procfs_sysinfo *sysinfo;
|
|
struct stat64 st;
|
|
unsigned int size;
|
|
uint64_t stat_size;
|
|
uint64_t total;
|
|
char *size_sym;
|
|
char *total_sym;
|
|
time_t boot_time;
|
|
struct cpuinfo_entry *cpu;
|
|
int i;
|
|
char nodepath[PATH_MAX + 1], *n;
|
|
DIR *dp;
|
|
struct dirent *dent;
|
|
|
|
dp = 0;
|
|
dent = 0;
|
|
n = ".";
|
|
|
|
switch(*type) {
|
|
case 's':
|
|
case 'n':
|
|
if (*type == 'n' && !*node) {
|
|
/* 'pidin net' show info for everyone in /net */
|
|
if ((i = find_netdir("", nodepath, PATH_MAX, 1)) == -1)
|
|
error_exit(1, "can't find node %s: %s\n", node, strerror(errno));
|
|
|
|
/* make sure the network manager is running, otherwise, we jsut
|
|
* got a "/"
|
|
*/
|
|
if ((fd = open("/dev/netmgr", O_RDONLY)) == -1)
|
|
error_exit(1, "Network manager is not running.\n");
|
|
close(fd);
|
|
|
|
if ((dp = opendir(nodepath)) == NULL)
|
|
error_exit(1, "can't opendir %s: %s\n", nodepath, strerror(errno));
|
|
|
|
if (!(dent = readdir(dp))) {
|
|
closedir(dp);
|
|
return;
|
|
}
|
|
|
|
n = dent->d_name;
|
|
printf(" ND Node CPU Release FreeMem BootTime\n");
|
|
} else {
|
|
/* 'pidin -n node info' or 'pidin -n node net' generate same result */
|
|
if ((i = find_netdir(node, nodepath, PATH_MAX, 0)) == -1)
|
|
error_exit(1, "can't find node %s: %s\n", node, strerror(errno));
|
|
}
|
|
break;
|
|
case 'r':
|
|
dsprc(dp);
|
|
return;
|
|
|
|
case 'P':
|
|
dspsyspage(strchr(argv,'='));
|
|
return;
|
|
|
|
case 'E':
|
|
if (*node != '\0')
|
|
error_exit(!0, "can only query 'extsched' on local node\n");
|
|
dspsched();
|
|
return;
|
|
|
|
default:
|
|
error_exit(1, "invalid option\n");
|
|
}
|
|
|
|
do {
|
|
snprintf(buffer, PATH_MAX, "%s%s/proc", nodepath, n);
|
|
if (dent) {
|
|
if (strlen(dent->d_name) > 15)
|
|
dent->d_name[15] = 0;
|
|
printf(" %-4d %-15s ", (int)dent->d_ino, dent->d_name);
|
|
}
|
|
|
|
if ((fd = open64(buffer, O_RDONLY)) == -1) {
|
|
if (dp) {
|
|
printf("%s\n", strerror(errno));
|
|
goto next;
|
|
}
|
|
error_exit(1, "couldn't open %s: %s\n", buffer, strerror(errno));
|
|
}
|
|
if ((sysinfo = load_syspage(fd, 0)) == NULL) {
|
|
if (dp) {
|
|
printf("%s\n", strerror(errno));
|
|
goto next;
|
|
}
|
|
error_exit(!0, "couldn't load syspage info for %s: %s\n", buffer, strerror(errno));
|
|
}
|
|
|
|
if (fstat64(fd, &st) == -1)
|
|
error_exit(1, "couldn't get stat info for %s: %s\n", buffer, strerror(errno));
|
|
stat_size = st.st_size;
|
|
total = get_total_mem(sysinfo);
|
|
boot_time = _SYSPAGE_ENTRY(sysinfo, qtime)->boot_time;
|
|
|
|
|
|
if (dent) {
|
|
printf("%2d ", sysinfo->num_cpu);
|
|
} else {
|
|
printf("CPU:");
|
|
}
|
|
|
|
|
|
switch(sysinfo->type) {
|
|
case SYSPAGE_X86:
|
|
printf("X86 ");
|
|
break;
|
|
case SYSPAGE_PPC:
|
|
printf("PPC ");
|
|
break;
|
|
case SYSPAGE_MIPS:
|
|
printf("MIPS ");
|
|
break;
|
|
case SYSPAGE_ARM:
|
|
printf("ARM ");
|
|
break;
|
|
case SYSPAGE_SH:
|
|
printf("SH ");
|
|
break;
|
|
default:
|
|
printf("Unknown(%d) ", sysinfo->type);
|
|
break;
|
|
}
|
|
|
|
size = PATH_MAX;
|
|
if (confstr_get(fd,_CS_RELEASE,&size, buffer) == -1) {
|
|
error_exit(1, "couldn't retrieve release info for %s: %s\n", buffer, strerror(errno));
|
|
}
|
|
|
|
if (dent) {
|
|
printf(" %-8s",buffer);
|
|
} else {
|
|
printf("Release:%-7s", buffer);
|
|
}
|
|
|
|
size = normalize_data_size(stat_size, &size_sym);
|
|
total = normalize_data_size(total, &total_sym);
|
|
strftime(buffer, sizeof buffer, "%b %d %T %Z %Y", localtime(&boot_time));
|
|
if (dent) {
|
|
char sizebuf[14];
|
|
snprintf(sizebuf, 14, "%u%sb/%u%sb", size, size_sym, (unsigned)total, total_sym);
|
|
printf("%-14s", sizebuf);
|
|
printf("%s", buffer);
|
|
} else {
|
|
printf("FreeMem:%u%sb/%u%sb ", size, size_sym, (unsigned)total, total_sym);
|
|
printf("BootTime:%s", buffer);
|
|
}
|
|
printf("\n");
|
|
|
|
|
|
|
|
if (!dent) {
|
|
for(i = 1, cpu = _SYSPAGE_ENTRY(sysinfo, cpuinfo); i <= sysinfo->num_cpu; i++, cpu++) {
|
|
printf("Processor%d: ", i);
|
|
if(sysinfo->type == SYSPAGE_PPC){
|
|
printf("%x ", cpu->cpu);
|
|
} else{
|
|
printf("%u ", cpu->cpu);
|
|
}
|
|
printf("%s ", &_SYSPAGE_ENTRY(sysinfo, strings)->data[cpu->name]);
|
|
printf("%dMHz ", cpu->speed);
|
|
if(cpu->flags & CPU_FLAG_FPU) {
|
|
printf("FPU ");
|
|
}
|
|
if(!(cpu->flags & CPU_FLAG_MMU)) {
|
|
printf("Physical ");
|
|
}
|
|
printf("\n");
|
|
}
|
|
}
|
|
free(sysinfo);
|
|
close(fd);
|
|
next:
|
|
if (dent && (dent = readdir(dp)))
|
|
n = dent->d_name;
|
|
} while (dent);
|
|
|
|
if (dp)
|
|
closedir(dp);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Structure used for sorting process information. This is currently
|
|
* set up to sort on a pid type key */
|
|
typedef struct sortentry{
|
|
pid_t pid;
|
|
pid_t key;
|
|
} sortentry_t;
|
|
|
|
static int
|
|
sortkey(const void *a, const void *b)
|
|
{
|
|
const sortentry_t *as = (const sortentry_t *)a;
|
|
const sortentry_t *bs = (const sortentry_t *)b;
|
|
|
|
return(as->key - bs->key );
|
|
}
|
|
|
|
|
|
static int
|
|
sortpid(const void *a, const void *b)
|
|
{
|
|
const sortentry_t *as = (const sortentry_t *)a;
|
|
const sortentry_t *bs = (const sortentry_t *)b;
|
|
|
|
return(as->pid - bs->pid);
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
getinfo(char *procname, struct shared_info *info_p)
|
|
{
|
|
int ret, fd;
|
|
|
|
if ((fd = open64(procname, O_RDONLY)) == -1) {
|
|
if(errno != ENOENT) {
|
|
warning_exit(1, 0, "couldn't open %s: %s\n", procname, strerror(errno));
|
|
}
|
|
return(1);
|
|
}
|
|
memset(info_p, 0, sizeof(*info_p));
|
|
ret = fill_info(info_p, fd);
|
|
close(fd);
|
|
return(ret);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int *
|
|
getpidlist(char *nodepath, const char *fmt)
|
|
{
|
|
|
|
|
|
char buf[50];
|
|
struct dirent *dirent;
|
|
DIR *dir;
|
|
int entries = 0;
|
|
pid_t *pid_list;
|
|
int i, cur = 0;
|
|
sortentry_t *sort_list;
|
|
int pidsort = 0;
|
|
char keyid = 'a';
|
|
|
|
snprintf(buf, 50, "%sproc", nodepath);
|
|
if (!(dir = opendir(buf))) {
|
|
error_exit(1, "couldn't open %s: %s\n", buf, strerror(errno));
|
|
}
|
|
|
|
/* Find number of entries. */
|
|
while (dirent = readdir(dir)) {
|
|
entries++;
|
|
}
|
|
|
|
/* Add one entry for terminator value. */
|
|
pid_list = malloc(sizeof(pid_t)*(entries + 1));
|
|
|
|
if (pid_list == NULL) {
|
|
closedir(dir);
|
|
return(NULL);
|
|
}
|
|
|
|
sort_list = (sortentry_t *)malloc(sizeof(sortentry_t)*(entries));
|
|
if (sort_list == NULL) {
|
|
free(pid_list);
|
|
closedir(dir);
|
|
return(NULL);
|
|
}
|
|
|
|
rewinddir(dir);
|
|
|
|
if (fmt != NULL) {
|
|
/* Find first character identifier in output format to determine
|
|
* which key should be used as primary for sorting. */
|
|
char *firstid = strchr(fmt, '%');
|
|
if (firstid != NULL) {
|
|
keyid = *(firstid+1);
|
|
}
|
|
}
|
|
|
|
while (dirent = readdir(dir)) {
|
|
pid_list[cur] = atoi(dirent->d_name);
|
|
|
|
/* Only analyze numeric entries. 0 indicates all ASCII... */
|
|
/* Potential problem here with numeric + ASCII, but these
|
|
* should never occur in the proc dir. */
|
|
if (pid_list[cur] != 0) {
|
|
struct shared_info info;
|
|
|
|
snprintf(buf, 50, "%sproc/%d/as", nodepath, pid_list[cur]);
|
|
if (getinfo(buf, &info) == 0) {
|
|
sort_list[cur].pid = pid_list[cur];
|
|
|
|
/* Bit of a nuisance having to fill all of these in, but
|
|
* there aren't really many that are likely to be useful
|
|
* for grouping other than sessions and process groups. */
|
|
|
|
switch(keyid) {
|
|
case 'L':
|
|
/* Session ID. */
|
|
sort_list[cur].key = info.info->sid;
|
|
break;
|
|
case 'P':
|
|
/* Process group. */
|
|
sort_list[cur].key = info.info->pgrp;
|
|
break;
|
|
default:
|
|
/* Sort on pid only.*/
|
|
sort_list[cur].key = info.info->pid;
|
|
pidsort = 1;
|
|
break;
|
|
}
|
|
/* Include pid in list to be displayed... */
|
|
cur++;
|
|
}
|
|
|
|
if (cur >= entries) {
|
|
/* Fall out if more entries found than allocated.
|
|
* No need to produce error since the output is only a sample
|
|
* at a given point in time anyway.
|
|
*/
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
closedir(dir);
|
|
|
|
/* Sort on primary key. */
|
|
qsort (sort_list, cur, sizeof(sortentry_t), sortkey);
|
|
|
|
/* Now perform secondary sort based on pid (if necessary). */
|
|
if (!pidsort) {
|
|
i = 0;
|
|
while (i < cur) {
|
|
int ns = 0;
|
|
pid_t curkey = sort_list[i].key;
|
|
while ((sort_list[i+ns].key == curkey) && (i+ns < cur)) {
|
|
ns++;
|
|
}
|
|
qsort(&sort_list[i], ns, sizeof(sortentry_t), sortpid);
|
|
i+=ns;
|
|
}
|
|
}
|
|
|
|
|
|
/* Copy sorted pids int pidlist for return to caller. */
|
|
for (i = 0; i < cur; i++) {
|
|
pid_list[i] = sort_list[i].pid;
|
|
}
|
|
|
|
/* End of array = 0. */
|
|
pid_list[cur] = 0;
|
|
free (sort_list);
|
|
|
|
return(pid_list);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
dspinfo(pid_t pid, char *pidname, int flags, const char *fmt, const char *mi_fmt)
|
|
{
|
|
char buffer[50], nodepath[50];
|
|
struct dirent *dirent;
|
|
DIR *dir;
|
|
int fd, len;
|
|
pid_t *pid_list;
|
|
|
|
if ((len = find_netdir(node, nodepath, 50, 0)) == -1)
|
|
error_exit(1, "could't find node %s: %s\n", node);
|
|
|
|
if (flags & DONT_RECURSE && pidname == NULL) {
|
|
snprintf(buffer, 50, "%sproc/%d/as", nodepath, pid);
|
|
if ((fd = open64(buffer, O_RDONLY)) == -1)
|
|
error_exit(1, "couldn't open %s: %s\n", buffer, strerror(errno));
|
|
piddspinfo(fd, NULL, pid, flags | DONT_RECURSE, fmt, mi_fmt);
|
|
close(fd);
|
|
return;
|
|
}
|
|
snprintf(buffer, 50, "%sproc", nodepath);
|
|
|
|
/* Try and get sorted list of pids. */
|
|
pid_list = getpidlist(nodepath, fmt);
|
|
if (pid_list != NULL) {
|
|
int *pid_e = pid_list;
|
|
while (*pid_e != 0) {
|
|
snprintf(buffer, 50, "%sproc/%d/as", nodepath, *pid_e);
|
|
if ((fd = open64(buffer, O_RDONLY)) != -1) {
|
|
piddspinfo(fd, pidname, *pid_e, flags | DONT_RECURSE, fmt, mi_fmt);
|
|
close(fd);
|
|
} else if(errno != ENOENT) {
|
|
warning_exit(1, 0, "couldn't open %s: %s\n", buffer, strerror(errno));
|
|
}
|
|
pid_e++;
|
|
}
|
|
/* pid_list was malloc'ed... */
|
|
free(pid_list);
|
|
return;
|
|
}
|
|
|
|
|
|
/* Sorted list unavailable (e.g. no memory...). Get unsorted entries. */
|
|
if (!(dir = opendir(buffer)))
|
|
error_exit(1, "couldn't open %s: %s\n", buffer, strerror(errno));
|
|
while (dirent = readdir(dir)) {
|
|
int fd;
|
|
|
|
/* skip over "/proc/self" entry */
|
|
if(!isdigit(dirent->d_name[0])) continue;
|
|
|
|
snprintf(buffer, 50, "%sproc/%s/as", nodepath, dirent->d_name);
|
|
if ((fd = open64(buffer, O_RDONLY)) != -1) {
|
|
piddspinfo(fd, pidname, atoi(dirent->d_name), flags | DONT_RECURSE, fmt, mi_fmt);
|
|
close(fd);
|
|
} else if(errno != ENOENT) {
|
|
warning_exit(1, 0, "couldn't open %s: %s\n", buffer, strerror(errno));
|
|
}
|
|
}
|
|
closedir(dir);
|
|
}
|
|
|
|
void
|
|
piddspinfo(int fd, char *pidname, pid_t pid, int flags, const char *fmt, const char *mf)
|
|
{
|
|
int tid, lasttid;
|
|
const char *f;
|
|
struct shared_info info;
|
|
int special = 0;
|
|
|
|
info.name = 0;
|
|
info.mem = 0;
|
|
info.info = 0;
|
|
info.irqs = 0;
|
|
info.channels = 0;
|
|
info.num_channels = 0;
|
|
info.num_irqs = 0;
|
|
info.timers = 0;
|
|
info.num_timers = 0;
|
|
info.coids = 0;
|
|
info.num_coids = 0;
|
|
info.tls = 0;
|
|
info.text = info.data = info.stack = 0;
|
|
info.memobjects = 0;
|
|
info.num_memobjects = 0;
|
|
info.flags = 0;
|
|
info.gprs = 0;
|
|
if (fill_info(&info, fd)) {
|
|
return;
|
|
}
|
|
|
|
if(pidname) {
|
|
fill_name(&info, fd);
|
|
|
|
if(!info.name) {
|
|
return;
|
|
}
|
|
if(!(f = strrchr(info.name->path, '/'))) {
|
|
f = info.name->path;
|
|
}
|
|
while(*f && *f == '/') { f++; }
|
|
if(strcmp(tail((char*) f), pidname) != 0) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (mf) {
|
|
info.flags |= SEPARATE_MEMORY;
|
|
}
|
|
|
|
for (lasttid = tid = 1; lasttid >= tid; lasttid = ++tid) {
|
|
f = fmt;
|
|
|
|
if (!(info.info->flags & _NTO_PF_ZOMBIE)) {
|
|
if (fill_status(1, &info, &tid, fd) || tid < lasttid)
|
|
break;
|
|
}
|
|
|
|
if (info.memobjects) {
|
|
int i;
|
|
struct memobjects *mo;
|
|
|
|
for(mo = info.memobjects, i = 0;i < info.num_memobjects; mo++, i++) {
|
|
free(mo->name);
|
|
}
|
|
free(info.memobjects);
|
|
info.memobjects = 0;
|
|
info.num_memobjects = 0;
|
|
}
|
|
info.text = info.data = info.stack = 0;
|
|
|
|
while (*f) {
|
|
struct format *format;
|
|
|
|
if (!(format = get_format(stdout, &f)))
|
|
break;
|
|
info.status = 0;
|
|
if (format->flags & MULTI_LINE) {
|
|
special++;
|
|
continue;
|
|
}
|
|
/* if we can do it as a zombie or this isn't a zombie ... */
|
|
if (!(format->flags & ZOMBIE_INVALID) ||
|
|
!(info.info->flags & _NTO_PF_ZOMBIE)) {
|
|
if (format->print(stdout, pid, &tid, format, fd, &info))
|
|
break;
|
|
} else {
|
|
if (format->flags & NA) {
|
|
format_data_string(stdout, format,
|
|
(info.info->flags & _NTO_PF_ZOMBIE) ? "(Zombie)" : na);
|
|
} else {
|
|
format_data_string(stdout, format, spaces);
|
|
}
|
|
}
|
|
}
|
|
fprintf(stdout, "\n");
|
|
if (special) {
|
|
f = fmt;
|
|
while (*f) {
|
|
struct format *format;
|
|
|
|
if (!(format = get_format(0, &f)))
|
|
break;
|
|
if ((format->flags & MULTI_LINE) &&
|
|
!(format->flags & PROCESS_ONLY) &&
|
|
format->print(stdout, pid, &tid, format, fd, &info))
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (info.info->flags & _NTO_PF_ZOMBIE)
|
|
break;
|
|
if (!(flags & DO_THREADS))
|
|
break;
|
|
}
|
|
|
|
if (special) {
|
|
fpos_t p1, p2;
|
|
fgetpos(stdout, &p1);
|
|
f = fmt;
|
|
while (*f) {
|
|
struct format *format;
|
|
|
|
if (!(format = get_format(0, &f)))
|
|
break;
|
|
if ((format->flags & (MULTI_LINE | PROCESS_ONLY)) == (MULTI_LINE | PROCESS_ONLY) &&
|
|
format->print(stdout, pid, &tid, format, fd, &info))
|
|
break;
|
|
}
|
|
fgetpos(stdout, &p2);
|
|
//Avoid putting two newlines when there is no extra output
|
|
if(memcmp(&p1, &p2, sizeof(p1)) != 0) {
|
|
fprintf(stdout, "\n");
|
|
}
|
|
}
|
|
|
|
if (mf) {
|
|
int i;
|
|
for (i = info.num_memobjects; i; i--) {
|
|
f = mf;
|
|
while(*f) {
|
|
struct format *format;
|
|
|
|
if (!(format = get_format(stdout, &f)))
|
|
break;
|
|
format->print(stdout, pid, &tid, format, fd, &info);
|
|
}
|
|
info.next_memobject++;
|
|
fprintf(stdout, "\n");
|
|
}
|
|
}
|
|
|
|
// need to clean up resources
|
|
if (info.mem)
|
|
free(info.mem);
|
|
if (info.tls)
|
|
free(info.tls);
|
|
if (info.irqs)
|
|
free(info.irqs);
|
|
if (info.gprs)
|
|
free(info.gprs);
|
|
if (info.timers)
|
|
free(info.timers);
|
|
if (info.memobjects) {
|
|
int i;
|
|
struct memobjects *mo;
|
|
|
|
for(mo = info.memobjects, i = 0;i < info.num_memobjects; mo++, i++) {
|
|
free(mo->name);
|
|
}
|
|
free(info.memobjects);
|
|
info.memobjects = 0;
|
|
}
|
|
if (info.coids) {
|
|
int i;
|
|
for(i = 0; i < info.num_coids; i++) {
|
|
if(info.coids[i].name) {
|
|
free(info.coids[i].name);
|
|
}
|
|
}
|
|
free(info.coids);
|
|
info.coids = 0;
|
|
info.num_coids = 0;
|
|
}
|
|
}
|
|
|
|
static char *
|
|
normalize_format(const char *f)
|
|
{
|
|
const char *p;
|
|
char *r, *s;
|
|
|
|
if (!(r = malloc(strlen(f) * 3 + 1)))
|
|
return 0;
|
|
for (s = r, p = f; *p; p++)
|
|
*s++ = '%', *s++ = *p, *s++ = ' ';
|
|
*s = 0;
|
|
return r;
|
|
}
|
|
|
|
// note this returns a pointer to static storage
|
|
static struct format *
|
|
get_format(FILE * fp, const char **f)
|
|
{
|
|
static struct format r;
|
|
const char *p = *f;
|
|
int w, c;
|
|
|
|
while (*p && *p != '%') {
|
|
if (fp)
|
|
fputc(*p, fp);
|
|
p++;
|
|
}
|
|
if (!*p || !*++p)
|
|
return *f = p, (struct format *) 0; // trailing % is ignored
|
|
|
|
w = strtol(p, (char **)&p, 10);
|
|
c = *p++;
|
|
if (formats[c].letter != c) {
|
|
error_exit(1, "Format letter '%c'(%#x) <=> '%c'(%#x) is invalid.\n", c, c, formats[c].letter, formats[c].letter);
|
|
}
|
|
if (!formats[c].print)
|
|
return errno = EINVAL, (struct format *) -1;
|
|
r.width = w ? w : formats[c].width;
|
|
r.title = formats[c].title;
|
|
r.print = formats[c].print;
|
|
r.flags = formats[c].flags;
|
|
if (r.width < 0)
|
|
r.flags |= DATA_LEFT_JUSTIFIED, r.width *= -1;
|
|
r.letter = c;
|
|
*f = p;
|
|
return &r;
|
|
}
|
|
|
|
static struct shorthand *
|
|
lookup_shorthand(const char *f)
|
|
{
|
|
struct shorthand *s, *match;
|
|
int len;
|
|
|
|
for (len =0; f[len] && isalnum(f[len]); len++ );
|
|
for (match = 0, s = shorthands; s->handle; s++) {
|
|
if (!strnicmp(s->handle, f, len)) {
|
|
if (match) {
|
|
match = 0;
|
|
break;
|
|
}
|
|
match = s;
|
|
}
|
|
}
|
|
return match;
|
|
}
|
|
|
|
void
|
|
error_exit(int printmsg, const char *fmt,...)
|
|
{
|
|
extern char *__progname;
|
|
|
|
if (printmsg) {
|
|
va_list arglist;
|
|
|
|
fprintf(stderr, "%s: ", __progname);
|
|
va_start(arglist, fmt);
|
|
vfprintf(stderr, fmt, arglist);
|
|
va_end(arglist);
|
|
}
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
void
|
|
warning_exit(int printmsg, int expectwarn, const char *fmt,...)
|
|
{
|
|
if (printmsg) {
|
|
va_list arglist;
|
|
va_start(arglist, fmt);
|
|
vfprintf(stderr, fmt, arglist);
|
|
va_end(arglist);
|
|
}
|
|
if (!expectwarn && exit_on_warning)
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
|
|
__SRCVERSION("pidin.c $Rev: 153052 $");
|