1 /* $NetBSD: rusers.c,v 1.26 2019/02/03 10:48:47 mrg Exp $ */ 2 3 /*- 4 * Copyright (c) 1993 John Brezak 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. The name of the author may not be used to endorse or promote products 16 * derived from this software without specific prior written permission. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR `AS IS'' AND ANY EXPRESS OR 19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 22 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 23 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 26 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 27 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 * POSSIBILITY OF SUCH DAMAGE. 29 */ 30 31 #include <sys/cdefs.h> 32 #ifndef lint 33 __RCSID("$NetBSD: rusers.c,v 1.26 2019/02/03 10:48:47 mrg Exp $"); 34 #endif /* not lint */ 35 36 #include <sys/types.h> 37 #include <sys/socket.h> 38 39 #include <rpc/rpc.h> 40 #include <arpa/inet.h> 41 42 #include <err.h> 43 #include <netdb.h> 44 #include <stdio.h> 45 #include <stdlib.h> 46 #include <string.h> 47 #include <time.h> 48 #include <unistd.h> 49 #include <utmp.h> 50 51 /* 52 * For now we only try version 2 of the protocol. The current 53 * version is 3 (rusers.h), but only Solaris and NetBSD seem 54 * to support it currently. 55 */ 56 #include <rpcsvc/rnusers.h> /* Old version */ 57 58 59 #define MAX_INT 0x7fffffff 60 61 static struct timeval timeout = { 25, 0 }; 62 static int longopt; 63 static int allopt; 64 65 static void allhosts(void); 66 static void onehost(char *); 67 static void remember_host(struct sockaddr *); 68 static int rusers_reply(char *, struct netbuf *, struct netconfig *); 69 static int search_host(struct sockaddr *); 70 __dead static void usage(void); 71 72 static struct host_list { 73 struct host_list *next; 74 int family; 75 union { 76 struct in6_addr _addr6; 77 struct in_addr _addr4; 78 } addr; 79 } *hosts; 80 81 #define addr6 addr._addr6 82 #define addr4 addr._addr4 83 84 static int 85 search_host(struct sockaddr *sa) 86 { 87 struct host_list *hp; 88 89 if (!hosts) 90 return(0); 91 92 for (hp = hosts; hp != NULL; hp = hp->next) { 93 switch (hp->family) { 94 case AF_INET6: 95 if (!memcmp(&hp->addr6, 96 &((struct sockaddr_in6 *)sa)->sin6_addr, 97 sizeof (struct in6_addr))) 98 return 1; 99 break; 100 case AF_INET: 101 if (!memcmp(&hp->addr4, 102 &((struct sockaddr_in *)sa)->sin_addr, 103 sizeof (struct in_addr))) 104 return 1; 105 break; 106 default: 107 break; 108 } 109 } 110 return(0); 111 } 112 113 static void 114 remember_host(struct sockaddr *sa) 115 { 116 struct host_list *hp; 117 118 if (!(hp = (struct host_list *)malloc(sizeof(struct host_list)))) { 119 err(1, "malloc"); 120 /* NOTREACHED */ 121 } 122 hp->family = sa->sa_family; 123 hp->next = hosts; 124 switch (sa->sa_family) { 125 case AF_INET6: 126 memcpy(&hp->addr6, &((struct sockaddr_in6 *)sa)->sin6_addr, 127 sizeof (struct in6_addr)); 128 break; 129 case AF_INET: 130 memcpy(&hp->addr4, &((struct sockaddr_in *)sa)->sin_addr, 131 sizeof (struct in_addr)); 132 break; 133 default: 134 err(1, "unknown address family"); 135 /* NOTREACHED */ 136 } 137 hosts = hp; 138 } 139 140 static int 141 rusers_reply(char *replyp, struct netbuf *raddrp, struct netconfig *nconf) 142 { 143 char host[NI_MAXHOST]; 144 int x; 145 struct utmpidlearr *up = (struct utmpidlearr *)replyp; 146 struct sockaddr *sa = raddrp->buf; 147 148 if (search_host(sa)) 149 return(0); 150 151 if (!allopt && !up->uia_cnt) 152 return(0); 153 154 if (getnameinfo(sa, sa->sa_len, host, sizeof host, NULL, 0, 0)) 155 return 0; 156 157 #define HOSTWID (int)sizeof(up->uia_arr[0]->ui_utmp.ut_host) 158 #define LINEWID (int)sizeof(up->uia_arr[0]->ui_utmp.ut_line) 159 #define NAMEWID (int)sizeof(up->uia_arr[0]->ui_utmp.ut_name) 160 161 if (!longopt) 162 printf("%-*.*s ", HOSTWID, HOSTWID, host); 163 164 for (x = 0; x < up->uia_cnt; x++) { 165 unsigned int minutes; 166 char date[26], idle[11]; 167 char remote[HOSTWID + 3]; /* "(" host ")" \0 */ 168 char local[HOSTWID + LINEWID + 2]; /* host ":" line \0 */ 169 time_t uttime; 170 171 if (!longopt) { 172 printf("%.*s ", NAMEWID, 173 up->uia_arr[x]->ui_utmp.ut_name); 174 continue; 175 } 176 177 snprintf(local, sizeof(local), "%.*s:%s", 178 HOSTWID, host, 179 up->uia_arr[x]->ui_utmp.ut_line); 180 181 uttime = up->uia_arr[x]->ui_utmp.ut_time; 182 snprintf(date, sizeof(date), "%s", 183 &(ctime(&uttime))[4]); 184 185 minutes = up->uia_arr[x]->ui_idle; 186 if (minutes == MAX_INT) 187 strcpy(idle, "??"); 188 else if (minutes == 0) 189 strcpy(idle, ""); 190 else { 191 unsigned int days, hours; 192 193 days = minutes / (24 * 60); 194 minutes %= (24 * 60); 195 hours = minutes / 60; 196 minutes %= 60; 197 198 if (days > 0) 199 snprintf(idle, sizeof(idle), "%d d ", days); 200 else if (hours > 0) 201 snprintf(idle, sizeof(idle), "%2d:%02d", 202 hours, minutes); 203 else 204 snprintf(idle, sizeof(idle), ":%02d", minutes); 205 } 206 207 if (up->uia_arr[x]->ui_utmp.ut_host[0] != '\0') 208 snprintf(remote, sizeof(remote), "(%.*s)", 209 HOSTWID, up->uia_arr[x]->ui_utmp.ut_host); 210 else 211 remote[0] = '\0'; 212 213 printf("%-*.*s %-*.*s %-12.12s %8.8s %s\n", 214 NAMEWID, NAMEWID, up->uia_arr[x]->ui_utmp.ut_name, 215 HOSTWID+LINEWID+1, HOSTWID+LINEWID+1, local, 216 date, idle, remote); 217 } 218 if (!longopt) 219 putchar('\n'); 220 221 remember_host(sa); 222 return(0); 223 } 224 225 static void 226 onehost(char *host) 227 { 228 struct utmpidlearr up; 229 CLIENT *rusers_clnt; 230 enum clnt_stat clnt_stat; 231 struct netbuf nb; 232 struct addrinfo *ai; 233 int ecode; 234 235 rusers_clnt = clnt_create(host, RUSERSPROG, RUSERSVERS_IDLE, "udp"); 236 if (rusers_clnt == NULL) { 237 clnt_pcreateerror(getprogname()); 238 exit(1); 239 } 240 241 ecode = getaddrinfo(host, NULL, NULL, &ai); 242 if (ecode != 0) 243 err(1, "%s", gai_strerror(ecode)); 244 245 memset((char *)&up, 0, sizeof(up)); 246 clnt_stat = clnt_call(rusers_clnt, RUSERSPROC_NAMES, xdr_void, NULL, 247 xdr_utmpidlearr, &up, timeout); 248 if (clnt_stat != RPC_SUCCESS) 249 errx(1, "%s", clnt_sperrno(clnt_stat)); 250 nb.buf = ai->ai_addr; 251 nb.len = nb.maxlen = ai->ai_addrlen; 252 rusers_reply((char *)&up, &nb, NULL); 253 freeaddrinfo(ai); 254 } 255 256 static void 257 allhosts(void) 258 { 259 struct utmpidlearr up; 260 enum clnt_stat clnt_stat; 261 262 memset((char *)&up, 0, sizeof(up)); 263 clnt_stat = rpc_broadcast(RUSERSPROG, RUSERSVERS_IDLE, 264 RUSERSPROC_NAMES, (xdrproc_t)xdr_void, NULL, 265 (xdrproc_t)xdr_utmpidlearr, (char *)&up, 266 (resultproc_t)rusers_reply, "udp"); 267 if (clnt_stat != RPC_SUCCESS && clnt_stat != RPC_TIMEDOUT) 268 errx(1, "%s", clnt_sperrno(clnt_stat)); 269 } 270 271 static void 272 usage(void) 273 { 274 fprintf(stderr, "usage: %s [-la] [hosts ...]\n", getprogname()); 275 exit(1); 276 } 277 278 int 279 main(int argc, char *argv[]) 280 { 281 int ch; 282 283 while ((ch = getopt(argc, argv, "al")) != -1) 284 switch (ch) { 285 case 'a': 286 allopt++; 287 break; 288 case 'l': 289 longopt++; 290 break; 291 default: 292 usage(); 293 /*NOTREACHED*/ 294 } 295 296 setlinebuf(stdout); 297 if (argc == optind) 298 allhosts(); 299 else { 300 for (; optind < argc; optind++) 301 (void) onehost(argv[optind]); 302 } 303 exit(0); 304 } 305