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