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
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.
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]
22 #pragma ident "%Z%%M% %I% %E% SMI"
25 * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
26 * Use is subject to license terms.
28 /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
29 /* All Rights Reserved */
31 * University Copyright- Copyright (c) 1982, 1986, 1988
32 * The Regents of the University of California
35 * University Acknowledgment- Portions of this document are derived from
36 * software developed by the University of California, Berkeley, and its
40 #pragma ident "%Z%%M% %I% %E% SMI"
45 #include <sys/types.h>
46 #include <netconfig.h>
49 #include <rpcsvc/rusers.h>
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. */
57 #define MACHINELEN 16 /* length of machine name printed out */
58 #define NUMENTRIES 256
59 #define min(a, b) ((a) < (b) ? (a) : (b))
63 int idle
; /* set to INT_MAX if not present */
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 */
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);
97 * rusers [-ahilu] [host...]
100 main(int argc
, char *argv
[])
105 struct utmpidlearr utmpidlearr
;
106 utmp_array utmp_array_res
;
109 total_entries
= NUMENTRIES
;
110 entry
= malloc(sizeof (struct entry
) * total_entries
);
112 while ((c
= getopt(argc
, argv
, ":ad:hilun:")) != -1) {
119 debug
= atoi(optarg
);
120 (void) printf("Will collect %d responses.\n", debug
);
143 case ':': /* required operand missing */
150 case '?': /* Unrecognized option */
158 for (; optind
< argc
; optind
++) {
160 singlehost(argv
[optind
]);
170 (void) printf("Collecting responses...\n");
171 (void) fflush(stdout
);
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
);
195 singlehost(char *name
)
198 struct utmpidlearr utmpidlearr
;
199 utmp_array utmp_array_res
;
201 if (curentry
>= total_entries
) {
204 total_entries
+= NUMENTRIES
;
205 if ((tmp
= reallocarray(entry
, total_entries
,
206 sizeof (struct entry
))) == NULL
)
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
,
216 if (err
== RPC_SUCCESS
) {
217 (void) print_info_3(&utmp_array_res
, name
);
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
);
227 if (err
!= RPC_SUCCESS
) {
228 (void) fprintf(stderr
, "%s: ", name
);
232 (void) print_info(&utmpidlearr
, name
);
236 * Collect responses from RUSERSVERS_IDLE broadcast, convert to
237 * RUSERSVERS_3 format, and store in entry database.
240 collectnames(void *resultsp
, struct netbuf
*raddrp
, struct netconfig
*nconf
)
242 struct utmpidlearr utmpidlearr
;
243 struct entry
*entryp
, *lim
;
244 struct nd_hostservlist
*hs
;
245 char host
[MACHINELEN
+ 1];
247 utmpidlearr
= *(struct utmpidlearr
*)resultsp
;
248 if (utmpidlearr
.uia_cnt
< 1 && !aflag
)
251 if (netdir_getbyaddr(nconf
, &hs
, raddrp
)) {
253 netdir_perror("netdir_getbyaddr");
255 /* netdir routine couldn't resolve addr;just print out uaddr */
256 (void) sprintf(host
, "%.*s", MACHINELEN
,
257 taddr2uaddr(nconf
, raddrp
));
259 (void) sprintf(host
, "%.*s", MACHINELEN
,
260 hs
->h_hostservs
->h_host
);
261 netdir_free((char *)hs
, ND_HOSTSERVLIST
);
264 * need to realloc more space if we have more than 256 machines
265 * that respond to broadcast
267 if (curentry
>= total_entries
) {
270 total_entries
+= NUMENTRIES
;
271 if ((tmp
= reallocarray(entry
, total_entries
,
272 sizeof(struct entry
))) == NULL
)
279 * weed out duplicates
281 lim
= entry
+ curentry
;
282 for (entryp
= entry
; entryp
< lim
; entryp
++) {
283 if (strcmp(entryp
->machine
, host
) == 0)
286 return (print_info((struct utmpidlearr
*)resultsp
, host
));
290 print_info(struct utmpidlearr
*utmpidlearrp
, const char *name
)
292 utmp_array
*iconvert
;
294 char host
[MACHINELEN
+ 1];
295 char username
[NMAX
+ 1];
297 cnt
= utmpidlearrp
->uia_cnt
;
298 (void) sprintf(host
, "%.*s", MACHINELEN
, name
);
301 * if raw, print this entry out immediately
302 * otherwise store for later sorting
305 if (lflag
&& (cnt
> 0))
306 for (i
= 0; i
< cnt
; i
++)
307 putline_2(host
, utmpidlearrp
->uia_arr
[i
]);
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
,
314 (void) printf(" %.*s", NMAX
, username
);
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");
324 (void) strlcpy(entry
[curentry
].machine
, name
, MACHINELEN
+ 1);
325 entry
[curentry
++].cnt
= 0;
326 if (dflag
&& (++debugcnt
>= debug
))
330 entry
[curentry
].machine
= malloc(MACHINELEN
+ 1);
331 if (entry
[curentry
].machine
== NULL
) {
332 (void) fprintf(stderr
, "Ran out of memory - exiting\n");
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
));
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
);
354 entry
[curentry
].idle
= minidle
;
356 if (dflag
&& (++debugcnt
>= debug
))
363 * Collect responses from RUSERSVERS_3 broadcast.
366 collectnames_3(void *resultsp
, struct netbuf
*raddrp
, struct netconfig
*nconf
)
369 struct entry
*entryp
, *lim
;
370 struct nd_hostservlist
*hs
;
371 char host
[MACHINELEN
+ 1];
373 uap
= (utmp_array
*)resultsp
;
374 if (uap
->utmp_array_len
< 1 && !aflag
)
377 if (netdir_getbyaddr(nconf
, &hs
, raddrp
)) {
379 netdir_perror("netdir_getbyaddr");
381 /* netdir routine couldn't resolve addr;just print out uaddr */
382 (void) sprintf(host
, "%.*s", MACHINELEN
,
383 taddr2uaddr(nconf
, raddrp
));
385 (void) sprintf(host
, "%.*s", MACHINELEN
,
386 hs
->h_hostservs
->h_host
);
387 netdir_free((char *)hs
, ND_HOSTSERVLIST
);
391 * need to realloc more space if we have more than 256 machines
392 * that respond to broadcast
394 if (curentry
>= total_entries
) {
397 total_entries
+= NUMENTRIES
;
398 if ((tmp
= reallocarray(entry
, total_entries
,
399 sizeof (struct entry
))) == NULL
)
406 * weed out duplicates
408 lim
= entry
+ curentry
;
409 for (entryp
= entry
; entryp
< lim
; entryp
++) {
410 if (strcmp(entryp
->machine
, host
) == 0)
413 return (print_info_3(uap
, host
));
417 print_info_3(utmp_array
*uap
, const char *name
)
420 char host
[MACHINELEN
+ 1];
422 cnt
= uap
->utmp_array_len
;
424 (void) sprintf(host
, "%.*s", MACHINELEN
, name
);
427 * if raw, print this entry out immediately
428 * otherwise store for later sorting
431 if (lflag
&& (cnt
> 0))
432 for (i
= 0; i
< cnt
; i
++)
433 putline_3(host
, &uap
->utmp_array_val
[i
]);
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
);
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");
447 (void) strlcpy(entry
[curentry
].machine
, name
, MACHINELEN
+ 1);
448 entry
[curentry
++].cnt
= 0;
449 if (dflag
&& (++debugcnt
>= debug
))
454 entry
[curentry
].machine
= malloc(MACHINELEN
+ 1);
455 if (entry
[curentry
].machine
== NULL
) {
456 (void) fprintf(stderr
, "Ran out of memory - exiting\n");
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
));
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
);
478 entry
[curentry
].idle
= minidle
;
480 if (dflag
&& (++debugcnt
>= debug
))
489 int (*compare
)(const void *, const void *);
491 /* the name of the machine should already be in the structure */
493 compare
= (int (*)(const void *, const void *))icompare
;
495 compare
= (int (*)(const void *, const void *))hcompare
;
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
);
508 for (j
= 0; j
< entry
[i
].cnt
; j
++)
509 putline_3(entry
[i
].machine
,
510 &entry
[i
].users
.utmp_array_val
[j
]);
516 hcompare(const struct entry
*a
, const struct entry
*b
)
518 return (strcmp(a
->machine
, b
->machine
));
522 ucompare(const struct entry
*a
, const struct entry
*b
)
524 return (b
->cnt
- a
->cnt
);
528 icompare(const struct entry
*a
, const struct entry
*b
)
530 return (a
->idle
- b
->idle
);
534 putline_2(char *host
, struct utmpidle
*uip
)
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)
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
552 (void) strncpy(buf
, up
->ut_name
, NAMEMAX
);
554 (void) printf("%-*.*s ", NAMEMAX
, NAMEMAX
, buf
);
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
);
562 cbuf
= (char *)ctime(&up
->ut_time
);
563 (void) printf(" %.12s ", cbuf
+4);
564 if (uip
->ui_idle
== INT_MAX
)
565 (void) printf(" ??");
567 prttime(uip
->ui_idle
, "");
568 if (up
->ut_host
[0]) {
569 (void) strncpy(buf
, up
->ut_host
, HOSTMAX
);
571 (void) printf(" (%.*s)", HOSTMAX
, buf
);
573 (void) putchar('\n');
577 putline_3(char *host
, rusers_utmp
*rup
)
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
);
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(" ??");
593 prttime(rup
->ut_idle
, "");
595 (void) printf(" (%.*s)", HMAX
, rup
->ut_host
);
596 (void) putchar('\n');
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".
605 prttime(uint_t tim
, char *tail
)
610 (void) printf("%3d:", tim
/60);
616 if (tim
> 0 || didhrs
) {
617 (void) printf(didhrs
&& tim
< 10 ? "%02d" : "%2d", tim
);
621 (void) printf("%s", tail
);
633 (void) printf("%12.12s: ", entry
[i
].machine
);
635 putline_3(entry
[i
].machine
, &entry
[i
].users
.utmp_array_val
[0]);
636 for (j
= 1; j
< entry
[i
].cnt
; j
++) {
638 putline_3(entry
[i
].machine
,
639 &entry
[i
].users
.utmp_array_val
[j
]);
649 (void) fprintf(stderr
, "Usage: rusers [-ahilu] [host ...]\n");