GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/rusers/rusers.c Lines: 0 377 0.0 %
Date: 2016-12-06 Branches: 0 282 0.0 %

Line Branch Exec Source
1
/*	$OpenBSD: rusers.c,v 1.38 2016/03/28 11:06:09 chl Exp $	*/
2
3
/*
4
 * Copyright (c) 2001, 2003 Todd C. Miller <Todd.Miller@courtesan.com>
5
 *
6
 * Permission to use, copy, modify, and distribute this software for any
7
 * purpose with or without fee is hereby granted, provided that the above
8
 * copyright notice and this permission notice appear in all copies.
9
 *
10
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17
 * Sponsored in part by the Defense Advanced Research Projects
18
 * Agency (DARPA) and Air Force Research Laboratory, Air Force
19
 * Materiel Command, USAF, under agreement number F39502-99-1-0512.
20
 */
21
/*-
22
 *  Copyright (c) 1993 John Brezak
23
 *  All rights reserved.
24
 *
25
 *  Redistribution and use in source and binary forms, with or without
26
 *  modification, are permitted provided that the following conditions
27
 *  are met:
28
 *  1. Redistributions of source code must retain the above copyright
29
 *     notice, this list of conditions and the following disclaimer.
30
 *  2. Redistributions in binary form must reproduce the above copyright
31
 *     notice, this list of conditions and the following disclaimer in the
32
 *     documentation and/or other materials provided with the distribution.
33
 *  3. The name of the author may not be used to endorse or promote products
34
 *     derived from this software without specific prior written permission.
35
 *
36
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR `AS IS'' AND ANY EXPRESS OR
37
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
38
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
39
 * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
40
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
41
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
42
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
43
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
44
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
45
 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
46
 * POSSIBILITY OF SUCH DAMAGE.
47
 */
48
49
#include <sys/ioctl.h>
50
#include <sys/socket.h>
51
#include <sys/signal.h>
52
#include <rpc/rpc.h>
53
#include <rpc/pmap_prot.h>
54
#include <rpc/pmap_rmt.h>
55
#include <rpcsvc/rusers.h>
56
#include <rpcsvc/rnusers.h>	/* Old protocol version */
57
#include <arpa/inet.h>
58
#include <net/if.h>
59
#include <err.h>
60
#include <errno.h>
61
#include <ifaddrs.h>
62
#include <netdb.h>
63
#include <stdio.h>
64
#include <stdlib.h>
65
#include <string.h>
66
#include <termios.h>
67
#include <unistd.h>
68
#include <limits.h>
69
70
#define MINIMUM(a, b)	(((a) < (b)) ? (a) : (b))
71
#define MAXIMUM(a, b)	(((a) > (b)) ? (a) : (b))
72
73
/* Preferred formatting */
74
#define HOST_WIDTH 17
75
#define LINE_WIDTH 8
76
#define NAME_WIDTH 8
77
78
#define MAX_BROADCAST_SIZE 1400
79
80
struct host_info {
81
	u_int count;
82
	u_int idle;
83
	char *host;
84
	rusers_utmp *users;
85
} *hostinfo;
86
87
void print_entry(struct host_info *, int);
88
void fmt_idle(int, char *, size_t);
89
void onehost(char *);
90
void allhosts(void);
91
void sorthosts(void);
92
void expandhosts(void);
93
void alarmclock(int);
94
char *estrndup(const char *, size_t);
95
struct host_info *add_host(char *);
96
int hcompare(const void *, const void *);
97
int icompare(const void *, const void *);
98
int ucompare(const void *, const void *);
99
bool_t rusers_reply(char *, struct sockaddr_in *);
100
bool_t rusers_reply_3(char *, struct sockaddr_in *);
101
enum clnt_stat get_reply(int, in_port_t, u_long, struct rpc_msg *,
102
    struct rmtcallres *, bool_t (*)(char *, struct sockaddr_in *));
