1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License").  You may not use this file except in compliance
7 * with the License.
8 *
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
13 *
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
19 *
20 * CDDL HEADER END
21 */
22#pragma ident	"%Z%%M%	%I%	%E% SMI"
23
24/*
25 * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
26 * Use is subject to license terms.
27 */
28/* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
29/* All Rights Reserved */
30/*
31 * University Copyright- Copyright (c) 1982, 1986, 1988
32 * The Regents of the University of California
33 * All Rights Reserved
34 *
35 * University Acknowledgment- Portions of this document are derived from
36 * software developed by the University of California, Berkeley, and its
37 * contributors.
38 */
39
40#pragma ident	"%Z%%M%	%I%	%E% SMI"
41
42#include <stdlib.h>
43#include <stdio.h>
44#include <unistd.h>
45#include <sys/types.h>
46#include <netconfig.h>
47#include <netdir.h>
48#include <rpc/rpc.h>
49#include <rpcsvc/rusers.h>
50#include <string.h>
51#include <limits.h>
52
53#define	NMAX	12		/* These are used as field width specifiers */
54#define	LMAX	8		/* when printing.			    */
55#define	HMAX	16		/* "Logged in" host name. */
56
57#define	MACHINELEN 16		/* length of machine name printed out */
58#define	NUMENTRIES 256
59#define	min(a, b) ((a) < (b) ? (a) : (b))
60
61struct entry {
62	int cnt;
63	int idle;		/* set to INT_MAX if not present */
64	char *machine;
65	utmp_array users;
66};
67
68static int curentry;
69static int total_entries;
70static struct entry *entry;
71static int hflag;		/* host: sort by machine name */
72static int iflag;		/* idle: sort by idle time */
73static int uflag;		/* users: sort by number of users */
74static int lflag;		/* print out long form */
75static int aflag;		/* all: list all machines */
76static int dflag;		/* debug: list only first n machines */
77static int sorted;
78static int debug;
79static int debugcnt;
80static char *nettype;
81
82static int hcompare(const struct entry *, const struct entry *);
83static int icompare(const struct entry *, const struct entry *);
84static int ucompare(const struct entry *, const struct entry *);
85static int print_info(struct utmpidlearr *, const char *);
86static int print_info_3(utmp_array *, const char *);
87static int collectnames(void *, struct netbuf *, struct netconfig *);
88static int collectnames_3(void *, struct netbuf *, struct netconfig *);
89static void singlehost(char *);
90static void printnames(void);
91static void putline_2(char *, struct utmpidle *);
92static void putline_3(char *, rusers_utmp *);
93static void prttime(uint_t, char *);
94static void usage(void);
95
96/*
97 * rusers [-ahilu] [host...]
98 */
99int
100main(int argc, char *argv[])
101{
102	int c;
103	uint_t errflag = 0;
104	uint_t single = 0;
105	struct utmpidlearr utmpidlearr;
106	utmp_array	utmp_array_res;
107
108	curentry = 0;
109	total_entries = NUMENTRIES;
110	entry = malloc(sizeof (struct entry) * total_entries);
111
112	while ((c = getopt(argc, argv, ":ad:hilun:")) != -1) {
113		switch (c) {
114		case 'a':
115			aflag++;
116			break;
117		case 'd':
118			dflag++;
119			debug = atoi(optarg);
120			(void) printf("Will collect %d responses.\n", debug);
121			break;
122		case 'h':
123			hflag++;
124			sorted++;
125			if (iflag || uflag)
126				errflag++;
127			break;
128		case 'i':
129			iflag++;
130			sorted++;
131			if (hflag || uflag)
132				errflag++;
133			break;
134		case 'u':
135			uflag++;
136			sorted++;
137			if (hflag || iflag)
138				errflag++;
139			break;
140		case 'l':
141			lflag++;
142			break;
143		case ':':	/* required operand missing */
144			errflag++;
145			break;
146		case 'n':
147			nettype = optarg;
148			break;
149		default:
150		case '?':	/* Unrecognized option */
151			errflag++;
152			break;
153		}
154	}
155	if (errflag)
156		usage();
157
158	for (; optind < argc; optind++) {
159		single++;
160		singlehost(argv[optind]);
161	}
162	if (single) {
163		if (sorted)
164			printnames();
165		free(entry);
166		exit(0);
167	}
168
169	if (sorted) {
170		(void) printf("Collecting responses...\n");
171		(void) fflush(stdout);
172	}
173	utmp_array_res.utmp_array_val = NULL;
174	utmp_array_res.utmp_array_len = 0;
175	(void) printf("Sending broadcast for rusersd protocol version 3...\n");
176	(void) rpc_broadcast(RUSERSPROG, RUSERSVERS_3,
177		RUSERSPROC_NAMES, (xdrproc_t)xdr_void, NULL,
178		(xdrproc_t)xdr_utmp_array, (char *)&utmp_array_res,
179		(resultproc_t)collectnames_3, nettype);
180	utmpidlearr.uia_arr = NULL;
181	(void) printf("Sending broadcast for rusersd protocol version 2...\n");
182	(void) rpc_broadcast(RUSERSPROG, RUSERSVERS_IDLE,
183		RUSERSPROC_NAMES, (xdrproc_t)xdr_void, NULL,
184		(xdrproc_t)xdr_utmpidlearr, (char *)&utmpidlearr,
185		(resultproc_t)collectnames, nettype);
186
187	if (sorted)
188		printnames();
189
190	free(entry);
191	return (0);
192}
193
194static void
195singlehost(char *name)
196{
197	enum clnt_stat err;
198	struct utmpidlearr utmpidlearr;
199	utmp_array	utmp_array_res;
200
201	if (curentry >= total_entries) {
202		struct entry *tmp;
203
204		total_entries += NUMENTRIES;
205		if ((tmp = realloc(entry, sizeof (struct entry)
206						* total_entries)) == NULL)
207			return;
208		entry = tmp;
209	}
210	utmp_array_res.utmp_array_val = NULL;
211	utmp_array_res.utmp_array_len = 0;
212	err = rpc_call(name, RUSERSPROG, RUSERSVERS_3,
213		RUSERSPROC_NAMES, (xdrproc_t)xdr_void, 0,
214		(xdrproc_t)xdr_utmp_array, (char *)&utmp_array_res,
215		nettype);
216	if (err == RPC_SUCCESS) {
217		(void) print_info_3(&utmp_array_res, name);
218		return;
219	}
220	if (err == RPC_PROGVERSMISMATCH) {
221		utmpidlearr.uia_arr = NULL;
222		err = rpc_call(name, RUSERSPROG, RUSERSVERS_IDLE,
223				RUSERSPROC_NAMES, (xdrproc_t)xdr_void, 0,
224				(xdrproc_t)xdr_utmpidlearr,
225				(char *)&utmpidlearr, nettype);
226	}
227	if (err != RPC_SUCCESS) {
228		(void) fprintf(stderr, "%s: ", name);
229		clnt_perrno(err);
230		return;
231	}
232	(void) print_info(&utmpidlearr, name);
233}
234
235/*
236 * Collect responses from RUSERSVERS_IDLE broadcast, convert to
237 * RUSERSVERS_3 format, and store in entry database.
238 */
239static int
240collectnames(void *resultsp, struct netbuf *raddrp, struct netconfig *nconf)
241{
242	struct utmpidlearr utmpidlearr;
243	struct entry *entryp, *lim;
244	struct nd_hostservlist *hs;
245	char host[MACHINELEN + 1];
246
247	utmpidlearr = *(struct utmpidlearr *)resultsp;
248	if (utmpidlearr.uia_cnt < 1 && !aflag)
249		return (0);
250
251	if (netdir_getbyaddr(nconf, &hs, raddrp)) {
252#ifdef DEBUG
253		netdir_perror("netdir_getbyaddr");
254#endif
255		/* netdir routine couldn't resolve addr;just print out uaddr */
256		(void) sprintf(host, "%.*s", MACHINELEN,
257						taddr2uaddr(nconf, raddrp));
258	} else {
259		(void) sprintf(host, "%.*s", MACHINELEN,
260						hs->h_hostservs->h_host);
261		netdir_free((char *)hs, ND_HOSTSERVLIST);
262	}
263	/*
264	 * need to realloc more space if we have more than 256 machines
265	 * that respond to broadcast
266	 */
267	if (curentry >= total_entries) {
268		struct entry *tmp;
269
270		total_entries += NUMENTRIES;
271		if ((tmp = realloc(entry, sizeof (struct entry)
272						* total_entries)) == NULL)
273			return (1);
274		entry = tmp;
275	}
276
277
278	/*
279	 * weed out duplicates
280	 */
281	lim = entry + curentry;
282	for (entryp = entry; entryp < lim; entryp++) {
283		if (strcmp(entryp->machine, host) == 0)
284			return (0);
285	}
286	return (print_info((struct utmpidlearr *)resultsp, host));
287}
288
289static int
290print_info(struct utmpidlearr *utmpidlearrp, const char *name)
291{
292	utmp_array *iconvert;
293	int i, cnt, minidle;
294	char host[MACHINELEN + 1];
295	char username[NMAX + 1];
296
297	cnt = utmpidlearrp->uia_cnt;
298	(void) sprintf(host, "%.*s", MACHINELEN, name);
299
300	/*
301	 * if raw, print this entry out immediately
302	 * otherwise store for later sorting
303	 */
304	if (!sorted) {
305		if (lflag && (cnt > 0))
306			for (i = 0; i < cnt; i++)
307				putline_2(host, utmpidlearrp->uia_arr[i]);
308		else {
309		    (void) printf("%-*.*s", MACHINELEN, MACHINELEN, host);
310		    for (i = 0; i < cnt; i++) {
311			(void) strlcpy(username,
312				    utmpidlearrp->uia_arr[i]->ui_utmp.ut_name,
313				    NMAX + 1);
314			(void) printf(" %.*s", NMAX, username);
315		    }
316		    (void) printf("\n");
317		}
318		/* store just the name */
319		entry[curentry].machine = malloc(MACHINELEN + 1);
320		if (entry[curentry].machine == NULL) {
321			(void) fprintf(stderr, "Ran out of memory - exiting\n");
322			exit(1);
323		}
324		(void) strlcpy(entry[curentry].machine, name, MACHINELEN + 1);
325		entry[curentry++].cnt = 0;
326		if (dflag && (++debugcnt >= debug))
327			return (1);
328		return (0);
329	}
330	entry[curentry].machine = malloc(MACHINELEN + 1);
331	if (entry[curentry].machine == NULL) {
332		(void) fprintf(stderr, "Ran out of memory - exiting\n");
333		exit(1);
334	}
335	(void) strlcpy(entry[curentry].machine, name, MACHINELEN + 1);
336	entry[curentry].cnt = cnt;
337	iconvert = &entry[curentry].users;
338	iconvert->utmp_array_len = cnt;
339	iconvert->utmp_array_val = malloc(cnt * sizeof (rusers_utmp));
340	minidle = INT_MAX;
341	for (i = 0; i < cnt; i++) {
342		iconvert->utmp_array_val[i].ut_user =
343			strdup(utmpidlearrp->uia_arr[i]->ui_utmp.ut_name);
344		iconvert->utmp_array_val[i].ut_line =
345			strdup(utmpidlearrp->uia_arr[i]->ui_utmp.ut_line);
346		iconvert->utmp_array_val[i].ut_host =
347			strdup(utmpidlearrp->uia_arr[i]->ui_utmp.ut_host);
348		iconvert->utmp_array_val[i].ut_time =
349			utmpidlearrp->uia_arr[i]->ui_utmp.ut_time;
350		iconvert->utmp_array_val[i].ut_idle =
351			utmpidlearrp->uia_arr[i]->ui_idle;
352		minidle = min(minidle, utmpidlearrp->uia_arr[i]->ui_idle);
353	}
354	entry[curentry].idle = minidle;
355	curentry++;
356	if (dflag && (++debugcnt >= debug))
357		return (1);
358	return (0);
359}
360
361
362/*
363 * Collect responses from RUSERSVERS_3 broadcast.
364 */
365static int
366collectnames_3(void *resultsp, struct netbuf *raddrp, struct netconfig *nconf)
367{
368	utmp_array *uap;
369	struct entry *entryp, *lim;
370	struct nd_hostservlist *hs;
371	char host[MACHINELEN + 1];
372
373	uap = (utmp_array *)resultsp;
374	if (uap->utmp_array_len < 1 && !aflag)
375		return (0);
376
377	if (netdir_getbyaddr(nconf, &hs, raddrp)) {
378#ifdef DEBUG
379	netdir_perror("netdir_getbyaddr");
380#endif
381		/* netdir routine couldn't resolve addr;just print out uaddr */
382		(void) sprintf(host, "%.*s", MACHINELEN,
383						taddr2uaddr(nconf, raddrp));
384	} else {
385		(void) sprintf(host, "%.*s", MACHINELEN,
386						hs->h_hostservs->h_host);
387		netdir_free((char *)hs, ND_HOSTSERVLIST);
388	}
389
390	/*
391	 * need to realloc more space if we have more than 256 machines
392	 * that respond to broadcast
393	 */
394	if (curentry >= total_entries) {
395		struct entry *tmp;
396
397		total_entries += NUMENTRIES;
398		if ((tmp = realloc(entry, sizeof (struct entry)
399						* total_entries)) == NULL)
400			return (1);
401		entry = tmp;
402	}
403
404
405	/*
406	 * weed out duplicates
407	 */
408	lim = entry + curentry;
409	for (entryp = entry; entryp < lim; entryp++) {
410		if (strcmp(entryp->machine, host) == 0)
411			return (0);
412	}
413	return (print_info_3(uap, host));
414}
415
416static int
417print_info_3(utmp_array *uap, const char *name)
418{
419	int i, cnt, minidle;
420	char host[MACHINELEN + 1];
421
422	cnt = uap->utmp_array_len;
423
424	(void) sprintf(host, "%.*s", MACHINELEN, name);
425
426	/*
427	 * if raw, print this entry out immediately
428	 * otherwise store for later sorting
429	 */
430	if (!sorted) {
431		if (lflag && (cnt > 0))
432			for (i = 0; i < cnt; i++)
433				putline_3(host, &uap->utmp_array_val[i]);
434		else {
435			(void) printf("%-*.*s", MACHINELEN, MACHINELEN, host);
436			for (i = 0; i < cnt; i++)
437				(void) printf(" %.*s", NMAX,
438				    uap->utmp_array_val[i].ut_user);
439			(void) printf("\n");
440		}
441		/* store just the name */
442		entry[curentry].machine = malloc(MACHINELEN + 1);
443		if (entry[curentry].machine == NULL) {
444			(void) fprintf(stderr, "Ran out of memory - exiting\n");
445			exit(1);
446		}
447		(void) strlcpy(entry[curentry].machine, name, MACHINELEN + 1);
448		entry[curentry++].cnt = 0;
449		if (dflag && (++debugcnt >= debug))
450			return (1);
451		return (0);
452	}
453
454	entry[curentry].machine = malloc(MACHINELEN + 1);
455	if (entry[curentry].machine == NULL) {
456		(void) fprintf(stderr, "Ran out of memory - exiting\n");
457		exit(1);
458	}
459	(void) strlcpy(entry[curentry].machine, name, MACHINELEN + 1);
460	entry[curentry].cnt = cnt;
461	entry[curentry].users.utmp_array_len = cnt;
462	entry[curentry].users.utmp_array_val = malloc(cnt *
463		sizeof (rusers_utmp));
464	minidle = INT_MAX;
465	for (i = 0; i < cnt; i++) {
466		entry[curentry].users.utmp_array_val[i].ut_user =
467			strdup(uap->utmp_array_val[i].ut_user);
468		entry[curentry].users.utmp_array_val[i].ut_line =
469			strdup(uap->utmp_array_val[i].ut_line);
470		entry[curentry].users.utmp_array_val[i].ut_host =
471			strdup(uap->utmp_array_val[i].ut_host);
472		entry[curentry].users.utmp_array_val[i].ut_time =
473			uap->utmp_array_val[i].ut_time;
474		entry[curentry].users.utmp_array_val[i].ut_idle =
475			uap->utmp_array_val[i].ut_idle;
476		minidle = min(minidle, uap->utmp_array_val[i].ut_idle);
477	}
478	entry[curentry].idle = minidle;
479	curentry++;
480	if (dflag && (++debugcnt >= debug))
481		return (1);
482	return (0);
483}
484
485static void
486printnames(void)
487{
488	int i, j;
489	int (*compare)(const void *, const void *);
490
491	/* the name of the machine should already be in the structure */
492	if (iflag)
493		compare = (int (*)(const void *, const void *))icompare;
494	else if (hflag)
495		compare = (int (*)(const void *, const void *))hcompare;
496	else
497		compare = (int (*)(const void *, const void *))ucompare;
498	qsort(entry, curentry, sizeof (struct entry), compare);
499	for (i = 0; i < curentry; i++) {
500		if (!lflag || (entry[i].cnt < 1)) {
501			(void) printf("%-*.*s", MACHINELEN,
502					MACHINELEN, entry[i].machine);
503			for (j = 0; j < entry[i].cnt; j++)
504				(void) printf(" %.*s", NMAX,
505				    entry[i].users.utmp_array_val[j].ut_user);
506			(void) printf("\n");
507		} else {
508			for (j = 0; j < entry[i].cnt; j++)
509				putline_3(entry[i].machine,
510					&entry[i].users.utmp_array_val[j]);
511		}
512	}
513}
514
515static int
516hcompare(const struct entry *a, const struct entry *b)
517{
518	return (strcmp(a->machine, b->machine));
519}
520
521static int
522ucompare(const struct entry *a, const struct entry *b)
523{
524	return (b->cnt - a->cnt);
525}
526
527static int
528icompare(const struct entry *a, const struct entry *b)
529{
530	return (a->idle - b->idle);
531}
532
533static void
534putline_2(char *host, struct utmpidle *uip)
535{
536	char *cbuf;
537	struct ru_utmp *up;
538	char buf[100];
539
540	up = &uip->ui_utmp;
541#define	NAMEMAX	((sizeof (up->ut_name) < NMAX) ? NMAX : sizeof (up->ut_name))
542#define	NAMEMIN	((sizeof (up->ut_name) > NMAX) ? NMAX : sizeof (up->ut_name))
543	/* Try and align this up nicely */
544#define	LINEMAX	sizeof (up->ut_line)
545#define	HOSTMAX	sizeof (up->ut_host)
546	/*
547	 * We copy the strings into a buffer because they aren't strictly
548	 * speaking strings but byte arrays (and they may not have a
549	 * terminating NULL.
550	 */
551
552	(void) strncpy(buf, up->ut_name, NAMEMAX);
553	buf[NAMEMIN] = '\0';
554	(void) printf("%-*.*s ", NAMEMAX, NAMEMAX, buf);
555
556	(void) strcpy(buf, host);
557	(void) strcat(buf, ":");
558	(void) strncat(buf, up->ut_line, LINEMAX);
559	buf[MACHINELEN+LINEMAX] = '\0';
560	(void) printf("%-*.*s", MACHINELEN+LINEMAX, MACHINELEN+LINEMAX, buf);
561
562	cbuf = (char *)ctime(&up->ut_time);
563	(void) printf("  %.12s  ", cbuf+4);
564	if (uip->ui_idle == INT_MAX)
565		(void) printf("    ??");
566	else
567		prttime(uip->ui_idle, "");
568	if (up->ut_host[0]) {
569		(void) strncpy(buf, up->ut_host, HOSTMAX);
570		buf[HOSTMAX] = '\0';
571		(void) printf(" (%.*s)", HOSTMAX, buf);
572	}
573	(void) putchar('\n');
574}
575
576static void
577putline_3(char *host, rusers_utmp *rup)
578{
579	char *cbuf;
580	char buf[100];
581
582	(void) printf("%-*.*s ", NMAX, NMAX, rup->ut_user);
583	(void) strcpy(buf, host);
584	(void) strcat(buf, ":");
585	(void) strncat(buf, rup->ut_line, LMAX);
586	(void) printf("%-*.*s", MACHINELEN+LMAX, MACHINELEN+LMAX, buf);
587
588	cbuf = (char *)ctime((time_t *)&rup->ut_time);
589	(void) printf("  %.12s  ", cbuf+4);
590	if (rup->ut_idle == INT_MAX)
591		(void) printf("    ??");
592	else
593		prttime(rup->ut_idle, "");
594	if (rup->ut_host[0])
595		(void) printf(" (%.*s)", HMAX, rup->ut_host);
596	(void) putchar('\n');
597}
598
599/*
600 * prttime prints a time in hours and minutes.
601 * The character string tail is printed at the end, obvious
602 * strings to pass are "", " ", or "am".
603 */
604static void
605prttime(uint_t tim, char *tail)
606{
607	int didhrs = 0;
608
609	if (tim >= 60) {
610		(void) printf("%3d:", tim/60);
611		didhrs++;
612	} else {
613		(void) printf("    ");
614	}
615	tim %= 60;
616	if (tim > 0 || didhrs) {
617		(void) printf(didhrs && tim < 10 ? "%02d" : "%2d", tim);
618	} else {
619		(void) printf("  ");
620	}
621	(void) printf("%s", tail);
622}
623
624#ifdef DEBUG
625/*
626 * for debugging
627 */
628int
629printit(int i)
630{
631	int j, v;
632
633	(void) printf("%12.12s: ", entry[i].machine);
634	if (entry[i].cnt) {
635		putline_3(entry[i].machine, &entry[i].users.utmp_array_val[0]);
636		for (j = 1; j < entry[i].cnt; j++) {
637			(void) printf("\t");
638			putline_3(entry[i].machine,
639				&entry[i].users.utmp_array_val[j]);
640		}
641	} else
642		(void) printf("\n");
643}
644#endif
645
646static void
647usage(void)
648{
649	(void) fprintf(stderr, "Usage: rusers [-ahilu] [host ...]\n");
650	free(entry);
651	exit(1);
652}
653