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