1 /* 2 * SPDX-License-Identifier: BSD-3-Clause 3 * 4 * Copyright (c) 1983, 1993, 1994 5 * The Regents of the University of California. 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. Neither the name of the University nor the names of its contributors 16 * may be used to endorse or promote products derived from this software 17 * without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 * SUCH DAMAGE. 30 */ 31 32 #include <sys/param.h> 33 34 #include <protocols/rwhod.h> 35 36 #include <dirent.h> 37 #include <err.h> 38 #include <errno.h> 39 #include <fcntl.h> 40 #include <stdio.h> 41 #include <stdlib.h> 42 #include <string.h> 43 #include <time.h> 44 #include <unistd.h> 45 46 static struct hs { 47 struct whod hs_wd; 48 int hs_nusers; 49 } *hs; 50 #define LEFTEARTH(h) (now - (h) > 4*24*60*60) 51 #define ISDOWN(h) (now - (h)->hs_wd.wd_recvtime > 11 * 60) 52 #define WHDRSIZE __offsetof(struct whod, wd_we) 53 54 static size_t nhosts; 55 static time_t now; 56 static int rflg = 1; 57 static DIR *dirp; 58 59 static int hscmp(const void *, const void *); 60 static char *interval(time_t, const char *); 61 static int iwidth(int); 62 static int lcmp(const void *, const void *); 63 static void ruptime(const char *, int, int (*)(const void *, const void *)); 64 static int tcmp(const void *, const void *); 65 static int ucmp(const void *, const void *); 66 static void usage(void); 67 68 int 69 main(int argc, char *argv[]) 70 { 71 int (*cmp)(const void *, const void *); 72 int aflg, ch; 73 74 aflg = 0; 75 cmp = hscmp; 76 while ((ch = getopt(argc, argv, "alrut")) != -1) 77 switch (ch) { 78 case 'a': 79 aflg = 1; 80 break; 81 case 'l': 82 cmp = lcmp; 83 break; 84 case 'r': 85 rflg = -1; 86 break; 87 case 't': 88 cmp = tcmp; 89 break; 90 case 'u': 91 cmp = ucmp; 92 break; 93 default: 94 usage(); 95 } 96 argc -= optind; 97 argv += optind; 98 99 if (chdir(_PATH_RWHODIR) || (dirp = opendir(".")) == NULL) 100 err(1, "%s", _PATH_RWHODIR); 101 102 ruptime(*argv, aflg, cmp); 103 while (*argv++ != NULL) { 104 if (*argv == NULL) 105 break; 106 ruptime(*argv, aflg, cmp); 107 } 108 exit(0); 109 } 110 111 static char * 112 interval(time_t tval, const char *updown) 113 { 114 static char resbuf[32]; 115 int days, hours, minutes; 116 117 if (tval < 0) { 118 (void)snprintf(resbuf, sizeof(resbuf), "%s ??:??", updown); 119 return (resbuf); 120 } 121 /* Round to minutes. */ 122 minutes = (tval + (60 - 1)) / 60; 123 hours = minutes / 60; 124 minutes %= 60; 125 days = hours / 24; 126 hours %= 24; 127 if (days) 128 (void)snprintf(resbuf, sizeof(resbuf), 129 "%s %4d+%02d:%02d", updown, days, hours, minutes); 130 else 131 (void)snprintf(resbuf, sizeof(resbuf), 132 "%s %2d:%02d", updown, hours, minutes); 133 return (resbuf); 134 } 135 136 /* Width to print a small nonnegative integer. */ 137 static int 138 iwidth(int w) 139 { 140 if (w < 10) 141 return (1); 142 if (w < 100) 143 return (2); 144 if (w < 1000) 145 return (3); 146 if (w < 10000) 147 return (4); 148 return (5); 149 } 150 151 #define HS(a) ((const struct hs *)(a)) 152 153 /* Alphabetical comparison. */ 154 static int 155 hscmp(const void *a1, const void *a2) 156 { 157 return (rflg * 158 strcmp(HS(a1)->hs_wd.wd_hostname, HS(a2)->hs_wd.wd_hostname)); 159 } 160 161 /* Load average comparison. */ 162 static int 163 lcmp(const void *a1, const void *a2) 164 { 165 if (ISDOWN(HS(a1))) 166 if (ISDOWN(HS(a2))) 167 return (tcmp(a1, a2)); 168 else 169 return (rflg); 170 else if (ISDOWN(HS(a2))) 171 return (-rflg); 172 else 173 return (rflg * 174 (HS(a2)->hs_wd.wd_loadav[0] - HS(a1)->hs_wd.wd_loadav[0])); 175 } 176 177 static void 178 ruptime(const char *host, int aflg, int (*cmp)(const void *, const void *)) 179 { 180 struct hs *hsp; 181 struct whod *wd; 182 struct whoent *we; 183 struct dirent *dp; 184 int fd, hostnamewidth, i, loadavwidth[3], userswidth, w; 185 size_t hspace; 186 ssize_t cc; 187 188 rewinddir(dirp); 189 hsp = NULL; 190 hostnamewidth = 0; 191 loadavwidth[0] = 4; 192 loadavwidth[1] = 4; 193 loadavwidth[2] = 4; 194 userswidth = 1; 195 (void)time(&now); 196 for (nhosts = hspace = 0; (dp = readdir(dirp)) != NULL;) { 197 if (dp->d_ino == 0 || strncmp(dp->d_name, "whod.", 5) != 0) 198 continue; 199 if ((fd = open(dp->d_name, O_RDONLY, 0)) < 0) { 200 warn("%s", dp->d_name); 201 continue; 202 } 203 204 if (nhosts == hspace) { 205 if ((hs = 206 realloc(hs, (hspace += 40) * sizeof(*hs))) == NULL) 207 err(1, NULL); 208 hsp = hs + nhosts; 209 } 210 211 wd = &hsp->hs_wd; 212 cc = read(fd, wd, sizeof(*wd)); 213 (void)close(fd); 214 if (cc < (ssize_t)WHDRSIZE) 215 continue; 216 217 if (host != NULL && strcasecmp(wd->wd_hostname, host) != 0) 218 continue; 219 if (LEFTEARTH(wd->wd_recvtime)) 220 continue; 221 222 if (hostnamewidth < (int)strlen(wd->wd_hostname)) 223 hostnamewidth = (int)strlen(wd->wd_hostname); 224 225 if (!ISDOWN(hsp)) { 226 for (i = 0; i < 3; i++) { 227 w = iwidth(wd->wd_loadav[i] / 100) + 3; 228 if (loadavwidth[i] < w) 229 loadavwidth[i] = w; 230 } 231 for (hsp->hs_nusers = 0, we = &wd->wd_we[0]; 232 (char *)(we + 1) <= (char *)wd + cc; we++) 233 if (aflg || we->we_idle < 3600) 234 ++hsp->hs_nusers; 235 if (userswidth < iwidth(hsp->hs_nusers)) 236 userswidth = iwidth(hsp->hs_nusers); 237 } 238 239 ++hsp; 240 ++nhosts; 241 } 242 if (nhosts == 0) { 243 if (host == NULL) 244 errx(1, "no hosts in %s", _PATH_RWHODIR); 245 else 246 warnx("host %s not in %s", host, _PATH_RWHODIR); 247 } 248 249 qsort(hs, nhosts, sizeof(hs[0]), cmp); 250 w = userswidth + loadavwidth[0] + loadavwidth[1] + loadavwidth[2]; 251 if (hostnamewidth + w > 41) 252 hostnamewidth = 41 - w; /* limit to 79 cols */ 253 for (i = 0; i < (int)nhosts; i++) { 254 hsp = &hs[i]; 255 wd = &hsp->hs_wd; 256 if (ISDOWN(hsp)) { 257 (void)printf("%-*.*s %s\n", 258 hostnamewidth, hostnamewidth, wd->wd_hostname, 259 interval(now - hsp->hs_wd.wd_recvtime, "down")); 260 continue; 261 } 262 (void)printf( 263 "%-*.*s %s, %*d user%s load %*.2f, %*.2f, %*.2f\n", 264 hostnamewidth, hostnamewidth, wd->wd_hostname, 265 interval((time_t)wd->wd_sendtime - 266 (time_t)wd->wd_boottime, " up"), 267 userswidth, hsp->hs_nusers, 268 hsp->hs_nusers == 1 ? ", " : "s,", 269 loadavwidth[0], wd->wd_loadav[0] / 100.0, 270 loadavwidth[1], wd->wd_loadav[1] / 100.0, 271 loadavwidth[2], wd->wd_loadav[2] / 100.0); 272 } 273 free(hs); 274 hs = NULL; 275 } 276 277 /* Number of users comparison. */ 278 static int 279 ucmp(const void *a1, const void *a2) 280 { 281 if (ISDOWN(HS(a1))) 282 if (ISDOWN(HS(a2))) 283 return (tcmp(a1, a2)); 284 else 285 return (rflg); 286 else if (ISDOWN(HS(a2))) 287 return (-rflg); 288 else 289 return (rflg * (HS(a2)->hs_nusers - HS(a1)->hs_nusers)); 290 } 291 292 /* Uptime comparison. */ 293 static int 294 tcmp(const void *a1, const void *a2) 295 { 296 return (rflg * ( 297 (ISDOWN(HS(a2)) ? HS(a2)->hs_wd.wd_recvtime - now 298 : HS(a2)->hs_wd.wd_sendtime - HS(a2)->hs_wd.wd_boottime) 299 - 300 (ISDOWN(HS(a1)) ? HS(a1)->hs_wd.wd_recvtime - now 301 : HS(a1)->hs_wd.wd_sendtime - HS(a1)->hs_wd.wd_boottime) 302 )); 303 } 304 305 static void 306 usage(void) 307 { 308 (void)fprintf(stderr, "usage: ruptime [-alrtu] [host ...]\n"); 309 exit(1); 310 } 311