1 /* NetHack 3.6	unixunix.c	$NHDT-Date: 1432512788 2015/05/25 00:13:08 $  $NHDT-Branch: master $:$NHDT-Revision: 1.22 $ */
2 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
3 /*-Copyright (c) Kenneth Lorber, Kensington, Maryland, 2015. */
4 /* NetHack may be freely redistributed.  See license for details. */
5 
6 /* This file collects some Unix dependencies */
7 
8 #include "hack.h" /* mainly for index() which depends on BSD */
9 
10 #include <errno.h>
11 #include <sys/stat.h>
12 #if defined(NO_FILE_LINKS) || defined(SUNOS4) || defined(POSIX_TYPES)
13 #include <fcntl.h>
14 #endif
15 #include <signal.h>
16 
17 #ifdef _M_UNIX
18 extern void NDECL(sco_mapon);
19 extern void NDECL(sco_mapoff);
20 #endif
21 #ifdef __linux__
22 extern void NDECL(linux_mapon);
23 extern void NDECL(linux_mapoff);
24 #endif
25 
26 #ifndef NHSTDC
27 extern int errno;
28 #endif
29 
30 static struct stat buf;
31 
32 /* see whether we should throw away this xlock file;
33    if yes, close it, otherwise leave it open */
34 static int
veryold(fd)35 veryold(fd)
36 int fd;
37 {
38     time_t date;
39 
40     if (fstat(fd, &buf))
41         return 0; /* cannot get status */
42 #ifndef INSURANCE
43     if (buf.st_size != sizeof (int))
44         return 0; /* not an xlock file */
45 #endif
46 #if defined(BSD) && !defined(POSIX_TYPES)
47     (void) time((long *) (&date));
48 #else
49     (void) time(&date);
50 #endif
51     if (date - buf.st_mtime < 3L * 24L * 60L * 60L) { /* recent */
52         int lockedpid; /* should be the same size as hackpid */
53 
54         if (read(fd, (genericptr_t) &lockedpid, sizeof lockedpid)
55             != sizeof lockedpid)
56             /* strange ... */
57             return 0;
58 
59 /* From: Rick Adams <seismo!rick> */
60 /* This will work on 4.1cbsd, 4.2bsd and system 3? & 5. */
61 /* It will do nothing on V7 or 4.1bsd. */
62 #ifndef NETWORK
63         /* It will do a VERY BAD THING if the playground is shared
64            by more than one machine! -pem */
65         if (!(kill(lockedpid, 0) == -1 && errno == ESRCH))
66 #endif
67             return 0;
68     }
69     (void) close(fd);
70     return 1;
71 }
72 
73 static int
eraseoldlocks()74 eraseoldlocks()
75 {
76     register int i;
77 
78     program_state.preserve_locks = 0; /* not required but shows intent */
79     /* cannot use maxledgerno() here, because we need to find a lock name
80      * before starting everything (including the dungeon initialization
81      * that sets astral_level, needed for maxledgerno()) up
82      */
83     for (i = 1; i <= MAXDUNGEON * MAXLEVEL + 1; i++) {
84         /* try to remove all */
85         set_levelfile_name(lock, i);
86         (void) unlink(fqname(lock, LEVELPREFIX, 0));
87     }
88     set_levelfile_name(lock, 0);
89     if (unlink(fqname(lock, LEVELPREFIX, 0)))
90         return 0; /* cannot remove it */
91     return 1;     /* success! */
92 }
93 
94 void
getlock()95 getlock()
96 {
97     register int i = 0, fd, c;
98     const char *fq_lock;
99 
100 #ifdef TTY_GRAPHICS
101     /* idea from rpick%ucqais@uccba.uc.edu
102      * prevent automated rerolling of characters
103      * test input (fd0) so that tee'ing output to get a screen dump still
104      * works
105      * also incidentally prevents development of any hack-o-matic programs
106      */
107     /* added check for window-system type -dlc */
108     if (!strcmp(windowprocs.name, "tty"))
109         if (!isatty(0))
110             error("You must play from a terminal.");
111 #endif
112 
113     /* we ignore QUIT and INT at this point */
114     if (!lock_file(HLOCK, LOCKPREFIX, 10)) {
115         wait_synch();
116         error("%s", "");
117     }
118 
119     /* default value of lock[] is "1lock" where '1' gets changed to
120        'a','b',&c below; override the default and use <uid><charname>
121        if we aren't restricting the number of simultaneous games */
122     if (!locknum)
123         Sprintf(lock, "%u%s", (unsigned) getuid(), plname);
124 
125     regularize(lock);
126     set_levelfile_name(lock, 0);
127 
128     if (locknum) {
129         if (locknum > 25)
130             locknum = 25;
131 
132         do {
133             lock[0] = 'a' + i++;
134             fq_lock = fqname(lock, LEVELPREFIX, 0);
135 
136             if ((fd = open(fq_lock, 0)) == -1) {
137                 if (errno == ENOENT)
138                     goto gotlock; /* no such file */
139                 perror(fq_lock);
140                 unlock_file(HLOCK);
141                 error("Cannot open %s", fq_lock);
142             }
143 
144             /* veryold() closes fd if true */
145             if (veryold(fd) && eraseoldlocks())
146                 goto gotlock;
147             (void) close(fd);
148         } while (i < locknum);
149 
150         unlock_file(HLOCK);
151         error("Too many hacks running now.");
152     } else {
153         fq_lock = fqname(lock, LEVELPREFIX, 0);
154         if ((fd = open(fq_lock, 0)) == -1) {
155             if (errno == ENOENT)
156                 goto gotlock; /* no such file */
157             perror(fq_lock);
158             unlock_file(HLOCK);
159             error("Cannot open %s", fq_lock);
160         }
161 
162         /* veryold() closes fd if true */
163         if (veryold(fd) && eraseoldlocks())
164             goto gotlock;
165         (void) close(fd);
166 
167       {
168         const char destroy_old_game_prompt[] =
169     "There is already a game in progress under your name.  Destroy old game?";
170 
171         if (iflags.window_inited) {
172             /* this is a candidate for paranoid_confirmation */
173             c = yn(destroy_old_game_prompt);
174         } else {
175             (void) printf("\n%s [yn] ", destroy_old_game_prompt);
176             (void) fflush(stdout);
177             if ((c = getchar()) != EOF) {
178                 int tmp;
179 
180                 (void) putchar(c);
181                 (void) fflush(stdout);
182                 while ((tmp = getchar()) != '\n' && tmp != EOF)
183                     ; /* eat rest of line and newline */
184             }
185         }
186       }
187         if (c == 'y' || c == 'Y') {
188             if (eraseoldlocks()) {
189                 goto gotlock;
190             } else {
191                 unlock_file(HLOCK);
192                 error("Couldn't destroy old game.");
193             }
194         } else {
195             unlock_file(HLOCK);
196             error("%s", "");
197         }
198     }
199 
200 gotlock:
201     fd = creat(fq_lock, FCMASK);
202     unlock_file(HLOCK);
203     if (fd == -1) {
204         error("cannot creat lock file (%s).", fq_lock);
205     } else {
206         if (write(fd, (genericptr_t) &hackpid, sizeof hackpid)
207             != sizeof hackpid) {
208             error("cannot write lock (%s)", fq_lock);
209         }
210         if (close(fd) == -1) {
211             error("cannot close lock (%s)", fq_lock);
212         }
213     }
214 }
215 
216 /* normalize file name - we don't like .'s, /'s, spaces */
217 void
regularize(s)218 regularize(s)
219 register char *s;
220 {
221     register char *lp;
222 
223     while ((lp = index(s, '.')) != 0 || (lp = index(s, '/')) != 0
224            || (lp = index(s, ' ')) != 0)
225         *lp = '_';
226 #if defined(SYSV) && !defined(AIX_31) && !defined(SVR4) && !defined(LINUX) \
227     && !defined(__APPLE__)
228 /* avoid problems with 14 character file name limit */
229 #ifdef COMPRESS
230     /* leave room for .e from error and .Z from compress appended to
231      * save files */
232     {
233 #ifdef COMPRESS_EXTENSION
234         int i = 12 - strlen(COMPRESS_EXTENSION);
235 #else
236         int i = 10; /* should never happen... */
237 #endif
238         if (strlen(s) > i)
239             s[i] = '\0';
240     }
241 #else
242     if (strlen(s) > 11)
243         /* leave room for .nn appended to level files */
244         s[11] = '\0';
245 #endif
246 #endif
247 }
248 
249 #if defined(TIMED_DELAY) && !defined(msleep) && defined(SYSV)
250 #include <poll.h>
251 
252 void
msleep(msec)253 msleep(msec)
254 unsigned msec; /* milliseconds */
255 {
256     struct pollfd unused;
257     int msecs = msec; /* poll API is signed */
258 
259     if (msecs < 0)
260         msecs = 0; /* avoid infinite sleep */
261     (void) poll(&unused, (unsigned long) 0, msecs);
262 }
263 #endif /* TIMED_DELAY for SYSV */
264 
265 #ifdef SHELL
266 int
dosh()267 dosh()
268 {
269     char *str;
270 
271 #ifdef SYSCF
272     if (!sysopt.shellers || !sysopt.shellers[0]
273         || !check_user_string(sysopt.shellers)) {
274         /* FIXME: should no longer assume a particular command keystroke,
275            and perhaps ought to say "unavailable" rather than "unknown" */
276         Norep("Unknown command '!'.");
277         return 0;
278     }
279 #endif
280     if (child(0)) {
281         if ((str = getenv("SHELL")) != (char *) 0)
282             (void) execl(str, str, (char *) 0);
283         else
284             (void) execl("/bin/sh", "sh", (char *) 0);
285         raw_print("sh: cannot execute.");
286         exit(EXIT_FAILURE);
287     }
288     return 0;
289 }
290 #endif /* SHELL */
291 
292 #if defined(SHELL) || defined(DEF_PAGER) || defined(DEF_MAILREADER)
293 int
child(wt)294 child(wt)
295 int wt;
296 {
297     register int f;
298 
299     suspend_nhwindows((char *) 0); /* also calls end_screen() */
300 #ifdef _M_UNIX
301     sco_mapon();
302 #endif
303 #ifdef __linux__
304     linux_mapon();
305 #endif
306     if ((f = fork()) == 0) { /* child */
307         (void) setgid(getgid());
308         (void) setuid(getuid());
309 #ifdef CHDIR
310         (void) chdir(getenv("HOME"));
311 #endif
312         return 1;
313     }
314     if (f == -1) { /* cannot fork */
315         pline("Fork failed.  Try again.");
316         return 0;
317     }
318     /* fork succeeded; wait for child to exit */
319 #ifndef NO_SIGNAL
320     (void) signal(SIGINT, SIG_IGN);
321     (void) signal(SIGQUIT, SIG_IGN);
322 #endif
323     (void) wait((int *) 0);
324 #ifdef _M_UNIX
325     sco_mapoff();
326 #endif
327 #ifdef __linux__
328     linux_mapoff();
329 #endif
330 #ifndef NO_SIGNAL
331     (void) signal(SIGINT, (SIG_RET_TYPE) done1);
332     if (wizard)
333         (void) signal(SIGQUIT, SIG_DFL);
334 #endif
335     if (wt) {
336         raw_print("");
337         wait_synch();
338     }
339     resume_nhwindows();
340     return 0;
341 }
342 #endif /* SHELL || DEF_PAGER || DEF_MAILREADER */
343 
344 #ifdef GETRES_SUPPORT
345 
346 extern int FDECL(nh_getresuid, (uid_t *, uid_t *, uid_t *));
347 extern uid_t NDECL(nh_getuid);
348 extern uid_t NDECL(nh_geteuid);
349 extern int FDECL(nh_getresgid, (gid_t *, gid_t *, gid_t *));
350 extern gid_t NDECL(nh_getgid);
351 extern gid_t NDECL(nh_getegid);
352 
353 /* the following several functions assume __STDC__ where parentheses
354    around the name of a function-like macro prevent macro expansion */
355 
356 int (getresuid)(ruid, euid, suid)
357 uid_t *ruid, *euid, *suid;
358 {
359     return nh_getresuid(ruid, euid, suid);
360 }
361 
uid_t(getuid)362 uid_t (getuid)()
363 {
364     return nh_getuid();
365 }
366 
uid_t(geteuid)367 uid_t (geteuid)()
368 {
369     return nh_geteuid();
370 }
371 
372 int (getresgid)(rgid, egid, sgid)
373 gid_t *rgid, *egid, *sgid;
374 {
375     return nh_getresgid(rgid, egid, sgid);
376 }
377 
gid_t(getgid)378 gid_t (getgid)()
379 {
380     return nh_getgid();
381 }
382 
gid_t(getegid)383 gid_t (getegid)()
384 {
385     return nh_getegid();
386 }
387 
388 #endif /* GETRES_SUPPORT */
389 
390 /* XXX should be ifdef PANICTRACE_GDB, but there's no such symbol yet */
391 #ifdef PANICTRACE
392 boolean
file_exists(path)393 file_exists(path)
394 const char *path;
395 {
396     struct stat sb;
397 
398     /* Just see if it's there - trying to figure out if we can actually
399      * execute it in all cases is too hard - we really just want to
400      * catch typos in SYSCF.
401      */
402     if (stat(path, &sb)) {
403         return FALSE;
404     }
405     return TRUE;
406 }
407 #endif
408