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 #ifndef lint 33 static const char copyright[] = 34 "@(#) Copyright (c) 1983, 1993, 1994\n\ 35 The Regents of the University of California. All rights reserved.\n"; 36 #endif /* not lint */ 37 38 #ifndef lint 39 static const char sccsid[] = "@(#)ruptime.c 8.2 (Berkeley) 4/5/94"; 40 #endif /* not lint */ 41 42 #include <sys/cdefs.h> 43 __FBSDID("$FreeBSD$"); 44 45 #include <sys/param.h> 46 47 #include <protocols/rwhod.h> 48 49 #include <dirent.h> 50 #include <err.h> 51 #include <errno.h> 52 #include <fcntl.h> 53 #include <stdio.h> 54 #include <stdlib.h> 55 #include <string.h> 56 #include <time.h> 57 #include <unistd.h> 58 59 static struct hs { 60 struct whod hs_wd; 61 int hs_nusers; 62 } *hs; 63 #define LEFTEARTH(h) (now - (h) > 4*24*60*60) 64 #define ISDOWN(h) (now - (h)->hs_wd.wd_recvtime > 11 * 60) 65 #define WHDRSIZE __offsetof(struct whod, wd_we) 66 67 static size_t nhosts; 68 static time_t now; 69 static int rflg = 1; 70 static DIR *dirp; 71 72 static int hscmp(const void *, const void *); 73 static char *interval(time_t, const char *); 74 static int iwidth(int); 75 static int lcmp(const void *, const void *); 76 static void ruptime(const char *, int, int (*)(const void *, const void *)); 77 static int tcmp(const void *, const void *); 78 static int ucmp(const void *, const void *); 79 static void usage(void); 80 81 int 82 main(int argc, char *argv[]) 83 { 84 int (*cmp)(const void *, const void *); 85 int aflg, ch; 86 87 aflg = 0; 88 cmp = hscmp; 89 while ((ch = getopt(argc, argv, "alrut")) != -1) 90 switch (ch) { 91 case 'a': 92 aflg = 1; 93 break; 94 case 'l': 95 cmp = lcmp; 96 break; 97 case 'r': 98 rflg = -1; 99 break; 100 case 't': 101 cmp = tcmp; 102 break; 103 case 'u': 104 cmp = ucmp; 105 break; 106 default: 107 usage(); 108 } 109 argc -= optind; 110 argv += optind; 111 112 if (chdir(_PATH_RWHODIR) || (dirp = opendir(".")) == NULL) 113 err(1, "%s", _PATH_RWHODIR); 114 115 ruptime(*argv, aflg, cmp); 116 while (*argv++ != NULL) { 117 if (*argv == NULL) 118 break; 119 ruptime(*argv, aflg, cmp); 120 } 121 exit(0); 122 } 123 124 static char * 125 interval(time_t tval, const char *updown) 126 { 127 static char resbuf[32]; 128 int days, hours, minutes; 129 130 if (tval < 0) { 131 (void)snprintf(resbuf, sizeof(resbuf), "%s ??:??", updown); 132 return (resbuf); 133 } 134 /* Round to minutes. */ 135 minutes = (tval + (60 - 1)) / 60; 136 hours = minutes / 60; 137 minutes %= 60; 138 days = hours / 24; 139 hours %= 24; 140 if (days) 141 (void)snprintf(resbuf, sizeof(resbuf), 142 "%s %4d+%02d:%02d", updown, days, hours, minutes); 143 else 144 (void)snprintf(resbuf, sizeof(resbuf), 145 "%s %2d:%02d", updown, hours, minutes); 146 return (resbuf); 147 } 148 149 /* Width to print a small nonnegative integer. */ 150 static int 151 iwidth(int w) 152 { 153 if (w < 10) 154 return (1); 155 if (w < 100) 156 return (2); 157 if (w < 1000) 158 return (3); 159 if (w < 10000) 160 return (4); 161 return (5); 162 } 163 164 #define HS(a) ((const struct hs *)(a)) 165 166 /* Alphabetical comparison. */ 167 static int 168 hscmp(const void *a1, const void *a2) 169 { 170 return (rflg * 171 strcmp(HS(a1)->hs_wd.wd_hostname, HS(a2)->hs_wd.wd_hostname)); 172 } 173 174 /* Load average comparison. */ 175 static int 176 lcmp(const void *a1, const void *a2) 177 { 178 if (ISDOWN(HS(a1))) 179 if (ISDOWN(HS(a2))) 180 return (tcmp(a1, a2)); 181 else 182 return (rflg); 183 else if (ISDOWN(HS(a2))) 184 return (-rflg); 185 else 186 return (rflg * 187 (HS(a2)->hs_wd.wd_loadav[0] - HS(a1)->hs_wd.wd_loadav[0])); 188 } 189 190 static void 191 ruptime(const char *host, int aflg, int (*cmp)(const void *, const void *)) 192 { 193 struct hs *hsp; 194 struct whod *wd; 195 struct whoent *we; 196 struct dirent *dp; 197 int fd, hostnamewidth, i, loadavwidth[3], userswidth, w; 198 size_t hspace; 199 ssize_t cc; 200 201 rewinddir(dirp); 202 hsp = NULL; 203 hostnamewidth = 0; 204 loadavwidth[0] = 4; 205 loadavwidth[1] = 4; 206 loadavwidth[2] = 4; 207 userswidth = 1; 208 (void)time(&now); 209 for (nhosts = hspace = 0; (dp = readdir(dirp)) != NULL;) { 210 if (dp->d_ino == 0 || strncmp(dp->d_name, "whod.", 5) != 0) 211 continue; 212 if ((fd = open(dp->d_name, O_RDONLY, 0)) < 0) { 213 warn("%s", dp->d_name); 214 continue; 215 } 216 217 if (nhosts == hspace) { 218 if ((hs = 219 realloc(hs, (hspace += 40) * sizeof(*hs))) == NULL) 220 err(1, NULL); 221 hsp = hs + nhosts; 222 } 223 224 wd = &hsp->hs_wd; 225 cc = read(fd, wd, sizeof(*wd)); 226 (void)close(fd); 227 if (cc < (ssize_t)WHDRSIZE) 228 continue; 229 230 if (host != NULL && strcasecmp(wd->wd_hostname, host) != 0) 231 continue; 232 if (LEFTEARTH(wd->wd_recvtime)) 233 continue; 234 235 if (hostnamewidth < (int)strlen(wd->wd_hostname)) 236 hostnamewidth = (int)strlen(wd->wd_hostname); 237 for (i = 0; i < 3; i++) { 238 w = iwidth(wd->wd_loadav[i] / 100) + 3; 239 if (loadavwidth[i] < w) 240 loadavwidth[i] = w; 241 } 242 243 for (hsp->hs_nusers = 0, we = &wd->wd_we[0]; 244 (char *)(we + 1) <= (char *)wd + cc; we++) 245 if (aflg || we->we_idle < 3600) 246 ++hsp->hs_nusers; 247 if (userswidth < iwidth(hsp->hs_nusers)) 248 userswidth = iwidth(hsp->hs_nusers); 249 ++hsp; 250 ++nhosts; 251 } 252 if (nhosts == 0) { 253 if (host == NULL) 254 errx(1, "no hosts in %s", _PATH_RWHODIR); 255 else 256 warnx("host %s not in %s", host, _PATH_RWHODIR); 257 } 258 259 qsort(hs, nhosts, sizeof(hs[0]), cmp); 260 w = userswidth + loadavwidth[0] + loadavwidth[1] + loadavwidth[2]; 261 if (hostnamewidth + w > 41) 262 hostnamewidth = 41 - w; /* limit to 79 cols */ 263 for (i = 0; i < (int)nhosts; i++) { 264 hsp = &hs[i]; 265 wd = &hsp->hs_wd; 266 if (ISDOWN(hsp)) { 267 (void)printf("%-*.*s %s\n", 268 hostnamewidth, hostnamewidth, wd->wd_hostname, 269 interval(now - hsp->hs_wd.wd_recvtime, "down")); 270 continue; 271 } 272 (void)printf( 273 "%-*.*s %s, %*d user%s load %*.2f, %*.2f, %*.2f\n", 274 hostnamewidth, hostnamewidth, wd->wd_hostname, 275 interval((time_t)wd->wd_sendtime - 276 (time_t)wd->wd_boottime, " up"), 277 userswidth, hsp->hs_nusers, 278 hsp->hs_nusers == 1 ? ", " : "s,", 279 loadavwidth[0], wd->wd_loadav[0] / 100.0, 280 loadavwidth[1], wd->wd_loadav[1] / 100.0, 281 loadavwidth[2], wd->wd_loadav[2] / 100.0); 282 } 283 free(hs); 284 hs = NULL; 285 } 286 287 /* Number of users comparison. */ 288 static int 289 ucmp(const void *a1, const void *a2) 290 { 291 if (ISDOWN(HS(a1))) 292 if (ISDOWN(HS(a2))) 293 return (tcmp(a1, a2)); 294 else 295 return (rflg); 296 else if (ISDOWN(HS(a2))) 297 return (-rflg); 298 else 299 return (rflg * (HS(a2)->hs_nusers - HS(a1)->hs_nusers)); 300 } 301 302 /* Uptime comparison. */ 303 static int 304 tcmp(const void *a1, const void *a2) 305 { 306 return (rflg * ( 307 (ISDOWN(HS(a2)) ? HS(a2)->hs_wd.wd_recvtime - now 308 : HS(a2)->hs_wd.wd_sendtime - HS(a2)->hs_wd.wd_boottime) 309 - 310 (ISDOWN(HS(a1)) ? HS(a1)->hs_wd.wd_recvtime - now 311 : HS(a1)->hs_wd.wd_sendtime - HS(a1)->hs_wd.wd_boottime) 312 )); 313 } 314 315 static void 316 usage(void) 317 { 318 (void)fprintf(stderr, "usage: ruptime [-alrtu] [host ...]\n"); 319 exit(1); 320 } 321