1    | /*	SCCS Id: @(#)unixunix.c	3.3	94/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   | static struct stat buf, hbuf;
26   | 
27   | void
28   | gethdate(name)
29   | 	const char *name;
30   | {
31   | /* old version - for people short of space
32   |  *
33   |  *	register char *np;
34   |  *	if(stat(name, &hbuf))
35   |  *		error("Cannot get status of %s.",
36   |  *			(np = rindex(name, '/')) ? np+1 : name);
37   |  */
38   | /* version using PATH from: seismo!gregc@ucsf-cgl.ARPA (Greg Couch) */
39   | 
40   | /*
41   |  * The problem with   #include	<sys/param.h>   is that this include file
42   |  * does not exist on all systems, and moreover, that it sometimes includes
43   |  * <sys/types.h> again, so that the compiler sees these typedefs twice.
44   |  */
45   | #define		MAXPATHLEN	1024
46   | 
47   | 	register const char *np, *path;
48   | 	char filename[MAXPATHLEN+1];
49   | 	int pathlen;
50   | 
51   | 	if (index(name, '/') != (char *)0 ||
52   | 					(path = getenv("PATH")) == (char *)0)
53   | 		path = "";
54   | 
55   | 	for (;;) {
56   | 		if ((np = index(path, ':')) == (char *)0)
57   | 			np = path + strlen(path);	/* point to end str */
58   | 		pathlen = np - path;
59   | 		if (pathlen > MAXPATHLEN)
60   | 			pathlen = MAXPATHLEN;
61   | 		if (pathlen <= 1) {			/* %% */
62   | 			(void) strncpy(filename, name, MAXPATHLEN);
63   | 		} else {
64   | 			(void) strncpy(filename, path, pathlen);
65   | 			filename[pathlen] = '/';
66   | 			(void) strncpy(filename + pathlen + 1, name,
67   | 					(MAXPATHLEN - 1) - pathlen);
68   | 		}
69   | 		filename[MAXPATHLEN] = '\0';
70   | 		if (stat(filename, &hbuf) == 0)
71   | 			return;
72   | 		if (*np == '\0')
73   | 			break;
74   | 		path = np + 1;
75   | 	}
76   | 	if (strlen(name) > BUFSZ/2)
77   | 		name = name + strlen(name) - BUFSZ/2;
78   | #if defined(BOS) && defined(NHSTDC)
79   | /*
80   |  *	This one is really **STUPID**.  I don't know why it's happening
81   |  *	as similar constructs work elsewhere, but...
82   |  */
83   | 	if((np = rindex(name, '/')))
84   | 	     error("Cannot get status of %s.", np+1);
85   | 	else error("Cannot get status of %s.", name);
86   | #else
87   | 	error("Cannot get status of %s.",
88   | 		(np = rindex(name, '/')) ? np+1 : name);
89   | #endif
90   | }
91   | 
92   | #if 0
93   | int
94   | uptodate(fd)
95   | int fd;
96   | {
97   | 	if(fstat(fd, &buf)) {
98   | 		pline("Cannot get status of saved level? ");
99   | 		wait_synch();
100  | 		return(0);
101  | 	}
102  | 	if(buf.st_mtime < hbuf.st_mtime) {
103  | 		pline("Saved level is out of date. ");
104  | 		wait_synch();
105  | 		return(0);
106  | 	}
107  | 	return(1);
108  | }
109  | #endif
110  | 
111  | /* see whether we should throw away this xlock file */
112  | static int
113  | veryold(fd)
114  | int fd;
115  | {
116  | 	time_t date;
117  | 
118  | 	if(fstat(fd, &buf)) return(0);			/* cannot get status */
119  | #ifndef INSURANCE
120  | 	if(buf.st_size != sizeof(int)) return(0);	/* not an xlock file */
121  | #endif
122  | #ifdef BSD
123  | 	(void) time((long *)(&date));
124  | #else
125  | 	(void) time(&date);
126  | #endif
127  | 	if(date - buf.st_mtime < 3L*24L*60L*60L) {	/* recent */
128  | #ifndef NETWORK
129  | 		extern int errno;
130  | #endif
131  | 		int lockedpid;	/* should be the same size as hackpid */
132  | 
133  | 		if(read(fd, (genericptr_t)&lockedpid, sizeof(lockedpid)) !=
134  | 			sizeof(lockedpid))
135  | 			/* strange ... */
136  | 			return(0);
137  | 
138  | 		/* From: Rick Adams <seismo!rick> */
139  | 		/* This will work on 4.1cbsd, 4.2bsd and system 3? & 5. */
140  | 		/* It will do nothing on V7 or 4.1bsd. */
141  | #ifndef NETWORK
142  | 		/* It will do a VERY BAD THING if the playground is shared
143  | 		   by more than one machine! -pem */
144  | 		if(!(kill(lockedpid, 0) == -1 && errno == ESRCH))
145  | #endif
146  | 			return(0);
147  | 	}
148  | 	(void) close(fd);
149  | 	return(1);
150  | }
151  | 
152  | static int
153  | eraseoldlocks()
154  | {
155  | 	register int i;
156  | 
157  | 	/* cannot use maxledgerno() here, because we need to find a lock name
158  | 	 * before starting everything (including the dungeon initialization
159  | 	 * that sets astral_level, needed for maxledgerno()) up
160  | 	 */
161  | 	for(i = 1; i <= MAXDUNGEON*MAXLEVEL + 1; i++) {
162  | 		/* try to remove all */
163  | 		set_levelfile_name(lock, i);
164  | 		(void) unlink(fqname(lock, LEVELPREFIX, 0));
165  | 	}
166  | 	set_levelfile_name(lock, 0);
167  | 	if (unlink(fqname(lock, LEVELPREFIX, 0)))
168  | 		return(0);				/* cannot remove it */
169  | 	return(1);					/* success! */
170  | }
171  | 
172  | void
173  | getlock()
174  | {
175  | 	extern int errno;
176  | 	register int i = 0, fd, c;
177  | 	const char *fq_lock;
178  | 
179  | #ifdef TTY_GRAPHICS
180  | 	/* idea from rpick%ucqais@uccba.uc.edu
181  | 	 * prevent automated rerolling of characters
182  | 	 * test input (fd0) so that tee'ing output to get a screen dump still
183  | 	 * works
184  | 	 * also incidentally prevents development of any hack-o-matic programs
185  | 	 */
186  | 	/* added check for window-system type -dlc */
187  | 	if (!strcmp(windowprocs.name, "tty"))
188  | 	    if (!isatty(0))
189  | 		error("You must play from a terminal.");
190  | #endif
191  | 
192  | 	/* we ignore QUIT and INT at this point */
193  | 	if (!lock_file(HLOCK, LOCKPREFIX, 10)) {
194  | 		wait_synch();
195  | 		error("%s", "");
196  | 	}
197  | 
198  | 	regularize(lock);
199  | 	set_levelfile_name(lock, 0);
200  | 
201  | 	if(locknum) {
202  | 		if(locknum > 25) locknum = 25;
203  | 
204  | 		do {
205  | 			lock[0] = 'a' + i++;
206  | 			fq_lock = fqname(lock, LEVELPREFIX, 0);
207  | 
208  | 			if((fd = open(fq_lock, 0)) == -1) {
209  | 			    if(errno == ENOENT) goto gotlock; /* no such file */
210  | 			    perror(fq_lock);
211  | 			    unlock_file(HLOCK);
212  | 			    error("Cannot open %s", fq_lock);
213  | 			}
214  | 
215  | 			if(veryold(fd) /* closes fd if true */
216  | 							&& eraseoldlocks())
217  | 				goto gotlock;
218  | 			(void) close(fd);
219  | 		} while(i < locknum);
220  | 
221  | 		unlock_file(HLOCK);
222  | 		error("Too many hacks running now.");
223  | 	} else {
224  | 		fq_lock = fqname(lock, LEVELPREFIX, 0);
225  | 		if((fd = open(fq_lock, 0)) == -1) {
226  | 			if(errno == ENOENT) goto gotlock;    /* no such file */
227  | 			perror(fq_lock);
228  | 			unlock_file(HLOCK);
229  | 			error("Cannot open %s", fq_lock);
230  | 		}
231  | 
232  | 		if(veryold(fd) /* closes fd if true */ && eraseoldlocks())
233  | 			goto gotlock;
234  | 		(void) close(fd);
235  | 
236  | 		if(iflags.window_inited) {
237  | 		    c = yn("There is already a game in progress under your name.  Destroy old game?");
238  | 		} else {
239  | 		    (void) printf("\nThere is already a game in progress under your name.");
240  | 		    (void) printf("  Destroy old game? [yn] ");
241  | 		    (void) fflush(stdout);
242  | 		    c = getchar();
243  | 		    (void) putchar(c);
244  | 		    (void) fflush(stdout);
245  | 		    while (getchar() != '\n') ; /* eat rest of line and newline */
246  | 		}
247  | 		if(c == 'y' || c == 'Y')
248  | 			if(eraseoldlocks())
249  | 				goto gotlock;
250  | 			else {
251  | 				unlock_file(HLOCK);
252  | 				error("Couldn't destroy old game.");
253  | 			}
254  | 		else {
255  | 			unlock_file(HLOCK);
256  | 			error("%s", "");
257  | 		}
258  | 	}
259  | 
260  | gotlock:
261  | 	fd = creat(fq_lock, FCMASK);
262  | 	unlock_file(HLOCK);
263  | 	if(fd == -1) {
264  | 		error("cannot creat lock file (%s).", fq_lock);
265  | 	} else {
266  | 		if(write(fd, (genericptr_t) &hackpid, sizeof(hackpid))
267  | 		    != sizeof(hackpid)){
268  | 			error("cannot write lock (%s)", fq_lock);
269  | 		}
270  | 		if(close(fd) == -1) {
271  | 			error("cannot close lock (%s)", fq_lock);
272  | 		}
273  | 	}
274  | }
275  | 
276  | void
277  | regularize(s)	/* normalize file name - we don't like .'s, /'s, spaces */
278  | register char *s;
279  | {
280  | 	register char *lp;
281  | #if defined(SYSV) && !defined(AIX_31) && defined(COMPRESS)
282  | 	int i;
283  | #endif
284  | 
285  | 	while((lp=index(s, '.')) || (lp=index(s, '/')) || (lp=index(s,' ')))
286  | 		*lp = '_';
287  | #if defined(SYSV) && !defined(AIX_31)
288  | 	/* avoid problems with 14 character file name limit */
289  | # ifdef COMPRESS
290  | 	/* leave room for .e from error and .Z from compress appended to
291  | 	 * save files */
292  | #  ifdef COMPRESS_EXTENSION
293  | 	i = 12 - strlen(COMPRESS_EXTENSION);
294  | #  else
295  | 	i = 10;		/* should never happen... */
296  | #  endif
297  | 	if(strlen(s) > i)
298  | 		s[i] = '\0';
299  | # else
300  | 	if(strlen(s) > 11)
301  | 		/* leave room for .nn appended to level files */
302  | 		s[11] = '\0';
303  | # endif
304  | #endif
305  | }
306  | 
307  | #ifdef SHELL
308  | int
309  | dosh()
310  | {
311  | 	register char *str;
312  | 	if(child(0)) {
313  | 		if((str = getenv("SHELL")) != (char*)0)
314  | 			(void) execl(str, str, (char *)0);
315  | 		else
316  | 			(void) execl("/bin/sh", "sh", (char *)0);
317  | 		raw_print("sh: cannot execute.");
318  | 		exit(EXIT_FAILURE);
319  | 	}
320  | 	return 0;
321  | }
322  | #endif /* SHELL */
323  | 
324  | #if defined(SHELL) || defined(DEF_PAGER) || defined(DEF_MAILREADER)
325  | int
326  | child(wt)
327  | int wt;
328  | {
329  | 	register int f;
330  | 	suspend_nhwindows((char *)0);	/* also calls end_screen() */
331  | #ifdef _M_UNIX
332  | 	sco_mapon();
333  | #endif
334  | #ifdef __linux__
335  | 	linux_mapon();
336  | #endif
337  | 	if((f = fork()) == 0){		/* child */
338  | 		(void) setgid(getgid());
339  | 		(void) setuid(getuid());
340  | #ifdef CHDIR
341  | 		(void) chdir(getenv("HOME"));
342  | #endif
343  | 		return(1);
344  | 	}
345  | 	if(f == -1) {	/* cannot fork */
346  | 		pline("Fork failed.  Try again.");
347  | 		return(0);
348  | 	}
349  | 	/* fork succeeded; wait for child to exit */
350  | 	(void) signal(SIGINT,SIG_IGN);
351  | 	(void) signal(SIGQUIT,SIG_IGN);
352  | 	(void) wait( (int *) 0);
353  | #ifdef _M_UNIX
354  | 	sco_mapoff();
355  | #endif
356  | #ifdef __linux__
357  | 	linux_mapoff();
358  | #endif
359  | 	(void) signal(SIGINT, (SIG_RET_TYPE) done1);
360  | #ifdef WIZARD
361  | 	if(wizard) (void) signal(SIGQUIT,SIG_DFL);
362  | #endif
363  | 	if(wt) {
364  | 		raw_print("");
365  | 		wait_synch();
366  | 	}
367  | 	resume_nhwindows();
368  | 	return(0);
369  | }
370  | #endif