1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 #pragma ident "%Z%%M% %I% %E% SMI" 23 24 /* 25 * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 26 * Use is subject to license terms. 27 */ 28 /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ 29 /* All Rights Reserved */ 30 /* 31 * University Copyright- Copyright (c) 1982, 1986, 1988 32 * The Regents of the University of California 33 * All Rights Reserved 34 * 35 * University Acknowledgment- Portions of this document are derived from 36 * software developed by the University of California, Berkeley, and its 37 * contributors. 38 */ 39 40 #pragma ident "%Z%%M% %I% %E% SMI" 41 42 #include <stdlib.h> 43 #include <stdio.h> 44 #include <unistd.h> 45 #include <sys/types.h> 46 #include <netconfig.h> 47 #include <netdir.h> 48 #include <rpc/rpc.h> 49 #include <rpcsvc/rusers.h> 50 #include <string.h> 51 #include <limits.h> 52 53 #define NMAX 12 /* These are used as field width specifiers */ 54 #define LMAX 8 /* when printing. */ 55 #define HMAX 16 /* "Logged in" host name. */ 56 57 #define MACHINELEN 16 /* length of machine name printed out */ 58 #define NUMENTRIES 256 59 #define min(a, b) ((a) < (b) ? (a) : (b)) 60 61 struct entry { 62 int cnt; 63 int idle; /* set to INT_MAX if not present */ 64 char *machine; 65 utmp_array users; 66 }; 67 68 static int curentry; 69 static int total_entries; 70 static struct entry *entry; 71 static int hflag; /* host: sort by machine name */ 72 static int iflag; /* idle: sort by idle time */ 73 static int uflag; /* users: sort by number of users */ 74 static int lflag; /* print out long form */ 75 static int aflag; /* all: list all machines */ 76 static int dflag; /* debug: list only first n machines */ 77 static int sorted; 78 static int debug; 79 static int debugcnt; 80 static char *nettype; 81 82 static int hcompare(const struct entry *, const struct entry *); 83 static int icompare(const struct entry *, const struct entry *); 84 static int ucompare(const struct entry *, const struct entry *); 85 static int print_info(struct utmpidlearr *, const char *); 86 static int print_info_3(utmp_array *, const char *); 87 static int collectnames(void *, struct netbuf *, struct netconfig *); 88 static int collectnames_3(void *, struct netbuf *, struct netconfig *); 89 static void singlehost(char *); 90 static void printnames(void); 91 static void putline_2(char *, struct utmpidle *); 92 static void putline_3(char *, rusers_utmp *); 93 static void prttime(uint_t, char *); 94 static void usage(void); 95 96 /* 97 * rusers [-ahilu] [host...] 98 */ 99 int 100 main(int argc, char *argv[]) 101 { 102 int c; 103 uint_t errflag = 0; 104 uint_t single = 0; 105 struct utmpidlearr utmpidlearr; 106 utmp_array utmp_array_res; 107 108 curentry = 0; 109 total_entries = NUMENTRIES; 110 entry = malloc(sizeof (struct entry) * total_entries); 111 112 while ((c = getopt(argc, argv, ":ad:hilun:")) != -1) { 113 switch (c) { 114 case 'a': 115 aflag++; 116 break; 117 case 'd': 118 dflag++; 119 debug = atoi(optarg); 120 (void) printf("Will collect %d responses.\n", debug); 121 break; 122 case 'h': 123 hflag++; 124 sorted++; 125 if (iflag || uflag) 126 errflag++; 127 break; 128 case 'i': 129 iflag++; 130 sorted++; 131 if (hflag || uflag) 132 errflag++; 133 break; 134 case 'u': 135 uflag++; 136 sorted++; 137 if (hflag || iflag) 138 errflag++; 139 break; 140 case 'l': 141 lflag++; 142 break; 143 case ':': /* required operand missing */ 144 errflag++; 145 break; 146 case 'n': 147 nettype = optarg; 148 break; 149 default: 150 case '?': /* Unrecognized option */ 151 errflag++; 152 break; 153 } 154 } 155 if (errflag) 156 usage(); 157 158 for (; optind < argc; optind++) { 159 single++; 160 singlehost(argv[optind]); 161 } 162 if (single) { 163 if (sorted) 164 printnames(); 165 free(entry); 166 exit(0); 167 } 168 169 if (sorted) { 170 (void) printf("Collecting responses...\n"); 171 (void) fflush(stdout); 172 } 173 utmp_array_res.utmp_array_val = NULL; 174 utmp_array_res.utmp_array_len = 0; 175 (void) printf("Sending broadcast for rusersd protocol version 3...\n"); 176 (void) rpc_broadcast(RUSERSPROG, RUSERSVERS_3, 177 RUSERSPROC_NAMES, (xdrproc_t)xdr_void, NULL, 178 (xdrproc_t)xdr_utmp_array, (char *)&utmp_array_res, 179 (resultproc_t)collectnames_3, nettype); 180 utmpidlearr.uia_arr = NULL; 181 (void) printf("Sending broadcast for rusersd protocol version 2...\n"); 182 (void) rpc_broadcast(RUSERSPROG, RUSERSVERS_IDLE, 183 RUSERSPROC_NAMES, (xdrproc_t)xdr_void, NULL, 184 (xdrproc_t)xdr_utmpidlearr, (char *)&utmpidlearr, 185 (resultproc_t)collectnames, nettype); 186 187 if (sorted) 188 printnames(); 189 190 free(entry); 191 return (0); 192 } 193 194 static void 195 singlehost(char *name) 196 { 197 enum clnt_stat err; 198 struct utmpidlearr utmpidlearr; 199 utmp_array utmp_array_res; 200 201 if (curentry >= total_entries) { 202 struct entry *tmp; 203 204 total_entries += NUMENTRIES; 205 if ((tmp = realloc(entry, sizeof (struct entry) 206 * total_entries)) == NULL) 207 return; 208 entry = tmp; 209 } 210 utmp_array_res.utmp_array_val = NULL; 211 utmp_array_res.utmp_array_len = 0; 212 err = rpc_call(name, RUSERSPROG, RUSERSVERS_3, 213 RUSERSPROC_NAMES, (xdrproc_t)xdr_void, 0, 214 (xdrproc_t)xdr_utmp_array, (char *)&utmp_array_res, 215 nettype); 216 if (err == RPC_SUCCESS) { 217 (void) print_info_3(&utmp_array_res, name); 218 return; 219 } 220 if (err == RPC_PROGVERSMISMATCH) { 221 utmpidlearr.uia_arr = NULL; 222 err = rpc_call(name, RUSERSPROG, RUSERSVERS_IDLE, 223 RUSERSPROC_NAMES, (xdrproc_t)xdr_void, 0, 224 (xdrproc_t)xdr_utmpidlearr, 225 (char *)&utmpidlearr, nettype); 226 } 227 if (err != RPC_SUCCESS) { 228 (void) fprintf(stderr, "%s: ", name); 229 clnt_perrno(err); 230 return; 231 } 232 (void) print_info(&utmpidlearr, name); 233 } 234 235 /* 236 * Collect responses from RUSERSVERS_IDLE broadcast, convert to 237 * RUSERSVERS_3 format, and store in entry database. 238 */ 239 static int 240 collectnames(void *resultsp, struct netbuf *raddrp, struct netconfig *nconf) 241 { 242 struct utmpidlearr utmpidlearr; 243 struct entry *entryp, *lim; 244 struct nd_hostservlist *hs; 245 char host[MACHINELEN + 1]; 246 247 utmpidlearr = *(struct utmpidlearr *)resultsp; 248 if (utmpidlearr.uia_cnt < 1 && !aflag) 249 return (0); 250 251 if (netdir_getbyaddr(nconf, &hs, raddrp)) { 252 #ifdef DEBUG 253 netdir_perror("netdir_getbyaddr"); 254 #endif 255 /* netdir routine couldn't resolve addr;just print out uaddr */ 256 (void) sprintf(host, "%.*s", MACHINELEN, 257 taddr2uaddr(nconf, raddrp)); 258 } else { 259 (void) sprintf(host, "%.*s", MACHINELEN, 260 hs->h_hostservs->h_host); 261 netdir_free((char *)hs, ND_HOSTSERVLIST); 262 } 263 /* 264 * need to realloc more space if we have more than 256 machines 265 * that respond to broadcast 266 */ 267 if (curentry >= total_entries) { 268 struct entry *tmp; 269 270 total_entries += NUMENTRIES; 271 if ((tmp = realloc(entry, sizeof (struct entry) 272 * total_entries)) == NULL) 273 return (1); 274 entry = tmp; 275 } 276 277 278 /* 279 * weed out duplicates 280 */ 281 lim = entry + curentry; 282 for (entryp = entry; entryp < lim; entryp++) { 283 if (strcmp(entryp->machine, host) == 0) 284 return (0); 285 } 286 return (print_info((struct utmpidlearr *)resultsp, host)); 287 } 288 289 static int 290 print_info(struct utmpidlearr *utmpidlearrp, const char *name) 291 { 292 utmp_array *iconvert; 293 int i, cnt, minidle; 294 char host[MACHINELEN + 1]; 295 char username[NMAX + 1]; 296 297 cnt = utmpidlearrp->uia_cnt; 298 (void) sprintf(host, "%.*s", MACHINELEN, name); 299 300 /* 301 * if raw, print this entry out immediately 302 * otherwise store for later sorting 303 */ 304 if (!sorted) { 305 if (lflag && (cnt > 0)) 306 for (i = 0; i < cnt; i++) 307 putline_2(host, utmpidlearrp->uia_arr[i]); 308 else { 309 (void) printf("%-*.*s", MACHINELEN, MACHINELEN, host); 310 for (i = 0; i < cnt; i++) { 311 (void) strlcpy(username, 312 utmpidlearrp->uia_arr[i]->ui_utmp.ut_name, 313 NMAX + 1); 314 (void) printf(" %.*s", NMAX, username); 315 } 316 (void) printf("\n"); 317 } 318 /* store just the name */ 319 entry[curentry].machine = malloc(MACHINELEN + 1); 320 if (entry[curentry].machine == NULL) { 321 (void) fprintf(stderr, "Ran out of memory - exiting\n"); 322 exit(1); 323 } 324 (void) strlcpy(entry[curentry].machine, name, MACHINELEN + 1); 325 entry[curentry++].cnt = 0; 326 if (dflag && (++debugcnt >= debug)) 327 return (1); 328 return (0); 329 } 330 entry[curentry].machine = malloc(MACHINELEN + 1); 331 if (entry[curentry].machine == NULL) { 332 (void) fprintf(stderr, "Ran out of memory - exiting\n"); 333 exit(1); 334 } 335 (void) strlcpy(entry[curentry].machine, name, MACHINELEN + 1); 336 entry[curentry].cnt = cnt; 337 iconvert = &entry[curentry].users; 338 iconvert->utmp_array_len = cnt; 339 iconvert->utmp_array_val = malloc(cnt * sizeof (rusers_utmp)); 340 minidle = INT_MAX; 341 for (i = 0; i < cnt; i++) { 342 iconvert->utmp_array_val[i].ut_user = 343 strdup(utmpidlearrp->uia_arr[i]->ui_utmp.ut_name); 344 iconvert->utmp_array_val[i].ut_line = 345 strdup(utmpidlearrp->uia_arr[i]->ui_utmp.ut_line); 346 iconvert->utmp_array_val[i].ut_host = 347 strdup(utmpidlearrp->uia_arr[i]->ui_utmp.ut_host); 348 iconvert->utmp_array_val[i].ut_time = 349 utmpidlearrp->uia_arr[i]->ui_utmp.ut_time; 350 iconvert->utmp_array_val[i].ut_idle = 351 utmpidlearrp->uia_arr[i]->ui_idle; 352 minidle = min(minidle, utmpidlearrp->uia_arr[i]->ui_idle); 353 } 354 entry[curentry].idle = minidle; 355 curentry++; 356 if (dflag && (++debugcnt >= debug)) 357 return (1); 358 return (0); 359 } 360 361 362 /* 363 * Collect responses from RUSERSVERS_3 broadcast. 364 */ 365 static int 366 collectnames_3(void *resultsp, struct netbuf *raddrp, struct netconfig *nconf) 367 { 368 utmp_array *uap; 369 struct entry *entryp, *lim; 370 struct nd_hostservlist *hs; 371 char host[MACHINELEN + 1]; 372 373 uap = (utmp_array *)resultsp; 374 if (uap->utmp_array_len < 1 && !aflag) 375 return (0); 376 377 if (netdir_getbyaddr(nconf, &hs, raddrp)) { 378 #ifdef DEBUG 379 netdir_perror("netdir_getbyaddr"); 380 #endif 381 /* netdir routine couldn't resolve addr;just print out uaddr */ 382 (void) sprintf(host, "%.*s", MACHINELEN, 383 taddr2uaddr(nconf, raddrp)); 384 } else { 385 (void) sprintf(host, "%.*s", MACHINELEN, 386 hs->h_hostservs->h_host); 387 netdir_free((char *)hs, ND_HOSTSERVLIST); 388 } 389 390 /* 391 * need to realloc more space if we have more than 256 machines 392 * that respond to broadcast 393 */ 394 if (curentry >= total_entries) { 395 struct entry *tmp; 396 397 total_entries += NUMENTRIES; 398 if ((tmp = realloc(entry, sizeof (struct entry) 399 * total_entries)) == NULL) 400 return (1); 401 entry = tmp; 402 } 403 404 405 /* 406 * weed out duplicates 407 */ 408 lim = entry + curentry; 409 for (entryp = entry; entryp < lim; entryp++) { 410 if (strcmp(entryp->machine, host) == 0) 411 return (0); 412 } 413 return (print_info_3(uap, host)); 414 } 415 416 static int 417 print_info_3(utmp_array *uap, const char *name) 418 { 419 int i, cnt, minidle; 420 char host[MACHINELEN + 1]; 421 422 cnt = uap->utmp_array_len; 423 424 (void) sprintf(host, "%.*s", MACHINELEN, name); 425 426 /* 427 * if raw, print this entry out immediately 428 * otherwise store for later sorting 429 */ 430 if (!sorted) { 431 if (lflag && (cnt > 0)) 432 for (i = 0; i < cnt; i++) 433 putline_3(host, &uap->utmp_array_val[i]); 434 else { 435 (void) printf("%-*.*s", MACHINELEN, MACHINELEN, host); 436 for (i = 0; i < cnt; i++) 437 (void) printf(" %.*s", NMAX, 438 uap->utmp_array_val[i].ut_user); 439 (void) printf("\n"); 440 } 441 /* store just the name */ 442 entry[curentry].machine = malloc(MACHINELEN + 1); 443 if (entry[curentry].machine == NULL) { 444 (void) fprintf(stderr, "Ran out of memory - exiting\n"); 445 exit(1); 446 } 447 (void) strlcpy(entry[curentry].machine, name, MACHINELEN + 1); 448 entry[curentry++].cnt = 0; 449 if (dflag && (++debugcnt >= debug)) 450 return (1); 451 return (0); 452 } 453 454 entry[curentry].machine = malloc(MACHINELEN + 1); 455 if (entry[curentry].machine == NULL) { 456 (void) fprintf(stderr, "Ran out of memory - exiting\n"); 457 exit(1); 458 } 459 (void) strlcpy(entry[curentry].machine, name, MACHINELEN + 1); 460 entry[curentry].cnt = cnt; 461 entry[curentry].users.utmp_array_len = cnt; 462 entry[curentry].users.utmp_array_val = malloc(cnt * 463 sizeof (rusers_utmp)); 464 minidle = INT_MAX; 465 for (i = 0; i < cnt; i++) { 466 entry[curentry].users.utmp_array_val[i].ut_user = 467 strdup(uap->utmp_array_val[i].ut_user); 468 entry[curentry].users.utmp_array_val[i].ut_line = 469 strdup(uap->utmp_array_val[i].ut_line); 470 entry[curentry].users.utmp_array_val[i].ut_host = 471 strdup(uap->utmp_array_val[i].ut_host); 472 entry[curentry].users.utmp_array_val[i].ut_time = 473 uap->utmp_array_val[i].ut_time; 474 entry[curentry].users.utmp_array_val[i].ut_idle = 475 uap->utmp_array_val[i].ut_idle; 476 minidle = min(minidle, uap->utmp_array_val[i].ut_idle); 477 } 478 entry[curentry].idle = minidle; 479 curentry++; 480 if (dflag && (++debugcnt >= debug)) 481 return (1); 482 return (0); 483 } 484 485 static void 486 printnames(void) 487 { 488 int i, j; 489 int (*compare)(const void *, const void *); 490 491 /* the name of the machine should already be in the structure */ 492 if (iflag) 493 compare = (int (*)(const void *, const void *))icompare; 494 else if (hflag) 495 compare = (int (*)(const void *, const void *))hcompare; 496 else 497 compare = (int (*)(const void *, const void *))ucompare; 498 qsort(entry, curentry, sizeof (struct entry), compare); 499 for (i = 0; i < curentry; i++) { 500 if (!lflag || (entry[i].cnt < 1)) { 501 (void) printf("%-*.*s", MACHINELEN, 502 MACHINELEN, entry[i].machine); 503 for (j = 0; j < entry[i].cnt; j++) 504 (void) printf(" %.*s", NMAX, 505 entry[i].users.utmp_array_val[j].ut_user); 506 (void) printf("\n"); 507 } else { 508 for (j = 0; j < entry[i].cnt; j++) 509 putline_3(entry[i].machine, 510 &entry[i].users.utmp_array_val[j]); 511 } 512 } 513 } 514 515 static int 516 hcompare(const struct entry *a, const struct entry *b) 517 { 518 return (strcmp(a->machine, b->machine)); 519 } 520 521 static int 522 ucompare(const struct entry *a, const struct entry *b) 523 { 524 return (b->cnt - a->cnt); 525 } 526 527 static int 528 icompare(const struct entry *a, const struct entry *b) 529 { 530 return (a->idle - b->idle); 531 } 532 533 static void 534 putline_2(char *host, struct utmpidle *uip) 535 { 536 char *cbuf; 537 struct ru_utmp *up; 538 char buf[100]; 539 540 up = &uip->ui_utmp; 541 #define NAMEMAX ((sizeof (up->ut_name) < NMAX) ? NMAX : sizeof (up->ut_name)) 542 #define NAMEMIN ((sizeof (up->ut_name) > NMAX) ? NMAX : sizeof (up->ut_name)) 543 /* Try and align this up nicely */ 544 #define LINEMAX sizeof (up->ut_line) 545 #define HOSTMAX sizeof (up->ut_host) 546 /* 547 * We copy the strings into a buffer because they aren't strictly 548 * speaking strings but byte arrays (and they may not have a 549 * terminating NULL. 550 */ 551 552 (void) strncpy(buf, up->ut_name, NAMEMAX); 553 buf[NAMEMIN] = '\0'; 554 (void) printf("%-*.*s ", NAMEMAX, NAMEMAX, buf); 555 556 (void) strcpy(buf, host); 557 (void) strcat(buf, ":"); 558 (void) strncat(buf, up->ut_line, LINEMAX); 559 buf[MACHINELEN+LINEMAX] = '\0'; 560 (void) printf("%-*.*s", MACHINELEN+LINEMAX, MACHINELEN+LINEMAX, buf); 561 562 cbuf = (char *)ctime(&up->ut_time); 563 (void) printf(" %.12s ", cbuf+4); 564 if (uip->ui_idle == INT_MAX) 565 (void) printf(" ??"); 566 else 567 prttime(uip->ui_idle, ""); 568 if (up->ut_host[0]) { 569 (void) strncpy(buf, up->ut_host, HOSTMAX); 570 buf[HOSTMAX] = '\0'; 571 (void) printf(" (%.*s)", HOSTMAX, buf); 572 } 573 (void) putchar('\n'); 574 } 575 576 static void 577 putline_3(char *host, rusers_utmp *rup) 578 { 579 char *cbuf; 580 char buf[100]; 581 582 (void) printf("%-*.*s ", NMAX, NMAX, rup->ut_user); 583 (void) strcpy(buf, host); 584 (void) strcat(buf, ":"); 585 (void) strncat(buf, rup->ut_line, LMAX); 586 (void) printf("%-*.*s", MACHINELEN+LMAX, MACHINELEN+LMAX, buf); 587 588 cbuf = (char *)ctime((time_t *)&rup->ut_time); 589 (void) printf(" %.12s ", cbuf+4); 590 if (rup->ut_idle == INT_MAX) 591 (void) printf(" ??"); 592 else 593 prttime(rup->ut_idle, ""); 594 if (rup->ut_host[0]) 595 (void) printf(" (%.*s)", HMAX, rup->ut_host); 596 (void) putchar('\n'); 597 } 598 599 /* 600 * prttime prints a time in hours and minutes. 601 * The character string tail is printed at the end, obvious 602 * strings to pass are "", " ", or "am". 603 */ 604 static void 605 prttime(uint_t tim, char *tail) 606 { 607 int didhrs = 0; 608 609 if (tim >= 60) { 610 (void) printf("%3d:", tim/60); 611 didhrs++; 612 } else { 613 (void) printf(" "); 614 } 615 tim %= 60; 616 if (tim > 0 || didhrs) { 617 (void) printf(didhrs && tim < 10 ? "%02d" : "%2d", tim); 618 } else { 619 (void) printf(" "); 620 } 621 (void) printf("%s", tail); 622 } 623 624 #ifdef DEBUG 625 /* 626 * for debugging 627 */ 628 int 629 printit(int i) 630 { 631 int j, v; 632 633 (void) printf("%12.12s: ", entry[i].machine); 634 if (entry[i].cnt) { 635 putline_3(entry[i].machine, &entry[i].users.utmp_array_val[0]); 636 for (j = 1; j < entry[i].cnt; j++) { 637 (void) printf("\t"); 638 putline_3(entry[i].machine, 639 &entry[i].users.utmp_array_val[j]); 640 } 641 } else 642 (void) printf("\n"); 643 } 644 #endif 645 646 static void 647 usage(void) 648 { 649 (void) fprintf(stderr, "Usage: rusers [-ahilu] [host ...]\n"); 650 free(entry); 651 exit(1); 652 } 653