1 |
|
|
/* $OpenBSD: rusers.c,v 1.38 2016/03/28 11:06:09 chl Exp $ */ |
2 |
|
|
|
3 |
|
|
/* |
4 |
|
|
* Copyright (c) 2001, 2003 Todd C. Miller <Todd.Miller@courtesan.com> |
5 |
|
|
* |
6 |
|
|
* Permission to use, copy, modify, and distribute this software for any |
7 |
|
|
* purpose with or without fee is hereby granted, provided that the above |
8 |
|
|
* copyright notice and this permission notice appear in all copies. |
9 |
|
|
* |
10 |
|
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
11 |
|
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
12 |
|
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
13 |
|
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
14 |
|
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
15 |
|
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
16 |
|
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
17 |
|
|
* Sponsored in part by the Defense Advanced Research Projects |
18 |
|
|
* Agency (DARPA) and Air Force Research Laboratory, Air Force |
19 |
|
|
* Materiel Command, USAF, under agreement number F39502-99-1-0512. |
20 |
|
|
*/ |
21 |
|
|
/*- |
22 |
|
|
* Copyright (c) 1993 John Brezak |
23 |
|
|
* All rights reserved. |
24 |
|
|
* |
25 |
|
|
* Redistribution and use in source and binary forms, with or without |
26 |
|
|
* modification, are permitted provided that the following conditions |
27 |
|
|
* are met: |
28 |
|
|
* 1. Redistributions of source code must retain the above copyright |
29 |
|
|
* notice, this list of conditions and the following disclaimer. |
30 |
|
|
* 2. Redistributions in binary form must reproduce the above copyright |
31 |
|
|
* notice, this list of conditions and the following disclaimer in the |
32 |
|
|
* documentation and/or other materials provided with the distribution. |
33 |
|
|
* 3. The name of the author may not be used to endorse or promote products |
34 |
|
|
* derived from this software without specific prior written permission. |
35 |
|
|
* |
36 |
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR `AS IS'' AND ANY EXPRESS OR |
37 |
|
|
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
38 |
|
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
39 |
|
|
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, |
40 |
|
|
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
41 |
|
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR |
42 |
|
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
43 |
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, |
44 |
|
|
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN |
45 |
|
|
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
46 |
|
|
* POSSIBILITY OF SUCH DAMAGE. |
47 |
|
|
*/ |
48 |
|
|
|
49 |
|
|
#include <sys/ioctl.h> |
50 |
|
|
#include <sys/socket.h> |
51 |
|
|
#include <sys/signal.h> |
52 |
|
|
#include <rpc/rpc.h> |
53 |
|
|
#include <rpc/pmap_prot.h> |
54 |
|
|
#include <rpc/pmap_rmt.h> |
55 |
|
|
#include <rpcsvc/rusers.h> |
56 |
|
|
#include <rpcsvc/rnusers.h> /* Old protocol version */ |
57 |
|
|
#include <arpa/inet.h> |
58 |
|
|
#include <net/if.h> |
59 |
|
|
#include <err.h> |
60 |
|
|
#include <errno.h> |
61 |
|
|
#include <ifaddrs.h> |
62 |
|
|
#include <netdb.h> |
63 |
|
|
#include <stdio.h> |
64 |
|
|
#include <stdlib.h> |
65 |
|
|
#include <string.h> |
66 |
|
|
#include <termios.h> |
67 |
|
|
#include <unistd.h> |
68 |
|
|
#include <limits.h> |
69 |
|
|
|
70 |
|
|
#define MINIMUM(a, b) (((a) < (b)) ? (a) : (b)) |
71 |
|
|
#define MAXIMUM(a, b) (((a) > (b)) ? (a) : (b)) |
72 |
|
|
|
73 |
|
|
/* Preferred formatting */ |
74 |
|
|
#define HOST_WIDTH 17 |
75 |
|
|
#define LINE_WIDTH 8 |
76 |
|
|
#define NAME_WIDTH 8 |
77 |
|
|
|
78 |
|
|
#define MAX_BROADCAST_SIZE 1400 |
79 |
|
|
|
80 |
|
|
struct host_info { |
81 |
|
|
u_int count; |
82 |
|
|
u_int idle; |
83 |
|
|
char *host; |
84 |
|
|
rusers_utmp *users; |
85 |
|
|
} *hostinfo; |
86 |
|
|
|
87 |
|
|
void print_entry(struct host_info *, int); |
88 |
|
|
void fmt_idle(int, char *, size_t); |
89 |
|
|
void onehost(char *); |
90 |
|
|
void allhosts(void); |
91 |
|
|
void sorthosts(void); |
92 |
|
|
void expandhosts(void); |
93 |
|
|
void alarmclock(int); |
94 |
|
|
char *estrndup(const char *, size_t); |
95 |
|
|
struct host_info *add_host(char *); |
96 |
|
|
int hcompare(const void *, const void *); |
97 |
|
|
int icompare(const void *, const void *); |
98 |
|
|
int ucompare(const void *, const void *); |
99 |
|
|
bool_t rusers_reply(char *, struct sockaddr_in *); |
100 |
|
|
bool_t rusers_reply_3(char *, struct sockaddr_in *); |
101 |
|
|
enum clnt_stat get_reply(int, in_port_t, u_long, struct rpc_msg *, |
102 |
|
|
struct rmtcallres *, bool_t (*)(char *, struct sockaddr_in *)); |
103 |
|
|
enum clnt_stat rpc_setup(int *, XDR *, struct rpc_msg *, |
104 |
|
|
struct rmtcallargs *, AUTH *, char *); |
105 |
|
|
__dead void usage(void); |
106 |
|
|
|
107 |
|
|
int aflag, hflag, iflag, lflag, uflag; |
108 |
|
|
u_int nentries, maxentries; |
109 |
|
|
long termwidth; |
110 |
|
|
extern char *__progname; |
111 |
|
|
|
112 |
|
|
int |
113 |
|
|
main(int argc, char **argv) |
114 |
|
|
{ |
115 |
|
|
struct winsize win; |
116 |
|
|
char *cp; |
117 |
|
|
int ch; |
118 |
|
|
|
119 |
|
|
while ((ch = getopt(argc, argv, "ahilu")) != -1) |
120 |
|
|
switch (ch) { |
121 |
|
|
case 'a': |
122 |
|
|
aflag = 1; |
123 |
|
|
break; |
124 |
|
|
case 'h': |
125 |
|
|
hflag = 1; |
126 |
|
|
break; |
127 |
|
|
case 'i': |
128 |
|
|
iflag = 1; |
129 |
|
|
break; |
130 |
|
|
case 'l': |
131 |
|
|
lflag = 1; |
132 |
|
|
break; |
133 |
|
|
case 'u': |
134 |
|
|
uflag = 1; |
135 |
|
|
break; |
136 |
|
|
default: |
137 |
|
|
usage(); |
138 |
|
|
/*NOTREACHED*/ |
139 |
|
|
} |
140 |
|
|
|
141 |
|
|
if (hflag + iflag + uflag > 1) |
142 |
|
|
usage(); |
143 |
|
|
|
144 |
|
|
termwidth = 0; |
145 |
|
|
if ((cp = getenv("COLUMNS")) != NULL) |
146 |
|
|
termwidth = strtonum(cp, 1, LONG_MAX, NULL); |
147 |
|
|
if (termwidth == 0 && ioctl(STDOUT_FILENO, TIOCGWINSZ, &win) == 0 && |
148 |
|
|
win.ws_col > 0) |
149 |
|
|
termwidth = win.ws_col; |
150 |
|
|
if (termwidth == 0) |
151 |
|
|
termwidth = 80; |
152 |
|
|
|
153 |
|
|
setvbuf(stdout, NULL, _IOLBF, 0); |
154 |
|
|
|
155 |
|
|
if (argc == optind) { |
156 |
|
|
if (hflag || iflag || uflag) { |
157 |
|
|
puts("Collecting responses..."); |
158 |
|
|
allhosts(); |
159 |
|
|
sorthosts(); |
160 |
|
|
} else |
161 |
|
|
allhosts(); |
162 |
|
|
} else { |
163 |
|
|
aflag = 1; |
164 |
|
|
for (; optind < argc; optind++) |
165 |
|
|
(void) onehost(argv[optind]); |
166 |
|
|
if (hflag || iflag || uflag) |
167 |
|
|
sorthosts(); |
168 |
|
|
} |
169 |
|
|
|
170 |
|
|
exit(0); |
171 |
|
|
} |
172 |
|
|
|
173 |
|
|
struct host_info * |
174 |
|
|
add_host(char *host) |
175 |
|
|
{ |
176 |
|
|
int i; |
177 |
|
|
|
178 |
|
|
for (i = 0; i < nentries; i++) { |
179 |
|
|
/* Existing entry. */ |
180 |
|
|
if (strcmp(host, hostinfo[i].host) == 0) |
181 |
|
|
return(NULL); |
182 |
|
|
} |
183 |
|
|
|
184 |
|
|
/* New entry, allocate space if needed and store. */ |
185 |
|
|
if (nentries == maxentries) { |
186 |
|
|
maxentries += 128; |
187 |
|
|
hostinfo = reallocarray(hostinfo, maxentries, |
188 |
|
|
sizeof(*hostinfo)); |
189 |
|
|
if (hostinfo == NULL) |
190 |
|
|
err(1, NULL); |
191 |
|
|
} |
192 |
|
|
if ((hostinfo[nentries].host = strdup(host)) == NULL) |
193 |
|
|
err(1, NULL); |
194 |
|
|
return(&hostinfo[nentries++]); |
195 |
|
|
} |
196 |
|
|
|
197 |
|
|
void |
198 |
|
|
fmt_idle(int idle, char *idle_time, size_t idle_time_len) |
199 |
|
|
{ |
200 |
|
|
int days, hours, minutes, seconds; |
201 |
|
|
|
202 |
|
|
switch (idle) { |
203 |
|
|
case 0: |
204 |
|
|
*idle_time = '\0'; |
205 |
|
|
break; |
206 |
|
|
case INT_MAX: |
207 |
|
|
strlcpy(idle_time, "??", idle_time_len); |
208 |
|
|
break; |
209 |
|
|
default: |
210 |
|
|
seconds = idle; |
211 |
|
|
days = seconds / (60*60*24); |
212 |
|
|
seconds %= (60*60*24); |
213 |
|
|
hours = seconds / (60*60); |
214 |
|
|
seconds %= (60*60); |
215 |
|
|
minutes = seconds / 60; |
216 |
|
|
seconds %= 60; |
217 |
|
|
if (idle >= (24*60*60)) |
218 |
|
|
snprintf(idle_time, idle_time_len, |
219 |
|
|
"%d day%s, %d:%02d:%02d", days, |
220 |
|
|
days > 1 ? "s" : "", hours, minutes, seconds); |
221 |
|
|
else if (idle >= (60*60)) |
222 |
|
|
snprintf(idle_time, idle_time_len, "%2d:%02d:%02d", |
223 |
|
|
hours, minutes, seconds); |
224 |
|
|
else if (idle > 60) |
225 |
|
|
snprintf(idle_time, idle_time_len, "%2d:%02d", |
226 |
|
|
minutes, seconds); |
227 |
|
|
else |
228 |
|
|
snprintf(idle_time, idle_time_len, " :%02d", idle); |
229 |
|
|
break; |
230 |
|
|
} |
231 |
|
|
} |
232 |
|
|
|
233 |
|
|
bool_t |
234 |
|
|
rusers_reply(char *replyp, struct sockaddr_in *raddrp) |
235 |
|
|
{ |
236 |
|
|
utmpidlearr *up = (utmpidlearr *)replyp; |
237 |
|
|
struct host_info *entry; |
238 |
|
|
struct hostent *hp; |
239 |
|
|
rusers_utmp *ut; |
240 |
|
|
char *host; |
241 |
|
|
int i; |
242 |
|
|
|
243 |
|
|
if (!aflag && up->uia_cnt == 0) |
244 |
|
|
return(0); |
245 |
|
|
|
246 |
|
|
hp = gethostbyaddr((char *)&raddrp->sin_addr, |
247 |
|
|
sizeof(struct in_addr), AF_INET); |
248 |
|
|
if (hp) |
249 |
|
|
host = hp->h_name; |
250 |
|
|
else |
251 |
|
|
host = inet_ntoa(raddrp->sin_addr); |
252 |
|
|
if ((entry = add_host(host)) == NULL) |
253 |
|
|
return(0); |
254 |
|
|
|
255 |
|
|
if (up->uia_cnt == 0) |
256 |
|
|
ut = NULL; |
257 |
|
|
else if ((ut = calloc(up->uia_cnt, sizeof(*ut))) == NULL) |
258 |
|
|
err(1, NULL); |
259 |
|
|
entry->users = ut; |
260 |
|
|
entry->count = up->uia_cnt; |
261 |
|
|
entry->idle = UINT_MAX; |
262 |
|
|
for (i = 0; i < up->uia_cnt; i++, ut++) { |
263 |
|
|
ut->ut_user = estrndup(up->uia_arr[i]->ui_utmp.ut_name, |
264 |
|
|
RNUSERS_MAXUSERLEN); |
265 |
|
|
ut->ut_line = estrndup(up->uia_arr[i]->ui_utmp.ut_line, |
266 |
|
|
RNUSERS_MAXLINELEN); |
267 |
|
|
ut->ut_host = estrndup(up->uia_arr[i]->ui_utmp.ut_host, |
268 |
|
|
RNUSERS_MAXHOSTLEN); |
269 |
|
|
ut->ut_time = up->uia_arr[i]->ui_utmp.ut_time; |
270 |
|
|
ut->ut_idle = up->uia_arr[i]->ui_idle; |
271 |
|
|
if (ut->ut_idle < entry->idle) |
272 |
|
|
entry->idle = ut->ut_idle; |
273 |
|
|
} |
274 |
|
|
|
275 |
|
|
if (!hflag && !iflag && !uflag) { |
276 |
|
|
print_entry(entry, lflag && entry->count); |
277 |
|
|
for (i = 0, ut = entry->users; i < entry->count; i++, ut++) { |
278 |
|
|
free(ut->ut_user); |
279 |
|
|
free(ut->ut_line); |
280 |
|
|
free(ut->ut_host); |
281 |
|
|
} |
282 |
|
|
free(entry->users); |
283 |
|
|
} |
284 |
|
|
|
285 |
|
|
return(0); |
286 |
|
|
} |
287 |
|
|
|
288 |
|
|
bool_t |
289 |
|
|
rusers_reply_3(char *replyp, struct sockaddr_in *raddrp) |
290 |
|
|
{ |
291 |
|
|
utmp_array *up3 = (utmp_array *)replyp; |
292 |
|
|
struct host_info *entry; |
293 |
|
|
struct hostent *hp; |
294 |
|
|
rusers_utmp *ut; |
295 |
|
|
char *host; |
296 |
|
|
int i; |
297 |
|
|
|
298 |
|
|
if (!aflag && up3->utmp_array_len == 0) |
299 |
|
|
return(0); |
300 |
|
|
|
301 |
|
|
hp = gethostbyaddr((char *)&raddrp->sin_addr, |
302 |
|
|
sizeof(struct in_addr), AF_INET); |
303 |
|
|
if (hp) |
304 |
|
|
host = hp->h_name; |
305 |
|
|
else |
306 |
|
|
host = inet_ntoa(raddrp->sin_addr); |
307 |
|
|
if ((entry = add_host(host)) == NULL) |
308 |
|
|
return(0); |
309 |
|
|
|
310 |
|
|
if (up3->utmp_array_len == 0) |
311 |
|
|
ut = NULL; |
312 |
|
|
else if ((ut = calloc(up3->utmp_array_len, sizeof(*ut))) == NULL) |
313 |
|
|
err(1, NULL); |
314 |
|
|
entry->users = ut; |
315 |
|
|
entry->count = up3->utmp_array_len; |
316 |
|
|
entry->idle = UINT_MAX; |
317 |
|
|
for (i = 0; i < up3->utmp_array_len; i++, ut++) { |
318 |
|
|
ut->ut_user = estrndup(up3->utmp_array_val[i].ut_user, |
319 |
|
|
RUSERS_MAXUSERLEN); |
320 |
|
|
ut->ut_line = estrndup(up3->utmp_array_val[i].ut_line, |
321 |
|
|
RUSERS_MAXLINELEN); |
322 |
|
|
ut->ut_host = estrndup(up3->utmp_array_val[i].ut_host, |
323 |
|
|
RUSERS_MAXHOSTLEN); |
324 |
|
|
ut->ut_time = up3->utmp_array_val[i].ut_time; |
325 |
|
|
ut->ut_idle = up3->utmp_array_val[i].ut_idle; |
326 |
|
|
if (ut->ut_idle < entry->idle) |
327 |
|
|
entry->idle = ut->ut_idle; |
328 |
|
|
} |
329 |
|
|
|
330 |
|
|
if (!hflag && !iflag && !uflag) { |
331 |
|
|
print_entry(entry, lflag && entry->count); |
332 |
|
|
for (i = 0, ut = entry->users; i < entry->count; i++, ut++) { |
333 |
|
|
free(ut->ut_user); |
334 |
|
|
free(ut->ut_line); |
335 |
|
|
free(ut->ut_host); |
336 |
|
|
} |
337 |
|
|
free(entry->users); |
338 |
|
|
} |
339 |
|
|
|
340 |
|
|
return(0); |
341 |
|
|
} |
342 |
|
|
|
343 |
|
|
void |
344 |
|
|
onehost(char *host) |
345 |
|
|
{ |
346 |
|
|
utmpidlearr up; |
347 |
|
|
utmp_array up3; |
348 |
|
|
CLIENT *rusers_clnt; |
349 |
|
|
struct sockaddr_in sin; |
350 |
|
|
struct hostent *hp; |
351 |
|
|
struct timeval tv = { 25, 0 }; |
352 |
|
|
int error; |
353 |
|
|
|
354 |
|
|
memset(&sin, 0, sizeof sin); |
355 |
|
|
|
356 |
|
|
hp = gethostbyname(host); |
357 |
|
|
if (hp == NULL) |
358 |
|
|
errx(1, "unknown host \"%s\"", host); |
359 |
|
|
|
360 |
|
|
/* Try version 3 first. */ |
361 |
|
|
rusers_clnt = clnt_create(host, RUSERSPROG, RUSERSVERS_3, "udp"); |
362 |
|
|
if (rusers_clnt == NULL) { |
363 |
|
|
clnt_pcreateerror(__progname); |
364 |
|
|
exit(1); |
365 |
|
|
} |
366 |
|
|
|
367 |
|
|
memset(&up3, 0, sizeof(up3)); |
368 |
|
|
error = clnt_call(rusers_clnt, RUSERSPROC_NAMES, xdr_void, NULL, |
369 |
|
|
xdr_utmp_array, &up3, tv); |
370 |
|
|
switch (error) { |
371 |
|
|
case RPC_SUCCESS: |
372 |
|
|
sin.sin_addr.s_addr = *(int *)hp->h_addr; |
373 |
|
|
rusers_reply_3((char *)&up3, &sin); |
374 |
|
|
clnt_destroy(rusers_clnt); |
375 |
|
|
return; |
376 |
|
|
case RPC_PROGVERSMISMATCH: |
377 |
|
|
clnt_destroy(rusers_clnt); |
378 |
|
|
break; |
379 |
|
|
default: |
380 |
|
|
clnt_perror(rusers_clnt, __progname); |
381 |
|
|
clnt_destroy(rusers_clnt); |
382 |
|
|
exit(1); |
383 |
|
|
} |
384 |
|
|
|
385 |
|
|
/* Fall back to version 2. */ |
386 |
|
|
rusers_clnt = clnt_create(host, RUSERSPROG, RUSERSVERS_IDLE, "udp"); |
387 |
|
|
if (rusers_clnt == NULL) { |
388 |
|
|
clnt_pcreateerror(__progname); |
389 |
|
|
exit(1); |
390 |
|
|
} |
391 |
|
|
|
392 |
|
|
memset(&up, 0, sizeof(up)); |
393 |
|
|
error = clnt_call(rusers_clnt, RUSERSPROC_NAMES, xdr_void, NULL, |
394 |
|
|
xdr_utmpidlearr, &up, tv); |
395 |
|
|
if (error != RPC_SUCCESS) { |
396 |
|
|
clnt_perror(rusers_clnt, __progname); |
397 |
|
|
clnt_destroy(rusers_clnt); |
398 |
|
|
exit(1); |
399 |
|
|
} |
400 |
|
|
sin.sin_addr.s_addr = *(int *)hp->h_addr; |
401 |
|
|
rusers_reply((char *)&up, &sin); |
402 |
|
|
clnt_destroy(rusers_clnt); |
403 |
|
|
} |
404 |
|
|
|
405 |
|
|
enum clnt_stat |
406 |
|
|
get_reply(int sock, in_port_t port, u_long xid, struct rpc_msg *msgp, |
407 |
|
|
struct rmtcallres *resp, bool_t (*callback)(char *, struct sockaddr_in *)) |
408 |
|
|
{ |
409 |
|
|
ssize_t inlen; |
410 |
|
|
socklen_t fromlen; |
411 |
|
|
struct sockaddr_in raddr; |
412 |
|
|
char inbuf[UDPMSGSIZE]; |
413 |
|
|
XDR xdr; |
414 |
|
|
|
415 |
|
|
retry: |
416 |
|
|
msgp->acpted_rply.ar_verf = _null_auth; |
417 |
|
|
msgp->acpted_rply.ar_results.where = (caddr_t)resp; |
418 |
|
|
msgp->acpted_rply.ar_results.proc = xdr_rmtcallres; |
419 |
|
|
|
420 |
|
|
fromlen = sizeof(struct sockaddr); |
421 |
|
|
inlen = recvfrom(sock, inbuf, sizeof(inbuf), 0, |
422 |
|
|
(struct sockaddr *)&raddr, &fromlen); |
423 |
|
|
if (inlen < 0) { |
424 |
|
|
if (errno == EINTR) |
425 |
|
|
goto retry; |
426 |
|
|
return (RPC_CANTRECV); |
427 |
|
|
} |
428 |
|
|
if (inlen < sizeof(u_int32_t)) |
429 |
|
|
goto retry; |
430 |
|
|
|
431 |
|
|
/* |
432 |
|
|
* If the reply we got matches our request, decode the |
433 |
|
|
* replay and pass it to the callback function. |
434 |
|
|
*/ |
435 |
|
|
xdrmem_create(&xdr, inbuf, (u_int)inlen, XDR_DECODE); |
436 |
|
|
if (xdr_replymsg(&xdr, msgp)) { |
437 |
|
|
if ((msgp->rm_xid == xid) && |
438 |
|
|
(msgp->rm_reply.rp_stat == MSG_ACCEPTED) && |
439 |
|
|
(msgp->acpted_rply.ar_stat == SUCCESS)) { |
440 |
|
|
raddr.sin_port = htons(port); |
441 |
|
|
(void)(*callback)(resp->results_ptr, &raddr); |
442 |
|
|
} |
443 |
|
|
} |
444 |
|
|
xdr.x_op = XDR_FREE; |
445 |
|
|
msgp->acpted_rply.ar_results.proc = xdr_void; |
446 |
|
|
(void)xdr_replymsg(&xdr, msgp); |
447 |
|
|
(void)(*resp->xdr_results)(&xdr, resp->results_ptr); |
448 |
|
|
xdr_destroy(&xdr); |
449 |
|
|
|
450 |
|
|
return(RPC_SUCCESS); |
451 |
|
|
} |
452 |
|
|
|
453 |
|
|
enum clnt_stat |
454 |
|
|
rpc_setup(int *fdp, XDR *xdr, struct rpc_msg *msg, struct rmtcallargs *args, |
455 |
|
|
AUTH *unix_auth, char *buf) |
456 |
|
|
{ |
457 |
|
|
int on = 1; |
458 |
|
|
|
459 |
|
|
if ((*fdp = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) |
460 |
|
|
return(RPC_CANTSEND); |
461 |
|
|
|
462 |
|
|
if (setsockopt(*fdp, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on)) < 0) |
463 |
|
|
return(RPC_CANTSEND); |
464 |
|
|
|
465 |
|
|
msg->rm_xid = arc4random(); |
466 |
|
|
msg->rm_direction = CALL; |
467 |
|
|
msg->rm_call.cb_rpcvers = RPC_MSG_VERSION; |
468 |
|
|
msg->rm_call.cb_prog = PMAPPROG; |
469 |
|
|
msg->rm_call.cb_vers = PMAPVERS; |
470 |
|
|
msg->rm_call.cb_proc = PMAPPROC_CALLIT; |
471 |
|
|
msg->rm_call.cb_cred = unix_auth->ah_cred; |
472 |
|
|
msg->rm_call.cb_verf = unix_auth->ah_verf; |
473 |
|
|
|
474 |
|
|
xdrmem_create(xdr, buf, MAX_BROADCAST_SIZE, XDR_ENCODE); |
475 |
|
|
if (!xdr_callmsg(xdr, msg) || !xdr_rmtcall_args(xdr, args)) |
476 |
|
|
return(RPC_CANTENCODEARGS); |
477 |
|
|
|
478 |
|
|
return(RPC_SUCCESS); |
479 |
|
|
} |
480 |
|
|
|
481 |
|
|
void |
482 |
|
|
allhosts(void) |
483 |
|
|
{ |
484 |
|
|
enum clnt_stat stat; |
485 |
|
|
struct itimerval timeout; |
486 |
|
|
AUTH *unix_auth; |
487 |
|
|
size_t outlen[2]; |
488 |
|
|
int sock[2] = { -1, -1 }; |
489 |
|
|
int i, maxfd, rval; |
490 |
|
|
u_long xid[2], port[2]; |
491 |
|
|
fd_set *fds = NULL; |
492 |
|
|
struct sockaddr_in *sin, baddr; |
493 |
|
|
struct rmtcallargs args; |
494 |
|
|
struct rmtcallres res[2]; |
495 |
|
|
struct rpc_msg msg[2]; |
496 |
|
|
struct ifaddrs *ifa, *ifap = NULL; |
497 |
|
|
char buf[2][MAX_BROADCAST_SIZE]; |
498 |
|
|
utmpidlearr up; |
499 |
|
|
utmp_array up3; |
500 |
|
|
XDR xdr; |
501 |
|
|
|
502 |
|
|
if ((unix_auth = authunix_create_default()) == NULL) |
503 |
|
|
err(1, "can't create auth handle"); |
504 |
|
|
|
505 |
|
|
if (getifaddrs(&ifap) != 0) |
506 |
|
|
err(1, "can't get list of interface addresses"); |
507 |
|
|
|
508 |
|
|
memset(&up, 0, sizeof(up)); |
509 |
|
|
memset(&up3, 0, sizeof(up3)); |
510 |
|
|
memset(&baddr, 0, sizeof(baddr)); |
511 |
|
|
memset(&res, 0, sizeof(res)); |
512 |
|
|
memset(&msg, 0, sizeof(msg)); |
513 |
|
|
memset(&timeout, 0, sizeof(timeout)); |
514 |
|
|
|
515 |
|
|
args.prog = RUSERSPROG; |
516 |
|
|
args.vers = RUSERSVERS_IDLE; |
517 |
|
|
args.proc = RUSERSPROC_NAMES; |
518 |
|
|
args.xdr_args = xdr_void; |
519 |
|
|
args.args_ptr = NULL; |
520 |
|
|
|
521 |
|
|
stat = rpc_setup(&sock[0], &xdr, &msg[0], &args, unix_auth, buf[0]); |
522 |
|
|
if (stat != RPC_SUCCESS) |
523 |
|
|
goto cleanup; |
524 |
|
|
xid[0] = msg[0].rm_xid; |
525 |
|
|
outlen[0] = xdr_getpos(&xdr); |
526 |
|
|
xdr_destroy(&xdr); |
527 |
|
|
|
528 |
|
|
args.vers = RUSERSVERS_3; |
529 |
|
|
stat = rpc_setup(&sock[1], &xdr, &msg[1], &args, unix_auth, buf[1]); |
530 |
|
|
if (stat != RPC_SUCCESS) |
531 |
|
|
goto cleanup; |
532 |
|
|
xid[1] = msg[1].rm_xid; |
533 |
|
|
outlen[1] = xdr_getpos(&xdr); |
534 |
|
|
xdr_destroy(&xdr); |
535 |
|
|
|
536 |
|
|
maxfd = MAXIMUM(sock[0], sock[1]) + 1; |
537 |
|
|
fds = calloc(howmany(maxfd, NFDBITS), sizeof(fd_mask)); |
538 |
|
|
if (fds == NULL) |
539 |
|
|
err(1, NULL); |
540 |
|
|
|
541 |
|
|
baddr.sin_len = sizeof(struct sockaddr_in); |
542 |
|
|
baddr.sin_family = AF_INET; |
543 |
|
|
baddr.sin_port = htons(PMAPPORT); |
544 |
|
|
baddr.sin_addr.s_addr = htonl(INADDR_ANY); |
545 |
|
|
|
546 |
|
|
res[0].port_ptr = &port[0]; |
547 |
|
|
res[0].xdr_results = xdr_utmpidlearr; |
548 |
|
|
res[0].results_ptr = (caddr_t)&up; |
549 |
|
|
|
550 |
|
|
res[1].port_ptr = &port[1]; |
551 |
|
|
res[1].xdr_results = xdr_utmp_array; |
552 |
|
|
res[1].results_ptr = (caddr_t)&up3; |
553 |
|
|
|
554 |
|
|
(void)signal(SIGALRM, alarmclock); |
555 |
|
|
|
556 |
|
|
/* |
557 |
|
|
* We do 6 runs through the loop. On even runs we send |
558 |
|
|
* a version 3 broadcast. On odd ones we send a version 2 |
559 |
|
|
* broadcast. This should give version 3 replies enough |
560 |
|
|
* of an 'edge' over the old version 2 ones in most cases. |
561 |
|
|
* We select() waiting for replies for 5 seconds in between |
562 |
|
|
* each broadcast. |
563 |
|
|
*/ |
564 |
|
|
for (i = 0; i < 6; i++) { |
565 |
|
|
for (ifa = ifap; ifa; ifa = ifa->ifa_next) { |
566 |
|
|
if (ifa->ifa_addr->sa_family != AF_INET || |
567 |
|
|
!(ifa->ifa_flags & IFF_BROADCAST) || |
568 |
|
|
!(ifa->ifa_flags & IFF_UP) || |
569 |
|
|
ifa->ifa_broadaddr == NULL || |
570 |
|
|
ifa->ifa_broadaddr->sa_family != AF_INET) |
571 |
|
|
continue; |
572 |
|
|
sin = (struct sockaddr_in *)ifa->ifa_broadaddr; |
573 |
|
|
baddr.sin_addr = sin->sin_addr; |
574 |
|
|
|
575 |
|
|
/* use protocol 2 or 3 depending on i (odd or even) */ |
576 |
|
|
if (i & 1) { |
577 |
|
|
if (sendto(sock[0], buf[0], outlen[0], 0, |
578 |
|
|
(struct sockaddr *)&baddr, |
579 |
|
|
sizeof(struct sockaddr)) != outlen[0]) |
580 |
|
|
err(1, "can't send broadcast packet"); |
581 |
|
|
} else { |
582 |
|
|
if (sendto(sock[1], buf[1], outlen[1], 0, |
583 |
|
|
(struct sockaddr *)&baddr, |
584 |
|
|
sizeof(struct sockaddr)) != outlen[1]) |
585 |
|
|
err(1, "can't send broadcast packet"); |
586 |
|
|
} |
587 |
|
|
} |
588 |
|
|
|
589 |
|
|
/* |
590 |
|
|
* We stay in the select loop for ~5 seconds |
591 |
|
|
*/ |
592 |
|
|
timeout.it_value.tv_sec = 5; |
593 |
|
|
timeout.it_value.tv_usec = 0; |
594 |
|
|
while (timerisset(&timeout.it_value)) { |
595 |
|
|
FD_SET(sock[0], fds); |
596 |
|
|
FD_SET(sock[1], fds); |
597 |
|
|
setitimer(ITIMER_REAL, &timeout, NULL); |
598 |
|
|
rval = select(maxfd, fds, NULL, NULL, NULL); |
599 |
|
|
setitimer(ITIMER_REAL, NULL, &timeout); |
600 |
|
|
if (rval == -1) { |
601 |
|
|
if (errno == EINTR) |
602 |
|
|
break; |
603 |
|
|
err(1, "select"); /* shouldn't happen */ |
604 |
|
|
} |
605 |
|
|
if (FD_ISSET(sock[1], fds)) { |
606 |
|
|
stat = get_reply(sock[1], (in_port_t)port[1], |
607 |
|
|
xid[1], &msg[1], &res[1], rusers_reply_3); |
608 |
|
|
if (stat != RPC_SUCCESS) |
609 |
|
|
goto cleanup; |
610 |
|
|
} |
611 |
|
|
if (FD_ISSET(sock[0], fds)) { |
612 |
|
|
stat = get_reply(sock[0], (in_port_t)port[0], |
613 |
|
|
xid[0], &msg[0], &res[0], rusers_reply); |
614 |
|
|
if (stat != RPC_SUCCESS) |
615 |
|
|
goto cleanup; |
616 |
|
|
} |
617 |
|
|
} |
618 |
|
|
} |
619 |
|
|
cleanup: |
620 |
|
|
if (ifap != NULL) |
621 |
|
|
freeifaddrs(ifap); |
622 |
|
|
free(fds); |
623 |
|
|
if (sock[0] >= 0) |
624 |
|
|
(void)close(sock[0]); |
625 |
|
|
if (sock[1] >= 0) |
626 |
|
|
(void)close(sock[1]); |
627 |
|
|
AUTH_DESTROY(unix_auth); |
628 |
|
|
if (stat != RPC_SUCCESS) { |
629 |
|
|
clnt_perrno(stat); |
630 |
|
|
exit(1); |
631 |
|
|
} |
632 |
|
|
} |
633 |
|
|
|
634 |
|
|
void |
635 |
|
|
print_entry(struct host_info *entry, int longfmt) |
636 |
|
|
{ |
637 |
|
|
char date[32], idle_time[64]; |
638 |
|
|
char remote[RUSERS_MAXHOSTLEN + 3]; |
639 |
|
|
struct rusers_utmp *ut; |
640 |
|
|
int i, len; |
641 |
|
|
|
642 |
|
|
if (!longfmt) |
643 |
|
|
printf("%-*.*s ", HOST_WIDTH, HOST_WIDTH, entry->host); |
644 |
|
|
|
645 |
|
|
for (i = 0, ut = entry->users; i < entry->count; i++, ut++) { |
646 |
|
|
if (longfmt) { |
647 |
|
|
time_t tim = ut->ut_time; |
648 |
|
|
strftime(date, sizeof(date), "%h %d %R", |
649 |
|
|
localtime(&tim)); |
650 |
|
|
date[sizeof(date) - 1] = '\0'; |
651 |
|
|
fmt_idle(ut->ut_idle, idle_time, sizeof(idle_time)); |
652 |
|
|
len = termwidth - |
653 |
|
|
(MAXIMUM(strlen(ut->ut_user), NAME_WIDTH) + 1 + |
654 |
|
|
HOST_WIDTH + 1 + LINE_WIDTH + 1 + strlen(date) + |
655 |
|
|
1 + MAXIMUM(8, strlen(idle_time)) + 1 + 2); |
656 |
|
|
if (len > 0 && ut->ut_host[0] != '\0') |
657 |
|
|
snprintf(remote, sizeof(remote), "(%.*s)", |
658 |
|
|
MINIMUM(len, RUSERS_MAXHOSTLEN), ut->ut_host); |
659 |
|
|
else |
660 |
|
|
remote[0] = '\0'; |
661 |
|
|
len = HOST_WIDTH - MINIMUM(HOST_WIDTH, strlen(entry->host)) + |
662 |
|
|
LINE_WIDTH - MINIMUM(LINE_WIDTH, strlen(ut->ut_line)); |
663 |
|
|
printf("%-*s %.*s:%.*s%-*s %-12s %8s %s\n", |
664 |
|
|
NAME_WIDTH, ut->ut_user, HOST_WIDTH, entry->host, |
665 |
|
|
LINE_WIDTH, ut->ut_line, len, "", date, |
666 |
|
|
idle_time, remote); |
667 |
|
|
} else { |
668 |
|
|
fputs(ut->ut_user, stdout); |
669 |
|
|
putchar(' '); |
670 |
|
|
} |
671 |
|
|
} |
672 |
|
|
if (!longfmt) |
673 |
|
|
putchar('\n'); |
674 |
|
|
} |
675 |
|
|
|
676 |
|
|
void |
677 |
|
|
expandhosts(void) |
678 |
|
|
{ |
679 |
|
|
struct host_info *new_hostinfo, *entry; |
680 |
|
|
u_int count; |
681 |
|
|
int i, j; |
682 |
|
|
|
683 |
|
|
for (i = 0, count = 0; i < nentries; i++) |
684 |
|
|
count += hostinfo[i].count; |
685 |
|
|
|
686 |
|
|
new_hostinfo = calloc(sizeof(*entry), count); |
687 |
|
|
if (new_hostinfo == NULL) |
688 |
|
|
err(1, NULL); |
689 |
|
|
for (i = 0, entry = new_hostinfo; i < nentries; i++) { |
690 |
|
|
for (j = 0; j < hostinfo[i].count; j++) { |
691 |
|
|
memcpy(entry, &hostinfo[i], sizeof(*entry)); |
692 |
|
|
entry->users = &hostinfo[i].users[j]; |
693 |
|
|
entry->idle = entry->users->ut_idle; |
694 |
|
|
entry->count = 1; |
695 |
|
|
entry++; |
696 |
|
|
} |
697 |
|
|
} |
698 |
|
|
free(hostinfo); |
699 |
|
|
hostinfo = new_hostinfo; |
700 |
|
|
nentries = maxentries = count; |
701 |
|
|
} |
702 |
|
|
|
703 |
|
|
void |
704 |
|
|
sorthosts(void) |
705 |
|
|
{ |
706 |
|
|
int i; |
707 |
|
|
int (*compar)(const void *, const void *); |
708 |
|
|
|
709 |
|
|
if (iflag && lflag) |
710 |
|
|
expandhosts(); |
711 |
|
|
|
712 |
|
|
if (hflag) |
713 |
|
|
compar = hcompare; |
714 |
|
|
else if (iflag) |
715 |
|
|
compar = icompare; |
716 |
|
|
else |
717 |
|
|
compar = ucompare; |
718 |
|
|
qsort(hostinfo, nentries, sizeof(*hostinfo), compar); |
719 |
|
|
|
720 |
|
|
for (i = 0; i < nentries; i++) |
721 |
|
|
print_entry(&hostinfo[i], lflag && hostinfo[i].count); |
722 |
|
|
} |
723 |
|
|
|
724 |
|
|
int |
725 |
|
|
hcompare(const void *aa, const void *bb) |
726 |
|
|
{ |
727 |
|
|
const struct host_info *a = (struct host_info *)aa; |
728 |
|
|
const struct host_info *b = (struct host_info *)bb; |
729 |
|
|
int rval; |
730 |
|
|
|
731 |
|
|
if ((rval = strcasecmp(a->host, b->host)) != 0) |
732 |
|
|
return(rval); |
733 |
|
|
|
734 |
|
|
if (a->idle < b->idle) |
735 |
|
|
return(-1); |
736 |
|
|
else if (a->idle > b->idle) |
737 |
|
|
return(1); |
738 |
|
|
|
739 |
|
|
if (a->count > b->count) |
740 |
|
|
return(-1); |
741 |
|
|
else if (a->count < b->count) |
742 |
|
|
return(1); |
743 |
|
|
|
744 |
|
|
return(0); |
745 |
|
|
} |
746 |
|
|
|
747 |
|
|
int |
748 |
|
|
icompare(const void *aa, const void *bb) |
749 |
|
|
{ |
750 |
|
|
const struct host_info *a = (struct host_info *)aa; |
751 |
|
|
const struct host_info *b = (struct host_info *)bb; |
752 |
|
|
|
753 |
|
|
if (a->idle < b->idle) |
754 |
|
|
return(-1); |
755 |
|
|
else if (a->idle > b->idle) |
756 |
|
|
return(1); |
757 |
|
|
|
758 |
|
|
if (a->count > b->count) |
759 |
|
|
return(-1); |
760 |
|
|
else if (a->count < b->count) |
761 |
|
|
return(1); |
762 |
|
|
|
763 |
|
|
return(strcasecmp(a->host, b->host)); |
764 |
|
|
} |
765 |
|
|
|
766 |
|
|
int |
767 |
|
|
ucompare(const void *aa, const void *bb) |
768 |
|
|
{ |
769 |
|
|
const struct host_info *a = (struct host_info *)aa; |
770 |
|
|
const struct host_info *b = (struct host_info *)bb; |
771 |
|
|
|
772 |
|
|
if (a->count > b->count) |
773 |
|
|
return(-1); |
774 |
|
|
else if (a->count < b->count) |
775 |
|
|
return(1); |
776 |
|
|
|
777 |
|
|
if (a->idle < b->idle) |
778 |
|
|
return(-1); |
779 |
|
|
else if (a->idle > b->idle) |
780 |
|
|
return(1); |
781 |
|
|
|
782 |
|
|
return(strcasecmp(a->host, b->host)); |
783 |
|
|
} |
784 |
|
|
|
785 |
|
|
void |
786 |
|
|
alarmclock(int signo) |
787 |
|
|
{ |
788 |
|
|
|
789 |
|
|
; /* just interrupt */ |
790 |
|
|
} |
791 |
|
|
|
792 |
|
|
char * |
793 |
|
|
estrndup(const char *src, size_t len) |
794 |
|
|
{ |
795 |
|
|
char *dst, *end; |
796 |
|
|
|
797 |
|
|
if ((end = memchr(src, '\0', len)) != NULL) |
798 |
|
|
len = end - src; |
799 |
|
|
|
800 |
|
|
if ((dst = malloc(len + 1)) == NULL) |
801 |
|
|
err(1, NULL); |
802 |
|
|
memcpy(dst, src, len); |
803 |
|
|
dst[len] = '\0'; |
804 |
|
|
|
805 |
|
|
return(dst); |
806 |
|
|
} |
807 |
|
|
|
808 |
|
|
void |
809 |
|
|
usage(void) |
810 |
|
|
{ |
811 |
|
|
|
812 |
|
|
fprintf(stderr, "usage: %s [-al] [-h | -i | -u] [hosts ...]\n", |
813 |
|
|
__progname); |
814 |
|
|
exit(1); |
815 |
|
|
} |