103
enum clnt_stat rpc_setup(int *, XDR *, struct rpc_msg *,
104
    struct rmtcallargs *, AUTH *, char *);
105
__dead void usage(void);
106
107
int aflag, hflag, iflag, lflag, uflag;
108
u_int nentries, maxentries;
109
long termwidth;
110
extern char *__progname;
111
112
int
113
main(int argc, char **argv)
114
{
115
	struct winsize win;
116
	char *cp;
117
	int ch;
118
119
	while ((ch = getopt(argc, argv, "ahilu")) != -1)
120
		switch (ch) {
121
		case 'a':
122
			aflag = 1;
123
			break;
124
		case 'h':
125
			hflag = 1;
126
			break;
127
		case 'i':
128
			iflag = 1;
129
			break;
130
		case 'l':
131
			lflag = 1;
132
			break;
133
		case 'u':
134
			uflag = 1;
135
			break;
136
		default:
137
			usage();
138
			/*NOTREACHED*/
139
		}
140
141
	if (hflag + iflag + uflag > 1)
142
		usage();
143
144
	termwidth = 0;
145
	if ((cp = getenv("COLUMNS")) != NULL)
146
		termwidth = strtonum(cp, 1, LONG_MAX, NULL);
147
	if (termwidth == 0 && ioctl(STDOUT_FILENO, TIOCGWINSZ, &win) == 0 &&
148
	    win.ws_col > 0)
149
		termwidth = win.ws_col;
150
	if (termwidth == 0)
151
		termwidth = 80;
152
153
	setvbuf(stdout, NULL, _IOLBF, 0);
154
155
	if (argc == optind) {
156
		if (hflag || iflag || uflag) {
157
			puts("Collecting responses...");
158
			allhosts();
159
			sorthosts();
160
		} else
161
			allhosts();
162
	} else {
163
		aflag = 1;
164
		for (; optind < argc; optind++)
165
			(void) onehost(argv[optind]);
166
		if (hflag || iflag || uflag)
167
			sorthosts();
168
	}
169
170
	exit(0);
171
}
172
173
struct host_info *
174
add_host(char *host)
175
{
176
	int i;
177
178
	for (i = 0; i < nentries; i++) {
179
		/* Existing entry. */
180
		if (strcmp(host, hostinfo[i].host) == 0)
181
			return(NULL);
182
	}
183
184
	/* New entry, allocate space if needed and store. */
185
	if (nentries == maxentries) {
186
		maxentries += 128;
187
		hostinfo = reallocarray(hostinfo, maxentries,
188
		    sizeof(*hostinfo));
189
		if (hostinfo == NULL)
190
			err(1, NULL);
191
	}
192
	if ((hostinfo[nentries].host = strdup(host)) == NULL)
193
		err(1, NULL);
194
	return(&hostinfo[nentries++]);
195
}
196
197
void
198
fmt_idle(int idle, char *idle_time, size_t idle_time_len)
199
{
200
	int days, hours, minutes, seconds;
201
202
	switch (idle) {
203
	case 0:
204
		*idle_time = '\0';
205
		break;
206
	case INT_MAX:
207
		strlcpy(idle_time, "??", idle_time_len);
208
		break;
209
	default:
210
		seconds = idle;
211
		days = seconds / (60*60*24);
212
		seconds %= (60*60*24);
213
		hours = seconds / (60*60);
214
		seconds %= (60*60);
215
		minutes = seconds / 60;
216
		seconds %= 60;
217
		if (idle >= (24*60*60))
218
			snprintf(idle_time, idle_time_len,
219
			    "%d day%s, %d:%02d:%02d", days,
220
			    days > 1 ? "s" : "", hours, minutes, seconds);
221
		else if (idle >= (60*60))
222
			snprintf(idle_time, idle_time_len, "%2d:%02d:%02d",
223
			    hours, minutes, seconds);
224
		else if (idle > 60)
225
			snprintf(idle_time, idle_time_len, "%2d:%02d",
226
			    minutes, seconds);
227
		else
228
			snprintf(idle_time, idle_time_len, "   :%02d", idle);
229
		break;
230
	}
231
}
232
233
bool_t
234
rusers_reply(char *replyp, struct sockaddr_in *raddrp)
235
{
236
	utmpidlearr *up = (utmpidlearr *)replyp;
237
	struct host_info *entry;
238
	struct hostent *hp;
239
	rusers_utmp *ut;
240
	char *host;
241
	int i;
242
243
	if (!aflag && up->uia_cnt == 0)
244
		return(0);
245
246
	hp = gethostbyaddr((char *)&raddrp->sin_addr,
247
	    sizeof(struct in_addr), AF_INET);
248
	if (hp)
249
		host = hp->h_name;
250
	else
251
		host = inet_ntoa(raddrp->sin_addr);
252
	if ((entry = add_host(host)) == NULL)
253
		return(0);
254
255
	if (up->uia_cnt == 0)
256
		ut = NULL;
257
	else if ((ut = calloc(up->uia_cnt, sizeof(*ut))) == NULL)
258
		err(1, NULL);
259
	entry->users = ut;
260
	entry->count = up->uia_cnt;
261
	entry->idle = UINT_MAX;
262
	for (i = 0; i < up->uia_cnt; i++, ut++) {
263
		ut->ut_user = estrndup(up->uia_arr[i]->ui_utmp.ut_name,
264
		    RNUSERS_MAXUSERLEN);
265
		ut->ut_line = estrndup(up->uia_arr[i]->ui_utmp.ut_line,
266
		    RNUSERS_MAXLINELEN);
267
		ut->ut_host = estrndup(up->uia_arr[i]->ui_utmp.ut_host,
268
		    RNUSERS_MAXHOSTLEN);
269
		ut->ut_time = up->uia_arr[i]->ui_utmp.ut_time;
270
		ut->ut_idle = up->uia_arr[i]->ui_idle;
271
		if (ut->ut_idle < entry->idle)
272
			entry->idle = ut->ut_idle;
273
	}
274
275
	if (!hflag && !iflag && !uflag) {
276
		print_entry(entry, lflag && entry->count);
277
		for (i = 0, ut = entry->users; i < entry->count; i++, ut++) {
278
			free(ut->ut_user);
279
			free(ut->ut_line);
280
			free(ut->ut_host);
281
		}
282
		free(entry->users);
283
	}
284
285
	return(0);
286
}
287
288
bool_t
289
rusers_reply_3(char *replyp, struct sockaddr_in *raddrp)
290
{
291
	utmp_array *up3 = (utmp_array *)replyp;
292
	struct host_info *entry;
293
	struct hostent *hp;
294
	rusers_utmp *ut;
295
	char *host;
296
	int i;
297
298
	if (!aflag && up3->utmp_array_len == 0)
299
		return(0);
300
301
	hp = gethostbyaddr((char *)&raddrp->sin_addr,
302
	    sizeof(struct in_addr), AF_INET);
303
	if (hp)
304
		host = hp->h_name;
305
	else
306
		host = inet_ntoa(raddrp->sin_addr);
307
	if ((entry = add_host(host)) == NULL)
308
		return(0);
309
310
	if (up3->utmp_array_len == 0)
311
		ut = NULL;
312
	else if ((ut = calloc(up3->utmp_array_len, sizeof(*ut))) == NULL)
313
		err(1, NULL);
314
	entry->users = ut;
315
	entry->count = up3->utmp_array_len;
316
	entry->idle = UINT_MAX;
317
	for (i = 0; i < up3->utmp_array_len; i++, ut++) {
318
		ut->ut_user = estrndup(up3->utmp_array_val[i].ut_user,
319
		    RUSERS_MAXUSERLEN);
320
		ut->ut_line = estrndup(up3->utmp_array_val[i].ut_line,
321
		    RUSERS_MAXLINELEN);
322
		ut->ut_host = estrndup(up3->utmp_array_val[i].ut_host,
323
		    RUSERS_MAXHOSTLEN);
324
		ut->ut_time = up3->utmp_array_val[i].ut_time;
325
		ut->ut_idle = up3->utmp_array_val[i].ut_idle;
326
		if (ut->ut_idle < entry->idle)
327
			entry->idle = ut->ut_idle;
328
	}
329
330
	if (!hflag && !iflag && !uflag) {
331
		print_entry(entry, lflag && entry->count);
332
		for (i = 0, ut = entry->users; i < entry->count; i++, ut++) {
333
			free(ut->ut_user);
334
			free(ut->ut_line);
335
			free(ut->ut_host);
336
		}
337
		free(entry->users);
338
	}
339
340
	return(0);
341
}
342
343
void
344
onehost(char *host)
345
{
346
	utmpidlearr up;
347
	utmp_array up3;
348
	CLIENT *rusers_clnt;
349
	struct sockaddr_in sin;
350
	struct hostent *hp;
351
	struct timeval tv = { 25, 0 };
352
	int error;
353
354
	memset(&sin, 0, sizeof sin);
355
356
	hp = gethostbyname(host);
357
	if (hp == NULL)
358
		errx(1, "unknown host \"%s\"", host);
359
360
	/* Try version 3 first. */
361
	rusers_clnt = clnt_create(host, RUSERSPROG, RUSERSVERS_3, "udp");
362
	if (rusers_clnt == NULL) {
363
		clnt_pcreateerror(__progname);
364
		exit(1);
365
	}
366
367
	memset(&up3, 0, sizeof(up3));
368
	error = clnt_call(rusers_clnt, RUSERSPROC_NAMES, xdr_void, NULL,
369
	    xdr_utmp_array, &up3, tv);
370
	switch (error) {
371
	case RPC_SUCCESS:
372
		sin.sin_addr.s_addr = *(int *)hp->h_addr;
373
		rusers_reply_3((char *)&up3, &sin);
374
		clnt_destroy(rusers_clnt);
375
		return;
376
	case RPC_PROGVERSMISMATCH:
377
		clnt_destroy(rusers_clnt);
378
		break;
379
	default:
380
		clnt_perror(rusers_clnt, __progname);
381
		clnt_destroy(rusers_clnt);
382
		exit(1);
383
	}
384
385
	/* Fall back to version 2. */
386
	rusers_clnt = clnt_create(host, RUSERSPROG, RUSERSVERS_IDLE, "udp");
387
	if (rusers_clnt == NULL) {
388
		clnt_pcreateerror(__progname);
389
		exit(1);
390
	}
391
392
	memset(&up, 0, sizeof(up));
393
	error = clnt_call(rusers_clnt, RUSERSPROC_NAMES, xdr_void, NULL,
394
	    xdr_utmpidlearr, &up, tv);
395
	if (error != RPC_SUCCESS) {
396
		clnt_perror(rusers_clnt, __progname);
397
		clnt_destroy(rusers_clnt);
398
		exit(1);
399
	}
400
	sin.sin_addr.s_addr = *(int *)hp->h_addr;
401
	rusers_reply((char *)&up, &sin);
402
	clnt_destroy(rusers_clnt);
403
}
404
405
enum clnt_stat
406
get_reply(int sock, in_port_t port, u_long xid, struct rpc_msg *msgp,
407
    struct rmtcallres *resp, bool_t (*callback)(char *, struct sockaddr_in *))
