rusers.c revision 7c478bd95313f5f23a4c958a745db2134aa03244
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* Copyright 2005 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
/* All Rights Reserved */
/*
* University Copyright- Copyright (c) 1982, 1986, 1988
* The Regents of the University of California
* All Rights Reserved
*
* University Acknowledgment- Portions of this document are derived from
* software developed by the University of California, Berkeley, and its
* contributors.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <netconfig.h>
#include <netdir.h>
#include <rpc/rpc.h>
#include <rpcsvc/rusers.h>
#include <string.h>
#include <limits.h>
#define NMAX 12 /* These are used as field width specifiers */
#define LMAX 8 /* when printing. */
#define HMAX 16 /* "Logged in" host name. */
#define MACHINELEN 16 /* length of machine name printed out */
#define NUMENTRIES 256
#define min(a, b) ((a) < (b) ? (a) : (b))
struct entry {
int cnt;
int idle; /* set to INT_MAX if not present */
char *machine;
utmp_array users;
};
static int curentry;
static int total_entries;
static struct entry *entry;
static int hflag; /* host: sort by machine name */
static int iflag; /* idle: sort by idle time */
static int uflag; /* users: sort by number of users */
static int lflag; /* print out long form */
static int aflag; /* all: list all machines */
static int dflag; /* debug: list only first n machines */
static int sorted;
static int debug;
static int debugcnt;
static char *nettype;
static int hcompare(const struct entry *, const struct entry *);
static int icompare(const struct entry *, const struct entry *);
static int ucompare(const struct entry *, const struct entry *);
static int print_info(struct utmpidlearr *, const char *);
static int print_info_3(utmp_array *, const char *);
static int collectnames(void *, struct netbuf *, struct netconfig *);
static int collectnames_3(void *, struct netbuf *, struct netconfig *);
static void singlehost(char *);
static void printnames(void);
static void putline_2(char *, struct utmpidle *);
static void putline_3(char *, rusers_utmp *);
static void prttime(uint_t, char *);
static void usage(void);
/*
* rusers [-ahilu] [host...]
*/
int
main(int argc, char *argv[])
{
int c;
uint_t errflag = 0;
uint_t single = 0;
struct utmpidlearr utmpidlearr;
utmp_array utmp_array_res;
curentry = 0;
total_entries = NUMENTRIES;
entry = malloc(sizeof (struct entry) * total_entries);
while ((c = getopt(argc, argv, ":ad:hilun:")) != -1) {
switch (c) {
case 'a':
aflag++;
break;
case 'd':
dflag++;
debug = atoi(optarg);
(void) printf("Will collect %d responses.\n", debug);
break;
case 'h':
hflag++;
sorted++;
if (iflag || uflag)
errflag++;
break;
case 'i':
iflag++;
sorted++;
if (hflag || uflag)
errflag++;
break;
case 'u':
uflag++;
sorted++;
if (hflag || iflag)
errflag++;
break;
case 'l':
lflag++;
break;
case ':': /* required operand missing */
errflag++;
break;
case 'n':
nettype = optarg;
break;
default:
case '?': /* Unrecognized option */
errflag++;
break;
}
}
if (errflag)
usage();
for (; optind < argc; optind++) {
single++;
singlehost(argv[optind]);
}
if (single) {
if (sorted)
printnames();
free(entry);
exit(0);
}
if (sorted) {
(void) printf("Collecting responses...\n");
(void) fflush(stdout);
}
utmp_array_res.utmp_array_val = NULL;
utmp_array_res.utmp_array_len = 0;
(void) printf("Sending broadcast for rusersd protocol version 3...\n");
(void) rpc_broadcast(RUSERSPROG, RUSERSVERS_3,
RUSERSPROC_NAMES, (xdrproc_t)xdr_void, NULL,
(xdrproc_t)xdr_utmp_array, (char *)&utmp_array_res,
(resultproc_t)collectnames_3, nettype);
utmpidlearr.uia_arr = NULL;
(void) printf("Sending broadcast for rusersd protocol version 2...\n");
(void) rpc_broadcast(RUSERSPROG, RUSERSVERS_IDLE,
RUSERSPROC_NAMES, (xdrproc_t)xdr_void, NULL,
(xdrproc_t)xdr_utmpidlearr, (char *)&utmpidlearr,
(resultproc_t)collectnames, nettype);
if (sorted)
printnames();
free(entry);
return (0);
}
static void
singlehost(char *name)
{
enum clnt_stat err;
struct utmpidlearr utmpidlearr;
utmp_array utmp_array_res;
if (curentry >= total_entries) {
struct entry *tmp;
total_entries += NUMENTRIES;
if ((tmp = realloc(entry, sizeof (struct entry)
* total_entries)) == NULL)
return;
entry = tmp;
}
utmp_array_res.utmp_array_val = NULL;
utmp_array_res.utmp_array_len = 0;
err = rpc_call(name, RUSERSPROG, RUSERSVERS_3,
RUSERSPROC_NAMES, (xdrproc_t)xdr_void, 0,
(xdrproc_t)xdr_utmp_array, (char *)&utmp_array_res,
nettype);
if (err == RPC_SUCCESS) {
(void) print_info_3(&utmp_array_res, name);
return;
}
if (err == RPC_PROGVERSMISMATCH) {
utmpidlearr.uia_arr = NULL;
err = rpc_call(name, RUSERSPROG, RUSERSVERS_IDLE,
RUSERSPROC_NAMES, (xdrproc_t)xdr_void, 0,
(xdrproc_t)xdr_utmpidlearr,
(char *)&utmpidlearr, nettype);
}
if (err != RPC_SUCCESS) {
(void) fprintf(stderr, "%s: ", name);
clnt_perrno(err);
return;
}
(void) print_info(&utmpidlearr, name);
}
/*
* Collect responses from RUSERSVERS_IDLE broadcast, convert to
* RUSERSVERS_3 format, and store in entry database.
*/
static int
collectnames(void *resultsp, struct netbuf *raddrp, struct netconfig *nconf)
{
struct utmpidlearr utmpidlearr;
struct entry *entryp, *lim;
struct nd_hostservlist *hs;
char host[MACHINELEN + 1];
utmpidlearr = *(struct utmpidlearr *)resultsp;
if (utmpidlearr.uia_cnt < 1 && !aflag)
return (0);
if (netdir_getbyaddr(nconf, &hs, raddrp)) {
#ifdef DEBUG
netdir_perror("netdir_getbyaddr");
#endif
/* netdir routine couldn't resolve addr;just print out uaddr */
(void) sprintf(host, "%.*s", MACHINELEN,
taddr2uaddr(nconf, raddrp));
} else {
(void) sprintf(host, "%.*s", MACHINELEN,
hs->h_hostservs->h_host);
netdir_free((char *)hs, ND_HOSTSERVLIST);
}
/*
* need to realloc more space if we have more than 256 machines
* that respond to broadcast
*/
if (curentry >= total_entries) {
struct entry *tmp;
total_entries += NUMENTRIES;
if ((tmp = realloc(entry, sizeof (struct entry)
* total_entries)) == NULL)
return (1);
entry = tmp;
}
/*
* weed out duplicates
*/
lim = entry + curentry;
for (entryp = entry; entryp < lim; entryp++) {
if (strcmp(entryp->machine, host) == 0)
return (0);
}
return (print_info((struct utmpidlearr *)resultsp, host));
}
static int
print_info(struct utmpidlearr *utmpidlearrp, const char *name)
{
utmp_array *iconvert;
int i, cnt, minidle;
char host[MACHINELEN + 1];
char username[NMAX + 1];
cnt = utmpidlearrp->uia_cnt;
(void) sprintf(host, "%.*s", MACHINELEN, name);
/*
* if raw, print this entry out immediately
* otherwise store for later sorting
*/
if (!sorted) {
if (lflag && (cnt > 0))
for (i = 0; i < cnt; i++)
putline_2(host, utmpidlearrp->uia_arr[i]);
else {
(void) printf("%-*.*s", MACHINELEN, MACHINELEN, host);
for (i = 0; i < cnt; i++) {
(void) strlcpy(username,
utmpidlearrp->uia_arr[i]->ui_utmp.ut_name,
NMAX + 1);
(void) printf(" %.*s", NMAX, username);
}
(void) printf("\n");
}
/* store just the name */
entry[curentry].machine = malloc(MACHINELEN + 1);
if (entry[curentry].machine == NULL) {
(void) fprintf(stderr, "Ran out of memory - exiting\n");
exit(1);
}
(void) strlcpy(entry[curentry].machine, name, MACHINELEN + 1);
entry[curentry++].cnt = 0;
if (dflag && (++debugcnt >= debug))
return (1);
return (0);
}
entry[curentry].machine = malloc(MACHINELEN + 1);
if (entry[curentry].machine == NULL) {
(void) fprintf(stderr, "Ran out of memory - exiting\n");
exit(1);
}
(void) strlcpy(entry[curentry].machine, name, MACHINELEN + 1);
entry[curentry].cnt = cnt;
iconvert = &entry[curentry].users;
iconvert->utmp_array_len = cnt;
iconvert->utmp_array_val = malloc(cnt * sizeof (rusers_utmp));
minidle = INT_MAX;
for (i = 0; i < cnt; i++) {
iconvert->utmp_array_val[i].ut_user =
strdup(utmpidlearrp->uia_arr[i]->ui_utmp.ut_name);
iconvert->utmp_array_val[i].ut_line =
strdup(utmpidlearrp->uia_arr[i]->ui_utmp.ut_line);
iconvert->utmp_array_val[i].ut_host =
strdup(utmpidlearrp->uia_arr[i]->ui_utmp.ut_host);
iconvert->utmp_array_val[i].ut_time =
utmpidlearrp->uia_arr[i]->ui_utmp.ut_time;
iconvert->utmp_array_val[i].ut_idle =
utmpidlearrp->uia_arr[i]->ui_idle;
minidle = min(minidle, utmpidlearrp->uia_arr[i]->ui_idle);
}
entry[curentry].idle = minidle;
curentry++;
if (dflag && (++debugcnt >= debug))
return (1);
return (0);
}
/*
* Collect responses from RUSERSVERS_3 broadcast.
*/
static int
collectnames_3(void *resultsp, struct netbuf *raddrp, struct netconfig *nconf)
{
utmp_array *uap;
struct entry *entryp, *lim;
struct nd_hostservlist *hs;
char host[MACHINELEN + 1];
uap = (utmp_array *)resultsp;
if (uap->utmp_array_len < 1 && !aflag)
return (0);
if (netdir_getbyaddr(nconf, &hs, raddrp)) {
#ifdef DEBUG
netdir_perror("netdir_getbyaddr");
#endif
/* netdir routine couldn't resolve addr;just print out uaddr */
(void) sprintf(host, "%.*s", MACHINELEN,
taddr2uaddr(nconf, raddrp));
} else {
(void) sprintf(host, "%.*s", MACHINELEN,
hs->h_hostservs->h_host);
netdir_free((char *)hs, ND_HOSTSERVLIST);
}
/*
* need to realloc more space if we have more than 256 machines
* that respond to broadcast
*/
if (curentry >= total_entries) {
struct entry *tmp;
total_entries += NUMENTRIES;
if ((tmp = realloc(entry, sizeof (struct entry)
* total_entries)) == NULL)
return (1);
entry = tmp;
}
/*
* weed out duplicates
*/
lim = entry + curentry;
for (entryp = entry; entryp < lim; entryp++) {
if (strcmp(entryp->machine, host) == 0)
return (0);
}
return (print_info_3(uap, host));
}
static int
print_info_3(utmp_array *uap, const char *name)
{
int i, cnt, minidle;
char host[MACHINELEN + 1];
cnt = uap->utmp_array_len;
(void) sprintf(host, "%.*s", MACHINELEN, name);
/*
* if raw, print this entry out immediately
* otherwise store for later sorting
*/
if (!sorted) {
if (lflag && (cnt > 0))
for (i = 0; i < cnt; i++)
putline_3(host, &uap->utmp_array_val[i]);
else {
(void) printf("%-*.*s", MACHINELEN, MACHINELEN, host);
for (i = 0; i < cnt; i++)
(void) printf(" %.*s", NMAX,
uap->utmp_array_val[i].ut_user);
(void) printf("\n");
}
/* store just the name */
entry[curentry].machine = malloc(MACHINELEN + 1);
if (entry[curentry].machine == NULL) {
(void) fprintf(stderr, "Ran out of memory - exiting\n");
exit(1);
}
(void) strlcpy(entry[curentry].machine, name, MACHINELEN + 1);
entry[curentry++].cnt = 0;
if (dflag && (++debugcnt >= debug))
return (1);
return (0);
}
entry[curentry].machine = malloc(MACHINELEN + 1);
if (entry[curentry].machine == NULL) {
(void) fprintf(stderr, "Ran out of memory - exiting\n");
exit(1);
}
(void) strlcpy(entry[curentry].machine, name, MACHINELEN + 1);
entry[curentry].cnt = cnt;
entry[curentry].users.utmp_array_len = cnt;
entry[curentry].users.utmp_array_val = malloc(cnt *
sizeof (rusers_utmp));
minidle = INT_MAX;
for (i = 0; i < cnt; i++) {
entry[curentry].users.utmp_array_val[i].ut_user =
strdup(uap->utmp_array_val[i].ut_user);
entry[curentry].users.utmp_array_val[i].ut_line =
strdup(uap->utmp_array_val[i].ut_line);
entry[curentry].users.utmp_array_val[i].ut_host =
strdup(uap->utmp_array_val[i].ut_host);
entry[curentry].users.utmp_array_val[i].ut_time =
uap->utmp_array_val[i].ut_time;
entry[curentry].users.utmp_array_val[i].ut_idle =
uap->utmp_array_val[i].ut_idle;
minidle = min(minidle, uap->utmp_array_val[i].ut_idle);
}
entry[curentry].idle = minidle;
curentry++;
if (dflag && (++debugcnt >= debug))
return (1);
return (0);
}
static void
printnames(void)
{
int i, j;
int (*compare)(const void *, const void *);
/* the name of the machine should already be in the structure */
if (iflag)
compare = (int (*)(const void *, const void *))icompare;
else if (hflag)
compare = (int (*)(const void *, const void *))hcompare;
else
compare = (int (*)(const void *, const void *))ucompare;
qsort(entry, curentry, sizeof (struct entry), compare);
for (i = 0; i < curentry; i++) {
if (!lflag || (entry[i].cnt < 1)) {
(void) printf("%-*.*s", MACHINELEN,
MACHINELEN, entry[i].machine);
for (j = 0; j < entry[i].cnt; j++)
(void) printf(" %.*s", NMAX,
entry[i].users.utmp_array_val[j].ut_user);
(void) printf("\n");
} else {
for (j = 0; j < entry[i].cnt; j++)
putline_3(entry[i].machine,
&entry[i].users.utmp_array_val[j]);
}
}
}
static int
hcompare(const struct entry *a, const struct entry *b)
{
return (strcmp(a->machine, b->machine));
}
static int
ucompare(const struct entry *a, const struct entry *b)
{
return (b->cnt - a->cnt);
}
static int
icompare(const struct entry *a, const struct entry *b)
{
return (a->idle - b->idle);
}
static void
putline_2(char *host, struct utmpidle *uip)
{
char *cbuf;
struct ru_utmp *up;
char buf[100];
up = &uip->ui_utmp;
#define NAMEMAX ((sizeof (up->ut_name) < NMAX) ? NMAX : sizeof (up->ut_name))
#define NAMEMIN ((sizeof (up->ut_name) > NMAX) ? NMAX : sizeof (up->ut_name))
/* Try and align this up nicely */
#define LINEMAX sizeof (up->ut_line)
#define HOSTMAX sizeof (up->ut_host)
/*
* We copy the strings into a buffer because they aren't strictly
* speaking strings but byte arrays (and they may not have a
* terminating NULL.
*/
(void) strncpy(buf, up->ut_name, NAMEMAX);
buf[NAMEMIN] = '\0';
(void) printf("%-*.*s ", NAMEMAX, NAMEMAX, buf);
(void) strcpy(buf, host);
(void) strcat(buf, ":");
(void) strncat(buf, up->ut_line, LINEMAX);
buf[MACHINELEN+LINEMAX] = '\0';
(void) printf("%-*.*s", MACHINELEN+LINEMAX, MACHINELEN+LINEMAX, buf);
cbuf = (char *)ctime(&up->ut_time);
(void) printf(" %.12s ", cbuf+4);
if (uip->ui_idle == INT_MAX)
(void) printf(" ??");
else
prttime(uip->ui_idle, "");
if (up->ut_host[0]) {
(void) strncpy(buf, up->ut_host, HOSTMAX);
buf[HOSTMAX] = '\0';
(void) printf(" (%.*s)", HOSTMAX, buf);
}
(void) putchar('\n');
}
static void
putline_3(char *host, rusers_utmp *rup)
{
char *cbuf;
char buf[100];
(void) printf("%-*.*s ", NMAX, NMAX, rup->ut_user);
(void) strcpy(buf, host);
(void) strcat(buf, ":");
(void) strncat(buf, rup->ut_line, LMAX);
(void) printf("%-*.*s", MACHINELEN+LMAX, MACHINELEN+LMAX, buf);
cbuf = (char *)ctime((time_t *)&rup->ut_time);
(void) printf(" %.12s ", cbuf+4);
if (rup->ut_idle == INT_MAX)
(void) printf(" ??");
else
prttime(rup->ut_idle, "");
if (rup->ut_host[0])
(void) printf(" (%.*s)", HMAX, rup->ut_host);
(void) putchar('\n');
}
/*
* prttime prints a time in hours and minutes.
* The character string tail is printed at the end, obvious
* strings to pass are "", " ", or "am".
*/
static void
prttime(uint_t tim, char *tail)
{
int didhrs = 0;
if (tim >= 60) {
(void) printf("%3d:", tim/60);
didhrs++;
} else {
(void) printf(" ");
}
tim %= 60;
if (tim > 0 || didhrs) {
(void) printf(didhrs && tim < 10 ? "%02d" : "%2d", tim);
} else {
(void) printf(" ");
}
(void) printf("%s", tail);
}
#ifdef DEBUG
/*
* for debugging
*/
int
printit(int i)
{
int j, v;
(void) printf("%12.12s: ", entry[i].machine);
if (entry[i].cnt) {
putline_3(entry[i].machine, &entry[i].users.utmp_array_val[0]);
for (j = 1; j < entry[i].cnt; j++) {
(void) printf("\t");
putline_3(entry[i].machine,
&entry[i].users.utmp_array_val[j]);
}
} else
(void) printf("\n");
}
#endif
static void
usage(void)
{
(void) fprintf(stderr, "Usage: rusers [-ahilu] [host ...]\n");
free(entry);
exit(1);
}