1 /*        $NetBSD: rup.c,v 1.29 2016/09/05 00:40:29 sevan 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. All advertising materials mentioning features or use of this software
16  *    must display the following acknowledgement:
17  *        This product includes software developed by the University of
18  *        California, Berkeley and its contributors.
19  * 4. Neither the name of the University nor the names of its contributors
20  *    may be used to endorse or promote products derived from this software
21  *    without specific prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33  * SUCH DAMAGE.
34  */
35 
36 #include <sys/cdefs.h>
37 #ifndef lint
38 __RCSID("$NetBSD: rup.c,v 1.29 2016/09/05 00:40:29 sevan Exp $");
39 #endif /* not lint */
40 
41 #include <sys/types.h>
42 #include <sys/socket.h>
43 #include <netinet/in.h>
44 #include <arpa/inet.h>
45 
46 #include <err.h>
47 #include <netdb.h>
48 #include <rpc/rpc.h>
49 #include <stdio.h>
50 #include <stdlib.h>
51 #include <string.h>
52 #include <time.h>
53 #include <unistd.h>
54 
55 #undef FSHIFT                           /* Use protocol's shift and scale values */
56 #undef FSCALE
57 #include <rpcsvc/rstat.h>
58 
59 #define HOST_WIDTH 24
60 
61 static int printtime;                   /* print the remote host(s)'s time */
62 
63 static struct host_list {
64           struct host_list *next;
65           int family;
66           union {
67                     struct in6_addr _addr6;
68                     struct in_addr _addr4;
69           } addr;
70 } *hosts;
71 
72 #define addr6 addr._addr6
73 #define addr4 addr._addr4
74 
75 static int search_host(struct sockaddr *);
76 static void remember_host(struct sockaddr *);
77 
78 static int
search_host(struct sockaddr * sa)79 search_host(struct sockaddr *sa)
80 {
81           struct host_list *hp;
82 
83           if (!hosts)
84                     return 0;
85 
86           for (hp = hosts; hp != NULL; hp = hp->next) {
87                     switch (hp->family) {
88                     case AF_INET6:
89                               if (!memcmp(&hp->addr6,
90                                   &((struct sockaddr_in6 *)(void *)sa)->sin6_addr,
91                                   sizeof (struct in6_addr)))
92                                         return 1;
93                               break;
94                     case AF_INET:
95                               if (!memcmp(&hp->addr4,
96                                   &((struct sockaddr_in *)(void *)sa)->sin_addr,
97                                   sizeof (struct in_addr)))
98                                         return 1;
99                               break;
100                     default:
101                               break;
102                     }
103           }
104           return 0;
105 }
106 
107 static void
remember_host(struct sockaddr * sa)108 remember_host(struct sockaddr *sa)
109 {
110           struct host_list *hp;
111 
112           if ((hp = malloc(sizeof(struct host_list))) == NULL) {
113                     err(1, "malloc");
114                     /* NOTREACHED */
115           }
116           hp->family = sa->sa_family;
117           hp->next = hosts;
118           switch (sa->sa_family) {
119           case AF_INET6:
120                     (void)memcpy(&hp->addr6,
121                         &((struct sockaddr_in6 *)(void *)sa)->sin6_addr,
122                         sizeof (struct in6_addr));
123                     break;
124           case AF_INET:
125                     (void)memcpy(&hp->addr4,
126                         &((struct sockaddr_in *)(void *)sa)->sin_addr,
127                         sizeof (struct in_addr));
128                     break;
129           default:
130                     errx(1, "unknown address family");
131                     /* NOTREACHED */
132           }
133           hosts = hp;
134 }
135 
136 struct rup_data {
137           const char *host;
138           struct statstime statstime;
139 };
140 static struct rup_data *rup_data;
141 static size_t rup_data_idx = 0;
142 static size_t rup_data_max = 0;
143 
144 enum sort_type {
145           SORT_NONE,
146           SORT_HOST,
147           SORT_LDAV,
148           SORT_UPTIME
149 };
150 static enum sort_type sort_type;
151 
152 static int compare(struct rup_data *, struct rup_data *);
153 static void remember_rup_data(const char *, struct statstime *);
154 static int rstat_reply(char *, struct netbuf *, struct netconfig *);
155 static void print_rup_data(const char *, statstime *);
156 static int onehost(char *);
157 static void allhosts(void);
158 static void usage(void) __dead;
159 
160 int
compare(struct rup_data * d1,struct rup_data * d2)161 compare(struct rup_data *d1, struct rup_data *d2)
162 {
163           switch(sort_type) {
164           case SORT_HOST:
165                     return strcmp(d1->host, d2->host);
166           case SORT_LDAV:
167                     return d1->statstime.avenrun[0]
168                         - d2->statstime.avenrun[0];
169           case SORT_UPTIME:
170                     return d1->statstime.boottime.tv_sec
171                         - d2->statstime.boottime.tv_sec;
172           default:
173                     /* something's really wrong here */
174                     abort();
175                     /*NOTREACHED*/
176           }
177 }
178 
179 static void
remember_rup_data(const char * host,struct statstime * st)180 remember_rup_data(const char *host, struct statstime *st)
181 {
182           struct rup_data *n;
183 
184         if (rup_data_idx >= rup_data_max) {
185                 n = realloc(rup_data,
186                     (rup_data_max + 16) * sizeof(struct rup_data));
187                 if (n == NULL) {
188                         err (1, "realloc");
189                               /* NOTREACHED */
190                 }
191                     rup_data = n;
192                 rup_data_max += 16;
193         }
194 
195           rup_data[rup_data_idx].host = strdup(host);
196           rup_data[rup_data_idx].statstime = *st;
197           rup_data_idx++;
198 }
199 
200 
201 static int
202 /*ARGSUSED*/
rstat_reply(char * replyp,struct netbuf * raddrp,struct netconfig * nconf)203 rstat_reply(char *replyp, struct netbuf *raddrp, struct netconfig *nconf)
204 {
205           char host[NI_MAXHOST];
206           statstime *host_stat = (statstime *)(void *)replyp;
207           struct sockaddr *sa = raddrp->buf;
208 
209           if (!search_host(sa)) {
210                     if (getnameinfo(sa, (socklen_t)sa->sa_len, host, sizeof host,
211                         NULL, 0, 0))
212                               return 0;
213 
214                     remember_host(sa);
215 
216                     if (sort_type != SORT_NONE) {
217                               remember_rup_data(host, host_stat);
218                     } else {
219                               print_rup_data(host, host_stat);
220                     }
221           }
222 
223           return 0;
224 }
225 
226 
227 static void
print_rup_data(const char * host,statstime * host_stat)228 print_rup_data(const char *host, statstime *host_stat)
229 {
230           struct tm *tmp_time;
231           struct tm host_time;
232           unsigned ups = 0, upm = 0, uph = 0, upd = 0;
233           time_t now;
234 
235           char days_buf[16];
236           char hours_buf[16];
237 
238           if (printtime)
239                     (void)printf("%-*.*s", HOST_WIDTH-4, HOST_WIDTH-4, host);
240           else
241                     (void)printf("%-*.*s", HOST_WIDTH, HOST_WIDTH, host);
242 
243           now = host_stat->curtime.tv_sec;
244           tmp_time = localtime(&now);
245           host_time = *tmp_time;
246 
247           host_stat->curtime.tv_sec -= host_stat->boottime.tv_sec;
248 
249           ups=host_stat->curtime.tv_sec;
250           upd=ups / (3600 * 24);
251           ups-=upd * 3600 * 24;
252           uph=ups / 3600;
253           ups-=uph * 3600;
254           upm=ups / 60;
255 
256           if (upd != 0)
257                     (void)snprintf(days_buf, sizeof(days_buf), "%3u day%s, ", upd,
258                         (upd > 1) ? "s" : "");
259           else
260                     days_buf[0] = '\0';
261 
262           if (uph != 0)
263                     (void)snprintf(hours_buf, sizeof(hours_buf), "%2u:%02u, ",
264                         uph, upm);
265           else
266                     if (upm != 0)
267                               (void)snprintf(hours_buf, sizeof(hours_buf),
268                                   "%2u min%s ", upm, (upm == 1) ? ", " : "s,");
269                     else if (ups < 60)
270                               (void)snprintf(hours_buf, sizeof(hours_buf),
271                                   "%2u secs ", ups);
272                     else
273                               hours_buf[0] = '\0';
274           if (printtime)
275                     (void)printf(" %2d:%02d%cm",
276                         (host_time.tm_hour % 12) ? (host_time.tm_hour % 12) : 12,
277                         host_time.tm_min, (host_time.tm_hour >= 12) ? 'p' : 'a');
278 
279           (void)printf(" up %9.9s%9.9s load average: %.2f %.2f %.2f\n",
280               days_buf, hours_buf, (double)host_stat->avenrun[0]/FSCALE,
281               (double)host_stat->avenrun[1]/FSCALE,
282               (double)host_stat->avenrun[2]/FSCALE);
283 }
284 
285 
286 static int
onehost(char * host)287 onehost(char *host)
288 {
289           CLIENT *rstat_clnt;
290           statstime host_stat;
291           static struct timeval timeout = {25, 0};
292 
293           rstat_clnt = clnt_create(host, RSTATPROG, RSTATVERS_TIME, "udp");
294           if (rstat_clnt == NULL) {
295                     warnx("%s", clnt_spcreateerror(host));
296                     return 1;
297           }
298 
299           (void)memset(&host_stat, 0, sizeof(host_stat));
300           if (clnt_call(rstat_clnt, RSTATPROC_STATS, xdr_void, NULL,
301               xdr_statstime, &host_stat, timeout) != RPC_SUCCESS) {
302                     warnx("%s",  clnt_sperror(rstat_clnt, host));
303                     clnt_destroy(rstat_clnt);
304                     return 1;
305           }
306 
307           print_rup_data(host, &host_stat);
308           clnt_destroy(rstat_clnt);
309           return 0;
310 }
311 
312 static void
allhosts(void)313 allhosts(void)
314 {
315           statstime host_stat;
316           enum clnt_stat clnt_stat;
317           size_t i;
318 
319           if (sort_type != SORT_NONE) {
320                     (void)printf("collecting responses...");
321                     (void)fflush(stdout);
322           }
323 
324           clnt_stat = rpc_broadcast(RSTATPROG, RSTATVERS_TIME, RSTATPROC_STATS,
325               (xdrproc_t)xdr_void, NULL, (xdrproc_t)xdr_statstime, (caddr_t)(void *)&host_stat,
326               (resultproc_t)rstat_reply, "udp");
327           if (clnt_stat != RPC_SUCCESS && clnt_stat != RPC_TIMEDOUT)
328                     errx(1, "%s", clnt_sperrno(clnt_stat));
329 
330           if (sort_type != SORT_NONE) {
331                     (void)putchar('\n');
332                     qsort(rup_data, rup_data_idx, sizeof(struct rup_data),
333                           (int (*)(const void*, const void*))compare);
334 
335                     for (i = 0; i < rup_data_idx; i++) {
336                               print_rup_data(rup_data[i].host, &rup_data[i].statstime);
337                     }
338           }
339 }
340 
341 int
main(int argc,char * argv[])342 main(int argc, char *argv[])
343 {
344           int ch, retval;
345 
346           setprogname(*argv);
347           sort_type = SORT_NONE;
348           retval = 0;
349           while ((ch = getopt(argc, argv, "dhlt")) != -1)
350                     switch (ch) {
351                     case 'd':
352                               printtime = 1;
353                               break;
354                     case 'h':
355                               sort_type = SORT_HOST;
356                               break;
357                     case 'l':
358                               sort_type = SORT_LDAV;
359                               break;
360                     case 't':
361                               sort_type = SORT_UPTIME;
362                               break;
363                     default:
364                               usage();
365                               /*NOTREACHED*/
366                     }
367 
368           (void)setlinebuf(stdout);
369 
370           if (argc == optind)
371                     allhosts();
372           else {
373                     for (; optind < argc; optind++)
374                               retval += onehost(argv[optind]);
375           }
376 
377           return retval ? EXIT_FAILURE : EXIT_SUCCESS;
378 }
379 
380 void
usage(void)381 usage(void)
382 {
383           (void)fprintf(stderr, "Usage: %s [-dhlt] [hosts ...]\n",
384               getprogname());
385           exit(EXIT_SUCCESS);
386 }
387