408
{
409
	ssize_t inlen;
410
	socklen_t fromlen;
411
	struct sockaddr_in raddr;
412
	char inbuf[UDPMSGSIZE];
413
	XDR xdr;
414
415
retry:
416
	msgp->acpted_rply.ar_verf = _null_auth;
417
	msgp->acpted_rply.ar_results.where = (caddr_t)resp;
418
	msgp->acpted_rply.ar_results.proc = xdr_rmtcallres;
419
420
	fromlen = sizeof(struct sockaddr);
421
	inlen = recvfrom(sock, inbuf, sizeof(inbuf), 0,
422
	    (struct sockaddr *)&raddr, &fromlen);
423
	if (inlen < 0) {
424
		if (errno == EINTR)
425
			goto retry;
426
		return (RPC_CANTRECV);
427
	}
428
	if (inlen < sizeof(u_int32_t))
429
		goto retry;
430
431
	/*
432
	 * If the reply we got matches our request, decode the
433
	 * replay and pass it to the callback function.
434
	 */
435
	xdrmem_create(&xdr, inbuf, (u_int)inlen, XDR_DECODE);
436
	if (xdr_replymsg(&xdr, msgp)) {
437
		if ((msgp->rm_xid == xid) &&
438
		    (msgp->rm_reply.rp_stat == MSG_ACCEPTED) &&
439
		    (msgp->acpted_rply.ar_stat == SUCCESS)) {
440
			raddr.sin_port = htons(port);
441
			(void)(*callback)(resp->results_ptr, &raddr);
442
		}
443
	}
444
	xdr.x_op = XDR_FREE;
445
	msgp->acpted_rply.ar_results.proc = xdr_void;
446
	(void)xdr_replymsg(&xdr, msgp);
447
	(void)(*resp->xdr_results)(&xdr, resp->results_ptr);
448
	xdr_destroy(&xdr);
449
450
	return(RPC_SUCCESS);
451
}
452
453
enum clnt_stat
454
rpc_setup(int *fdp, XDR *xdr, struct rpc_msg *msg, struct rmtcallargs *args,
455
    AUTH *unix_auth, char *buf)
