1 1.25 rillig /* $NetBSD: log.c,v 1.25 2021/05/02 12:50:43 rillig Exp $ */ 2 1.3 cgd 3 1.1 cgd /*- 4 1.3 cgd * Copyright (c) 1990, 1993 5 1.3 cgd * The Regents of the University of California. All rights reserved. 6 1.1 cgd * 7 1.1 cgd * This code is derived from software contributed to Berkeley by 8 1.1 cgd * Ed James. 9 1.1 cgd * 10 1.1 cgd * Redistribution and use in source and binary forms, with or without 11 1.1 cgd * modification, are permitted provided that the following conditions 12 1.1 cgd * are met: 13 1.1 cgd * 1. Redistributions of source code must retain the above copyright 14 1.1 cgd * notice, this list of conditions and the following disclaimer. 15 1.1 cgd * 2. Redistributions in binary form must reproduce the above copyright 16 1.1 cgd * notice, this list of conditions and the following disclaimer in the 17 1.1 cgd * documentation and/or other materials provided with the distribution. 18 1.12 agc * 3. Neither the name of the University nor the names of its contributors 19 1.1 cgd * may be used to endorse or promote products derived from this software 20 1.1 cgd * without specific prior written permission. 21 1.1 cgd * 22 1.1 cgd * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 23 1.1 cgd * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 24 1.1 cgd * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 25 1.1 cgd * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 26 1.1 cgd * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 27 1.1 cgd * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 28 1.1 cgd * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 29 1.1 cgd * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 30 1.1 cgd * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 31 1.1 cgd * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32 1.1 cgd * SUCH DAMAGE. 33 1.1 cgd */ 34 1.1 cgd 35 1.1 cgd /* 36 1.1 cgd * Copyright (c) 1987 by Ed James, UC Berkeley. All rights reserved. 37 1.1 cgd * 38 1.1 cgd * Copy permission is hereby granted provided that this notice is 39 1.1 cgd * retained on all partial or complete copies. 40 1.1 cgd * 41 1.1 cgd * For more info on this and all of my stuff, mail edjames (at) berkeley.edu. 42 1.1 cgd */ 43 1.1 cgd 44 1.5 lukem #include <sys/cdefs.h> 45 1.1 cgd #ifndef lint 46 1.3 cgd #if 0 47 1.3 cgd static char sccsid[] = "@(#)log.c 8.1 (Berkeley) 5/31/93"; 48 1.3 cgd #else 49 1.25 rillig __RCSID("$NetBSD: log.c,v 1.25 2021/05/02 12:50:43 rillig Exp $"); 50 1.3 cgd #endif 51 1.11 cgd #endif /* not lint */ 52 1.1 cgd 53 1.22 dholland #include <sys/types.h> 54 1.22 dholland #include <sys/utsname.h> 55 1.23 christos #include <sys/stat.h> /* for umask(2) */ 56 1.22 dholland #include <stdio.h> 57 1.22 dholland #include <stdlib.h> 58 1.22 dholland #include <string.h> 59 1.22 dholland #include <unistd.h> 60 1.22 dholland #include <fcntl.h> 61 1.22 dholland #include <pwd.h> 62 1.22 dholland #include <err.h> 63 1.22 dholland 64 1.1 cgd #include "pathnames.h" 65 1.22 dholland #include "def.h" 66 1.22 dholland #include "struct.h" 67 1.22 dholland #include "extern.h" 68 1.22 dholland #include "tunable.h" 69 1.1 cgd 70 1.9 hubertf static FILE *score_fp; 71 1.9 hubertf 72 1.21 dholland static int 73 1.14 jmc compar(const void *va, const void *vb) 74 1.5 lukem { 75 1.8 hubertf const SCORE *a, *b; 76 1.5 lukem 77 1.8 hubertf a = (const SCORE *)va; 78 1.8 hubertf b = (const SCORE *)vb; 79 1.1 cgd if (b->planes == a->planes) 80 1.1 cgd return (b->time - a->time); 81 1.1 cgd else 82 1.1 cgd return (b->planes - a->planes); 83 1.1 cgd } 84 1.1 cgd 85 1.1 cgd #define SECAMIN 60 86 1.1 cgd #define MINAHOUR 60 87 1.1 cgd #define HOURADAY 24 88 1.1 cgd #define SECAHOUR (SECAMIN * MINAHOUR) 89 1.1 cgd #define SECADAY (SECAHOUR * HOURADAY) 90 1.1 cgd #define DAY(t) ((t) / SECADAY) 91 1.1 cgd #define HOUR(t) (((t) % SECADAY) / SECAHOUR) 92 1.1 cgd #define MIN(t) (((t) % SECAHOUR) / SECAMIN) 93 1.1 cgd #define SEC(t) ((t) % SECAMIN) 94 1.1 cgd 95 1.21 dholland static const char * 96 1.14 jmc timestr(int t) 97 1.1 cgd { 98 1.1 cgd static char s[80]; 99 1.1 cgd 100 1.1 cgd if (DAY(t) > 0) 101 1.20 dholland (void)snprintf(s, sizeof(s), "%dd+%02dhrs", DAY(t), HOUR(t)); 102 1.1 cgd else if (HOUR(t) > 0) 103 1.20 dholland (void)snprintf(s, sizeof(s), "%d:%02d:%02d", HOUR(t), MIN(t), 104 1.20 dholland SEC(t)); 105 1.1 cgd else if (MIN(t) > 0) 106 1.20 dholland (void)snprintf(s, sizeof(s), "%d:%02d", MIN(t), SEC(t)); 107 1.1 cgd else if (SEC(t) > 0) 108 1.20 dholland (void)snprintf(s, sizeof(s), ":%02d", SEC(t)); 109 1.1 cgd else 110 1.1 cgd *s = '\0'; 111 1.1 cgd 112 1.1 cgd return (s); 113 1.1 cgd } 114 1.1 cgd 115 1.9 hubertf void 116 1.14 jmc open_score_file(void) 117 1.9 hubertf { 118 1.9 hubertf mode_t old_mask; 119 1.9 hubertf int score_fd; 120 1.9 hubertf int flags; 121 1.9 hubertf 122 1.9 hubertf old_mask = umask(0); 123 1.18 jnemeth #if defined(O_NOFOLLOW) 124 1.18 jnemeth score_fd = open(_PATH_SCORE, O_CREAT|O_RDWR|O_NOFOLLOW, 0664); 125 1.18 jnemeth #else 126 1.9 hubertf score_fd = open(_PATH_SCORE, O_CREAT|O_RDWR, 0664); 127 1.18 jnemeth #endif 128 1.16 rpaulo (void)umask(old_mask); 129 1.9 hubertf if (score_fd < 0) { 130 1.9 hubertf warn("open %s", _PATH_SCORE); 131 1.9 hubertf return; 132 1.9 hubertf } 133 1.9 hubertf if (score_fd < 3) 134 1.9 hubertf exit(1); 135 1.9 hubertf /* Set the close-on-exec flag. If this fails for any reason, quit 136 1.9 hubertf * rather than leave the score file open to tampering. */ 137 1.9 hubertf flags = fcntl(score_fd, F_GETFD); 138 1.9 hubertf if (flags < 0) 139 1.9 hubertf err(1, "fcntl F_GETFD"); 140 1.9 hubertf flags |= FD_CLOEXEC; 141 1.9 hubertf if (fcntl(score_fd, F_SETFD, flags) == -1) 142 1.9 hubertf err(1, "fcntl F_SETFD"); 143 1.9 hubertf /* 144 1.25 rillig * This is done to take advantage of stdio, while still 145 1.9 hubertf * allowing a O_CREAT during the open(2) of the log file. 146 1.9 hubertf */ 147 1.9 hubertf score_fp = fdopen(score_fd, "r+"); 148 1.9 hubertf if (score_fp == NULL) { 149 1.9 hubertf warn("fdopen %s", _PATH_SCORE); 150 1.9 hubertf return; 151 1.9 hubertf } 152 1.9 hubertf } 153 1.9 hubertf 154 1.5 lukem int 155 1.14 jmc log_score(int list_em) 156 1.1 cgd { 157 1.9 hubertf int i, num_scores = 0, good, changed = 0, found = 0; 158 1.1 cgd struct passwd *pw; 159 1.5 lukem char *cp; 160 1.1 cgd SCORE score[100], thisscore; 161 1.14 jmc struct utsname lname; 162 1.9 hubertf long offset; 163 1.1 cgd 164 1.24 pgoyette if (safe_planes == 1) 165 1.24 pgoyette printf("You directed 1 plane safely to its destination.\n\n"); 166 1.24 pgoyette else 167 1.24 pgoyette printf("You directed %d planes safely to their destinations.\n\n", 168 1.24 pgoyette safe_planes); 169 1.24 pgoyette 170 1.9 hubertf if (score_fp == NULL) { 171 1.9 hubertf warnx("no score file available"); 172 1.1 cgd return (-1); 173 1.1 cgd } 174 1.9 hubertf 175 1.1 cgd #ifdef BSD 176 1.9 hubertf if (flock(fileno(score_fp), LOCK_EX) < 0) 177 1.1 cgd #endif 178 1.1 cgd #ifdef SYSV 179 1.18 jnemeth if (lockf(fileno(score_fp), F_LOCK, 1) < 0) 180 1.1 cgd #endif 181 1.1 cgd { 182 1.6 lukem warn("flock %s", _PATH_SCORE); 183 1.1 cgd return (-1); 184 1.1 cgd } 185 1.1 cgd for (;;) { 186 1.9 hubertf good = fscanf(score_fp, SCORE_SCANF_FMT, 187 1.25 rillig score[num_scores].name, 188 1.25 rillig score[num_scores].host, 189 1.16 rpaulo score[num_scores].game, 190 1.25 rillig &score[num_scores].planes, 191 1.16 rpaulo &score[num_scores].time, 192 1.16 rpaulo &score[num_scores].real_time); 193 1.1 cgd if (good != 6 || ++num_scores >= NUM_SCORES) 194 1.1 cgd break; 195 1.1 cgd } 196 1.1 cgd if (!test_mode && !list_em) { 197 1.1 cgd if ((pw = (struct passwd *) getpwuid(getuid())) == NULL) { 198 1.25 rillig (void)fprintf(stderr, 199 1.1 cgd "getpwuid failed for uid %d. Who are you?\n", 200 1.9 hubertf (int)getuid()); 201 1.1 cgd return (-1); 202 1.1 cgd } 203 1.18 jnemeth (void)strlcpy(thisscore.name, pw->pw_name, SCORE_NAME_LEN); 204 1.16 rpaulo (void)uname(&lname); 205 1.25 rillig (void)strlcpy(thisscore.host, lname.nodename, 206 1.16 rpaulo sizeof(thisscore.host)); 207 1.1 cgd 208 1.14 jmc cp = strrchr(filename, '/'); 209 1.1 cgd if (cp == NULL) { 210 1.25 rillig (void)fprintf(stderr, "log: where's the '/' in %s?\n", 211 1.14 jmc filename); 212 1.1 cgd return (-1); 213 1.1 cgd } 214 1.1 cgd cp++; 215 1.18 jnemeth (void)strlcpy(thisscore.game, cp, SCORE_GAME_LEN); 216 1.1 cgd 217 1.1 cgd thisscore.time = clck; 218 1.1 cgd thisscore.planes = safe_planes; 219 1.1 cgd thisscore.real_time = time(0) - start_time; 220 1.1 cgd 221 1.1 cgd for (i = 0; i < num_scores; i++) { 222 1.1 cgd if (strcmp(thisscore.name, score[i].name) == 0 && 223 1.1 cgd strcmp(thisscore.host, score[i].host) == 0 && 224 1.1 cgd strcmp(thisscore.game, score[i].game) == 0) { 225 1.1 cgd if (thisscore.time > score[i].time) { 226 1.1 cgd score[i].time = thisscore.time; 227 1.1 cgd score[i].planes = thisscore.planes; 228 1.1 cgd score[i].real_time = 229 1.16 rpaulo thisscore.real_time; 230 1.1 cgd changed++; 231 1.1 cgd } 232 1.1 cgd found++; 233 1.1 cgd break; 234 1.1 cgd } 235 1.1 cgd } 236 1.1 cgd if (!found) { 237 1.1 cgd for (i = 0; i < num_scores; i++) { 238 1.1 cgd if (thisscore.time > score[i].time) { 239 1.1 cgd if (num_scores < NUM_SCORES) 240 1.1 cgd num_scores++; 241 1.16 rpaulo (void)memcpy(&score[num_scores - 1], 242 1.16 rpaulo &score[i], sizeof (score[i])); 243 1.16 rpaulo (void)memcpy(&score[i], &thisscore, 244 1.16 rpaulo sizeof (score[i])); 245 1.1 cgd changed++; 246 1.1 cgd break; 247 1.1 cgd } 248 1.1 cgd } 249 1.1 cgd } 250 1.1 cgd if (!found && !changed && num_scores < NUM_SCORES) { 251 1.16 rpaulo (void)memcpy(&score[num_scores], &thisscore, 252 1.16 rpaulo sizeof (score[num_scores])); 253 1.1 cgd num_scores++; 254 1.1 cgd changed++; 255 1.1 cgd } 256 1.1 cgd 257 1.1 cgd if (changed) { 258 1.1 cgd if (found) 259 1.16 rpaulo (void)puts("You beat your previous score!"); 260 1.1 cgd else 261 1.16 rpaulo (void)puts("You made the top players list!"); 262 1.16 rpaulo qsort(score, (size_t)num_scores, sizeof (*score), 263 1.16 rpaulo compar); 264 1.9 hubertf rewind(score_fp); 265 1.1 cgd for (i = 0; i < num_scores; i++) 266 1.16 rpaulo (void)fprintf(score_fp, "%s %s %s %d %d %d\n", 267 1.25 rillig score[i].name, score[i].host, 268 1.16 rpaulo score[i].game, score[i].planes, 269 1.16 rpaulo score[i].time, score[i].real_time); 270 1.16 rpaulo (void)fflush(score_fp); 271 1.9 hubertf if (ferror(score_fp)) 272 1.9 hubertf warn("error writing %s", _PATH_SCORE); 273 1.9 hubertf /* It is just possible that updating an entry could 274 1.9 hubertf * have reduced the length of the file, so we 275 1.9 hubertf * truncate it. The seeks are required for stream/fd 276 1.9 hubertf * synchronisation by POSIX.1. */ 277 1.9 hubertf offset = ftell(score_fp); 278 1.16 rpaulo (void)lseek(fileno(score_fp), (off_t)0, SEEK_SET); 279 1.16 rpaulo (void)ftruncate(fileno(score_fp), (off_t)offset); 280 1.9 hubertf rewind(score_fp); 281 1.1 cgd } else { 282 1.1 cgd if (found) 283 1.16 rpaulo (void)puts( 284 1.16 rpaulo "You didn't beat your previous score."); 285 1.1 cgd else 286 1.16 rpaulo (void)puts( 287 1.16 rpaulo "You didn't make the top players list."); 288 1.1 cgd } 289 1.16 rpaulo (void)putchar('\n'); 290 1.1 cgd } 291 1.1 cgd #ifdef BSD 292 1.16 rpaulo (void)flock(fileno(score_fp), LOCK_UN); 293 1.1 cgd #endif 294 1.1 cgd #ifdef SYSV 295 1.1 cgd /* lock will evaporate upon close */ 296 1.1 cgd #endif 297 1.16 rpaulo (void)fclose(score_fp); 298 1.16 rpaulo (void)printf("%2s: %-8s %-8s %-18s %4s %9s %4s\n", "#", "name", 299 1.16 rpaulo "host", "game", "time", "real time", "planes safe"); 300 1.15 rpaulo (void)printf("-------------------------------------------------------"); 301 1.17 rpaulo (void)printf("-------------------------\n"); 302 1.1 cgd for (i = 0; i < num_scores; i++) { 303 1.5 lukem cp = strchr(score[i].host, '.'); 304 1.1 cgd if (cp != NULL) 305 1.1 cgd *cp = '\0'; 306 1.16 rpaulo (void)printf("%2d: %-8s %-8s %-18s %4d %9s %4d\n", i + 1, 307 1.16 rpaulo score[i].name, score[i].host, score[i].game, 308 1.16 rpaulo score[i].time, timestr(score[i].real_time), 309 1.16 rpaulo score[i].planes); 310 1.1 cgd } 311 1.16 rpaulo (void)putchar('\n'); 312 1.1 cgd return (0); 313 1.5 lukem } 314 1.5 lukem 315 1.16 rpaulo /* ARGSUSED */ 316 1.5 lukem void 317 1.19 perry log_score_quit(int dummy __unused) 318 1.5 lukem { 319 1.5 lukem (void)log_score(0); 320 1.5 lukem exit(0); 321 1.1 cgd } 322