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