1 /*	SCCS Id: @(#)unixunix.c	3.4	1994/11/07	*/
2 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
3 /* NetHack may be freely redistributed.  See license for details. */
4 
5 /* This file collects some Unix dependencies */
6 
7 #include "hack.h"	/* mainly for index() which depends on BSD */
8 
9 #include <errno.h>
10 #include <sys/stat.h>
11 #if defined(NO_FILE_LINKS) || defined(SUNOS4) || defined(POSIX_TYPES)
12 #include <fcntl.h>
13 #endif
14 #include <signal.h>
15 
16 #ifdef _M_UNIX
17 extern void NDECL(sco_mapon);
18 extern void NDECL(sco_mapoff);
19 #endif
20 #ifdef __linux__
21 extern void NDECL(linux_mapon);
22 extern void NDECL(linux_mapoff);
23 #endif
24 
25 #ifdef LINUX
26 extern void NDECL(linux_mapon);
27 extern void NDECL(linux_mapoff);
28 #endif
29 
30 static int FDECL (veryold, (int));
31 static int NDECL (eraseoldlocks);
32 
33 #ifndef NHSTDC
34 extern int errno;
35 #endif
36 
37 static struct stat buf;
38 
39 /* see whether we should throw away this xlock file */
40 static int
veryold(fd)41 veryold(fd)
42 int fd;
43 {
44 	time_t date;
45 
46 	if(fstat(fd, &buf)) return(0);			/* cannot get status */
47 #ifndef INSURANCE
48 	if(buf.st_size != sizeof(int)) return(0);	/* not an xlock file */
49 #endif
50 #if defined(BSD) && !defined(POSIX_TYPES)
51 	(void) time((long *)(&date));
52 #else
53 	(void) time(&date);
54 #endif
55 	if(date - buf.st_mtime < 3L*24L*60L*60L) {	/* recent */
56 		int lockedpid;	/* should be the same size as hackpid */
57 
58 		if(read(fd, (genericptr_t)&lockedpid, sizeof(lockedpid)) !=
59 			sizeof(lockedpid))
60 			/* strange ... */
61 			return(0);
62 
63 		/* From: Rick Adams <seismo!rick> */
64 		/* This will work on 4.1cbsd, 4.2bsd and system 3? & 5. */
65 		/* It will do nothing on V7 or 4.1bsd. */
66 #ifndef NETWORK
67 		/* It will do a VERY BAD THING if the playground is shared
68 		   by more than one machine! -pem */
69 		if(!(kill(lockedpid, 0) == -1 && errno == ESRCH))
70 #endif
71 			return(0);
72 	}
73 	(void) close(fd);
74 	return(1);
75 }
76 
77 static int
eraseoldlocks()78 eraseoldlocks()
79 {
80 	register int i;
81 
82 	/* cannot use maxledgerno() here, because we need to find a lock name
83 	 * before starting everything (including the dungeon initialization
84 	 * that sets astral_level, needed for maxledgerno()) up
85 	 */
86 	for(i = 1; i <= MAXDUNGEON*MAXLEVEL + 1; i++) {
87 		/* try to remove all */
88 		set_levelfile_name(lock, i);
89 #ifdef FILE_AREAS
90 		(void) remove_area(FILE_AREA_LEVL, lock);
91 #else
92 		(void) unlink(fqname(lock, LEVELPREFIX, 0));
93 #endif
94 	}
95 	set_levelfile_name(lock, 0);
96 #ifdef FILE_AREAS
97 	if (remove_area(FILE_AREA_LEVL, lock))
98 #else
99 	if (unlink(fqname(lock, LEVELPREFIX, 0)))
100 #endif
101 		return(0);				/* cannot remove it */
102 	return(1);					/* success! */
103 }
104 
105 void
getlock()106 getlock()
107 {
108 	register int i = 0, fd, c;
109 #ifndef FILE_AREAS
110 	const char *fq_lock;
111 #endif
112 
113 #ifdef TTY_GRAPHICS
114 	/* idea from rpick%ucqais@uccba.uc.edu
115 	 * prevent automated rerolling of characters
116 	 * test input (fd0) so that tee'ing output to get a screen dump still
117 	 * works
118 	 * also incidentally prevents development of any hack-o-matic programs
119 	 */
120 	/* added check for window-system type -dlc */
121 	if (!strcmp(windowprocs.name, "tty"))
122 	    if (!isatty(0))
123 		error("You must play from a terminal.");
124 #endif
125 
126 	/* we ignore QUIT and INT at this point */
127 #ifndef FILE_AREAS
128 	if (!lock_file(HLOCK, LOCKPREFIX, 10)) {
129 #else
130 	if (!lock_file_area(HLOCK_AREA, HLOCK, 10)) {
131 #endif
132 		wait_synch();
133 		error("%s", "");
134 	}
135 
136 	regularize(lock);
137 	set_levelfile_name(lock, 0);
138 
139 	if(locknum) {
140 		if(locknum > 25) locknum = 25;
141 
142 		do {
143 			lock[0] = 'a' + i++;
144 
145 #ifndef FILE_AREAS
146 			fq_lock = fqname(lock, LEVELPREFIX, 0);
147 			if((fd = open(fq_lock, 0, 0)) == -1) {
148 #else
149 			if((fd = open_area(FILE_AREA_LEVL, lock, 0, 0)) == -1) {
150 #endif
151 			    if(errno == ENOENT) goto gotlock; /* no such file */
152 #ifndef FILE_AREAS
153 			    perror(fq_lock);
154 			    unlock_file(HLOCK);
155 			    error("Cannot open %s", fq_lock);
156 #else
157 			    perror(lock);
158 			    unlock_file_area(HLOCK_AREA, HLOCK);
159 			    error("Cannot open %s", lock);
160 #endif
161 			}
162 
163 			if(veryold(fd) /* closes fd if true */
164 							&& eraseoldlocks())
165 				goto gotlock;
166 			(void) close(fd);
167 		} while(i < locknum);
168 
169 		unlock_file_area(HLOCK_AREA, HLOCK);
170 		error("Too many hacks running now.");
171 	} else {
172 #ifndef FILE_AREAS
173 		fq_lock = fqname(lock, LEVELPREFIX, 0);
174 		if((fd = open(fq_lock, 0, 0)) == -1) {
175 #else
176 		if((fd = open_area(FILE_AREA_LEVL, lock, 0, 0)) == -1) {
177 #endif
178 			if(errno == ENOENT) goto gotlock;    /* no such file */
179 #ifndef FILE_AREAS
180 			perror(fq_lock);
181 			unlock_file(HLOCK);
182 			error("Cannot open %s", fq_lock);
183 #else
184 			perror(lock);
185 			unlock_file_area(HLOCK_AREA, HLOCK);
186 			error("Cannot open %s", lock);
187 #endif
188 		}
189 
190 		if(veryold(fd) /* closes fd if true */ && eraseoldlocks())
191 			goto gotlock;
192 		(void) close(fd);
193 
194 		if(iflags.window_inited) {
195 		    c = yn("There is already a game in progress under your name.  Destroy old game?");
196 		} else {
197 		    (void) printf("\nThere is already a game in progress under your name.");
198 		    (void) printf("  Destroy old game? [yn] ");
199 		    (void) fflush(stdout);
200 		    c = getchar();
201 		    (void) putchar(c);
202 		    (void) fflush(stdout);
203 		    while (getchar() != '\n') ; /* eat rest of line and newline */
204 		}
205 		if(c == 'y' || c == 'Y')
206 			if(eraseoldlocks())
207 				goto gotlock;
208 			else {
209 				unlock_file_area(HLOCK_AREA, HLOCK);
210 				error("Couldn't destroy old game.");
211 			}
212 		else {
213 			unlock_file_area(HLOCK_AREA, HLOCK);
214 			error("%s", "");
215 		}
216 	}
217 
218 gotlock:
219 #ifndef FILE_AREAS
220 	fd = creat(fq_lock, FCMASK);
221 #else
222 	fd = creat_area(FILE_AREA_LEVL, lock, FCMASK);
223 #endif
224 	unlock_file_area(HLOCK_AREA, HLOCK);
225 	if(fd == -1) {
226 #ifndef FILE_AREAS
227 		error("cannot creat lock file (%s).", fq_lock);
228 #else
229 		error("cannot creat lock file (%s in %s).", lock,
230 		  FILE_AREA_LEVL);
231 #endif
232 	} else {
233 		if(write(fd, (genericptr_t) &hackpid, sizeof(hackpid))
234 		    != sizeof(hackpid)){
235 #ifndef FILE_AREAS
236 			error("cannot write lock (%s)", fq_lock);
237 #else
238 			error("cannot write lock (%s in %s)", lock,
239 			  FILE_AREA_LEVL);
240 #endif
241 		}
242 		if(close(fd) == -1) {
243 #ifndef FILE_AREAS
244 			error("cannot close lock (%s)", fq_lock);
245 #else
246 			error("cannot close lock (%s in %s)", lock,
247 			  FILE_AREA_LEVL);
248 #endif
249 		}
250 	}
251 }
252 
253 void
254 regularize(s)	/* normalize file name - we don't like .'s, /'s, spaces */
255 register char *s;
256 {
257 	register char *lp;
258 
259 	while((lp=index(s, '.')) || (lp=index(s, '/')) || (lp=index(s,' ')))
260 		*lp = '_';
261 #if defined(SYSV) && !defined(AIX_31) && !defined(SVR4) && !defined(LINUX) && !defined(__APPLE__)
262 	/* avoid problems with 14 character file name limit */
263 # ifdef COMPRESS
264 	/* leave room for .e from error and .Z from compress appended to
265 	 * save files */
266 	{
267 #  ifdef COMPRESS_EXTENSION
268 	    int i = 12 - strlen(COMPRESS_EXTENSION);
269 #  else
270 	    int i = 10;		/* should never happen... */
271 #  endif
272 	    if(strlen(s) > i)
273 		s[i] = '\0';
274 	}
275 # else
276 	if(strlen(s) > 11)
277 		/* leave room for .nn appended to level files */
278 		s[11] = '\0';
279 # endif
280 #endif
281 }
282 
283 #if defined(TIMED_DELAY) && !defined(msleep) && defined(SYSV)
284 #include <poll.h>
285 
286 void
287 msleep(msec)
288 unsigned msec;				/* milliseconds */
289 {
290 	struct pollfd unused;
291 	int msecs = msec;		/* poll API is signed */
292 
293 	if (msecs < 0) msecs = 0;	/* avoid infinite sleep */
294 	(void) poll(&unused, (unsigned long)0, msecs);
295 }
296 #endif /* TIMED_DELAY for SYSV */
297 
298 #ifdef SHELL
299 int
300 dosh()
301 {
302 	register char *str;
303 	if(child(0)) {
304 		if((str = getenv("SHELL")) != (char*)0)
305 			(void) execl(str, str, (char *)0);
306 		else
307 			(void) execl("/bin/sh", "sh", (char *)0);
308 		raw_print("sh: cannot execute.");
309 		exit(EXIT_FAILURE);
310 	}
311 	return 0;
312 }
313 #endif /* SHELL */
314 
315 #if defined(SHELL) || defined(DEF_PAGER) || defined(DEF_MAILREADER)
316 int
317 child(wt)
318 int wt;
319 {
320 	register int f;
321 	suspend_nhwindows((char *)0);	/* also calls end_screen() */
322 #ifdef _M_UNIX
323 	sco_mapon();
324 #endif
325 #ifdef LINUX
326 	linux_mapon();
327 #endif
328 #ifdef __linux__
329 	linux_mapon();
330 #endif
331 	if((f = fork()) == 0){		/* child */
332 		(void) setgid(getgid());
333 		(void) setuid(getuid());
334 #ifdef CHDIR
335 		(void) chdir(getenv("HOME"));
336 #endif
337 		return(1);
338 	}
339 	if(f == -1) {	/* cannot fork */
340 		pline("Fork failed.  Try again.");
341 		return(0);
342 	}
343 	/* fork succeeded; wait for child to exit */
344 	(void) signal(SIGINT,SIG_IGN);
345 	(void) signal(SIGQUIT,SIG_IGN);
346 	(void) wait( (int *) 0);
347 #ifdef _M_UNIX
348 	sco_mapoff();
349 #endif
350 #ifdef __linux__
351 	linux_mapoff();
352 #endif
353 #ifdef LINUX
354 	linux_mapoff();
355 #endif
356 	(void) signal(SIGINT, (SIG_RET_TYPE) done1);
357 #ifdef WIZARD
358 	if(wizard) (void) signal(SIGQUIT,SIG_DFL);
359 #endif
360 	if(wt) {
361 		raw_print("");
362 		wait_synch();
363 	}
364 	resume_nhwindows();
365 	return(0);
366 }
367 #endif
368 
369 #ifdef FILE_AREAS
370 
371 /*
372  * Unix file areas are directories with trailing slashes.
373  */
374 
375 char *make_file_name(filearea, filename)
376 const char *filearea, *filename;
377 {
378 	char *buf;
379 	int lenarea;
380 	if (filearea && filename[0]!='/')
381 	{
382 		lenarea = strlen(filearea);
383 		buf = (char *)alloc(lenarea+strlen(filename)+1);
384 		strcpy(buf, filearea);
385 		strcpy(buf+lenarea, filename);
386 	}
387 	else
388 	{
389 		buf = (char *)alloc(strlen(filename)+1);
390 		strcpy(buf, filename);
391 	}
392 	return buf;
393 }
394 
395 FILE *
396 fopen_datafile_area(filearea, filename, mode, use_scoreprefix)
397 const char *filearea, *filename, *mode;
398 boolean use_scoreprefix;
399 {
400 	FILE *fp;
401 	char *buf;
402 	buf = make_file_name(filearea, filename);
403 	fp = fopen(buf, mode);
404 	return fp;
405 }
406 
407 int
408 chmod_area(filearea, filename, mode)
409 const char *filearea, *filename;
410 int mode;
411 {
412 	int retval;
413 	char *buf;
414 	buf = make_file_name(filearea, filename);
415 	retval = chmod(buf, mode);
416 	free(buf);
417 	return retval;
418 }
419 
420 int
421 open_area(filearea, filename, flags, mode)
422 const char *filearea, *filename;
423 int flags, mode;
424 {
425 	int fd;
426 	char *buf;
427 	buf = make_file_name(filearea, filename);
428 	fd = open(buf, flags, mode);
429 	free(buf);
430 	return fd;
431 }
432 
433 int
434 creat_area(filearea, filename, mode)
435 const char *filearea, *filename;
436 int mode;
437 {
438 	int fd;
439 	char *buf;
440 	buf = make_file_name(filearea, filename);
441 	fd = creat(buf, mode);
442 	free(buf);
443 	return fd;
444 }
445 
446 int
447 rename_area(filearea, oldfilename, newfilename)
448 const char *filearea, *oldfilename, *newfilename;
449 {
450 	int retval;
451 	char *oldpath,*newpath;
452 	int lenarea;
453 	oldpath = make_file_name(filearea, oldfilename);
454 	newpath = make_file_name(filearea, newfilename);
455 	retval = rename(oldpath, newpath);
456 	free(oldpath);
457 	free(newpath);
458 	return retval;
459 }
460 
461 int
462 remove_area(filearea, filename)
463 const char *filearea, *filename;
464 {
465 	int retval;
466 	char *buf;
467 	buf = make_file_name(filearea, filename);
468 	retval = remove(buf);
469 	free(buf);
470 	return retval;
471 }
472 
473 FILE *
474 freopen_area(filearea, filename, mode, stream)
475 const char *filearea, *filename, *mode;
476 FILE *stream;
477 {
478 	FILE *fp;
479 	char *buf;
480 	buf = make_file_name(filearea, filename);
481 	fp = freopen(buf, mode, stream);
482 	free(buf);
483 	return fp;
484 }
485 
486 /* ----------  BEGIN FILE LOCKING HANDLING ----------- */
487 
488 /*
489  * ALI
490  *
491  * We assume that filenames are unique and so locks
492  * don't need to take the filearea into account. Since
493  * these locks are only used for RECORD, LOGFILE and HLOCK,
494  * this assumption is currently valid.
495  */
496 
497 static int nesting = 0;
498 
499 static int lockfd=-1;   /* for lock_file_area() to pass to unlock_file_area() */
500 
501 #define HUP     if (!program_state.done_hup)
502 
503 static char *
504 make_lockname(filename, lockname)
505 const char *filename;
506 char *lockname;
507 {
508 # ifdef NO_FILE_LINKS
509 	Strcpy(lockname, LOCKDIR);
510 	if (LOCKDIR[sizeof(LOCKDIR)-2]!='/')
511 		Strcat(lockname, "/");
512 	Strcat(lockname, filename);
513 # else
514 # ifdef FILE_AREAS
515 	Strcpy(lockname, FILE_AREA_VAR);
516 	Strcat(lockname, filename);
517 # else
518 	Strcpy(lockname, filename);
519 # endif
520 # endif	/* NO_FILE_LINKS */
521 	Strcat(lockname, "_lock");
522 	return lockname;
523 }
524 
525 static boolean
526 assert_lock(filename, lockname)
527 const char *filename, *lockname;
528 {
529 # ifdef NO_FILE_LINKS
530     return (lockfd = open(lockname, O_RDWR|O_CREAT|O_EXCL, 0666)) != -1;
531 # else
532     if (link(filename, lockname) == -1)
533 	if (errno == EXDEV)
534 	    return (lockfd = open(lockname, O_RDWR|O_CREAT|O_EXCL, 0666)) != -1;
535 	else
536 	    return FALSE;
537     else
538     {
539 	lockfd=-1;
540 	return TRUE;
541     }
542 # endif
543 }
544 
545 /*
546  * lock a file
547  *
548  * Note: The area says where the file is, not where the lock is.
549  */
550 
551 boolean
552 lock_file_area(filearea, filename, retryct)
553 const char *filearea, *filename;
554 int retryct;
555 {
556 	int retval=TRUE;
557 	char *lockname, locknambuf[BUFSZ];
558 	char *buf;
559 
560 	nesting++;
561 	if (nesting > 1) {
562 	    impossible("TRIED TO NEST LOCKS");
563 	    return TRUE;
564 	}
565 
566 	buf = make_file_name(filearea, filename);
567 	lockname = make_lockname(filename, locknambuf);
568 
569 	while (retval && !assert_lock(buf, lockname)) {
570 	    register int errnosv = errno;
571 
572 	    switch (errnosv) {  /* George Barbanis */
573 	    case EEXIST:
574 		if (retryct--) {
575 		    HUP raw_printf(
576 			    "Waiting for access to %s.  (%d retries left).",
577 			    filename, retryct);
578 # if defined(SYSV) || defined(ULTRIX)
579 		    (void)
580 # endif
581 			sleep(1);
582 		} else {
583 		    HUP (void) raw_print("I give up.  Sorry.");
584 		    HUP raw_printf("Perhaps there is an old %s around?",
585 					lockname);
586 		    nesting--;
587 		    retval = FALSE;
588 		}
589 
590 		break;
591 	    case ENOENT:
592 		HUP raw_printf("Can't find file %s to lock!", filename);
593 		nesting--;
594 		retval = FALSE;
595 	    case EACCES:
596 		HUP raw_printf("No write permission to lock %s!", filename);
597 		nesting--;
598 		retval = FALSE;
599 	    default:
600 		HUP perror(lockname);
601 		HUP raw_printf(
602 			     "Cannot lock %s for unknown reason (%d).",
603 			       filename, errnosv);
604 		nesting--;
605 		retval = FALSE;
606 	    }
607 
608 	}
609 
610 	free(buf);
611 	return retval;
612 }
613 
614 /* unlock file, which must be currently locked by lock_file_area */
615 void
616 unlock_file_area(filearea, filename)
617 const char *filearea, *filename;
618 {
619 	char *lockname, locknambuf[BUFSZ];
620 
621 	if (nesting == 1) {
622 		lockname = make_lockname(filename, locknambuf);
623 
624 		if (unlink(lockname) < 0)
625 			HUP raw_printf("Can't unlink %s.", lockname);
626 		if (lockfd!=-1)
627 		{
628 			(void) close(lockfd);
629 			lockfd=-1;
630 		}
631 	}
632 
633 	nesting--;
634 }
635 
636 /* ----------  END FILE LOCKING HANDLING ----------- */
637 
638 #endif	/* FILE_AREAS */
639 
640 #ifdef GETRES_SUPPORT
641 
642 extern int FDECL(nh_getresuid, (uid_t *, uid_t *, uid_t *));
643 extern uid_t NDECL(nh_getuid);
644 extern uid_t NDECL(nh_geteuid);
645 extern int FDECL(nh_getresgid, (gid_t *, gid_t *, gid_t *));
646 extern gid_t NDECL(nh_getgid);
647 extern gid_t NDECL(nh_getegid);
648 
649 int
650 (getresuid)(ruid, euid, suid)
651 uid_t *ruid, *euid, *suid;
652 {
653     return nh_getresuid(ruid, euid, suid);
654 }
655 
656 uid_t
657 (getuid)()
658 {
659     return nh_getuid();
660 }
661 
662 uid_t
663 (geteuid)()
664 {
665     return nh_geteuid();
666 }
667 
668 int
669 (getresgid)(rgid, egid, sgid)
670 gid_t *rgid, *egid, *sgid;
671 {
672     return nh_getresgid(rgid, egid, sgid);
673 }
674 
675 gid_t
676 (getgid)()
677 {
678     return nh_getgid();
679 }
680 
681 gid_t
682 (getegid)()
683 {
684     return nh_getegid();
685 }
686 
687 #endif	/* GETRES_SUPPORT */
688