/*
- * Extended `who' command, version 1.8.
+ * Extended `who' command, version 1.12.
*
- * (c) 1996, 1997 Martin Mares <mj@atrey.karlin.mff.cuni.cz>
+ * (c) 1996--2010 Martin Mares <mj@ucw.cz>
*
* This software may be freely distributed and used according to the terms
* of the GNU General Public License. See file COPYING in any of the GNU packages.
#include <pwd.h>
#include <sys/stat.h>
#include <asm/param.h>
+#include <sys/ioctl.h>
+#include <ctype.h>
#undef DEBUG
+#define MAXHOSTSIZE 24
+
+#ifdef DEBUG
+#define DBG(x,y...) printf(x,##y)
+#else
+#define DBG(x,y...) do { } while(0)
+#endif
struct procrec {
struct procrec *next;
int pid, ppid, pgrp, sess;
- int usr, state;
+ int uid, state, distance;
time_t ctim, tim;
- char cmd_line[34];
+ struct utrec *user;
+ char cmd_line[1];
};
static struct procrec *first_procrec;
struct utrec {
struct utrec *next;
pid_t login_pid;
- char line_id[4];
+ char line[UT_LINESIZE];
time_t login_time, click_time, total_time;
char user_name[UT_NAMESIZE+1];
- int usr;
- char host_name[17];
- char line_name[12];
+ int uid;
+ char host_name[MAXHOSTSIZE+1];
int mesg, detach;
struct procrec *proc;
};
static struct utrec *first_utrec;
-static int usercnt, zombies;
+static int usercnt, zombies, maxhost = 10;
static time_t now;
static void *
{
static struct utmp *ut;
static struct passwd *pw;
+ char *z;
utmpname(UTMP_FILE);
while (ut = getutent())
if (ut->ut_type == USER_PROCESS && ut->ut_user[0])
struct utrec *u = xmalloc(sizeof(struct utrec));
struct stat st;
char device[32];
+ size_t hlen;
+ bzero(u, sizeof(*u));
u->next = first_utrec;
first_utrec = u;
u->login_pid = ut->ut_pid;
- u->line_id[0] = ut->ut_id[0];
- u->line_id[1] = ut->ut_id[1];
- u->line_id[2] = 0;
+ z = ut->ut_line;
+ if (!strncmp(z, "tty", 3))
+ {
+ if (z[3] >= '0' && z[3] <= '9')
+ sprintf(u->line, "c%s", z+3);
+ else
+ strcpy(u->line, z+3);
+ }
+ else if (!strncmp(z, "pts/", 4))
+ strcpy(u->line, z+4);
u->login_time = ut->ut_time;
strcpy_padded(u->user_name, ut->ut_user, UT_NAMESIZE);
pw = getpwnam(u->user_name);
if (pw)
- u->usr = pw->pw_uid;
+ u->uid = pw->pw_uid;
else
- u->usr = -1;
- strcpy_padded(u->host_name, ut->ut_host, 16);
- strcpy(u->line_name, ut->ut_line);
- if (!u->line_id[0])
+ u->uid = -1;
+ strcpy_padded(u->host_name, ut->ut_host,
+ (MAXHOSTSIZE > UT_HOSTSIZE ? UT_HOSTSIZE : MAXHOSTSIZE));
+ if ((hlen = strlen(u->host_name)) > maxhost)
+ maxhost = hlen;
+ strcpy(u->line, ut->ut_line);
+ if (*u->line)
{
- char *z = u->line_name;
- if (!strncmp(u->line_name, "tty", 3))
- z += 3;
- strncpy(u->line_id, z, 3);
- u->line_id[3] = 0;
+ char *z = u->line;
+ if (!strncmp(z, "tty", 3))
+ {
+ if (isdigit(z[3]))
+ sprintf(u->line, "c%s", z+3);
+ else
+ strcpy(u->line, z+3);
+ }
+ else if (!strncmp(z, "pts/", 4))
+ strcpy(u->line, z+4);
+ else
+ strcpy(u->line, z);
}
strcpy(device, "/dev/");
- strcat(device, u->line_name);
+ strcat(device, ut->ut_line);
if (stat(device, &st))
u->mesg = 2;
else
{
u->mesg = !!(S_IWGRP & st.st_mode);
u->click_time = st.st_atime;
- if (u->click_time < st.st_atime)
- u->click_time = st.st_atime;
+#if 0
+ if (u->click_time < st.st_mtime)
+ u->click_time = st.st_mtime;
if (u->click_time < st.st_ctime)
u->click_time = st.st_ctime;
+#endif
}
u->detach = 0;
usercnt++;
-#ifdef DEBUG
- printf("UT %s %s %d %d\n", u->user_name, u->host_name,
- u->usr, u->login_pid);
-#endif
+ DBG("UTMP: %s %s %d %d\n", u->user_name, u->host_name,
+ u->uid, u->login_pid);
}
endutent();
}
}
static void
-dispsys(void)
+line1(void)
{
FILE *f;
- char line[256], a[32], b[32], c[32];
- char *z, *w;
- w = NULL;
+ char line[256];
+ char *z;
+ int num_cpus = 0;
+
if (f = fopen("/proc/version", "r"))
{
fgets(line, 256, f);
printf(line);
fclose(f);
}
+
+ if (gethostname(line, sizeof(line)) >= 0)
+ printf(" (%s)", line);
+
if (f = fopen("/proc/cpuinfo", "r"))
{
- strcpy(a, "???");
- b[0] = 0;
- c[0] = 0;
- for(;;)
+ while (fgets(line, 256, f))
{
- fgets(line, 256, f);
- if (feof(f))
- break;
- if (!strncmp(line, "cpu\t\t: ", 7))
- {
- z = line+7;
- w = a;
- }
- else if (!strncmp(line, "model\t\t: ", 9))
- {
- z = line+9;
- w = b;
- }
- else if (!strncmp(line, "mask\t\t: ", 8))
- {
- z = line+8;
- w = c;
- }
- else
- z = NULL;
- if (z)
- {
- line[strlen(line)-1] = 0;
- strcpy(w, z);
- if (!strcmp(w, "Unknown"))
- w[0] = 0;
- }
+ if (!strncmp(line, "processor\t", 9))
+ num_cpus++;
}
- printf(" on %s", a);
- if (*b)
- printf("-%s", b);
- if (*c)
- printf("-%s", c);
+ printf(" [%d CPU%s]", num_cpus, (num_cpus != 1 ? "s" : ""));
fclose(f);
}
+
if (f = fopen("/proc/uptime", "r"))
{
int p,q,p1,q1;
fgets(line, 256, f);
sscanf(line, "%d.%d%d.%d", &p, &p1, &q, &q1);
- printf(", up ");
+ printf(" up ");
puttime(p);
- printf(", run ");
- puttime(p-q);
+ printf(" run ");
+ puttime(num_cpus*p - q);
fclose(f);
}
+
{
- struct tm *tm;
now = time(NULL);
- tm = localtime(&now);
- printf(" at %02d-%02d-%02d/%d %02d.%02d:%02d\n", tm->tm_mday, tm->tm_mon+1,
- tm->tm_year, (tm->tm_wday+6)%7, tm->tm_hour, tm->tm_min, tm->tm_sec);
+ struct tm *tm = localtime(&now);
+ strftime(line, sizeof(line), "%a %Y-%m-%d %H.%M:%S", tm);
+ printf(" on %s\n", line);
}
+}
+
+static void
+line2(void)
+{
+ FILE *f;
+ char line[256];
+ char *z;
+
if (f = fopen("/proc/loadavg", "r"))
{
int i = 3;
printf("LAV=%s", line);
fclose(f);
}
+
if (f = fopen("/proc/meminfo", "r"))
{
- int free, buffers, stotal, sfree;
+ int free, buffers, cached, stotal, sfree;
free = buffers = stotal = sfree = 0;
while (fgets(line, 256, f))
{
sscanf(line+8, "%d", &free);
else if (!strncmp(line, "Buffers:", 8))
sscanf(line+8, "%d", &buffers);
+ else if (!strncmp(line, "Cached:", 7))
+ sscanf(line+7, "%d", &cached);
else if (!strncmp(line, "SwapTotal:", 10))
sscanf(line+10, "%d", &stotal);
else if (!strncmp(line, "SwapFree:", 9))
sscanf(line+9, "%d", &sfree);
}
printf(", free ");
- memory(1024*(free+buffers));
+ memory(1024*(free+buffers+cached));
printf(" of RAM");
if (stotal != sfree)
{
printf(" of swap");
}
}
- printf(", %d user%s.\n", usercnt, usercnt == 1 ? "" : "s");
+ printf(", %d user%s\n", usercnt, usercnt == 1 ? "" : "s");
}
static void
{
DIR *d;
struct dirent *e;
+ struct winsize win;
+ unsigned cmdcols;
+#define LEFTSIZE 35
+
+ if (!ioctl(1, TIOCGWINSZ, &win) && win.ws_col >= LEFTSIZE + maxhost + 10 && win.ws_col < 1024)
+ cmdcols = win.ws_col;
+ else
+ cmdcols = 80;
+ cmdcols -= LEFTSIZE + maxhost;
if (d = opendir("/proc"))
{
while (e = readdir(d))
{
int i;
- sscanf(e->d_name, "%d", &i);
- if (i)
+ if (sscanf(e->d_name, "%d", &i)==1)
{
- struct procrec *p = xmalloc(sizeof(struct procrec));
+ struct procrec *p = xmalloc(sizeof(struct procrec) + cmdcols);
struct stat st;
char name[256], eman[1024];
int fil;
+ bzero(p, sizeof(*p));
p->pid = i;
strcpy(name, "/proc/");
strcat(name, e->d_name);
if (!stat(name, &st))
{
- p->next = first_procrec;
- first_procrec = p;
- p->usr = st.st_uid;
+ p->uid = st.st_uid;
strcpy(eman, name);
strcat(eman, "/cmdline");
if ((fil = open(eman, O_RDONLY)) >= 0)
{
- int z = read(fil, p->cmd_line, sizeof(p->cmd_line) - 1);
+ int y, z = read(fil, p->cmd_line, cmdcols);
if (z < 0)
z = 0;
- p->cmd_line[z] = 0x91;
+ for (y=0; y<z; y++)
+ if (!p->cmd_line[y])
+ p->cmd_line[y] = ' ';
+ p->cmd_line[z] = 0;
close(fil);
}
strcpy(eman, name);
if ((fil = open(eman, O_RDONLY)) >= 0)
{
int z = read(fil, eman, 1023);
- char *k;
+ char *j, *k;
int trash, utim, stim, cutim, cstim;
if (z < 0)
z = 0;
eman[z] = 0;
- k = strchr(eman, ')');
- if (k)
+ if ((j = strchr(eman, '(')) && (k = strchr(j+1, ')')))
{
+ *k = 0;
+ if (!p->cmd_line[0])
+ {
+ strncpy(p->cmd_line, j+1, cmdcols);
+ p->cmd_line[cmdcols] = 0;
+ }
p->state = k[2];
if (p->state == 'Z')
zombies++;
&p->ppid, &p->pgrp, &p->sess, &trash, &trash,
&trash, &trash, &trash, &trash, &trash, &utim,
&stim, &cutim, &cstim);
+ DBG("PROC: pid=%d ppid=%d pgrp=%d sess=%d uid=%d\n", p->pid, p->ppid, p->pgrp, p->sess, p->uid);
+ p->next = first_procrec;
+ first_procrec = p;
}
p->tim = utim + stim;
p->ctim = cutim + cstim;
static void
solve(void)
{
- struct utrec *u = first_utrec;
- struct procrec *m, *n, *o, *q;
+ struct utrec *u;
+ struct procrec *m, *n;
struct passwd *pw;
- while (u)
- {
- m = first_procrec;
- while (m && m->pid != u->login_pid)
- m = m->next;
- o = m;
- if (m)
- {
- retry:
- n = first_procrec;
- q = o;
- while (n)
- {
- if (q->sess == n->sess)
- n->usr = -1;
- if (q->pid == n->ppid && n->usr == -1)
- {
- o = n;
- goto retry;
- }
- n = n->next;
- }
- }
- u->proc = o;
- if (m)
- u->total_time = m->ctim;
-#ifdef DEBUG
- if (o)
- {
- printf("%s %s\n", u->user_name, o->cmd_line);
- }
-#endif
- u = u->next;
- }
- for(u=first_utrec; u; u=u->next)
+ int cnt;
+
+ /* Recognize login session process trees and find leaves of these trees */
+ for (u=first_utrec; u; u=u->next)
{
- for(m=first_procrec; m; m=m->next)
- if (u->usr == m->usr)
+ for (m=first_procrec; m; m=m->next)
+ if (m->pid == u->login_pid)
{
- m->usr = -1;
- u->detach++;
-#ifdef DEBUG
- printf("** %s %s\n", u->user_name, m->cmd_line);
-#endif
+ m->user = u;
+ u->total_time = m->ctim;
+ u->proc = m;
+ DBG("** %d is login process for %s@%s\n", m->pid, u->user_name, u->line);
}
}
+ do
+ {
+ cnt = 0;
+ for (m=first_procrec; m; m=m->next)
+ if (!m->user)
+ for (n=first_procrec; n; n=n->next)
+ if (n->pid == m->ppid && n->user)
+ {
+ u = m->user = n->user;
+ m->distance = n->distance + 1;
+ if (!u->proc || u->proc->distance < m->distance)
+ u->proc = m;
+ cnt++;
+ DBG("** %d at distance %d for %s@%s\n", m->pid, m->distance, u->user_name, u->line);
+ break;
+ }
+ }
+ while (cnt);
+
+ /* Process the remaining processes */
for(m=first_procrec; m; m=m->next)
- if (m->usr >= 2 && m->ppid == 1) /* Exclude no_user,root,bin */
- {
- for(u=first_utrec; u; u=u->next)
- if (u->usr == m->usr)
- break;
- if (!u)
- {
- u = xmalloc(sizeof(struct utrec));
- u->next = first_utrec;
- first_utrec = u;
- u->login_pid = 0;
- u->line_id[0] = 0;
- pw = getpwuid(u->usr = m->usr);
- if (pw)
- strcpy(u->user_name, pw->pw_name);
- else
- sprintf(u->user_name, "<%d>", m->usr);
- u->host_name[0] = 0;
- u->line_name[0] = 0;
- u->mesg = 3;
- u->detach = 0;
- u->proc = NULL;
- u->total_time = 0;
- }
- if (!u->proc)
- u->proc = m;
- u->total_time += m->ctim;
- u->detach ++;
-#ifdef DEBUG
- printf("// %s %s\n", u->user_name, m->cmd_line);
-#endif
- }
+ {
+ if (m->user)
+ continue;
+ for(u=first_utrec; u; u=u->next)
+ if (u->uid == m->uid)
+ break;
+ if (!u)
+ {
+ if (m->uid < 2 || m->ppid != 1)
+ continue;
+ u = xmalloc(sizeof(struct utrec));
+ bzero(u, sizeof(*u));
+ u->next = first_utrec;
+ first_utrec = u;
+ pw = getpwuid(u->uid = m->uid);
+ if (pw)
+ strcpy(u->user_name, pw->pw_name);
+ else
+ sprintf(u->user_name, "<%d>", m->uid);
+ u->mesg = 3;
+ }
+ if (!u->proc)
+ u->proc = m;
+ u->total_time += m->ctim;
+ u->detach++;
+ DBG("** daemon %s (%s)\n", m->cmd_line, u->user_name);
+ }
}
static inline int
return k;
if (!a->host_name[0] && b->host_name[0])
return -1;
- return strcmp(a->line_id, b->line_id);
+ return strcmp(a->line, b->line);
}
static void
show(void)
{
struct utrec *u;
- int i;
unsigned char *c;
- printf("Name Li MD From LogT IdleT RunT Command\n");
+ printf("Name Li MD %-*s LogT IdleT RunT Command\n", maxhost, "From");
for(u=first_utrec; u; u=u->next)
{
- if (!u->line_id[1])
- {
- if (u->line_id[0])
- {
- u->line_id[1] = u->line_id[0];
- u->line_id[0] = 'c';
- }
- else
- u->line_id[0] = u->line_id[1] = '-';
- }
- printf("%-8.8s %-3s %c%c %-17.17s ",
+ if (!u->line[0])
+ strcpy(u->line, "--");
+ printf("%-8.8s %-3s %c%c %-*.*s ",
u->user_name,
- u->line_id,
+ u->line,
u->mesg ? ' ' : '-',
u->detach ? 'D' : ' ',
+ maxhost,
+ maxhost,
u->host_name);
if (u->mesg <= 1)
{
putchar(' ');
puttime((unsigned)u->total_time/(unsigned)HZ);
putchar(' ');
- i = 24;
- c = u->proc->cmd_line;
- while (*c != 0x91 && i--)
+ for (c = u->proc->cmd_line; *c; c++)
{
if (*c < 0x20 || (*c >= 0x7f && *c < 0xa0))
putchar(' ');
else
putchar(*c);
- c++;
}
}
else
int main(void)
{
readutmp();
- dispsys();
+ line1();
+ line2();
readproc();
solve();
sort();