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
main(int argc,char * argv[])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
singlehost(char * name)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
collectnames(void * resultsp,struct netbuf * raddrp,struct netconfig * nconf)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
print_info(struct utmpidlearr * utmpidlearrp,const char * name)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
collectnames_3(void * resultsp,struct netbuf * raddrp,struct netconfig * nconf)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
print_info_3(utmp_array * uap,const char * name)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
printnames(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
hcompare(const struct entry * a,const struct entry * b)516 hcompare(const struct entry *a, const struct entry *b)
517 {
518 return (strcmp(a->machine, b->machine));
519 }
520
521 static int
ucompare(const struct entry * a,const struct entry * b)522 ucompare(const struct entry *a, const struct entry *b)
523 {
524 return (b->cnt - a->cnt);
525 }
526
527 static int
icompare(const struct entry * a,const struct entry * b)528 icompare(const struct entry *a, const struct entry *b)
529 {
530 return (a->idle - b->idle);
531 }
532
533 static void
putline_2(char * host,struct utmpidle * uip)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
putline_3(char * host,rusers_utmp * rup)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
prttime(uint_t tim,char * tail)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
printit(int i)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
usage(void)647 usage(void)
648 {
649 (void) fprintf(stderr, "Usage: rusers [-ahilu] [host ...]\n");
650 free(entry);
651 exit(1);
652 }
653