19b50d902SRodney W. Grimes /*
28a16b7a1SPedro F. Giffuni * SPDX-License-Identifier: BSD-3-Clause
38a16b7a1SPedro F. Giffuni *
49b50d902SRodney W. Grimes * Copyright (c) 1983, 1993, 1994
59b50d902SRodney W. Grimes * The Regents of the University of California. All rights reserved.
69b50d902SRodney W. Grimes *
79b50d902SRodney W. Grimes * Redistribution and use in source and binary forms, with or without
89b50d902SRodney W. Grimes * modification, are permitted provided that the following conditions
99b50d902SRodney W. Grimes * are met:
109b50d902SRodney W. Grimes * 1. Redistributions of source code must retain the above copyright
119b50d902SRodney W. Grimes * notice, this list of conditions and the following disclaimer.
129b50d902SRodney W. Grimes * 2. Redistributions in binary form must reproduce the above copyright
139b50d902SRodney W. Grimes * notice, this list of conditions and the following disclaimer in the
149b50d902SRodney W. Grimes * documentation and/or other materials provided with the distribution.
15fbbd9655SWarner Losh * 3. Neither the name of the University nor the names of its contributors
169b50d902SRodney W. Grimes * may be used to endorse or promote products derived from this software
179b50d902SRodney W. Grimes * without specific prior written permission.
189b50d902SRodney W. Grimes *
199b50d902SRodney W. Grimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
209b50d902SRodney W. Grimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
219b50d902SRodney W. Grimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
229b50d902SRodney W. Grimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
239b50d902SRodney W. Grimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
249b50d902SRodney W. Grimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
259b50d902SRodney W. Grimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
269b50d902SRodney W. Grimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
279b50d902SRodney W. Grimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
289b50d902SRodney W. Grimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
299b50d902SRodney W. Grimes * SUCH DAMAGE.
309b50d902SRodney W. Grimes */
319b50d902SRodney W. Grimes
329b50d902SRodney W. Grimes #include <sys/param.h>
339b50d902SRodney W. Grimes
349b50d902SRodney W. Grimes #include <protocols/rwhod.h>
359b50d902SRodney W. Grimes
369b50d902SRodney W. Grimes #include <dirent.h>
379b50d902SRodney W. Grimes #include <err.h>
38821df508SXin LI #include <errno.h>
399b50d902SRodney W. Grimes #include <fcntl.h>
409b50d902SRodney W. Grimes #include <stdio.h>
419b50d902SRodney W. Grimes #include <stdlib.h>
429b50d902SRodney W. Grimes #include <string.h>
439b50d902SRodney W. Grimes #include <time.h>
449b50d902SRodney W. Grimes #include <unistd.h>
459b50d902SRodney W. Grimes
4659880300SEd Schouten static struct hs {
4773b018e3SEd Schouten struct whod hs_wd;
489b50d902SRodney W. Grimes int hs_nusers;
499b50d902SRodney W. Grimes } *hs;
5002dfd2b2SSuleiman Souhlal #define LEFTEARTH(h) (now - (h) > 4*24*60*60)
5173b018e3SEd Schouten #define ISDOWN(h) (now - (h)->hs_wd.wd_recvtime > 11 * 60)
5273b018e3SEd Schouten #define WHDRSIZE __offsetof(struct whod, wd_we)
539b50d902SRodney W. Grimes
5459880300SEd Schouten static size_t nhosts;
5559880300SEd Schouten static time_t now;
5659880300SEd Schouten static int rflg = 1;
5759880300SEd Schouten static DIR *dirp;
589b50d902SRodney W. Grimes
5959880300SEd Schouten static int hscmp(const void *, const void *);
6059880300SEd Schouten static char *interval(time_t, const char *);
61e245deadSBruce Evans static int iwidth(int);
6259880300SEd Schouten static int lcmp(const void *, const void *);
6359880300SEd Schouten static void ruptime(const char *, int, int (*)(const void *, const void *));
6459880300SEd Schouten static int tcmp(const void *, const void *);
6559880300SEd Schouten static int ucmp(const void *, const void *);
6659880300SEd Schouten static void usage(void);
679b50d902SRodney W. Grimes
689b50d902SRodney W. Grimes int
main(int argc,char * argv[])69a827060aSMark Murray main(int argc, char *argv[])
709b50d902SRodney W. Grimes {
71d3cb5dedSWarner Losh int (*cmp)(const void *, const void *);
72e7cb5ef7SJuli Mallett int aflg, ch;
739b50d902SRodney W. Grimes
749b50d902SRodney W. Grimes aflg = 0;
759b50d902SRodney W. Grimes cmp = hscmp;
761c8af878SWarner Losh while ((ch = getopt(argc, argv, "alrut")) != -1)
779b50d902SRodney W. Grimes switch (ch) {
789b50d902SRodney W. Grimes case 'a':
799b50d902SRodney W. Grimes aflg = 1;
809b50d902SRodney W. Grimes break;
819b50d902SRodney W. Grimes case 'l':
829b50d902SRodney W. Grimes cmp = lcmp;
839b50d902SRodney W. Grimes break;
849b50d902SRodney W. Grimes case 'r':
859b50d902SRodney W. Grimes rflg = -1;
869b50d902SRodney W. Grimes break;
879b50d902SRodney W. Grimes case 't':
889b50d902SRodney W. Grimes cmp = tcmp;
899b50d902SRodney W. Grimes break;
909b50d902SRodney W. Grimes case 'u':
919b50d902SRodney W. Grimes cmp = ucmp;
929b50d902SRodney W. Grimes break;
939b50d902SRodney W. Grimes default:
949b50d902SRodney W. Grimes usage();
959b50d902SRodney W. Grimes }
969b50d902SRodney W. Grimes argc -= optind;
979b50d902SRodney W. Grimes argv += optind;
989b50d902SRodney W. Grimes
999b50d902SRodney W. Grimes if (chdir(_PATH_RWHODIR) || (dirp = opendir(".")) == NULL)
1009b50d902SRodney W. Grimes err(1, "%s", _PATH_RWHODIR);
1019b50d902SRodney W. Grimes
102e7cb5ef7SJuli Mallett ruptime(*argv, aflg, cmp);
103e7cb5ef7SJuli Mallett while (*argv++ != NULL) {
104e7cb5ef7SJuli Mallett if (*argv == NULL)
105e7cb5ef7SJuli Mallett break;
106e7cb5ef7SJuli Mallett ruptime(*argv, aflg, cmp);
1079b50d902SRodney W. Grimes }
1089b50d902SRodney W. Grimes exit(0);
1099b50d902SRodney W. Grimes }
1109b50d902SRodney W. Grimes
11159880300SEd Schouten static char *
interval(time_t tval,const char * updown)112a827060aSMark Murray interval(time_t tval, const char *updown)
1139b50d902SRodney W. Grimes {
1149b50d902SRodney W. Grimes static char resbuf[32];
1159b50d902SRodney W. Grimes int days, hours, minutes;
1169b50d902SRodney W. Grimes
117fdb7c7dcSJoerg Wunsch if (tval < 0) {
1189b50d902SRodney W. Grimes (void)snprintf(resbuf, sizeof(resbuf), "%s ??:??", updown);
1199b50d902SRodney W. Grimes return (resbuf);
1209b50d902SRodney W. Grimes }
12173b018e3SEd Schouten /* Round to minutes. */
122656dcd43SGarrett Wollman minutes = (tval + (60 - 1)) / 60;
123656dcd43SGarrett Wollman hours = minutes / 60;
124656dcd43SGarrett Wollman minutes %= 60;
125656dcd43SGarrett Wollman days = hours / 24;
126656dcd43SGarrett Wollman hours %= 24;
1279b50d902SRodney W. Grimes if (days)
1289b50d902SRodney W. Grimes (void)snprintf(resbuf, sizeof(resbuf),
12973b018e3SEd Schouten "%s %4d+%02d:%02d", updown, days, hours, minutes);
1309b50d902SRodney W. Grimes else
1319b50d902SRodney W. Grimes (void)snprintf(resbuf, sizeof(resbuf),
1329b50d902SRodney W. Grimes "%s %2d:%02d", updown, hours, minutes);
1339b50d902SRodney W. Grimes return (resbuf);
1349b50d902SRodney W. Grimes }
1359b50d902SRodney W. Grimes
136e245deadSBruce Evans /* Width to print a small nonnegative integer. */
137e245deadSBruce Evans static int
iwidth(int w)138e245deadSBruce Evans iwidth(int w)
139e245deadSBruce Evans {
140e245deadSBruce Evans if (w < 10)
141e245deadSBruce Evans return (1);
142e245deadSBruce Evans if (w < 100)
143e245deadSBruce Evans return (2);
144e245deadSBruce Evans if (w < 1000)
145e245deadSBruce Evans return (3);
146e245deadSBruce Evans if (w < 10000)
147e245deadSBruce Evans return (4);
148e245deadSBruce Evans return (5);
149e245deadSBruce Evans }
150e245deadSBruce Evans
151a827060aSMark Murray #define HS(a) ((const struct hs *)(a))
1529b50d902SRodney W. Grimes
1539b50d902SRodney W. Grimes /* Alphabetical comparison. */
15459880300SEd Schouten static int
hscmp(const void * a1,const void * a2)155a827060aSMark Murray hscmp(const void *a1, const void *a2)
1569b50d902SRodney W. Grimes {
1579b50d902SRodney W. Grimes return (rflg *
15873b018e3SEd Schouten strcmp(HS(a1)->hs_wd.wd_hostname, HS(a2)->hs_wd.wd_hostname));
1599b50d902SRodney W. Grimes }
1609b50d902SRodney W. Grimes
1619b50d902SRodney W. Grimes /* Load average comparison. */
16259880300SEd Schouten static int
lcmp(const void * a1,const void * a2)163a827060aSMark Murray lcmp(const void *a1, const void *a2)
1649b50d902SRodney W. Grimes {
1659b50d902SRodney W. Grimes if (ISDOWN(HS(a1)))
1669b50d902SRodney W. Grimes if (ISDOWN(HS(a2)))
1679b50d902SRodney W. Grimes return (tcmp(a1, a2));
1689b50d902SRodney W. Grimes else
1699b50d902SRodney W. Grimes return (rflg);
1709b50d902SRodney W. Grimes else if (ISDOWN(HS(a2)))
1719b50d902SRodney W. Grimes return (-rflg);
1729b50d902SRodney W. Grimes else
1739b50d902SRodney W. Grimes return (rflg *
17473b018e3SEd Schouten (HS(a2)->hs_wd.wd_loadav[0] - HS(a1)->hs_wd.wd_loadav[0]));
1759b50d902SRodney W. Grimes }
1769b50d902SRodney W. Grimes
17759880300SEd Schouten static void
ruptime(const char * host,int aflg,int (* cmp)(const void *,const void *))178e7cb5ef7SJuli Mallett ruptime(const char *host, int aflg, int (*cmp)(const void *, const void *))
179e7cb5ef7SJuli Mallett {
180e7cb5ef7SJuli Mallett struct hs *hsp;
181e7cb5ef7SJuli Mallett struct whod *wd;
182e7cb5ef7SJuli Mallett struct whoent *we;
183e7cb5ef7SJuli Mallett struct dirent *dp;
184e245deadSBruce Evans int fd, hostnamewidth, i, loadavwidth[3], userswidth, w;
185e7cb5ef7SJuli Mallett size_t hspace;
18673b018e3SEd Schouten ssize_t cc;
187e7cb5ef7SJuli Mallett
188e7cb5ef7SJuli Mallett rewinddir(dirp);
189e7cb5ef7SJuli Mallett hsp = NULL;
190e245deadSBruce Evans hostnamewidth = 0;
191e245deadSBruce Evans loadavwidth[0] = 4;
192e245deadSBruce Evans loadavwidth[1] = 4;
193e245deadSBruce Evans loadavwidth[2] = 4;
194e245deadSBruce Evans userswidth = 1;
195887019fbSAntoine Brodin (void)time(&now);
196e7cb5ef7SJuli Mallett for (nhosts = hspace = 0; (dp = readdir(dirp)) != NULL;) {
197e7cb5ef7SJuli Mallett if (dp->d_ino == 0 || strncmp(dp->d_name, "whod.", 5) != 0)
198e7cb5ef7SJuli Mallett continue;
199e7cb5ef7SJuli Mallett if ((fd = open(dp->d_name, O_RDONLY, 0)) < 0) {
200e7cb5ef7SJuli Mallett warn("%s", dp->d_name);
201e7cb5ef7SJuli Mallett continue;
202e7cb5ef7SJuli Mallett }
203e7cb5ef7SJuli Mallett
204e7cb5ef7SJuli Mallett if (nhosts == hspace) {
205e7cb5ef7SJuli Mallett if ((hs =
206e7cb5ef7SJuli Mallett realloc(hs, (hspace += 40) * sizeof(*hs))) == NULL)
207e7cb5ef7SJuli Mallett err(1, NULL);
208e7cb5ef7SJuli Mallett hsp = hs + nhosts;
209e7cb5ef7SJuli Mallett }
210e7cb5ef7SJuli Mallett
21173b018e3SEd Schouten wd = &hsp->hs_wd;
21273b018e3SEd Schouten cc = read(fd, wd, sizeof(*wd));
21373b018e3SEd Schouten (void)close(fd);
21473b018e3SEd Schouten if (cc < (ssize_t)WHDRSIZE)
21573b018e3SEd Schouten continue;
216e7cb5ef7SJuli Mallett
217e245deadSBruce Evans if (host != NULL && strcasecmp(wd->wd_hostname, host) != 0)
21873b018e3SEd Schouten continue;
21973b018e3SEd Schouten if (LEFTEARTH(wd->wd_recvtime))
22073b018e3SEd Schouten continue;
22173b018e3SEd Schouten
222e245deadSBruce Evans if (hostnamewidth < (int)strlen(wd->wd_hostname))
223e245deadSBruce Evans hostnamewidth = (int)strlen(wd->wd_hostname);
224ba2c1f62SYoshihiro Takahashi
225ba2c1f62SYoshihiro Takahashi if (!ISDOWN(hsp)) {
226e245deadSBruce Evans for (i = 0; i < 3; i++) {
227e245deadSBruce Evans w = iwidth(wd->wd_loadav[i] / 100) + 3;
228e245deadSBruce Evans if (loadavwidth[i] < w)
229e245deadSBruce Evans loadavwidth[i] = w;
230e245deadSBruce Evans }
23173b018e3SEd Schouten for (hsp->hs_nusers = 0, we = &wd->wd_we[0];
23273b018e3SEd Schouten (char *)(we + 1) <= (char *)wd + cc; we++)
233e7cb5ef7SJuli Mallett if (aflg || we->we_idle < 3600)
234e7cb5ef7SJuli Mallett ++hsp->hs_nusers;
235e245deadSBruce Evans if (userswidth < iwidth(hsp->hs_nusers))
236e245deadSBruce Evans userswidth = iwidth(hsp->hs_nusers);
237ba2c1f62SYoshihiro Takahashi }
238ba2c1f62SYoshihiro Takahashi
239e7cb5ef7SJuli Mallett ++hsp;
240e7cb5ef7SJuli Mallett ++nhosts;
241e7cb5ef7SJuli Mallett }
242e7cb5ef7SJuli Mallett if (nhosts == 0) {
243e7cb5ef7SJuli Mallett if (host == NULL)
244e7cb5ef7SJuli Mallett errx(1, "no hosts in %s", _PATH_RWHODIR);
245e7cb5ef7SJuli Mallett else
246e7cb5ef7SJuli Mallett warnx("host %s not in %s", host, _PATH_RWHODIR);
247e7cb5ef7SJuli Mallett }
248e7cb5ef7SJuli Mallett
249e7cb5ef7SJuli Mallett qsort(hs, nhosts, sizeof(hs[0]), cmp);
250e245deadSBruce Evans w = userswidth + loadavwidth[0] + loadavwidth[1] + loadavwidth[2];
251e245deadSBruce Evans if (hostnamewidth + w > 41)
252e245deadSBruce Evans hostnamewidth = 41 - w; /* limit to 79 cols */
253e7cb5ef7SJuli Mallett for (i = 0; i < (int)nhosts; i++) {
254e7cb5ef7SJuli Mallett hsp = &hs[i];
25573b018e3SEd Schouten wd = &hsp->hs_wd;
256e7cb5ef7SJuli Mallett if (ISDOWN(hsp)) {
257e245deadSBruce Evans (void)printf("%-*.*s %s\n",
258e245deadSBruce Evans hostnamewidth, hostnamewidth, wd->wd_hostname,
25973b018e3SEd Schouten interval(now - hsp->hs_wd.wd_recvtime, "down"));
260e7cb5ef7SJuli Mallett continue;
261e7cb5ef7SJuli Mallett }
262e7cb5ef7SJuli Mallett (void)printf(
263e245deadSBruce Evans "%-*.*s %s, %*d user%s load %*.2f, %*.2f, %*.2f\n",
264e245deadSBruce Evans hostnamewidth, hostnamewidth, wd->wd_hostname,
26573b018e3SEd Schouten interval((time_t)wd->wd_sendtime -
26673b018e3SEd Schouten (time_t)wd->wd_boottime, " up"),
267e245deadSBruce Evans userswidth, hsp->hs_nusers,
268e7cb5ef7SJuli Mallett hsp->hs_nusers == 1 ? ", " : "s,",
269e245deadSBruce Evans loadavwidth[0], wd->wd_loadav[0] / 100.0,
270e245deadSBruce Evans loadavwidth[1], wd->wd_loadav[1] / 100.0,
271e245deadSBruce Evans loadavwidth[2], wd->wd_loadav[2] / 100.0);
272e7cb5ef7SJuli Mallett }
273e7cb5ef7SJuli Mallett free(hs);
274e7cb5ef7SJuli Mallett hs = NULL;
275e7cb5ef7SJuli Mallett }
276e7cb5ef7SJuli Mallett
2779b50d902SRodney W. Grimes /* Number of users comparison. */
27859880300SEd Schouten static int
ucmp(const void * a1,const void * a2)279a827060aSMark Murray ucmp(const void *a1, const void *a2)
2809b50d902SRodney W. Grimes {
2819b50d902SRodney W. Grimes if (ISDOWN(HS(a1)))
2829b50d902SRodney W. Grimes if (ISDOWN(HS(a2)))
2839b50d902SRodney W. Grimes return (tcmp(a1, a2));
2849b50d902SRodney W. Grimes else
2859b50d902SRodney W. Grimes return (rflg);
2869b50d902SRodney W. Grimes else if (ISDOWN(HS(a2)))
2879b50d902SRodney W. Grimes return (-rflg);
2889b50d902SRodney W. Grimes else
2899b50d902SRodney W. Grimes return (rflg * (HS(a2)->hs_nusers - HS(a1)->hs_nusers));
2909b50d902SRodney W. Grimes }
2919b50d902SRodney W. Grimes
2929b50d902SRodney W. Grimes /* Uptime comparison. */
29359880300SEd Schouten static int
tcmp(const void * a1,const void * a2)294a827060aSMark Murray tcmp(const void *a1, const void *a2)
2959b50d902SRodney W. Grimes {
2969b50d902SRodney W. Grimes return (rflg * (
29773b018e3SEd Schouten (ISDOWN(HS(a2)) ? HS(a2)->hs_wd.wd_recvtime - now
29873b018e3SEd Schouten : HS(a2)->hs_wd.wd_sendtime - HS(a2)->hs_wd.wd_boottime)
2999b50d902SRodney W. Grimes -
30073b018e3SEd Schouten (ISDOWN(HS(a1)) ? HS(a1)->hs_wd.wd_recvtime - now
30173b018e3SEd Schouten : HS(a1)->hs_wd.wd_sendtime - HS(a1)->hs_wd.wd_boottime)
3029b50d902SRodney W. Grimes ));
3039b50d902SRodney W. Grimes }
3049b50d902SRodney W. Grimes
30559880300SEd Schouten static void
usage(void)306a827060aSMark Murray usage(void)
3079b50d902SRodney W. Grimes {
308f682f10cSRuslan Ermilov (void)fprintf(stderr, "usage: ruptime [-alrtu] [host ...]\n");
3099b50d902SRodney W. Grimes exit(1);
3109b50d902SRodney W. Grimes }
311