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