xref: /openbsd/usr.bin/rusers/rusers.c (revision a54653fd)
1 /*	$OpenBSD: rusers.c,v 1.43 2020/12/29 19:52:16 benno Exp $	*/
2 
3 /*
4  * Copyright (c) 2001, 2003 Todd C. Miller <millert@openbsd.org>
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
main(int argc,char ** argv)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 *
add_host(char * host)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
fmt_idle(int idle,char * idle_time,size_t idle_time_len)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
rusers_reply(char * replyp,struct sockaddr_in * raddrp)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
rusers_reply_3(char * replyp,struct sockaddr_in * raddrp)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
onehost(char * host)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
get_reply(int sock,in_port_t port,u_long xid,struct rpc_msg * msgp,struct rmtcallres * resp,bool_t (* callback)(char *,struct sockaddr_in *))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 == -1) {
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
rpc_setup(int * fdp,XDR * xdr,struct rpc_msg * msg,struct rmtcallargs * args,AUTH * unix_auth,char * buf)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)) == -1)
461 		return(RPC_CANTSEND);
462 
463 	if (setsockopt(*fdp, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on)) == -1)
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
allhosts(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 == NULL ||
562 			    ifa->ifa_addr->sa_family != AF_INET ||
563 			    !(ifa->ifa_flags & IFF_BROADCAST) ||
564 			    !(ifa->ifa_flags & IFF_UP) ||
565 			    ifa->ifa_broadaddr == NULL ||
566 			    ifa->ifa_broadaddr->sa_family != AF_INET)
567 				continue;
568 			sin = (struct sockaddr_in *)ifa->ifa_broadaddr;
569 			baddr.sin_addr = sin->sin_addr;
570 
571 			/* use protocol 2 or 3 depending on i (odd or even) */
572 			if (i & 1) {
573 				if (sendto(sock[0], buf[0], outlen[0], 0,
574 				    (struct sockaddr *)&baddr,
575 				    sizeof(baddr)) != outlen[0])
576 					err(1, "can't send broadcast packet");
577 			} else {
578 				if (sendto(sock[1], buf[1], outlen[1], 0,
579 				    (struct sockaddr *)&baddr,
580 				    sizeof(baddr)) != outlen[1])
581 					err(1, "can't send broadcast packet");
582 			}
583 		}
584 
585 		/*
586 		 * We stay in the poll loop for ~5 seconds
587 		 */
588 		timeout.it_value.tv_sec = 5;
589 		timeout.it_value.tv_usec = 0;
590 		while (timerisset(&timeout.it_value)) {
591 			pfd[0].fd = sock[0];
592 			pfd[0].events = POLLIN;
593 			pfd[1].fd = sock[1];
594 			pfd[1].events = POLLIN;
595 			setitimer(ITIMER_REAL, &timeout, NULL);
596 			rval = poll(pfd, 2, 0);
597 			setitimer(ITIMER_REAL, NULL, &timeout);
598 			if (rval == -1) {
599 				if (errno == EINTR)
600 					break;
601 				err(1, "poll");	/* shouldn't happen */
602 			}
603 			if (pfd[1].revents & POLLIN) {
604 				stat = get_reply(sock[1], (in_port_t)port[1],
605 				    xid[1], &msg[1], &res[1], rusers_reply_3);
606 				if (stat != RPC_SUCCESS)
607 					goto cleanup;
608 			}
609 			if (pfd[0].revents & POLLIN) {
610 				stat = get_reply(sock[0], (in_port_t)port[0],
611 				    xid[0], &msg[0], &res[0], rusers_reply);
612 				if (stat != RPC_SUCCESS)
613 					goto cleanup;
614 			}
615 		}
616 	}
617 cleanup:
618 	if (ifap != NULL)
619 		freeifaddrs(ifap);
620 	if (sock[0] >= 0)
621 		(void)close(sock[0]);
622 	if (sock[1] >= 0)
623 		(void)close(sock[1]);
624 	AUTH_DESTROY(unix_auth);
625 	if (stat != RPC_SUCCESS) {
626 		clnt_perrno(stat);
627 		exit(1);
628 	}
629 }
630 
631 void
print_entry(struct host_info * entry,int longfmt)632 print_entry(struct host_info *entry, int longfmt)
633 {
634 	char date[32], idle_time[64];
635 	char remote[RUSERS_MAXHOSTLEN + 3];
636 	struct rusers_utmp *ut;
637 	int i, len;
638 
639 	if (!longfmt)
640 		printf("%-*.*s ", HOST_WIDTH, HOST_WIDTH, entry->host);
641 
642 	for (i = 0, ut = entry->users; i < entry->count; i++, ut++) {
643 		if (longfmt) {
644 			time_t tim = ut->ut_time;
645 			strftime(date, sizeof(date), "%h %d %R",
646 			    localtime(&tim));
647 			date[sizeof(date) - 1] = '\0';
648 			fmt_idle(ut->ut_idle, idle_time, sizeof(idle_time));
649 			len = termwidth -
650 			    (MAXIMUM(strlen(ut->ut_user), NAME_WIDTH) + 1 +
651 			    HOST_WIDTH + 1 + LINE_WIDTH + 1 + strlen(date) +
652 			    1 + MAXIMUM(8, strlen(idle_time)) + 1 + 2);
653 			if (len > 0 && ut->ut_host[0] != '\0')
654 				snprintf(remote, sizeof(remote), "(%.*s)",
655 				    MINIMUM(len, RUSERS_MAXHOSTLEN), ut->ut_host);
656 			else
657 				remote[0] = '\0';
658 			len = HOST_WIDTH - MINIMUM(HOST_WIDTH, strlen(entry->host)) +
659 			    LINE_WIDTH - MINIMUM(LINE_WIDTH, strlen(ut->ut_line));
660 			printf("%-*s %.*s:%.*s%-*s %-12s %8s %s\n",
661 			    NAME_WIDTH, ut->ut_user, HOST_WIDTH, entry->host,
662 			    LINE_WIDTH, ut->ut_line, len, "", date,
663 			    idle_time, remote);
664 		} else {
665 			fputs(ut->ut_user, stdout);
666 			putchar(' ');
667 		}
668 	}
669 	if (!longfmt)
670 		putchar('\n');
671 }
672 
673 void
expandhosts(void)674 expandhosts(void)
675 {
676 	struct host_info *new_hostinfo, *entry;
677 	u_int count;
678 	int i, j;
679 
680 	for (i = 0, count = 0; i < nentries; i++)
681 		count += hostinfo[i].count;
682 
683 	new_hostinfo = calloc(sizeof(*entry), count);
684 	if (new_hostinfo == NULL)
685 		err(1, NULL);
686 	for (i = 0, entry = new_hostinfo; i < nentries; i++) {
687 		for (j = 0; j < hostinfo[i].count; j++) {
688 			memcpy(entry, &hostinfo[i], sizeof(*entry));
689 			entry->users = &hostinfo[i].users[j];
690 			entry->idle = entry->users->ut_idle;
691 			entry->count = 1;
692 			entry++;
693 		}
694 	}
695 	free(hostinfo);
696 	hostinfo = new_hostinfo;
697 	nentries = maxentries = count;
698 }
699 
700 void
sorthosts(void)701 sorthosts(void)
702 {
703 	int i;
704 	int (*compar)(const void *, const void *);
705 
706 	if (iflag && lflag)
707 		expandhosts();
708 
709 	if (hflag)
710 		compar = hcompare;
711 	else if (iflag)
712 		compar = icompare;
713 	else
714 		compar = ucompare;
715 	qsort(hostinfo, nentries, sizeof(*hostinfo), compar);
716 
717 	for (i = 0; i < nentries; i++)
718 		print_entry(&hostinfo[i], lflag && hostinfo[i].count);
719 }
720 
721 int
hcompare(const void * aa,const void * bb)722 hcompare(const void *aa, const void *bb)
723 {
724 	const struct host_info *a = (struct host_info *)aa;
725 	const struct host_info *b = (struct host_info *)bb;
726 	int rval;
727 
728 	if ((rval = strcasecmp(a->host, b->host)) != 0)
729 		return(rval);
730 
731 	if (a->idle < b->idle)
732 		return(-1);
733 	else if (a->idle > b->idle)
734 		return(1);
735 
736 	if (a->count > b->count)
737 		return(-1);
738 	else if (a->count < b->count)
739 		return(1);
740 
741 	return(0);
742 }
743 
744 int
icompare(const void * aa,const void * bb)745 icompare(const void *aa, const void *bb)
746 {
747 	const struct host_info *a = (struct host_info *)aa;
748 	const struct host_info *b = (struct host_info *)bb;
749 
750 	if (a->idle < b->idle)
751 		return(-1);
752 	else if (a->idle > b->idle)
753 		return(1);
754 
755 	if (a->count > b->count)
756 		return(-1);
757 	else if (a->count < b->count)
758 		return(1);
759 
760 	return(strcasecmp(a->host, b->host));
761 }
762 
763 int
ucompare(const void * aa,const void * bb)764 ucompare(const void *aa, const void *bb)
765 {
766 	const struct host_info *a = (struct host_info *)aa;
767 	const struct host_info *b = (struct host_info *)bb;
768 
769 	if (a->count > b->count)
770 		return(-1);
771 	else if (a->count < b->count)
772 		return(1);
773 
774 	if (a->idle < b->idle)
775 		return(-1);
776 	else if (a->idle > b->idle)
777 		return(1);
778 
779 	return(strcasecmp(a->host, b->host));
780 }
781 
782 void
alarmclock(int signo)783 alarmclock(int signo)
784 {
785 
786 	;		/* just interrupt */
787 }
788 
789 char *
estrndup(const char * src,size_t len)790 estrndup(const char *src, size_t len)
791 {
792 	char *dst, *end;
793 
794 	if ((end = memchr(src, '\0', len)) != NULL)
795 		len = end - src;
796 
797 	if ((dst = malloc(len + 1)) == NULL)
798 		err(1, NULL);
799 	memcpy(dst, src, len);
800 	dst[len] = '\0';
801 
802 	return(dst);
803 }
804 
805 void
usage(void)806 usage(void)
807 {
808 
809 	fprintf(stderr, "usage: %s [-al] [-h | -i | -u] [hosts ...]\n",
810 	    __progname);
811 	exit(1);
812 }
813