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