2011-06-05 20:34:42 +08:00
|
|
|
/*
|
|
|
|
* w - show what logged in users are doing.
|
|
|
|
*
|
|
|
|
* Almost entirely rewritten from scratch by Charles Blake circa
|
|
|
|
* June 1996. Some vestigal traces of the original may exist.
|
|
|
|
* That was done in 1993 by Larry Greenfield with some fixes by
|
|
|
|
* Michael K. Johnson.
|
2002-12-15 08:30:17 +08:00
|
|
|
*
|
|
|
|
* Changes by Albert Cahalan, 2002.
|
2012-03-02 20:29:36 +08:00
|
|
|
*
|
|
|
|
* This library is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
|
|
* License as published by the Free Software Foundation; either
|
|
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This library is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
* Lesser General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
|
|
* License along with this library; if not, write to the Free Software
|
|
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
2002-02-02 06:47:29 +08:00
|
|
|
*/
|
2011-10-09 19:05:02 +08:00
|
|
|
|
|
|
|
#include "c.h"
|
2012-03-23 20:32:24 +08:00
|
|
|
#include "fileutils.h"
|
2011-10-09 19:05:02 +08:00
|
|
|
#include "nls.h"
|
2016-04-16 15:03:57 +08:00
|
|
|
#include <proc/procps.h>
|
2011-10-09 19:05:02 +08:00
|
|
|
|
2002-02-02 06:47:29 +08:00
|
|
|
#include <ctype.h>
|
|
|
|
#include <errno.h>
|
|
|
|
#include <fcntl.h>
|
2011-06-05 20:34:42 +08:00
|
|
|
#include <getopt.h>
|
|
|
|
#include <limits.h>
|
|
|
|
#include <locale.h>
|
|
|
|
#include <locale.h>
|
|
|
|
#include <pwd.h>
|
|
|
|
#include <pwd.h>
|
2010-12-20 20:22:24 +08:00
|
|
|
#include <signal.h>
|
2002-02-02 06:47:29 +08:00
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <sys/ioctl.h>
|
|
|
|
#include <sys/mman.h>
|
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <sys/types.h>
|
2011-06-05 20:34:42 +08:00
|
|
|
#include <termios.h>
|
2002-02-02 06:47:29 +08:00
|
|
|
#include <time.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <utmp.h>
|
2012-05-29 20:20:36 +08:00
|
|
|
#include <arpa/inet.h>
|
2002-02-02 06:47:29 +08:00
|
|
|
|
|
|
|
static int ignoreuser = 0; /* for '-u' */
|
2009-11-24 08:00:39 +08:00
|
|
|
static int oldstyle = 0; /* for '-o' */
|
2002-02-02 06:47:29 +08:00
|
|
|
|
|
|
|
typedef struct utmp utmp_t;
|
|
|
|
|
|
|
|
#ifdef W_SHOWFROM
|
2011-06-05 20:34:42 +08:00
|
|
|
# define FROM_STRING "on"
|
2002-02-02 06:47:29 +08:00
|
|
|
#else
|
2011-06-05 20:34:42 +08:00
|
|
|
# define FROM_STRING "off"
|
2002-02-02 06:47:29 +08:00
|
|
|
#endif
|
|
|
|
|
2012-07-15 18:42:56 +08:00
|
|
|
#define MAX_CMD_WIDTH 512
|
2015-07-21 20:45:02 +08:00
|
|
|
#define MIN_CMD_WIDTH 7
|
2012-07-15 18:42:56 +08:00
|
|
|
|
2011-06-05 20:34:42 +08:00
|
|
|
/*
|
|
|
|
* This routine is careful since some programs leave utmp strings
|
|
|
|
* unprintable. Always outputs at least 16 chars padded with
|
|
|
|
* spaces on the right if necessary.
|
2002-02-02 06:47:29 +08:00
|
|
|
*/
|
2011-06-05 20:34:42 +08:00
|
|
|
static void print_host(const char *restrict host, int len, const int fromlen)
|
|
|
|
{
|
|
|
|
const char *last;
|
|
|
|
int width = 0;
|
|
|
|
|
|
|
|
if (len > fromlen)
|
|
|
|
len = fromlen;
|
|
|
|
last = host + len;
|
|
|
|
for (; host < last; host++) {
|
2012-05-29 20:20:36 +08:00
|
|
|
if (*host == '\0') break;
|
2011-06-05 20:34:42 +08:00
|
|
|
if (isprint(*host) && *host != ' ') {
|
|
|
|
fputc(*host, stdout);
|
|
|
|
++width;
|
|
|
|
} else {
|
2011-12-27 02:07:41 +08:00
|
|
|
fputc('-', stdout);
|
|
|
|
++width;
|
2011-06-05 20:34:42 +08:00
|
|
|
break;
|
|
|
|
}
|
2002-02-02 06:47:29 +08:00
|
|
|
}
|
|
|
|
|
2011-06-05 20:34:42 +08:00
|
|
|
/*
|
|
|
|
* space-fill, and a '-' too if needed to ensure the
|
|
|
|
* column exists
|
|
|
|
*/
|
2015-03-03 01:41:07 +08:00
|
|
|
if (!width) {
|
|
|
|
fputc('-', stdout);
|
|
|
|
++width;
|
|
|
|
}
|
2011-06-05 20:34:42 +08:00
|
|
|
while (width++ < fromlen)
|
|
|
|
fputc(' ', stdout);
|
2002-02-02 06:47:29 +08:00
|
|
|
}
|
|
|
|
|
2012-05-29 20:20:36 +08:00
|
|
|
|
2012-06-20 20:19:14 +08:00
|
|
|
/* This routine prints the display part of the host or IPv6 link address interface */
|
|
|
|
static void print_display_or_interface(const char *restrict host, int len, int restlen)
|
2012-05-29 20:20:36 +08:00
|
|
|
{
|
2012-06-20 20:19:14 +08:00
|
|
|
char *disp,*tmp;
|
2012-05-29 20:20:36 +08:00
|
|
|
|
2012-06-20 20:19:14 +08:00
|
|
|
if (restlen <= 0) return; /* not enough space for printing anything */
|
2012-05-29 20:20:36 +08:00
|
|
|
|
2012-06-20 20:19:14 +08:00
|
|
|
/* search for a collon (might be a display) */
|
2012-05-29 20:20:36 +08:00
|
|
|
disp = (char *)host;
|
2012-06-20 20:19:14 +08:00
|
|
|
while ( (disp < (host + len)) && (*disp != ':') && isprint(*disp) ) disp++;
|
2012-05-29 20:20:36 +08:00
|
|
|
|
2012-06-20 20:19:14 +08:00
|
|
|
/* colon found */
|
|
|
|
if (*disp == ':') {
|
|
|
|
/* detect multiple colons -> IPv6 in the host (not a display) */
|
|
|
|
tmp = disp+1;
|
|
|
|
while ( (tmp < (host + len)) && (*tmp != ':') && isprint(*tmp) ) tmp++;
|
2012-05-29 20:20:36 +08:00
|
|
|
|
2012-06-20 20:19:14 +08:00
|
|
|
if (*tmp != ':') { /* multiple colons not found - it's a display */
|
2012-05-29 20:20:36 +08:00
|
|
|
|
2012-06-20 20:19:14 +08:00
|
|
|
/* number of chars till the end of the input field */
|
|
|
|
len -= (disp - host);
|
|
|
|
|
|
|
|
/* if it is still longer than the rest of the output field, then cut it */
|
|
|
|
if (len > restlen) len = restlen;
|
|
|
|
|
|
|
|
/* print the display */
|
|
|
|
while ((len > 0) && isprint(*disp) && (*disp != ' ')) {
|
|
|
|
len--; restlen--;
|
2012-05-29 20:20:36 +08:00
|
|
|
fputc(*disp, stdout);
|
|
|
|
disp++;
|
2012-06-20 20:19:14 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
if ((len > 0) && (*disp != '\0')) { /* space or nonprintable found - replace with dash and stop printing */
|
|
|
|
restlen--;
|
2012-05-29 20:20:36 +08:00
|
|
|
fputc('-', stdout);
|
|
|
|
}
|
2012-06-20 20:19:14 +08:00
|
|
|
} else { /* multiple colons found - it's an IPv6 address */
|
2013-03-31 13:00:00 +08:00
|
|
|
|
2012-06-20 20:19:14 +08:00
|
|
|
/* search for % (interface separator in case of IPv6 link address) */
|
|
|
|
while ( (tmp < (host + len)) && (*tmp != '%') && isprint(*tmp) ) tmp++;
|
|
|
|
|
|
|
|
if (*tmp == '%') { /* interface separator found */
|
2013-03-31 13:00:00 +08:00
|
|
|
|
2012-06-20 20:19:14 +08:00
|
|
|
/* number of chars till the end of the input field */
|
|
|
|
len -= (tmp - host);
|
|
|
|
|
|
|
|
/* if it is still longer than the rest of the output field, then cut it */
|
|
|
|
if (len > restlen) len = restlen;
|
|
|
|
|
|
|
|
/* print the interface */
|
|
|
|
while ((len > 0) && isprint(*tmp) && (*tmp != ' ')) {
|
|
|
|
len--; restlen--;
|
|
|
|
fputc(*tmp, stdout);
|
|
|
|
tmp++;
|
|
|
|
}
|
|
|
|
if ((len > 0) && (*tmp != '\0')) { /* space or nonprintable found - replace with dash and stop printing */
|
|
|
|
restlen--;
|
|
|
|
fputc('-', stdout);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-05-29 20:20:36 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* padding with spaces */
|
|
|
|
while (restlen > 0) {
|
|
|
|
fputc(' ', stdout);
|
|
|
|
restlen--;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* This routine prints either the hostname or the IP address of the remote */
|
|
|
|
static void print_from(const utmp_t *restrict const u, const int ip_addresses, const int fromlen) {
|
|
|
|
char buf[fromlen + 1];
|
2012-05-30 19:37:13 +08:00
|
|
|
char buf_ipv6[INET6_ADDRSTRLEN];
|
2012-05-29 20:20:36 +08:00
|
|
|
int len;
|
2016-03-11 07:04:27 +08:00
|
|
|
#ifndef __CYGWIN__
|
2012-05-29 20:20:36 +08:00
|
|
|
int32_t ut_addr_v6[4]; /* IP address of the remote host */
|
|
|
|
|
|
|
|
if (ip_addresses) { /* -i switch used */
|
|
|
|
memcpy(&ut_addr_v6, &u->ut_addr_v6, sizeof(ut_addr_v6));
|
|
|
|
if (IN6_IS_ADDR_V4MAPPED(&ut_addr_v6)) {
|
|
|
|
/* map back */
|
|
|
|
ut_addr_v6[0] = ut_addr_v6[3];
|
|
|
|
ut_addr_v6[1] = 0;
|
|
|
|
ut_addr_v6[2] = 0;
|
|
|
|
ut_addr_v6[3] = 0;
|
|
|
|
}
|
|
|
|
if (ut_addr_v6[1] || ut_addr_v6[2] || ut_addr_v6[3]) {
|
|
|
|
/* IPv6 */
|
2012-05-30 19:37:13 +08:00
|
|
|
if (!inet_ntop(AF_INET6, &ut_addr_v6, buf_ipv6, sizeof(buf_ipv6))) {
|
2012-05-29 20:20:36 +08:00
|
|
|
strcpy(buf, ""); /* invalid address, clean the buffer */
|
2012-05-30 19:37:13 +08:00
|
|
|
} else {
|
2013-03-11 14:00:00 +08:00
|
|
|
strncpy(buf, buf_ipv6, fromlen); /* address valid, copy to buffer */
|
2012-05-29 20:20:36 +08:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
/* IPv4 */
|
|
|
|
if (!(ut_addr_v6[0] && inet_ntop(AF_INET, &ut_addr_v6[0], buf, sizeof(buf)))) {
|
|
|
|
strcpy(buf, ""); /* invalid address, clean the buffer */
|
|
|
|
}
|
|
|
|
}
|
|
|
|
buf[fromlen] = '\0';
|
|
|
|
|
|
|
|
len = strlen(buf);
|
|
|
|
if (len) { /* IP address is non-empty, print it (and concatenate with display, if present) */
|
|
|
|
fputs(buf, stdout);
|
2012-06-20 20:19:14 +08:00
|
|
|
/* show the display part of the host or IPv6 link addr. interface, if present */
|
|
|
|
print_display_or_interface(u->ut_host, UT_HOSTSIZE, fromlen - len);
|
2012-05-29 20:20:36 +08:00
|
|
|
} else { /* IP address is empty, print the host instead */
|
|
|
|
print_host(u->ut_host, UT_HOSTSIZE, fromlen);
|
|
|
|
}
|
|
|
|
} else { /* -i switch NOT used */
|
|
|
|
print_host(u->ut_host, UT_HOSTSIZE, fromlen);
|
|
|
|
}
|
2016-03-11 07:04:27 +08:00
|
|
|
#else
|
|
|
|
print_host(u->ut_host, UT_HOSTSIZE, fromlen);
|
|
|
|
#endif
|
2012-05-29 20:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-06-05 20:34:42 +08:00
|
|
|
/* compact 7 char format for time intervals (belongs in libproc?) */
|
|
|
|
static void print_time_ival7(time_t t, int centi_sec, FILE * fout)
|
|
|
|
{
|
|
|
|
if ((long)t < (long)0) {
|
|
|
|
/* system clock changed? */
|
|
|
|
printf(" ? ");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (oldstyle) {
|
|
|
|
if (t >= 48 * 60 * 60)
|
|
|
|
/* > 2 days */
|
2011-10-09 19:05:02 +08:00
|
|
|
fprintf(fout, _(" %2ludays"), t / (24 * 60 * 60));
|
2011-06-05 20:34:42 +08:00
|
|
|
else if (t >= 60 * 60)
|
|
|
|
/* > 1 hour */
|
2013-10-11 07:07:10 +08:00
|
|
|
/* Translation Hint: Hours:Minutes */
|
2011-06-05 20:34:42 +08:00
|
|
|
fprintf(fout, " %2lu:%02u ", t / (60 * 60),
|
|
|
|
(unsigned)((t / 60) % 60));
|
|
|
|
else if (t > 60)
|
|
|
|
/* > 1 minute */
|
2013-10-11 07:07:10 +08:00
|
|
|
/* Translation Hint: Minutes:Seconds */
|
2011-10-09 19:05:02 +08:00
|
|
|
fprintf(fout, _(" %2lu:%02um"), t / 60, (unsigned)t % 60);
|
2011-06-05 20:34:42 +08:00
|
|
|
else
|
|
|
|
fprintf(fout, " ");
|
|
|
|
} else {
|
|
|
|
if (t >= 48 * 60 * 60)
|
|
|
|
/* 2 days or more */
|
2011-10-09 19:05:02 +08:00
|
|
|
fprintf(fout, _(" %2ludays"), t / (24 * 60 * 60));
|
2011-06-05 20:34:42 +08:00
|
|
|
else if (t >= 60 * 60)
|
|
|
|
/* 1 hour or more */
|
2013-10-11 07:07:10 +08:00
|
|
|
/* Translation Hint: Hours:Minutes */
|
2011-10-09 19:05:02 +08:00
|
|
|
fprintf(fout, _(" %2lu:%02um"), t / (60 * 60),
|
2011-06-05 20:34:42 +08:00
|
|
|
(unsigned)((t / 60) % 60));
|
|
|
|
else if (t > 60)
|
|
|
|
/* 1 minute or more */
|
2013-10-11 07:07:10 +08:00
|
|
|
/* Translation Hint: Minutes:Seconds */
|
2011-06-05 20:34:42 +08:00
|
|
|
fprintf(fout, " %2lu:%02u ", t / 60, (unsigned)t % 60);
|
|
|
|
else
|
2013-10-11 07:07:10 +08:00
|
|
|
/* Translation Hint: Seconds:Centiseconds */
|
2011-10-09 19:05:02 +08:00
|
|
|
fprintf(fout, _(" %2lu.%02us"), t, centi_sec);
|
2011-06-05 20:34:42 +08:00
|
|
|
}
|
2002-02-02 06:47:29 +08:00
|
|
|
}
|
|
|
|
|
2011-06-05 20:34:42 +08:00
|
|
|
/* stat the device file to get an idle time */
|
|
|
|
static time_t idletime(const char *restrict const tty)
|
|
|
|
{
|
|
|
|
struct stat sbuf;
|
|
|
|
if (stat(tty, &sbuf) != 0)
|
|
|
|
return 0;
|
|
|
|
return time(NULL) - sbuf.st_atime;
|
2002-02-02 06:47:29 +08:00
|
|
|
}
|
|
|
|
|
2011-06-05 20:34:42 +08:00
|
|
|
/* 7 character formatted login time */
|
|
|
|
|
|
|
|
static void print_logintime(time_t logt, FILE * fout)
|
|
|
|
{
|
2012-03-03 19:03:40 +08:00
|
|
|
|
|
|
|
/* Abbreviated of weekday can be longer than 3 characters,
|
|
|
|
* see for instance hu_HU. Using 16 is few bytes more than
|
|
|
|
* enough. */
|
|
|
|
char time_str[16];
|
2011-06-05 20:34:42 +08:00
|
|
|
time_t curt;
|
|
|
|
struct tm *logtm, *curtm;
|
|
|
|
int today;
|
2002-02-02 06:47:29 +08:00
|
|
|
|
2011-06-05 20:34:42 +08:00
|
|
|
curt = time(NULL);
|
|
|
|
curtm = localtime(&curt);
|
|
|
|
/* localtime returns a pointer to static memory */
|
|
|
|
today = curtm->tm_yday;
|
|
|
|
logtm = localtime(&logt);
|
|
|
|
if (curt - logt > 12 * 60 * 60 && logtm->tm_yday != today) {
|
2012-03-03 19:03:40 +08:00
|
|
|
if (curt - logt > 6 * 24 * 60 * 60) {
|
|
|
|
strftime(time_str, sizeof(time_str), "%b", logtm);
|
2011-06-05 20:34:42 +08:00
|
|
|
fprintf(fout, " %02d%3s%02d", logtm->tm_mday,
|
2012-03-03 19:03:40 +08:00
|
|
|
time_str, logtm->tm_year % 100);
|
|
|
|
} else {
|
|
|
|
strftime(time_str, sizeof(time_str), "%a", logtm);
|
|
|
|
fprintf(fout, " %3s%02d ", time_str,
|
2011-06-05 20:34:42 +08:00
|
|
|
logtm->tm_hour);
|
2012-03-03 19:03:40 +08:00
|
|
|
}
|
2011-06-05 20:34:42 +08:00
|
|
|
} else {
|
|
|
|
fprintf(fout, " %02d:%02d ", logtm->tm_hour, logtm->tm_min);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-08-31 20:49:11 +08:00
|
|
|
/*
|
|
|
|
* Get the Device ID of the given TTY
|
|
|
|
*/
|
|
|
|
static int get_tty_device(const char *restrict const name)
|
|
|
|
{
|
|
|
|
struct stat st;
|
|
|
|
static char buf[32];
|
|
|
|
char *dev_paths[] = { "/dev/%s", "/dev/tty%s", "/dev/pts/%s", NULL};
|
|
|
|
int i;
|
|
|
|
|
|
|
|
if (name[0] == '/' && stat(name, &st) == 0)
|
|
|
|
return st.st_rdev;
|
|
|
|
|
|
|
|
for (i=0; dev_paths[i] != NULL; i++) {
|
|
|
|
snprintf(buf, 32, dev_paths[i], name);
|
|
|
|
if (stat(buf, &st) == 0)
|
|
|
|
return st.st_rdev;
|
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2011-06-05 20:34:42 +08:00
|
|
|
/*
|
|
|
|
* This function scans the process table accumulating total cpu
|
|
|
|
* times for any processes "associated" with this login session.
|
|
|
|
* It also searches for the "best" process to report as "(w)hat"
|
|
|
|
* the user for that login session is doing currently. This the
|
|
|
|
* essential core of 'w'.
|
2002-02-02 06:47:29 +08:00
|
|
|
*/
|
2015-08-31 20:49:11 +08:00
|
|
|
static int find_best_proc(
|
|
|
|
const utmp_t * restrict const u,
|
|
|
|
const char *restrict const tty,
|
|
|
|
unsigned long long *restrict const jcpu,
|
|
|
|
unsigned long long *restrict const pcpu,
|
|
|
|
char *cmdline)
|
2011-06-05 20:34:42 +08:00
|
|
|
{
|
2016-07-21 13:00:00 +08:00
|
|
|
#define PIDS_GETINT(e) PIDS_VAL(EU_ ## e, s_int, reap->stacks[i])
|
|
|
|
#define PIDS_GETULL(e) PIDS_VAL(EU_ ## e, ull_int, reap->stacks[i])
|
|
|
|
#define PIDS_GETSTR(e) PIDS_VAL(EU_ ## e, str, reap->stacks[i])
|
2015-08-31 20:49:11 +08:00
|
|
|
unsigned uid = ~0U;
|
|
|
|
int found_utpid = 0;
|
|
|
|
int i, total_procs, line;
|
|
|
|
unsigned long long best_time = 0;
|
|
|
|
unsigned long long secondbest_time = 0;
|
|
|
|
|
2016-07-21 13:00:00 +08:00
|
|
|
struct pids_info *info=NULL;
|
2016-05-14 13:00:00 +08:00
|
|
|
struct pids_fetch *reap;
|
2015-08-31 20:49:11 +08:00
|
|
|
enum pids_item items[] = {
|
2016-07-21 13:00:00 +08:00
|
|
|
PIDS_ID_TGID,
|
|
|
|
PIDS_TIME_START,
|
|
|
|
PIDS_ID_EUID,
|
|
|
|
PIDS_ID_RUID,
|
|
|
|
PIDS_ID_TPGID,
|
|
|
|
PIDS_ID_PGRP,
|
|
|
|
PIDS_TTY,
|
|
|
|
PIDS_TICS_ALL,
|
|
|
|
PIDS_CMDLINE};
|
2015-08-31 20:49:11 +08:00
|
|
|
enum rel_items {
|
|
|
|
EU_TGID, EU_START, EU_EUID, EU_RUID, EU_TPGID, EU_PGRP, EU_TTY,
|
|
|
|
EU_TICS_ALL, EU_CMDLINE};
|
|
|
|
|
|
|
|
*jcpu = 0;
|
|
|
|
*pcpu = 0;
|
|
|
|
if (!ignoreuser) {
|
|
|
|
char buf[UT_NAMESIZE + 1];
|
|
|
|
struct passwd *passwd_data;
|
|
|
|
strncpy(buf, u->ut_user, UT_NAMESIZE);
|
|
|
|
buf[UT_NAMESIZE] = '\0';
|
|
|
|
if ((passwd_data = getpwnam(buf)) == NULL)
|
|
|
|
return 0;
|
|
|
|
uid = passwd_data->pw_uid;
|
|
|
|
/* OK to have passwd_data go out of scope here */
|
|
|
|
}
|
|
|
|
|
|
|
|
line = get_tty_device(tty);
|
|
|
|
|
2016-05-14 13:00:00 +08:00
|
|
|
if (procps_pids_new(&info, items, 9) < 0)
|
2015-08-31 20:49:11 +08:00
|
|
|
xerrx(EXIT_FAILURE,
|
|
|
|
_("Unable to create pid info structure"));
|
2016-07-21 13:00:00 +08:00
|
|
|
if ((reap = procps_pids_reap(info, PIDS_FETCH_TASKS_ONLY)) == NULL)
|
2015-08-31 20:49:11 +08:00
|
|
|
xerrx(EXIT_FAILURE,
|
|
|
|
_("Unable to load process information"));
|
2016-06-23 13:00:00 +08:00
|
|
|
total_procs = reap->counts->total;
|
2015-08-31 20:49:11 +08:00
|
|
|
|
|
|
|
for (i=0; i < total_procs; i++) {
|
|
|
|
/* is this the login process? */
|
|
|
|
if (PIDS_GETINT(TGID) == u->ut_pid) {
|
|
|
|
found_utpid = 1;
|
|
|
|
if (!best_time) {
|
|
|
|
best_time = PIDS_GETULL(START);
|
|
|
|
strncpy(cmdline, PIDS_GETSTR(CMDLINE), MAX_CMD_WIDTH);
|
|
|
|
*pcpu = PIDS_GETULL(TICS_ALL);
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
if (PIDS_GETINT(TTY) != line)
|
|
|
|
continue;
|
2016-07-21 13:00:00 +08:00
|
|
|
(*jcpu) += PIDS_VAL(EU_TICS_ALL, ull_int, reap->stacks[i]);
|
2015-08-31 20:49:11 +08:00
|
|
|
if (!(secondbest_time && PIDS_GETULL(START) <= secondbest_time)) {
|
|
|
|
secondbest_time = PIDS_GETULL(START);
|
|
|
|
if (cmdline[0] == '-' && cmdline[1] == '\0') {
|
|
|
|
strncpy(cmdline, PIDS_GETSTR(CMDLINE), MAX_CMD_WIDTH);
|
|
|
|
*pcpu = PIDS_GETULL(TICS_ALL);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (
|
|
|
|
(!ignoreuser && uid != PIDS_GETINT(EUID)
|
|
|
|
&& uid != PIDS_GETINT(RUID))
|
|
|
|
|| (PIDS_GETINT(PGRP) != PIDS_GETINT(TPGID))
|
|
|
|
|| (PIDS_GETULL(START) <= best_time)
|
|
|
|
)
|
|
|
|
continue;
|
|
|
|
best_time = PIDS_GETULL(START);
|
|
|
|
strncpy(cmdline, PIDS_GETSTR(CMDLINE), MAX_CMD_WIDTH);
|
|
|
|
*pcpu = PIDS_GETULL(TICS_ALL);
|
|
|
|
}
|
|
|
|
procps_pids_unref(&info);
|
|
|
|
return found_utpid;
|
|
|
|
#undef PIDS_GETINT
|
|
|
|
#undef PIDS_GETULL
|
|
|
|
#undef PIDS_GETSTR
|
2002-02-02 06:47:29 +08:00
|
|
|
}
|
|
|
|
|
2015-08-31 20:49:11 +08:00
|
|
|
static void showinfo(
|
|
|
|
utmp_t * u, int formtype, int maxcmd, int from,
|
|
|
|
const int userlen, const int fromlen, const int ip_addresses)
|
2011-06-05 20:34:42 +08:00
|
|
|
{
|
2015-08-31 20:49:11 +08:00
|
|
|
unsigned long long jcpu, pcpu;
|
|
|
|
unsigned i;
|
|
|
|
char uname[UT_NAMESIZE + 1] = "", tty[5 + UT_LINESIZE + 1] = "/dev/";
|
|
|
|
long hertz;
|
|
|
|
char cmdline[MAX_CMD_WIDTH + 1];
|
|
|
|
|
|
|
|
strcpy(cmdline, "-");
|
|
|
|
|
|
|
|
hertz = procps_hertz_get();
|
|
|
|
for (i = 0; i < UT_LINESIZE; i++)
|
|
|
|
/* clean up tty if garbled */
|
|
|
|
if (isalnum(u->ut_line[i]) || (u->ut_line[i] == '/'))
|
|
|
|
tty[i + 5] = u->ut_line[i];
|
|
|
|
else
|
|
|
|
tty[i + 5] = '\0';
|
|
|
|
|
|
|
|
if (find_best_proc(u, tty + 5, &jcpu, &pcpu, cmdline) == 0)
|
|
|
|
/*
|
|
|
|
* just skip if stale utmp entry (i.e. login proc doesn't
|
|
|
|
* exist). If there is a desire a cmdline flag could be
|
|
|
|
* added to optionally show it with a prefix of (stale)
|
|
|
|
* in front of cmd or something like that.
|
|
|
|
*/
|
|
|
|
return;
|
|
|
|
|
|
|
|
/* force NUL term for printf */
|
|
|
|
strncpy(uname, u->ut_user, UT_NAMESIZE);
|
|
|
|
|
|
|
|
if (formtype) {
|
|
|
|
printf("%-*.*s%-9.8s", userlen + 1, userlen, uname, u->ut_line);
|
|
|
|
if (from)
|
|
|
|
print_from(u, ip_addresses, fromlen);
|
|
|
|
print_logintime(u->ut_time, stdout);
|
|
|
|
if (*u->ut_line == ':')
|
|
|
|
/* idle unknown for xdm logins */
|
|
|
|
printf(" ?xdm? ");
|
|
|
|
else
|
|
|
|
print_time_ival7(idletime(tty), 0, stdout);
|
|
|
|
print_time_ival7(jcpu / hertz, (jcpu % hertz) * (100. / hertz),
|
|
|
|
stdout);
|
|
|
|
if (pcpu > 0)
|
|
|
|
print_time_ival7(pcpu / hertz,
|
|
|
|
(pcpu % hertz) * (100. / hertz),
|
|
|
|
stdout);
|
|
|
|
else
|
|
|
|
printf(" ? ");
|
|
|
|
} else {
|
|
|
|
printf("%-*.*s%-9.8s", userlen + 1, userlen, u->ut_user,
|
|
|
|
u->ut_line);
|
|
|
|
if (from)
|
|
|
|
print_from(u, ip_addresses, fromlen);
|
|
|
|
if (*u->ut_line == ':')
|
|
|
|
/* idle unknown for xdm logins */
|
|
|
|
printf(" ?xdm? ");
|
|
|
|
else
|
|
|
|
print_time_ival7(idletime(tty), 0, stdout);
|
|
|
|
}
|
2015-09-06 13:00:00 +08:00
|
|
|
printf(" %.*s\n", maxcmd, cmdline);
|
2011-06-05 20:34:42 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static void __attribute__ ((__noreturn__))
|
|
|
|
usage(FILE * out)
|
|
|
|
{
|
2011-10-09 19:05:02 +08:00
|
|
|
fputs(USAGE_HEADER, out);
|
2011-06-05 20:34:42 +08:00
|
|
|
fprintf(out,
|
2015-09-06 13:00:00 +08:00
|
|
|
_(" %s [options] [user]\n"), program_invocation_short_name);
|
2011-10-09 19:05:02 +08:00
|
|
|
fputs(USAGE_OPTIONS, out);
|
2013-10-11 07:07:10 +08:00
|
|
|
fputs(_(" -h, --no-header do not print header\n"),out);
|
|
|
|
fputs(_(" -u, --no-current ignore current process username\n"),out);
|
|
|
|
fputs(_(" -s, --short short format\n"),out);
|
|
|
|
fputs(_(" -f, --from show remote hostname field\n"),out);
|
|
|
|
fputs(_(" -o, --old-style old style output\n"),out);
|
|
|
|
fputs(_(" -i, --ip-addr display IP address instead of hostname (if possible)\n"), out);
|
2011-10-09 19:05:02 +08:00
|
|
|
fputs(USAGE_SEPARATOR, out);
|
|
|
|
fputs(_(" --help display this help and exit\n"), out);
|
|
|
|
fputs(USAGE_VERSION, out);
|
|
|
|
fprintf(out, USAGE_MAN_TAIL("w(1)"));
|
2011-06-05 20:34:42 +08:00
|
|
|
|
|
|
|
exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
|
2002-02-02 06:47:29 +08:00
|
|
|
}
|
|
|
|
|
2011-06-05 20:34:42 +08:00
|
|
|
int main(int argc, char **argv)
|
|
|
|
{
|
|
|
|
char *user = NULL, *p;
|
|
|
|
utmp_t *u;
|
|
|
|
struct winsize win;
|
2012-05-29 20:20:36 +08:00
|
|
|
int ch;
|
|
|
|
int maxcmd = 80;
|
2011-06-05 20:34:42 +08:00
|
|
|
int userlen = 8;
|
|
|
|
int fromlen = 16;
|
|
|
|
char *env_var;
|
|
|
|
|
2012-05-29 20:20:36 +08:00
|
|
|
/* switches (defaults) */
|
|
|
|
int header = 1;
|
|
|
|
int longform = 1;
|
|
|
|
int from = 1;
|
|
|
|
int ip_addresses = 0;
|
|
|
|
|
2011-06-05 20:34:42 +08:00
|
|
|
enum {
|
|
|
|
HELP_OPTION = CHAR_MAX + 1
|
|
|
|
};
|
|
|
|
|
|
|
|
static const struct option longopts[] = {
|
|
|
|
{"no-header", no_argument, NULL, 'h'},
|
|
|
|
{"no-current", no_argument, NULL, 'u'},
|
2013-07-19 20:50:02 +08:00
|
|
|
{"short", no_argument, NULL, 's'},
|
2011-06-05 20:34:42 +08:00
|
|
|
{"from", no_argument, NULL, 'f'},
|
|
|
|
{"old-style", no_argument, NULL, 'o'},
|
2013-07-19 20:50:02 +08:00
|
|
|
{"ip-addr", no_argument, NULL, 'i'},
|
2011-06-05 20:34:42 +08:00
|
|
|
{"help", no_argument, NULL, HELP_OPTION},
|
|
|
|
{"version", no_argument, NULL, 'V'},
|
|
|
|
{NULL, 0, NULL, 0}
|
|
|
|
};
|
2002-02-02 06:47:29 +08:00
|
|
|
|
2013-02-21 01:31:48 +08:00
|
|
|
#ifdef HAVE_PROGRAM_INVOCATION_NAME
|
2012-01-03 15:48:43 +08:00
|
|
|
program_invocation_name = program_invocation_short_name;
|
2013-02-21 01:31:48 +08:00
|
|
|
#endif
|
2011-12-07 20:27:21 +08:00
|
|
|
setlocale (LC_ALL, "");
|
|
|
|
bindtextdomain(PACKAGE, LOCALEDIR);
|
|
|
|
textdomain(PACKAGE);
|
2012-03-23 20:32:24 +08:00
|
|
|
atexit(close_stdout);
|
2011-12-07 20:27:21 +08:00
|
|
|
|
2002-02-02 06:47:29 +08:00
|
|
|
#ifndef W_SHOWFROM
|
2011-06-05 20:34:42 +08:00
|
|
|
from = 0;
|
2002-02-02 06:47:29 +08:00
|
|
|
#endif
|
|
|
|
|
2011-12-07 20:27:21 +08:00
|
|
|
while ((ch =
|
2012-05-29 20:20:36 +08:00
|
|
|
getopt_long(argc, argv, "husfoVi", longopts, NULL)) != -1)
|
2011-06-05 20:34:42 +08:00
|
|
|
switch (ch) {
|
|
|
|
case 'h':
|
|
|
|
header = 0;
|
|
|
|
break;
|
|
|
|
case 'l':
|
|
|
|
longform = 1;
|
|
|
|
break;
|
|
|
|
case 's':
|
|
|
|
longform = 0;
|
|
|
|
break;
|
|
|
|
case 'f':
|
|
|
|
from = !from;
|
|
|
|
break;
|
|
|
|
case 'V':
|
2011-10-09 19:05:02 +08:00
|
|
|
printf(PROCPS_NG_VERSION);
|
2011-06-05 20:34:42 +08:00
|
|
|
exit(0);
|
|
|
|
case 'u':
|
|
|
|
ignoreuser = 1;
|
|
|
|
break;
|
|
|
|
case 'o':
|
|
|
|
oldstyle = 1;
|
|
|
|
break;
|
2012-05-29 20:20:36 +08:00
|
|
|
case 'i':
|
|
|
|
ip_addresses = 1;
|
|
|
|
from = 1;
|
|
|
|
break;
|
2011-06-05 20:34:42 +08:00
|
|
|
case HELP_OPTION:
|
|
|
|
usage(stdout);
|
|
|
|
default:
|
|
|
|
usage(stderr);
|
|
|
|
}
|
2002-02-02 06:47:29 +08:00
|
|
|
|
2011-06-05 20:34:42 +08:00
|
|
|
if ((argv[optind]))
|
|
|
|
user = (argv[optind]);
|
2002-02-02 06:47:29 +08:00
|
|
|
|
2009-11-24 08:00:44 +08:00
|
|
|
/* Get user field length from environment */
|
2011-06-05 20:34:42 +08:00
|
|
|
if ((env_var = getenv("PROCPS_USERLEN")) != NULL) {
|
2012-05-20 18:20:32 +08:00
|
|
|
int ut_namesize = UT_NAMESIZE;
|
2011-06-05 20:34:42 +08:00
|
|
|
userlen = atoi(env_var);
|
2012-05-20 18:20:32 +08:00
|
|
|
if (userlen < 8 || ut_namesize < userlen) {
|
2012-01-03 15:48:43 +08:00
|
|
|
xwarnx
|
2012-05-20 18:20:32 +08:00
|
|
|
(_("User length environment PROCPS_USERLEN must be between 8 and %i, ignoring.\n"),
|
|
|
|
ut_namesize);
|
2011-06-05 20:34:42 +08:00
|
|
|
userlen = 8;
|
|
|
|
}
|
2009-11-24 08:00:44 +08:00
|
|
|
}
|
|
|
|
/* Get from field length from environment */
|
2011-06-05 20:34:42 +08:00
|
|
|
if ((env_var = getenv("PROCPS_FROMLEN")) != NULL) {
|
|
|
|
fromlen = atoi(env_var);
|
2012-03-03 18:38:52 +08:00
|
|
|
if (fromlen < 8 || UT_HOSTSIZE < fromlen) {
|
2012-01-03 15:48:43 +08:00
|
|
|
xwarnx
|
2012-01-14 05:38:47 +08:00
|
|
|
(_("from length environment PROCPS_FROMLEN must be between 8 and %d, ignoring\n"),
|
2012-03-03 18:38:52 +08:00
|
|
|
UT_HOSTSIZE);
|
2011-06-05 20:34:42 +08:00
|
|
|
fromlen = 16;
|
|
|
|
}
|
2009-11-24 08:00:44 +08:00
|
|
|
}
|
2011-06-05 20:34:42 +08:00
|
|
|
if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &win) != -1 && win.ws_col > 0)
|
|
|
|
maxcmd = win.ws_col;
|
2012-02-26 06:54:51 +08:00
|
|
|
else if ((p = getenv("COLUMNS")))
|
2011-06-05 20:34:42 +08:00
|
|
|
maxcmd = atoi(p);
|
2002-02-02 06:47:29 +08:00
|
|
|
else
|
2012-07-15 18:42:56 +08:00
|
|
|
maxcmd = MAX_CMD_WIDTH;
|
2015-07-21 20:45:02 +08:00
|
|
|
if (MAX_CMD_WIDTH < maxcmd)
|
2012-07-15 18:42:56 +08:00
|
|
|
maxcmd = MAX_CMD_WIDTH;
|
2011-06-05 20:34:42 +08:00
|
|
|
maxcmd -= 21 + userlen + (from ? fromlen : 0) + (longform ? 20 : 0);
|
2015-07-21 20:45:02 +08:00
|
|
|
if (maxcmd < MIN_CMD_WIDTH)
|
|
|
|
maxcmd = MIN_CMD_WIDTH;
|
2011-06-05 20:34:42 +08:00
|
|
|
|
|
|
|
|
|
|
|
if (header) {
|
|
|
|
/* print uptime and headers */
|
2015-06-29 20:09:59 +08:00
|
|
|
printf("%s\n", procps_uptime_sprint());
|
2011-12-18 01:32:47 +08:00
|
|
|
/* Translation Hint: Following five uppercase messages are
|
|
|
|
* headers. Try to keep alignment intact. */
|
|
|
|
printf(_("%-*s TTY "), userlen, _("USER"));
|
2011-06-05 20:34:42 +08:00
|
|
|
if (from)
|
2012-03-03 19:31:49 +08:00
|
|
|
printf("%-*s", fromlen - 1, _("FROM"));
|
2011-06-05 20:34:42 +08:00
|
|
|
if (longform)
|
2011-12-18 01:32:47 +08:00
|
|
|
printf(_(" LOGIN@ IDLE JCPU PCPU WHAT\n"));
|
2011-06-05 20:34:42 +08:00
|
|
|
else
|
2011-12-18 01:32:47 +08:00
|
|
|
printf(_(" IDLE WHAT\n"));
|
2002-12-09 15:00:07 +08:00
|
|
|
}
|
2011-06-05 20:34:42 +08:00
|
|
|
|
|
|
|
utmpname(UTMP_FILE);
|
|
|
|
setutent();
|
|
|
|
if (user) {
|
|
|
|
for (;;) {
|
|
|
|
u = getutent();
|
2016-04-16 15:03:57 +08:00
|
|
|
if (!u)
|
2011-06-05 20:34:42 +08:00
|
|
|
break;
|
|
|
|
if (u->ut_type != USER_PROCESS)
|
|
|
|
continue;
|
2012-02-26 06:50:47 +08:00
|
|
|
if (!strncmp(u->ut_user, user, UT_NAMESIZE))
|
2011-06-05 20:34:42 +08:00
|
|
|
showinfo(u, longform, maxcmd, from, userlen,
|
2012-05-29 20:20:36 +08:00
|
|
|
fromlen, ip_addresses);
|
2011-06-05 20:34:42 +08:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
for (;;) {
|
|
|
|
u = getutent();
|
2016-04-16 15:03:57 +08:00
|
|
|
if (!u)
|
2011-06-05 20:34:42 +08:00
|
|
|
break;
|
|
|
|
if (u->ut_type != USER_PROCESS)
|
|
|
|
continue;
|
|
|
|
if (*u->ut_user)
|
|
|
|
showinfo(u, longform, maxcmd, from, userlen,
|
2012-05-29 20:20:36 +08:00
|
|
|
fromlen, ip_addresses);
|
2011-06-05 20:34:42 +08:00
|
|
|
}
|
2002-12-09 15:00:07 +08:00
|
|
|
}
|
2011-06-05 20:34:42 +08:00
|
|
|
endutent();
|
2002-02-02 06:47:29 +08:00
|
|
|
|
2011-06-05 20:34:42 +08:00
|
|
|
return EXIT_SUCCESS;
|
2002-02-02 06:47:29 +08:00
|
|
|
}
|