/* SCCS Id: @(#)unixunix.c 3.4 1994/11/07 */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /* NetHack may be freely redistributed. See license for details. */ /* This file collects some Unix dependencies */ #include "hack.h" /* mainly for index() which depends on BSD */ #include #include #if defined(NO_FILE_LINKS) || defined(SUNOS4) || defined(POSIX_TYPES) #include #endif #include #ifdef _M_UNIX extern void NDECL(sco_mapon); extern void NDECL(sco_mapoff); #endif #ifdef __linux__ extern void NDECL(linux_mapon); extern void NDECL(linux_mapoff); #endif #ifdef LINUX extern void NDECL(linux_mapon); extern void NDECL(linux_mapoff); #endif static int FDECL (veryold, (int)); static int NDECL (eraseoldlocks); #ifndef NHSTDC extern int errno; #endif static struct stat buf; /* see whether we should throw away this xlock file */ static int veryold(fd) int fd; { time_t date; if(fstat(fd, &buf)) return(0); /* cannot get status */ #ifndef INSURANCE if(buf.st_size != sizeof(int)) return(0); /* not an xlock file */ #endif #if defined(BSD) && !defined(POSIX_TYPES) (void) time((long *)(&date)); #else (void) time(&date); #endif if(date - buf.st_mtime < 3L*24L*60L*60L) { /* recent */ int lockedpid; /* should be the same size as hackpid */ if(read(fd, (genericptr_t)&lockedpid, sizeof(lockedpid)) != sizeof(lockedpid)) /* strange ... */ return(0); /* From: Rick Adams */ /* This will work on 4.1cbsd, 4.2bsd and system 3? & 5. */ /* It will do nothing on V7 or 4.1bsd. */ #ifndef NETWORK /* It will do a VERY BAD THING if the playground is shared by more than one machine! -pem */ if(!(kill(lockedpid, 0) == -1 && errno == ESRCH)) #endif return(0); } (void) close(fd); return(1); } static int eraseoldlocks() { register int i; /* cannot use maxledgerno() here, because we need to find a lock name * before starting everything (including the dungeon initialization * that sets astral_level, needed for maxledgerno()) up */ for(i = 1; i <= MAXDUNGEON*MAXLEVEL + 1; i++) { /* try to remove all */ set_levelfile_name(lock, i); #ifdef FILE_AREAS (void) remove_area(FILE_AREA_LEVL, lock); #else (void) unlink(fqname(lock, LEVELPREFIX, 0)); #endif } set_levelfile_name(lock, 0); #ifdef FILE_AREAS if (remove_area(FILE_AREA_LEVL, lock)) #else if (unlink(fqname(lock, LEVELPREFIX, 0))) #endif return(0); /* cannot remove it */ return(1); /* success! */ } void getlock() { register int i = 0, fd, c; #ifndef FILE_AREAS const char *fq_lock; #endif #ifdef TTY_GRAPHICS /* idea from rpick%ucqais@uccba.uc.edu * prevent automated rerolling of characters * test input (fd0) so that tee'ing output to get a screen dump still * works * also incidentally prevents development of any hack-o-matic programs */ /* added check for window-system type -dlc */ if (!strcmp(windowprocs.name, "tty")) if (!isatty(0)) error("You must play from a terminal."); #endif /* we ignore QUIT and INT at this point */ #ifndef FILE_AREAS if (!lock_file(HLOCK, LOCKPREFIX, 10)) { #else if (!lock_file_area(HLOCK_AREA, HLOCK, 10)) { #endif wait_synch(); error("%s", ""); } regularize(lock); set_levelfile_name(lock, 0); if(locknum) { if(locknum > 25) locknum = 25; do { lock[0] = 'a' + i++; #ifndef FILE_AREAS fq_lock = fqname(lock, LEVELPREFIX, 0); if((fd = open(fq_lock, 0, 0)) == -1) { #else if((fd = open_area(FILE_AREA_LEVL, lock, 0, 0)) == -1) { #endif if(errno == ENOENT) goto gotlock; /* no such file */ #ifndef FILE_AREAS perror(fq_lock); unlock_file(HLOCK); error("Cannot open %s", fq_lock); #else perror(lock); unlock_file_area(HLOCK_AREA, HLOCK); error("Cannot open %s", lock); #endif } if(veryold(fd) /* closes fd if true */ && eraseoldlocks()) goto gotlock; (void) close(fd); } while(i < locknum); unlock_file_area(HLOCK_AREA, HLOCK); error("Too many hacks running now."); } else { #ifndef FILE_AREAS fq_lock = fqname(lock, LEVELPREFIX, 0); if((fd = open(fq_lock, 0, 0)) == -1) { #else if((fd = open_area(FILE_AREA_LEVL, lock, 0, 0)) == -1) { #endif if(errno == ENOENT) goto gotlock; /* no such file */ #ifndef FILE_AREAS perror(fq_lock); unlock_file(HLOCK); error("Cannot open %s", fq_lock); #else perror(lock); unlock_file_area(HLOCK_AREA, HLOCK); error("Cannot open %s", lock); #endif } if(veryold(fd) /* closes fd if true */ && eraseoldlocks()) goto gotlock; (void) close(fd); if(iflags.window_inited) { c = yn("There is already a game in progress under your name. Destroy old game?"); } else { (void) printf("\nThere is already a game in progress under your name."); (void) printf(" Destroy old game? [yn] "); (void) fflush(stdout); c = getchar(); (void) putchar(c); (void) fflush(stdout); while (getchar() != '\n') ; /* eat rest of line and newline */ } if(c == 'y' || c == 'Y') if(eraseoldlocks()) goto gotlock; else { unlock_file_area(HLOCK_AREA, HLOCK); error("Couldn't destroy old game."); } else { unlock_file_area(HLOCK_AREA, HLOCK); error("%s", ""); } } gotlock: #ifndef FILE_AREAS fd = creat(fq_lock, FCMASK); #else fd = creat_area(FILE_AREA_LEVL, lock, FCMASK); #endif unlock_file_area(HLOCK_AREA, HLOCK); if(fd == -1) { #ifndef FILE_AREAS error("cannot creat lock file (%s).", fq_lock); #else error("cannot creat lock file (%s in %s).", lock, FILE_AREA_LEVL); #endif } else { if(write(fd, (genericptr_t) &hackpid, sizeof(hackpid)) != sizeof(hackpid)){ #ifndef FILE_AREAS error("cannot write lock (%s)", fq_lock); #else error("cannot write lock (%s in %s)", lock, FILE_AREA_LEVL); #endif } if(close(fd) == -1) { #ifndef FILE_AREAS error("cannot close lock (%s)", fq_lock); #else error("cannot close lock (%s in %s)", lock, FILE_AREA_LEVL); #endif } } } void regularize(s) /* normalize file name - we don't like .'s, /'s, spaces */ register char *s; { register char *lp; while((lp=index(s, '.')) || (lp=index(s, '/')) || (lp=index(s,' '))) *lp = '_'; #if defined(SYSV) && !defined(AIX_31) && !defined(SVR4) && !defined(LINUX) && !defined(__APPLE__) /* avoid problems with 14 character file name limit */ # ifdef COMPRESS /* leave room for .e from error and .Z from compress appended to * save files */ { # ifdef COMPRESS_EXTENSION int i = 12 - strlen(COMPRESS_EXTENSION); # else int i = 10; /* should never happen... */ # endif if(strlen(s) > i) s[i] = '\0'; } # else if(strlen(s) > 11) /* leave room for .nn appended to level files */ s[11] = '\0'; # endif #endif } #if defined(TIMED_DELAY) && !defined(msleep) && defined(SYSV) #include void msleep(msec) unsigned msec; /* milliseconds */ { struct pollfd unused; int msecs = msec; /* poll API is signed */ if (msecs < 0) msecs = 0; /* avoid infinite sleep */ (void) poll(&unused, (unsigned long)0, msecs); } #endif /* TIMED_DELAY for SYSV */ #ifdef SHELL int dosh() { register char *str; if(child(0)) { if((str = getenv("SHELL")) != (char*)0) (void) execl(str, str, (char *)0); else (void) execl("/bin/sh", "sh", (char *)0); raw_print("sh: cannot execute."); exit(EXIT_FAILURE); } return 0; } #endif /* SHELL */ #if defined(SHELL) || defined(DEF_PAGER) || defined(DEF_MAILREADER) int child(wt) int wt; { register int f; suspend_nhwindows((char *)0); /* also calls end_screen() */ #ifdef _M_UNIX sco_mapon(); #endif #ifdef LINUX linux_mapon(); #endif #ifdef __linux__ linux_mapon(); #endif if((f = fork()) == 0){ /* child */ (void) setgid(getgid()); (void) setuid(getuid()); #ifdef CHDIR (void) chdir(getenv("HOME")); #endif return(1); } if(f == -1) { /* cannot fork */ pline("Fork failed. Try again."); return(0); } /* fork succeeded; wait for child to exit */ (void) signal(SIGINT,SIG_IGN); (void) signal(SIGQUIT,SIG_IGN); (void) wait( (int *) 0); #ifdef _M_UNIX sco_mapoff(); #endif #ifdef __linux__ linux_mapoff(); #endif #ifdef LINUX linux_mapoff(); #endif (void) signal(SIGINT, (SIG_RET_TYPE) done1); #ifdef WIZARD if(wizard) (void) signal(SIGQUIT,SIG_DFL); #endif if(wt) { raw_print(""); wait_synch(); } resume_nhwindows(); return(0); } #endif #ifdef FILE_AREAS /* * Unix file areas are directories with trailing slashes. */ char *make_file_name(filearea, filename) const char *filearea, *filename; { char *buf; int lenarea; if (filearea && filename[0]!='/') { lenarea = strlen(filearea); buf = (char *)alloc(lenarea+strlen(filename)+1); strcpy(buf, filearea); strcpy(buf+lenarea, filename); } else { buf = (char *)alloc(strlen(filename)+1); strcpy(buf, filename); } return buf; } FILE * fopen_datafile_area(filearea, filename, mode, use_scoreprefix) const char *filearea, *filename, *mode; boolean use_scoreprefix; { FILE *fp; char *buf; buf = make_file_name(filearea, filename); fp = fopen(buf, mode); return fp; } int chmod_area(filearea, filename, mode) const char *filearea, *filename; int mode; { int retval; char *buf; buf = make_file_name(filearea, filename); retval = chmod(buf, mode); free(buf); return retval; } int open_area(filearea, filename, flags, mode) const char *filearea, *filename; int flags, mode; { int fd; char *buf; buf = make_file_name(filearea, filename); fd = open(buf, flags, mode); free(buf); return fd; } int creat_area(filearea, filename, mode) const char *filearea, *filename; int mode; { int fd; char *buf; buf = make_file_name(filearea, filename); fd = creat(buf, mode); free(buf); return fd; } int rename_area(filearea, oldfilename, newfilename) const char *filearea, *oldfilename, *newfilename; { int retval; char *oldpath,*newpath; int lenarea; oldpath = make_file_name(filearea, oldfilename); newpath = make_file_name(filearea, newfilename); retval = rename(oldpath, newpath); free(oldpath); free(newpath); return retval; } int remove_area(filearea, filename) const char *filearea, *filename; { int retval; char *buf; buf = make_file_name(filearea, filename); retval = remove(buf); free(buf); return retval; } FILE * freopen_area(filearea, filename, mode, stream) const char *filearea, *filename, *mode; FILE *stream; { FILE *fp; char *buf; buf = make_file_name(filearea, filename); fp = freopen(buf, mode, stream); free(buf); return fp; } /* ---------- BEGIN FILE LOCKING HANDLING ----------- */ /* * ALI * * We assume that filenames are unique and so locks * don't need to take the filearea into account. Since * these locks are only used for RECORD, LOGFILE and HLOCK, * this assumption is currently valid. */ static int nesting = 0; static int lockfd=-1; /* for lock_file_area() to pass to unlock_file_area() */ #define HUP if (!program_state.done_hup) static char * make_lockname(filename, lockname) const char *filename; char *lockname; { # ifdef NO_FILE_LINKS Strcpy(lockname, LOCKDIR); if (LOCKDIR[sizeof(LOCKDIR)-2]!='/') Strcat(lockname, "/"); Strcat(lockname, filename); # else # ifdef FILE_AREAS Strcpy(lockname, FILE_AREA_VAR); Strcat(lockname, filename); # else Strcpy(lockname, filename); # endif # endif /* NO_FILE_LINKS */ Strcat(lockname, "_lock"); return lockname; } static boolean assert_lock(filename, lockname) const char *filename, *lockname; { # ifdef NO_FILE_LINKS return (lockfd = open(lockname, O_RDWR|O_CREAT|O_EXCL, 0666)) != -1; # else if (link(filename, lockname) == -1) if (errno == EXDEV) return (lockfd = open(lockname, O_RDWR|O_CREAT|O_EXCL, 0666)) != -1; else return FALSE; else { lockfd=-1; return TRUE; } # endif } /* * lock a file * * Note: The area says where the file is, not where the lock is. */ boolean lock_file_area(filearea, filename, retryct) const char *filearea, *filename; int retryct; { int retval=TRUE; char *lockname, locknambuf[BUFSZ]; char *buf; nesting++; if (nesting > 1) { impossible("TRIED TO NEST LOCKS"); return TRUE; } buf = make_file_name(filearea, filename); lockname = make_lockname(filename, locknambuf); while (retval && !assert_lock(buf, lockname)) { register int errnosv = errno; switch (errnosv) { /* George Barbanis */ case EEXIST: if (retryct--) { HUP raw_printf( "Waiting for access to %s. (%d retries left).", filename, retryct); # if defined(SYSV) || defined(ULTRIX) (void) # endif sleep(1); } else { HUP (void) raw_print("I give up. Sorry."); HUP raw_printf("Perhaps there is an old %s around?", lockname); nesting--; retval = FALSE; } break; case ENOENT: HUP raw_printf("Can't find file %s to lock!", filename); nesting--; retval = FALSE; case EACCES: HUP raw_printf("No write permission to lock %s!", filename); nesting--; retval = FALSE; default: HUP perror(lockname); HUP raw_printf( "Cannot lock %s for unknown reason (%d).", filename, errnosv); nesting--; retval = FALSE; } } free(buf); return retval; } /* unlock file, which must be currently locked by lock_file_area */ void unlock_file_area(filearea, filename) const char *filearea, *filename; { char *lockname, locknambuf[BUFSZ]; if (nesting == 1) { lockname = make_lockname(filename, locknambuf); if (unlink(lockname) < 0) HUP raw_printf("Can't unlink %s.", lockname); if (lockfd!=-1) { (void) close(lockfd); lockfd=-1; } } nesting--; } /* ---------- END FILE LOCKING HANDLING ----------- */ #endif /* FILE_AREAS */ #ifdef GETRES_SUPPORT extern int FDECL(nh_getresuid, (uid_t *, uid_t *, uid_t *)); extern uid_t NDECL(nh_getuid); extern uid_t NDECL(nh_geteuid); extern int FDECL(nh_getresgid, (gid_t *, gid_t *, gid_t *)); extern gid_t NDECL(nh_getgid); extern gid_t NDECL(nh_getegid); int (getresuid)(ruid, euid, suid) uid_t *ruid, *euid, *suid; { return nh_getresuid(ruid, euid, suid); } uid_t (getuid)() { return nh_getuid(); } uid_t (geteuid)() { return nh_geteuid(); } int (getresgid)(rgid, egid, sgid) gid_t *rgid, *egid, *sgid; { return nh_getresgid(rgid, egid, sgid); } gid_t (getgid)() { return nh_getgid(); } gid_t (getegid)() { return nh_getegid(); } #endif /* GETRES_SUPPORT */