456
{
457
	int on = 1;
458
459
	if ((*fdp = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0)
460
		return(RPC_CANTSEND);
461
462
	if (setsockopt(*fdp, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on)) < 0)
463
		return(RPC_CANTSEND);
464
465
	msg->rm_xid = arc4random();
466
	msg->rm_direction = CALL;
467
	msg->rm_call.cb_rpcvers = RPC_MSG_VERSION;
468
	msg->rm_call.cb_prog = PMAPPROG;
469
	msg->rm_call.cb_vers = PMAPVERS;
470
	msg->rm_call.cb_proc = PMAPPROC_CALLIT;
471
	msg->rm_call.cb_cred = unix_auth->ah_cred;
472
	msg->rm_call.cb_verf = unix_auth->ah_verf;
473
474
	xdrmem_create(xdr, buf, MAX_BROADCAST_SIZE, XDR_ENCODE);
475
	if (!xdr_callmsg(xdr, msg) || !xdr_rmtcall_args(xdr, args))
476
		return(RPC_CANTENCODEARGS);
477
478
	return(RPC_SUCCESS);
479
}
480
481
void
482
allhosts(void)
483
{
484
	enum clnt_stat stat;
485
	struct itimerval timeout;
486
	AUTH *unix_auth;
487
	size_t outlen[2];
488
	int sock[2] = { -1, -1 };
489
	int i, maxfd, rval;
490
	u_long xid[2], port[2];
491
	fd_set *fds = NULL;
492
	struct sockaddr_in *sin, baddr;
493
	struct rmtcallargs args;
494
	struct rmtcallres res[2];
495
	struct rpc_msg msg[2];
496
	struct ifaddrs *ifa, *ifap = NULL;
497
	char buf[2][MAX_BROADCAST_SIZE];
498
	utmpidlearr up;
499
	utmp_array up3;
500
	XDR xdr;
501
502
	if ((unix_auth = authunix_create_default()) == NULL)
503
		err(1, "can't create auth handle");
504
505
	if (getifaddrs(&ifap) != 0)
506
		err(1, "can't get list of interface addresses");
507
508
	memset(&up, 0, sizeof(up));
509
	memset(&up3, 0, sizeof(up3));
510
	memset(&baddr, 0, sizeof(baddr));
511
	memset(&res, 0, sizeof(res));
512
	memset(&msg, 0, sizeof(msg));
513
	memset(&timeout, 0, sizeof(timeout));
514
515
	args.prog = RUSERSPROG;
516
	args.vers = RUSERSVERS_IDLE;
517
	args.proc = RUSERSPROC_NAMES;
518
	args.xdr_args = xdr_void;
519
	args.args_ptr = NULL;
520
521
	stat = rpc_setup(&sock[0], &xdr, &msg[0], &args, unix_auth, buf[0]);
522
	if (stat != RPC_SUCCESS)
523
		goto cleanup;
524
	xid[0] = msg[0].rm_xid;
525
	outlen[0] = xdr_getpos(&xdr);
526
	xdr_destroy(&xdr);
527
528
	args.vers = RUSERSVERS_3;
529
	stat = rpc_setup(&sock[1], &xdr, &msg[1], &args, unix_auth, buf[1]);
530
	if (stat != RPC_SUCCESS)
531
		goto cleanup;
532
	xid[1] = msg[1].rm_xid;
533
	outlen[1] = xdr_getpos(&xdr);
534
	xdr_destroy(&xdr);
535
536
	maxfd = MAXIMUM(sock[0], sock[1]) + 1;
537
	fds = calloc(howmany(maxfd, NFDBITS), sizeof(fd_mask));
538
	if (fds == NULL)
539
		err(1, NULL);
540
541
	baddr.sin_len = sizeof(struct sockaddr_in);
542
	baddr.sin_family = AF_INET;
543
	baddr.sin_port = htons(PMAPPORT);
544
	baddr.sin_addr.s_addr = htonl(INADDR_ANY);
545
546
	res[0].port_ptr = &port[0];
547
	res[0].xdr_results = xdr_utmpidlearr;
548
	res[0].results_ptr = (caddr_t)&up;
549
550
	res[1].port_ptr = &port[1];
551
	res[1].xdr_results = xdr_utmp_array;
552
	res[1].results_ptr = (caddr_t)&up3;
553
554
	(void)signal(SIGALRM, alarmclock);
555
556
	/*
557
	 * We do 6 runs through the loop.  On even runs we send
558
	 * a version 3 broadcast.  On odd ones we send a version 2
559
	 * broadcast.  This should give version 3 replies enough
560
	 * of an 'edge' over the old version 2 ones in most cases.
561
	 * We select() waiting for replies for 5 seconds in between
562
	 * each broadcast.
563
	 */
564
	for (i = 0; i < 6; i++) {
565
		for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
566
			if (ifa->ifa_addr->sa_family != AF_INET ||
567
			    !(ifa->ifa_flags & IFF_BROADCAST) ||
568
			    !(ifa->ifa_flags & IFF_UP) ||
569
			    ifa->ifa_broadaddr == NULL ||
570
			    ifa->ifa_broadaddr->sa_family != AF_INET)
571
				continue;
572
			sin = (struct sockaddr_in *)ifa->ifa_broadaddr;
573
			baddr.sin_addr = sin->sin_addr;
574
575
			/* use protocol 2 or 3 depending on i (odd or even) */
576
			if (i & 1) {
577
				if (sendto(sock[0], buf[0], outlen[0], 0,
578
				    (struct sockaddr *)&baddr,
579
				    sizeof(struct sockaddr)) != outlen[0])
580
					err(1, "can't send broadcast packet");
581
			} else {
582
				if (sendto(sock[1], buf[1], outlen[1], 0,
583
				    (struct sockaddr *)&baddr,
584
				    sizeof(struct sockaddr)) != outlen[1])
585
					err(1, "can't send broadcast packet");
586
			}
587
		}
588
589
		/*
590
		 * We stay in the select loop for ~5 seconds
591
		 */
592
		timeout.it_value.tv_sec = 5;
593
		timeout.it_value.tv_usec = 0;
594
		while (timerisset(&timeout.it_value)) {
595
			FD_SET(sock[0], fds);
596
			FD_SET(sock[1], fds);
597
			setitimer(ITIMER_REAL, &timeout, NULL);
598
			rval = select(maxfd, fds, NULL, NULL, NULL);
599
			setitimer(ITIMER_REAL, NULL, &timeout);
600
			if (rval == -1) {
601
				if (errno == EINTR)
602
					break;
603
				err(1, "select");	/* shouldn't happen */
604
			}
605
			if (FD_ISSET(sock[1], fds)) {
606
				stat = get_reply(sock[1], (in_port_t)port[1],
607
				    xid[1], &msg[1], &res[1], rusers_reply_3);
608
				if (stat != RPC_SUCCESS)
609
					goto cleanup;
610
			}
611
			if (FD_ISSET(sock[0], fds)) {
612
				stat = get_reply(sock[0], (in_port_t)port[0],
613
				    xid[0], &msg[0], &res[0], rusers_reply);
614
				if (stat != RPC_SUCCESS)
615
					goto cleanup;
616
			}
617
		}
618
	}
