xref: /openbsd/games/sail/sync.c (revision 76d0caae)
1 /*	$OpenBSD: sync.c,v 1.16 2019/06/28 13:32:52 deraadt Exp $	*/
2 /*	$NetBSD: sync.c,v 1.9 1998/08/30 09:19:40 veego Exp $	*/
3 
4 /*
5  * Copyright (c) 1983, 1993
6  *	The Regents of the University of California.  All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. Neither the name of the University nor the names of its contributors
17  *    may be used to endorse or promote products derived from this software
18  *    without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  */
32 
33 #include <sys/stat.h>
34 
35 #include <errno.h>
36 #ifdef LOCK_EX
37 #include <fcntl.h>
38 #endif
39 #include <signal.h>
40 #include <stdlib.h>
41 #include <string.h>
42 #include <time.h>
43 #include <unistd.h>
44 
45 #include "extern.h"
46 #include "machdep.h"
47 #include "pathnames.h"
48 #include "player.h"
49 
50 #define BUFSIZE 4096
51 
52 static const char SF[] = _PATH_SYNC;
53 static const char LF[] = _PATH_LOCK;
54 static char sync_buf[BUFSIZE];
55 static char *sync_bp = sync_buf;
56 static char sync_lock[sizeof SF];
57 static char sync_file[sizeof LF];
58 static long sync_seek;
59 static FILE *sync_fp;
60 
61 void
62 fmtship(char *buf, size_t len, const char *fmt, struct ship *ship)
63 {
64 	if (len == 0)
65 		abort();	/* XXX */
66 
67 	while (*fmt && len > 1) {
68 		if (*fmt == '$' && fmt[1] == '$') {
69 			size_t l;
70 			snprintf(buf, len, "%s (%c%c)",
71 			    ship->shipname, colours(ship), sterncolour(ship));
72 			l = strlen(buf);
73 			buf += l;
74 			len -= l;
75 			fmt += 2;
76 		} else {
77 			*buf++ = *fmt++;
78 			len--;
79 		}
80 	}
81 
82 	*buf = '\0';
83 }
84 
85 
86 void
87 makesignal(struct ship *from, const char *fmt, struct ship *ship, ...)
88 {
89 	char message[BUFSIZ];
90 	char format[BUFSIZ];
91 	va_list ap;
92 
93 	va_start(ap, ship);
94 	fmtship(format, sizeof(format), fmt, ship);
95 	(void) vsnprintf(message, sizeof message, format, ap);
96 	va_end(ap);
97 	Writestr(W_SIGNAL, from, message);
98 }
99 
100 void
101 makemsg(struct ship *from, const char *fmt, ...)
102 {
103 	char message[BUFSIZ];
104 	va_list ap;
105 
106 	va_start(ap, fmt);
107 	(void) vsnprintf(message, sizeof message, fmt, ap);
108 	va_end(ap);
109 	Writestr(W_SIGNAL, from, message);
110 }
111 
112 int
113 sync_exists(int game)
114 {
115 	char buf[sizeof sync_file];
116 	struct stat s;
117 	time_t t;
118 
119 	(void) snprintf(buf, sizeof buf, SF, game);
120 	(void) time(&t);
121 	setegid(egid);
122 	if (stat(buf, &s) == -1) {
123 		setegid(gid);
124 		return 0;
125 	}
126 	if (s.st_mtime < t - 60*60*2) {		/* 2 hours */
127 		(void) unlink(buf);
128 		(void) snprintf(buf, sizeof buf, LF, game);
129 		(void) unlink(buf);
130 		setegid(gid);
131 		return 0;
132 	}
133 	setegid(gid);
134 	return 1;
135 }
136 
137 int
138 sync_open(void)
139 {
140 	struct stat tmp;
141 
142 	if (sync_fp != NULL)
143 		(void) fclose(sync_fp);
144 	(void) snprintf(sync_lock, sizeof sync_lock, LF, game);
145 	(void) snprintf(sync_file, sizeof sync_file, SF, game);
146 	setegid(egid);
147 	if (stat(sync_file, &tmp) == -1) {
148 		mode_t omask = umask(002);
149 		sync_fp = fopen(sync_file, "w+");
150 		(void) umask(omask);
151 	} else
152 		sync_fp = fopen(sync_file, "r+");
153 	setegid(gid);
154 	if (sync_fp == NULL)
155 		return -1;
156 	sync_seek = 0;
157 	return 0;
158 }
159 
160 void
161 sync_close(int remove)
162 {
163 	if (sync_fp != 0)
164 		(void) fclose(sync_fp);
165 	if (remove) {
166 		setegid(egid);
167 		(void) unlink(sync_file);
168 		setegid(gid);
169 	}
170 }
171 
172 void
173 Write(int type, struct ship *ship, long a, long b, long c, long d)
174 {
175 	(void) snprintf(sync_bp, sync_buf + sizeof sync_buf - sync_bp,
176 		"%d %d 0 %ld %ld %ld %ld\n",
177 	       type, ship->file->index, a, b, c, d);
178 	while (*sync_bp++)
179 		;
180 	sync_bp--;
181 	if (sync_bp >= &sync_buf[sizeof sync_buf])
182 		abort();
183 	(void) sync_update(type, ship, NULL, a, b, c, d);
184 }
185 
186 void
187 Writestr(int type, struct ship *ship, const char *a)
188 {
189 	(void) snprintf(sync_bp, sync_buf + sizeof sync_buf - sync_bp,
190 		"%d %d 1 %s\n",
191 		type, ship->file->index, a);
192 	while (*sync_bp++)
193 		;
194 	sync_bp--;
195 	if (sync_bp >= &sync_buf[sizeof sync_buf])
196 		abort();
197 	(void) sync_update(type, ship, a, 0, 0, 0, 0);
198 }
199 
200 int
201 Sync(void)
202 {
203 	sig_t sighup, sigint;
204 	int n;
205 	int type, shipnum, isstr;
206 	char *astr;
207 	long a, b, c, d;
208 	char buf[80];
209 	char erred = 0;
210 
211 	sighup = signal(SIGHUP, SIG_IGN);
212 	sigint = signal(SIGINT, SIG_IGN);
213 	for (n = TIMEOUT; --n >= 0;) {
214 #ifdef LOCK_EX
215 		if (flock(fileno(sync_fp), LOCK_EX|LOCK_NB) >= 0)
216 			break;
217 		if (errno != EWOULDBLOCK)
218 			return -1;
219 #else
220 		setegid(egid);
221 		if (link(sync_file, sync_lock) >= 0) {
222 			setegid(gid);
223 			break;
224 		}
225 		setegid(gid);
226 		if (errno != EEXIST)
227 			return -1;
228 #endif
229 		sleep(1);
230 	}
231 	if (n <= 0)
232 		return -1;
233 	(void) fseek(sync_fp, sync_seek, SEEK_SET);
234 	for (;;) {
235 		switch (fscanf(sync_fp, "%d%d%d", &type, &shipnum, &isstr)) {
236 		case 3:
237 			break;
238 		case EOF:
239 			goto out;
240 		default:
241 			goto bad;
242 		}
243 		if (shipnum < 0 || shipnum >= cc->vessels)
244 			goto bad;
245 		if (isstr != 0 && isstr != 1)
246 			goto bad;
247 		if (isstr) {
248 			int ch;
249 			char *p;
250 
251 			for (p = buf;;) {
252 				ch = getc(sync_fp);
253 				switch (ch) {
254 				case '\n':
255 				case EOF:
256 					break;
257 				default:
258 					if (p < buf + sizeof buf)
259 						*p++ = ch;
260 					continue;
261 				}
262 				break;
263 			}
264 			*p = 0;
265 			for (p = buf; *p == ' '; p++)
266 				;
267 			astr = p;
268 			a = b = c = d = 0;
269 		} else {
270 			if (fscanf(sync_fp, "%ld%ld%ld%ld", &a, &b, &c, &d) != 4)
271 				goto bad;
272 			astr = NULL;
273 		}
274 		if (sync_update(type, SHIP(shipnum), astr, a, b, c, d) < 0)
275 			goto bad;
276 	}
277 bad:
278 	erred++;
279 out:
280 	if (!erred && sync_bp != sync_buf) {
281 		(void) fseek(sync_fp, 0L, SEEK_END);
282 		(void) fwrite(sync_buf, sizeof *sync_buf, sync_bp - sync_buf,
283 			sync_fp);
284 		(void) fflush(sync_fp);
285 		sync_bp = sync_buf;
286 	}
287 	sync_seek = ftell(sync_fp);
288 #ifdef LOCK_EX
289 	(void) flock(fileno(sync_fp), LOCK_UN);
290 #else
291 	setegid(egid);
292 	(void) unlink(sync_lock);
293 	setegid(gid);
294 #endif
295 	(void) signal(SIGHUP, sighup);
296 	(void) signal(SIGINT, sigint);
297 	return erred ? -1 : 0;
298 }
299 
300 int
301 sync_update(int type, struct ship *ship, const char *astr, long a, long b,
302     long c, long d)
303 {
304 	switch (type) {
305 	case W_DBP: {
306 		struct BP *p = &ship->file->DBP[a];
307 		p->turnsent = b;
308 		p->toship = SHIP(c);
309 		p->mensent = d;
310 		break;
311 		}
312 	case W_OBP: {
313 		struct BP *p = &ship->file->OBP[a];
314 		p->turnsent = b;
315 		p->toship = SHIP(c);
316 		p->mensent = d;
317 		break;
318 		}
319 	case W_FOUL: {
320 		struct snag *p = &ship->file->foul[a];
321 		if (SHIP(a)->file->dir == 0)
322 			break;
323 		if (p->sn_count++ == 0)
324 			p->sn_turn = turn;
325 		ship->file->nfoul++;
326 		break;
327 		}
328 	case W_GRAP: {
329 		struct snag *p = &ship->file->grap[a];
330 		if (SHIP(a)->file->dir == 0)
331 			break;
332 		if (p->sn_count++ == 0)
333 			p->sn_turn = turn;
334 		ship->file->ngrap++;
335 		break;
336 		}
337 	case W_UNFOUL: {
338 		struct snag *p = &ship->file->foul[a];
339 		if (p->sn_count > 0) {
340 			if (b) {
341 				ship->file->nfoul -= p->sn_count;
342 				p->sn_count = 0;
343 			} else {
344 				ship->file->nfoul--;
345 				p->sn_count--;
346 			}
347 		}
348 		break;
349 		}
350 	case W_UNGRAP: {
351 		struct snag *p = &ship->file->grap[a];
352 		if (p->sn_count > 0) {
353 			if (b) {
354 				ship->file->ngrap -= p->sn_count;
355 				p->sn_count = 0;
356 			} else {
357 				ship->file->ngrap--;
358 				p->sn_count--;
359 			}
360 		}
361 		break;
362 		}
363 	case W_SIGNAL:
364 		if (mode == MODE_PLAYER) {
365 			if (nobells)
366 				Signal("$$: %s", ship, astr);
367 			else
368 				Signal("\7$$: %s", ship, astr);
369 		}
370 		break;
371 	case W_CREW: {
372 		struct shipspecs *s = ship->specs;
373 		s->crew1 = a;
374 		s->crew2 = b;
375 		s->crew3 = c;
376 		break;
377 		}
378 	case W_CAPTAIN:
379 		(void) strlcpy(ship->file->captain, astr,
380 			sizeof ship->file->captain);
381 		break;
382 	case W_CAPTURED:
383 		if (a < 0)
384 			ship->file->captured = 0;
385 		else
386 			ship->file->captured = SHIP(a);
387 		break;
388 	case W_CLASS:
389 		ship->specs->class = a;
390 		break;
391 	case W_DRIFT:
392 		ship->file->drift = a;
393 		break;
394 	case W_EXPLODE:
395 		if ((ship->file->explode = a) == 2)
396 			ship->file->dir = 0;
397 		break;
398 	case W_FS:
399 		ship->file->FS = a;
400 		break;
401 	case W_GUNL: {
402 		struct shipspecs *s = ship->specs;
403 		s->gunL = a;
404 		s->carL = b;
405 		break;
406 		}
407 	case W_GUNR: {
408 		struct shipspecs *s = ship->specs;
409 		s->gunR = a;
410 		s->carR = b;
411 		break;
412 		}
413 	case W_HULL:
414 		ship->specs->hull = a;
415 		break;
416 	case W_MOVE:
417 		(void) strlcpy(ship->file->movebuf, astr,
418 			sizeof ship->file->movebuf);
419 		break;
420 	case W_PCREW:
421 		ship->file->pcrew = a;
422 		break;
423 	case W_POINTS:
424 		ship->file->points = a;
425 		break;
426 	case W_QUAL:
427 		ship->specs->qual = a;
428 		break;
429 	case W_RIGG: {
430 		struct shipspecs *s = ship->specs;
431 		s->rig1 = a;
432 		s->rig2 = b;
433 		s->rig3 = c;
434 		s->rig4 = d;
435 		break;
436 		}
437 	case W_RIG1:
438 		ship->specs->rig1 = a;
439 		break;
440 	case W_RIG2:
441 		ship->specs->rig2 = a;
442 		break;
443 	case W_RIG3:
444 		ship->specs->rig3 = a;
445 		break;
446 	case W_RIG4:
447 		ship->specs->rig4 = a;
448 		break;
449 	case W_COL:
450 		ship->file->col = a;
451 		break;
452 	case W_DIR:
453 		ship->file->dir = a;
454 		break;
455 	case W_ROW:
456 		ship->file->row = a;
457 		break;
458 	case W_SINK:
459 		if ((ship->file->sink = a) == 2)
460 			ship->file->dir = 0;
461 		break;
462 	case W_STRUCK:
463 		ship->file->struck = a;
464 		break;
465 	case W_TA:
466 		ship->specs->ta = a;
467 		break;
468 	case W_ALIVE:
469 		alive = 1;
470 		break;
471 	case W_TURN:
472 		turn = a;
473 		break;
474 	case W_WIND:
475 		winddir = a;
476 		windspeed = b;
477 		break;
478 	case W_BEGIN:
479 		(void) strlcpy(ship->file->captain, "begin",
480 		    sizeof ship->file->captain);
481 		people++;
482 		break;
483 	case W_END:
484 		*ship->file->captain = 0;
485 		ship->file->points = 0;
486 		people--;
487 		break;
488 	case W_DDEAD:
489 		hasdriver = 0;
490 		break;
491 	default:
492 		fprintf(stderr, "sync_update: unknown type %d\r\n", type);
493 		return -1;
494 	}
495 	return 0;
496 }
497