GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/rusers/rusers.c Lines: 0 401 0.0 %
Date: 2017-11-07 Branches: 0 252 0.0 %

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