1 /* $OpenBSD: rusers.c,v 1.39 2016/08/05 10:34:18 jca 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(struct sockaddr); 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_len = sizeof(struct sockaddr_in); 538 baddr.sin_family = AF_INET; 539 baddr.sin_port = htons(PMAPPORT); 540 baddr.sin_addr.s_addr = htonl(INADDR_ANY); 541 542 res[0].port_ptr = &port[0]; 543 res[0].xdr_results = xdr_utmpidlearr; 544 res[0].results_ptr = (caddr_t)&up; 545 546 res[1].port_ptr = &port[1]; 547 res[1].xdr_results = xdr_utmp_array; 548 res[1].results_ptr = (caddr_t)&up3; 549 550 (void)signal(SIGALRM, alarmclock); 551 552 /* 553 * We do 6 runs through the loop. On even runs we send 554 * a version 3 broadcast. On odd ones we send a version 2 555 * broadcast. This should give version 3 replies enough 556 * of an 'edge' over the old version 2 ones in most cases. 557 * We poll() waiting for replies for 5 seconds in between 558 * each broadcast. 559 */ 560 for (i = 0; i < 6; i++) { 561 for (ifa = ifap; ifa; ifa = ifa->ifa_next) { 562 if (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(struct sockaddr)) != 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(struct sockaddr)) != 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 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 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 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 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 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 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 783 alarmclock(int signo) 784 { 785 786 ; /* just interrupt */ 787 } 788 789 char * 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 806 usage(void) 807 { 808 809 fprintf(stderr, "usage: %s [-al] [-h | -i | -u] [hosts ...]\n", 810 __progname); 811 exit(1); 812 } 813