619
cleanup:
620
	if (ifap != NULL)
621
		freeifaddrs(ifap);
622
	free(fds);
623
	if (sock[0] >= 0)
624
		(void)close(sock[0]);
625
	if (sock[1] >= 0)
626
		(void)close(sock[1]);
627
	AUTH_DESTROY(unix_auth);
628
	if (stat != RPC_SUCCESS) {
629
		clnt_perrno(stat);
630
		exit(1);
631
	}
632
}
633
634
void
635
print_entry(struct host_info *entry, int longfmt)
636
{
637
	char date[32], idle_time[64];
638
	char remote[RUSERS_MAXHOSTLEN + 3];
639
	struct rusers_utmp *ut;
640
	int i, len;
641
642
	if (!longfmt)
643
		printf("%-*.*s ", HOST_WIDTH, HOST_WIDTH, entry->host);
644
645
	for (i = 0, ut = entry->users; i < entry->count; i++, ut++) {
646
		if (longfmt) {
647
			time_t tim = ut->ut_time;
648
			strftime(date, sizeof(date), "%h %d %R",
649
			    localtime(&tim));
650
			date[sizeof(date) - 1] = '\0';
651
			fmt_idle(ut->ut_idle, idle_time, sizeof(idle_time));
652
			len = termwidth -
653
			    (MAXIMUM(strlen(ut->ut_user), NAME_WIDTH) + 1 +
654
			    HOST_WIDTH + 1 + LINE_WIDTH + 1 + strlen(date) +
655
			    1 + MAXIMUM(8, strlen(idle_time)) + 1 + 2);
656
			if (len > 0 && ut->ut_host[0] != '\0')
657
				snprintf(remote, sizeof(remote), "(%.*s)",
658
				    MINIMUM(len, RUSERS_MAXHOSTLEN), ut->ut_host);
659
			else
660
				remote[0] = '\0';
661
			len = HOST_WIDTH - MINIMUM(HOST_WIDTH, strlen(entry->host)) +
662
			    LINE_WIDTH - MINIMUM(LINE_WIDTH, strlen(ut->ut_line));
663
			printf("%-*s %.*s:%.*s%-*s %-12s %8s %s\n",
664
			    NAME_WIDTH, ut->ut_user, HOST_WIDTH, entry->host,
665
			    LINE_WIDTH, ut->ut_line, len, "", date,
666
			    idle_time, remote);
667
		} else {
668
			fputs(ut->ut_user, stdout);
669
			putchar(' ');
670
		}
671
	}
672
	if (!longfmt)
673
		putchar('\n');
674
}
675
676
void
677
expandhosts(void)
678
{
679
	struct host_info *new_hostinfo, *entry;
680
	u_int count;
681
	int i, j;
682
683
	for (i = 0, count = 0; i < nentries; i++)
684
		count += hostinfo[i].count;
685
686
	new_hostinfo = calloc(sizeof(*entry), count);
687
	if (new_hostinfo == NULL)
688
		err(1, NULL);
689
	for (i = 0, entry = new_hostinfo; i < nentries; i++) {
690
		for (j = 0; j < hostinfo[i].count; j++) {
691
			memcpy(entry, &hostinfo[i], sizeof(*entry));
692
			entry->users = &hostinfo[i].users[j];
693
			entry->idle = entry->users->ut_idle;
694
			entry->count = 1;
695
			entry++;
696
		}
697
	}
698
	free(hostinfo);
699
	hostinfo = new_hostinfo;
700
	nentries = maxentries = count;
701
}
702
703
void
704
sorthosts(void)
705
{
706
	int i;
707
	int (*compar)(const void *, const void *);
708
709
	if (iflag && lflag)
710
		expandhosts();
711
712
	if (hflag)
713
		compar = hcompare;
714
	else if (iflag)
715
		compar = icompare;
716
	else
717
		compar = ucompare;
718
	qsort(hostinfo, nentries, sizeof(*hostinfo), compar);
719
720
	for (i = 0; i < nentries; i++)
721
		print_entry(&hostinfo[i], lflag && hostinfo[i].count);
722
}
723
724
int
725
hcompare(const void *aa, const void *bb)
726
{
727
	const struct host_info *a = (struct host_info *)aa;
728
	const struct host_info *b = (struct host_info *)bb;
729
	int rval;
730
731
	if ((rval = strcasecmp(a->host, b->host)) != 0)
732
		return(rval);
733
734
	if (a->idle < b->idle)
735
		return(-1);
736
	else if (a->idle > b->idle)
737
		return(1);
738
739
	if (a->count > b->count)
740
		return(-1);
741
	else if (a->count < b->count)
742
		return(1);
743
744
	return(0);
745
}
746
747
int
748
icompare(const void *aa, const void *bb)
749
{
750
	const struct host_info *a = (struct host_info *)aa;
751
	const struct host_info *b = (struct host_info *)bb;
752
753
	if (a->idle < b->idle)
754
		return(-1);
755
	else if (a->idle > b->idle)
756
		return(1);
757
758
	if (a->count > b->count)
759
		return(-1);
760
	else if (a->count < b->count)
761
		return(1);
762
763
	return(strcasecmp(a->host, b->host));
764
}
765
766
int
767
ucompare(const void *aa, const void *bb)
768
{
769
	const struct host_info *a = (struct host_info *)aa;
770
	const struct host_info *b = (struct host_info *)bb;
771
772
	if (a->count > b->count)
773
		return(-1);
774
	else if (a->count < b->count)
775
		return(1);
776
777
	if (a->idle < b->idle)
778
		return(-1);
779
	else if (a->idle > b->idle)
780
		return(1);
781
782
	return(strcasecmp(a->host, b->host));
783
}
784
785
void
786
alarmclock(int signo)
787
{
788
789
	;		/* just interrupt */
790
}
791
792
char *
793
estrndup(const char *src, size_t len)
794
{
795
	char *dst, *end;
796
797
	if ((end = memchr(src, '\0', len)) != NULL)
798
		len = end - src;
799
800
	if ((dst = malloc(len + 1)) == NULL)
801
		err(1, NULL);
802
	memcpy(dst, src, len);
803
	dst[len] = '\0';
804
805
	return(dst);
806
}
807
808
void
809
usage(void)
810
{
811
812
	fprintf(stderr, "usage: %s [-al] [-h | -i | -u] [hosts ...]\n",
813
	    __progname);
814
	exit(1);
815
}