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