diff --git a/library/DFMemInfo.cpp b/library/DFMemInfo.cpp index a2724461b..b83648ef8 100644 --- a/library/DFMemInfo.cpp +++ b/library/DFMemInfo.cpp @@ -95,7 +95,7 @@ memory_info::memory_info(const memory_info &old) d->strings = old.d->strings; d->base = old.d->base; //d->classes = old.d->classes; - for(int i = 0; i < old.d->classes.size(); i++) + for(uint32_t i = 0; i < old.d->classes.size(); i++) { t_class * copy = new t_class(*old.d->classes[i]); d->classes.push_back(copy); diff --git a/library/DFMemInfo.h b/library/DFMemInfo.h index b4ae92e7d..3fa41bad9 100644 --- a/library/DFMemInfo.h +++ b/library/DFMemInfo.h @@ -68,7 +68,7 @@ namespace DFHack } ~t_class() { - for(int i = 0; i < subs.size();i++) + for(uint32_t i = 0; i < subs.size();i++) { delete subs[i]; } diff --git a/library/DFProcess-linux-SHM.cpp b/library/DFProcess-linux-SHM.cpp index 1ae294355..89dc8a308 100644 --- a/library/DFProcess-linux-SHM.cpp +++ b/library/DFProcess-linux-SHM.cpp @@ -52,6 +52,8 @@ class SHMProcess::Private suspended = false; identified = false; useYield = false; + my_SVfileLock = -1; + my_CLfileLock = -1; }; ~Private(){}; memory_info * my_descriptor; @@ -60,6 +62,8 @@ class SHMProcess::Private char *my_shm; int my_shmid; Process* q; + int my_SVfileLock; + int my_CLfileLock; bool attached; bool suspended; @@ -70,6 +74,9 @@ class SHMProcess::Private bool Aux_Core_Attach(bool & versionOK, pid_t & PID); bool waitWhile (uint32_t state); + bool GetLocks(); + bool AreLocksOk(); + void FreeLocks(); }; // some helpful macros to keep the code bloat in check @@ -95,7 +102,8 @@ bool SHMProcess::Private::waitWhile (uint32_t state) if(cnt == 10000)// check if the other process is still there { - shmctl(my_shmid, IPC_STAT, &descriptor); + /* + shmctl(my_shmid, IPC_STAT, &descriptor); if(descriptor.shm_nattch == 1)// DF crashed or exited - no way to tell? { //detach the shared memory @@ -112,6 +120,24 @@ bool SHMProcess::Private::waitWhile (uint32_t state) { cnt = 0; } + */ + if(!AreLocksOk()) + { + //detach the shared memory + shmdt(my_shm); + attached = suspended = false; + + // we aren't the current process anymore + g_pProcess = NULL; + FreeLocks(); + + throw Error::SHMServerDisappeared(); + return false; + } + else + { + cnt = 0; + } } if(useYield) { @@ -166,6 +192,72 @@ bool SHMProcess::Private::Aux_Core_Attach(bool & versionOK, pid_t & PID) return true; } +bool SHMProcess::Private::AreLocksOk() +{ + // both locks are inited (we hold our lock) + if(my_CLfileLock != -1 && my_SVfileLock != -1) + { + if(lockf(my_SVfileLock,F_TEST,0) == -1) // and server holds its lock + { + return true; // OK, locks are good + } + } + // locks are bad + return false; +} + +void SHMProcess::Private::FreeLocks() +{ + if(my_CLfileLock != -1) + { + lockf(my_CLfileLock,F_ULOCK,0); + close(my_CLfileLock); + my_CLfileLock = -1; + } + if(my_SVfileLock != -1) + { + close(my_SVfileLock); + my_SVfileLock = -1; + } +} + +bool SHMProcess::Private::GetLocks() +{ + char name[256]; + // try to acquire locks + // look at the server lock, if it's locked, the server is present + sprintf(name, "/tmp/DFHack/%d/SVlock",my_pid,name); + my_SVfileLock = open(name,O_WRONLY); + if(my_SVfileLock == -1) + { + return false; + } + + if(lockf( my_SVfileLock, F_TEST, 0 ) != -1) + { + close(my_SVfileLock); + return false; + } + + // open the client lock, try to lock it + sprintf(name, "/tmp/DFHack/%d/CLlock",my_pid,name); + my_CLfileLock = open(name,O_WRONLY); + if(my_CLfileLock == -1) + { + close(my_SVfileLock); + return false; + } + if(lockf(my_CLfileLock,F_TLOCK, 0) == -1) + { + // couldn't acquire lock + close(my_SVfileLock); + close(my_CLfileLock); + return false; + } + // ok, we have all the locks! + return true; +} + SHMProcess::SHMProcess(uint32_t PID, vector< memory_info* >& known_versions) : d(new Private()) { @@ -189,18 +281,15 @@ SHMProcess::SHMProcess(uint32_t PID, vector< memory_info* >& known_versions) return; } - /* - * Check if there are two processes connected to the segment - */ - shmid_ds descriptor; - shmctl(d->my_shmid, IPC_STAT, &descriptor); - if(descriptor.shm_nattch != 2)// badness + // set pid and gets lock for it + d->my_pid = PID; + if(!d->GetLocks()) { - fprintf(stderr,"dfhack: %d : invalid no. of processes connected\n", (int) descriptor.shm_nattch); - fprintf(stderr,"detach: %d",shmdt(d->my_shm)); + fprintf(stderr,"Couldn't get locks for PID %d'\n", PID); + shmdt(d->my_shm); return; } - + /* * Test bridge version, get PID, sync Yield */ @@ -209,12 +298,16 @@ SHMProcess::SHMProcess(uint32_t PID, vector< memory_info* >& known_versions) { fprintf(stderr,"DF terminated during reading\n"); shmdt(d->my_shm); + // free locks + d->FreeLocks(); return; } if(!bridgeOK) { fprintf(stderr,"SHM bridge version mismatch\n"); shmdt(d->my_shm); + // free locks + d->FreeLocks(); return; } @@ -225,6 +318,8 @@ SHMProcess::SHMProcess(uint32_t PID, vector< memory_info* >& known_versions) { perror("readlink"); shmdt(d->my_shm); + // free locks + d->FreeLocks(); return; } @@ -240,6 +335,9 @@ SHMProcess::SHMProcess(uint32_t PID, vector< memory_info* >& known_versions) // at this point, DF is stopped and waiting for commands. make it run again D_SHMCMD = CORE_RUNNING; shmdt(d->my_shm); // detach so we don't attach twice when attach() is called + + // free locks + d->FreeLocks(); } bool SHMProcess::isSuspended() @@ -410,9 +508,16 @@ bool SHMProcess::attach() int status; if(g_pProcess != 0) { - cerr << "there's already a different process attached" << endl; + // FIXME: throw exception here - programmer error + cerr << "client is already attached to a process!" << endl; return false; } + if(!d->GetLocks()) + { + cerr << "server is full or not really there!" << endl; + return false; + } + /* * Attach the segment */ @@ -427,10 +532,12 @@ bool SHMProcess::attach() } d->attached = false; cerr << "unable to suspend" << endl; - // FIXME: detach sehment here + shmdt(d->my_shm); + d->FreeLocks(); return false; } cerr << "unable to attach" << endl; + d->FreeLocks(); return false; } @@ -451,9 +558,11 @@ bool SHMProcess::detach() d->suspended = false; d->my_shm = 0; g_pProcess = 0; + d->FreeLocks(); return true; } // fail if we can't detach + // FIXME: throw exception here?? perror("failed to detach shared segment"); return false; } diff --git a/shmserver/shms-linux.cpp b/shmserver/shms-linux.cpp index 79e185203..f27c547df 100644 --- a/shmserver/shms-linux.cpp +++ b/shmserver/shms-linux.cpp @@ -33,6 +33,8 @@ distribution. #include #include #include +#include +#include #include #include #include @@ -49,10 +51,14 @@ char *shm = 0; int shmid = 0; bool inited = 0; +int fd_svlock = 0; +int fd_cllock = 0; + + /******************************************************************************* * SHM part starts here * *******************************************************************************/ - +/* // FIXME: add error checking? bool isValidSHM() { @@ -61,6 +67,25 @@ bool isValidSHM() //fprintf(stderr,"ID %d, attached: %d\n",shmid, descriptor.shm_nattch); return (descriptor.shm_nattch == 2); } +*/ + +// is the other side still there? +bool isValidSHM() +{ + // try if CL lock file is free + int result = lockf(fd_cllock,F_TEST,0); + /* + F_TEST: Test the lock: + return 0 if the specified section is unlocked or locked by this process; + return -1, set errno to EAGAIN (EACCES on some other systems), if another process holds a lock. + */ + + // if nobody holds the lock, the SHM isn't valid. file locks are unlocked when the owner closes or crashes. + if(result == 0) return false; + + return true; +} + uint32_t OS_getPID() { return getpid(); @@ -84,6 +109,23 @@ void SHM_Init ( void ) return; } inited = true; + char name[256]; + char name2[256]; + + // make folder structure for our lock files + sprintf(name, "/tmp/DFHack/%d",OS_getPID()); + mode_t createmode = S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH; + mkdir("/tmp/DFHack", createmode); + mkdir(name, createmode); + + // create and lock the server lock file + sprintf(name2, "%s/SVlock",name); + fd_svlock = open(name2,O_WRONLY | O_CREAT, createmode); + lockf( fd_svlock, F_LOCK, 0 ); + + // create the client lock file + sprintf(name2, "%s/CLlock",name); + fd_cllock = open(name2,O_WRONLY | O_CREAT, createmode); // name for the segment, an accident waiting to happen key_t key = SHM_KEY + OS_getPID(); @@ -134,6 +176,14 @@ void SHM_Destroy ( void ) shmctl(shmid, IPC_STAT, &descriptor); } shmctl(shmid,IPC_RMID,NULL); + + // unlock and close server lock, close client lock + lockf(fd_svlock,F_ULOCK,0); + close(fd_svlock); + close(fd_cllock); + fd_svlock = 0; + fd_cllock = 0; + fprintf(stderr,"dfhack: destroyed shared segment.\n"); inited = false; }