1*1182a44cSrillig /* $NetBSD: log.c,v 1.25 2021/05/02 12:50:43 rillig Exp $ */
2101657d1Scgd
361f28255Scgd /*-
4101657d1Scgd * Copyright (c) 1990, 1993
5101657d1Scgd * The Regents of the University of California. All rights reserved.
661f28255Scgd *
761f28255Scgd * This code is derived from software contributed to Berkeley by
861f28255Scgd * Ed James.
961f28255Scgd *
1061f28255Scgd * Redistribution and use in source and binary forms, with or without
1161f28255Scgd * modification, are permitted provided that the following conditions
1261f28255Scgd * are met:
1361f28255Scgd * 1. Redistributions of source code must retain the above copyright
1461f28255Scgd * notice, this list of conditions and the following disclaimer.
1561f28255Scgd * 2. Redistributions in binary form must reproduce the above copyright
1661f28255Scgd * notice, this list of conditions and the following disclaimer in the
1761f28255Scgd * documentation and/or other materials provided with the distribution.
18e5aeb4eaSagc * 3. Neither the name of the University nor the names of its contributors
1961f28255Scgd * may be used to endorse or promote products derived from this software
2061f28255Scgd * without specific prior written permission.
2161f28255Scgd *
2261f28255Scgd * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2361f28255Scgd * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2461f28255Scgd * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2561f28255Scgd * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2661f28255Scgd * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2761f28255Scgd * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2861f28255Scgd * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2961f28255Scgd * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
3061f28255Scgd * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3161f28255Scgd * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3261f28255Scgd * SUCH DAMAGE.
3361f28255Scgd */
3461f28255Scgd
3561f28255Scgd /*
3661f28255Scgd * Copyright (c) 1987 by Ed James, UC Berkeley. All rights reserved.
3761f28255Scgd *
3861f28255Scgd * Copy permission is hereby granted provided that this notice is
3961f28255Scgd * retained on all partial or complete copies.
4061f28255Scgd *
4161f28255Scgd * For more info on this and all of my stuff, mail edjames@berkeley.edu.
4261f28255Scgd */
4361f28255Scgd
44ca57cf90Slukem #include <sys/cdefs.h>
4561f28255Scgd #ifndef lint
46101657d1Scgd #if 0
47101657d1Scgd static char sccsid[] = "@(#)log.c 8.1 (Berkeley) 5/31/93";
48101657d1Scgd #else
49*1182a44cSrillig __RCSID("$NetBSD: log.c,v 1.25 2021/05/02 12:50:43 rillig Exp $");
50101657d1Scgd #endif
51d594ce93Scgd #endif /* not lint */
5261f28255Scgd
5301dcf47eSdholland #include <sys/types.h>
5401dcf47eSdholland #include <sys/utsname.h>
55fa60103eSchristos #include <sys/stat.h> /* for umask(2) */
5601dcf47eSdholland #include <stdio.h>
5701dcf47eSdholland #include <stdlib.h>
5801dcf47eSdholland #include <string.h>
5901dcf47eSdholland #include <unistd.h>
6001dcf47eSdholland #include <fcntl.h>
6101dcf47eSdholland #include <pwd.h>
6201dcf47eSdholland #include <err.h>
6301dcf47eSdholland
6461f28255Scgd #include "pathnames.h"
6501dcf47eSdholland #include "def.h"
6601dcf47eSdholland #include "struct.h"
6701dcf47eSdholland #include "extern.h"
6801dcf47eSdholland #include "tunable.h"
6961f28255Scgd
70d5f85ed9Shubertf static FILE *score_fp;
71d5f85ed9Shubertf
727a2ed764Sdholland static int
compar(const void * va,const void * vb)734931378aSjmc compar(const void *va, const void *vb)
7461f28255Scgd {
75a9d35e83Shubertf const SCORE *a, *b;
76ca57cf90Slukem
77a9d35e83Shubertf a = (const SCORE *)va;
78a9d35e83Shubertf b = (const SCORE *)vb;
7961f28255Scgd if (b->planes == a->planes)
8061f28255Scgd return (b->time - a->time);
8161f28255Scgd else
8261f28255Scgd return (b->planes - a->planes);
8361f28255Scgd }
8461f28255Scgd
8561f28255Scgd #define SECAMIN 60
8661f28255Scgd #define MINAHOUR 60
8761f28255Scgd #define HOURADAY 24
8861f28255Scgd #define SECAHOUR (SECAMIN * MINAHOUR)
8961f28255Scgd #define SECADAY (SECAHOUR * HOURADAY)
9061f28255Scgd #define DAY(t) ((t) / SECADAY)
9161f28255Scgd #define HOUR(t) (((t) % SECADAY) / SECAHOUR)
9261f28255Scgd #define MIN(t) (((t) % SECAHOUR) / SECAMIN)
9361f28255Scgd #define SEC(t) ((t) % SECAMIN)
9461f28255Scgd
957a2ed764Sdholland static const char *
timestr(int t)964931378aSjmc timestr(int t)
9761f28255Scgd {
9861f28255Scgd static char s[80];
9961f28255Scgd
10061f28255Scgd if (DAY(t) > 0)
1012e221153Sdholland (void)snprintf(s, sizeof(s), "%dd+%02dhrs", DAY(t), HOUR(t));
10261f28255Scgd else if (HOUR(t) > 0)
1032e221153Sdholland (void)snprintf(s, sizeof(s), "%d:%02d:%02d", HOUR(t), MIN(t),
1042e221153Sdholland SEC(t));
10561f28255Scgd else if (MIN(t) > 0)
1062e221153Sdholland (void)snprintf(s, sizeof(s), "%d:%02d", MIN(t), SEC(t));
10761f28255Scgd else if (SEC(t) > 0)
1082e221153Sdholland (void)snprintf(s, sizeof(s), ":%02d", SEC(t));
10961f28255Scgd else
11061f28255Scgd *s = '\0';
11161f28255Scgd
11261f28255Scgd return (s);
11361f28255Scgd }
11461f28255Scgd
115d5f85ed9Shubertf void
open_score_file(void)1164931378aSjmc open_score_file(void)
11761f28255Scgd {
118d5f85ed9Shubertf mode_t old_mask;
119d5f85ed9Shubertf int score_fd;
120d5f85ed9Shubertf int flags;
12161f28255Scgd
122d5f85ed9Shubertf old_mask = umask(0);
123c8e9ec4eSjnemeth #if defined(O_NOFOLLOW)
124c8e9ec4eSjnemeth score_fd = open(_PATH_SCORE, O_CREAT|O_RDWR|O_NOFOLLOW, 0664);
125c8e9ec4eSjnemeth #else
126d5f85ed9Shubertf score_fd = open(_PATH_SCORE, O_CREAT|O_RDWR, 0664);
127c8e9ec4eSjnemeth #endif
128b0282a1eSrpaulo (void)umask(old_mask);
129d5f85ed9Shubertf if (score_fd < 0) {
13036b47bd3Slukem warn("open %s", _PATH_SCORE);
131d5f85ed9Shubertf return;
13261f28255Scgd }
133d5f85ed9Shubertf if (score_fd < 3)
134d5f85ed9Shubertf exit(1);
135d5f85ed9Shubertf /* Set the close-on-exec flag. If this fails for any reason, quit
136d5f85ed9Shubertf * rather than leave the score file open to tampering. */
137d5f85ed9Shubertf flags = fcntl(score_fd, F_GETFD);
138d5f85ed9Shubertf if (flags < 0)
139d5f85ed9Shubertf err(1, "fcntl F_GETFD");
140d5f85ed9Shubertf flags |= FD_CLOEXEC;
141d5f85ed9Shubertf if (fcntl(score_fd, F_SETFD, flags) == -1)
142d5f85ed9Shubertf err(1, "fcntl F_SETFD");
14361f28255Scgd /*
14461f28255Scgd * This is done to take advantage of stdio, while still
14561f28255Scgd * allowing a O_CREAT during the open(2) of the log file.
14661f28255Scgd */
147d5f85ed9Shubertf score_fp = fdopen(score_fd, "r+");
148d5f85ed9Shubertf if (score_fp == NULL) {
14936b47bd3Slukem warn("fdopen %s", _PATH_SCORE);
150d5f85ed9Shubertf return;
151d5f85ed9Shubertf }
152d5f85ed9Shubertf }
153d5f85ed9Shubertf
154d5f85ed9Shubertf int
log_score(int list_em)1554931378aSjmc log_score(int list_em)
156d5f85ed9Shubertf {
157d5f85ed9Shubertf int i, num_scores = 0, good, changed = 0, found = 0;
158d5f85ed9Shubertf struct passwd *pw;
159d5f85ed9Shubertf char *cp;
160d5f85ed9Shubertf SCORE score[100], thisscore;
1614931378aSjmc struct utsname lname;
162d5f85ed9Shubertf long offset;
163d5f85ed9Shubertf
164a9c53af5Spgoyette if (safe_planes == 1)
165a9c53af5Spgoyette printf("You directed 1 plane safely to its destination.\n\n");
166a9c53af5Spgoyette else
167a9c53af5Spgoyette printf("You directed %d planes safely to their destinations.\n\n",
168a9c53af5Spgoyette safe_planes);
169a9c53af5Spgoyette
170d5f85ed9Shubertf if (score_fp == NULL) {
171d5f85ed9Shubertf warnx("no score file available");
17261f28255Scgd return (-1);
17361f28255Scgd }
174d5f85ed9Shubertf
17561f28255Scgd #ifdef BSD
176d5f85ed9Shubertf if (flock(fileno(score_fp), LOCK_EX) < 0)
17761f28255Scgd #endif
17861f28255Scgd #ifdef SYSV
179c8e9ec4eSjnemeth if (lockf(fileno(score_fp), F_LOCK, 1) < 0)
18061f28255Scgd #endif
18161f28255Scgd {
18236b47bd3Slukem warn("flock %s", _PATH_SCORE);
18361f28255Scgd return (-1);
18461f28255Scgd }
18561f28255Scgd for (;;) {
186d5f85ed9Shubertf good = fscanf(score_fp, SCORE_SCANF_FMT,
18761f28255Scgd score[num_scores].name,
18861f28255Scgd score[num_scores].host,
18961f28255Scgd score[num_scores].game,
19061f28255Scgd &score[num_scores].planes,
19161f28255Scgd &score[num_scores].time,
19261f28255Scgd &score[num_scores].real_time);
19361f28255Scgd if (good != 6 || ++num_scores >= NUM_SCORES)
19461f28255Scgd break;
19561f28255Scgd }
19661f28255Scgd if (!test_mode && !list_em) {
19761f28255Scgd if ((pw = (struct passwd *) getpwuid(getuid())) == NULL) {
198b0282a1eSrpaulo (void)fprintf(stderr,
19961f28255Scgd "getpwuid failed for uid %d. Who are you?\n",
200d5f85ed9Shubertf (int)getuid());
20161f28255Scgd return (-1);
20261f28255Scgd }
203c8e9ec4eSjnemeth (void)strlcpy(thisscore.name, pw->pw_name, SCORE_NAME_LEN);
204b0282a1eSrpaulo (void)uname(&lname);
205b0282a1eSrpaulo (void)strlcpy(thisscore.host, lname.nodename,
206b0282a1eSrpaulo sizeof(thisscore.host));
20761f28255Scgd
2084931378aSjmc cp = strrchr(filename, '/');
20961f28255Scgd if (cp == NULL) {
210b0282a1eSrpaulo (void)fprintf(stderr, "log: where's the '/' in %s?\n",
2114931378aSjmc filename);
21261f28255Scgd return (-1);
21361f28255Scgd }
21461f28255Scgd cp++;
215c8e9ec4eSjnemeth (void)strlcpy(thisscore.game, cp, SCORE_GAME_LEN);
21661f28255Scgd
21761f28255Scgd thisscore.time = clck;
21861f28255Scgd thisscore.planes = safe_planes;
21961f28255Scgd thisscore.real_time = time(0) - start_time;
22061f28255Scgd
22161f28255Scgd for (i = 0; i < num_scores; i++) {
22261f28255Scgd if (strcmp(thisscore.name, score[i].name) == 0 &&
22361f28255Scgd strcmp(thisscore.host, score[i].host) == 0 &&
22461f28255Scgd strcmp(thisscore.game, score[i].game) == 0) {
22561f28255Scgd if (thisscore.time > score[i].time) {
22661f28255Scgd score[i].time = thisscore.time;
22761f28255Scgd score[i].planes = thisscore.planes;
22861f28255Scgd score[i].real_time =
22961f28255Scgd thisscore.real_time;
23061f28255Scgd changed++;
23161f28255Scgd }
23261f28255Scgd found++;
23361f28255Scgd break;
23461f28255Scgd }
23561f28255Scgd }
23661f28255Scgd if (!found) {
23761f28255Scgd for (i = 0; i < num_scores; i++) {
23861f28255Scgd if (thisscore.time > score[i].time) {
23961f28255Scgd if (num_scores < NUM_SCORES)
24061f28255Scgd num_scores++;
241b0282a1eSrpaulo (void)memcpy(&score[num_scores - 1],
242b0282a1eSrpaulo &score[i], sizeof (score[i]));
243b0282a1eSrpaulo (void)memcpy(&score[i], &thisscore,
24461f28255Scgd sizeof (score[i]));
24561f28255Scgd changed++;
24661f28255Scgd break;
24761f28255Scgd }
24861f28255Scgd }
24961f28255Scgd }
25061f28255Scgd if (!found && !changed && num_scores < NUM_SCORES) {
251b0282a1eSrpaulo (void)memcpy(&score[num_scores], &thisscore,
25261f28255Scgd sizeof (score[num_scores]));
25361f28255Scgd num_scores++;
25461f28255Scgd changed++;
25561f28255Scgd }
25661f28255Scgd
25761f28255Scgd if (changed) {
25861f28255Scgd if (found)
259b0282a1eSrpaulo (void)puts("You beat your previous score!");
26061f28255Scgd else
261b0282a1eSrpaulo (void)puts("You made the top players list!");
262b0282a1eSrpaulo qsort(score, (size_t)num_scores, sizeof (*score),
263b0282a1eSrpaulo compar);
264d5f85ed9Shubertf rewind(score_fp);
26561f28255Scgd for (i = 0; i < num_scores; i++)
266b0282a1eSrpaulo (void)fprintf(score_fp, "%s %s %s %d %d %d\n",
26761f28255Scgd score[i].name, score[i].host,
26861f28255Scgd score[i].game, score[i].planes,
26961f28255Scgd score[i].time, score[i].real_time);
270b0282a1eSrpaulo (void)fflush(score_fp);
271d5f85ed9Shubertf if (ferror(score_fp))
272d5f85ed9Shubertf warn("error writing %s", _PATH_SCORE);
273d5f85ed9Shubertf /* It is just possible that updating an entry could
274d5f85ed9Shubertf * have reduced the length of the file, so we
275d5f85ed9Shubertf * truncate it. The seeks are required for stream/fd
276d5f85ed9Shubertf * synchronisation by POSIX.1. */
277d5f85ed9Shubertf offset = ftell(score_fp);
278b0282a1eSrpaulo (void)lseek(fileno(score_fp), (off_t)0, SEEK_SET);
279b0282a1eSrpaulo (void)ftruncate(fileno(score_fp), (off_t)offset);
280d5f85ed9Shubertf rewind(score_fp);
28161f28255Scgd } else {
28261f28255Scgd if (found)
283b0282a1eSrpaulo (void)puts(
284b0282a1eSrpaulo "You didn't beat your previous score.");
28561f28255Scgd else
286b0282a1eSrpaulo (void)puts(
287b0282a1eSrpaulo "You didn't make the top players list.");
28861f28255Scgd }
289b0282a1eSrpaulo (void)putchar('\n');
29061f28255Scgd }
29161f28255Scgd #ifdef BSD
292b0282a1eSrpaulo (void)flock(fileno(score_fp), LOCK_UN);
29361f28255Scgd #endif
29461f28255Scgd #ifdef SYSV
29561f28255Scgd /* lock will evaporate upon close */
29661f28255Scgd #endif
297b0282a1eSrpaulo (void)fclose(score_fp);
298b0282a1eSrpaulo (void)printf("%2s: %-8s %-8s %-18s %4s %9s %4s\n", "#", "name",
299b0282a1eSrpaulo "host", "game", "time", "real time", "planes safe");
300279b6fbaSrpaulo (void)printf("-------------------------------------------------------");
30107b524b4Srpaulo (void)printf("-------------------------\n");
30261f28255Scgd for (i = 0; i < num_scores; i++) {
303ca57cf90Slukem cp = strchr(score[i].host, '.');
30461f28255Scgd if (cp != NULL)
30561f28255Scgd *cp = '\0';
306b0282a1eSrpaulo (void)printf("%2d: %-8s %-8s %-18s %4d %9s %4d\n", i + 1,
30761f28255Scgd score[i].name, score[i].host, score[i].game,
30861f28255Scgd score[i].time, timestr(score[i].real_time),
30961f28255Scgd score[i].planes);
31061f28255Scgd }
311b0282a1eSrpaulo (void)putchar('\n');
31261f28255Scgd return (0);
31361f28255Scgd }
314ca57cf90Slukem
315b0282a1eSrpaulo /* ARGSUSED */
316ca57cf90Slukem void
log_score_quit(int dummy __unused)3178b0f9554Sperry log_score_quit(int dummy __unused)
318ca57cf90Slukem {
319ca57cf90Slukem (void)log_score(0);
320ca57cf90Slukem exit(0);
321ca57cf90Slukem }
322