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