1 /* $OpenBSD: rusers.c,v 1.42 2019/06/28 13:35:03 deraadt 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 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 == -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 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 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 } 812