per-process file locks on linux (stored in /tmp/DFHack/PID/)

minor signed/unsigned fix
develop
Petr Mrázek 2010-03-09 21:25:17 +01:00
parent 5c4ef73f4d
commit 62ae8df573
4 changed files with 174 additions and 15 deletions

@ -95,7 +95,7 @@ memory_info::memory_info(const memory_info &old)
d->strings = old.d->strings; d->strings = old.d->strings;
d->base = old.d->base; d->base = old.d->base;
//d->classes = old.d->classes; //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]); t_class * copy = new t_class(*old.d->classes[i]);
d->classes.push_back(copy); d->classes.push_back(copy);

@ -68,7 +68,7 @@ namespace DFHack
} }
~t_class() ~t_class()
{ {
for(int i = 0; i < subs.size();i++) for(uint32_t i = 0; i < subs.size();i++)
{ {
delete subs[i]; delete subs[i];
} }

@ -52,6 +52,8 @@ class SHMProcess::Private
suspended = false; suspended = false;
identified = false; identified = false;
useYield = false; useYield = false;
my_SVfileLock = -1;
my_CLfileLock = -1;
}; };
~Private(){}; ~Private(){};
memory_info * my_descriptor; memory_info * my_descriptor;
@ -60,6 +62,8 @@ class SHMProcess::Private
char *my_shm; char *my_shm;
int my_shmid; int my_shmid;
Process* q; Process* q;
int my_SVfileLock;
int my_CLfileLock;
bool attached; bool attached;
bool suspended; bool suspended;
@ -70,6 +74,9 @@ class SHMProcess::Private
bool Aux_Core_Attach(bool & versionOK, pid_t & PID); bool Aux_Core_Attach(bool & versionOK, pid_t & PID);
bool waitWhile (uint32_t state); bool waitWhile (uint32_t state);
bool GetLocks();
bool AreLocksOk();
void FreeLocks();
}; };
// some helpful macros to keep the code bloat in check // some helpful macros to keep the code bloat in check
@ -95,6 +102,7 @@ bool SHMProcess::Private::waitWhile (uint32_t state)
if(cnt == 10000)// check if the other process is still there 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? if(descriptor.shm_nattch == 1)// DF crashed or exited - no way to tell?
{ {
@ -112,6 +120,24 @@ bool SHMProcess::Private::waitWhile (uint32_t state)
{ {
cnt = 0; 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) if(useYield)
{ {
@ -166,6 +192,72 @@ bool SHMProcess::Private::Aux_Core_Attach(bool & versionOK, pid_t & PID)
return true; 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) SHMProcess::SHMProcess(uint32_t PID, vector< memory_info* >& known_versions)
: d(new Private()) : d(new Private())
{ {
@ -189,15 +281,12 @@ SHMProcess::SHMProcess(uint32_t PID, vector< memory_info* >& known_versions)
return; return;
} }
/* // set pid and gets lock for it
* Check if there are two processes connected to the segment d->my_pid = PID;
*/ if(!d->GetLocks())
shmid_ds descriptor;
shmctl(d->my_shmid, IPC_STAT, &descriptor);
if(descriptor.shm_nattch != 2)// badness
{ {
fprintf(stderr,"dfhack: %d : invalid no. of processes connected\n", (int) descriptor.shm_nattch); fprintf(stderr,"Couldn't get locks for PID %d'\n", PID);
fprintf(stderr,"detach: %d",shmdt(d->my_shm)); shmdt(d->my_shm);
return; return;
} }
@ -209,12 +298,16 @@ SHMProcess::SHMProcess(uint32_t PID, vector< memory_info* >& known_versions)
{ {
fprintf(stderr,"DF terminated during reading\n"); fprintf(stderr,"DF terminated during reading\n");
shmdt(d->my_shm); shmdt(d->my_shm);
// free locks
d->FreeLocks();
return; return;
} }
if(!bridgeOK) if(!bridgeOK)
{ {
fprintf(stderr,"SHM bridge version mismatch\n"); fprintf(stderr,"SHM bridge version mismatch\n");
shmdt(d->my_shm); shmdt(d->my_shm);
// free locks
d->FreeLocks();
return; return;
} }
@ -225,6 +318,8 @@ SHMProcess::SHMProcess(uint32_t PID, vector< memory_info* >& known_versions)
{ {
perror("readlink"); perror("readlink");
shmdt(d->my_shm); shmdt(d->my_shm);
// free locks
d->FreeLocks();
return; 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 // at this point, DF is stopped and waiting for commands. make it run again
D_SHMCMD = CORE_RUNNING; D_SHMCMD = CORE_RUNNING;
shmdt(d->my_shm); // detach so we don't attach twice when attach() is called shmdt(d->my_shm); // detach so we don't attach twice when attach() is called
// free locks
d->FreeLocks();
} }
bool SHMProcess::isSuspended() bool SHMProcess::isSuspended()
@ -410,9 +508,16 @@ bool SHMProcess::attach()
int status; int status;
if(g_pProcess != 0) 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; return false;
} }
if(!d->GetLocks())
{
cerr << "server is full or not really there!" << endl;
return false;
}
/* /*
* Attach the segment * Attach the segment
*/ */
@ -427,10 +532,12 @@ bool SHMProcess::attach()
} }
d->attached = false; d->attached = false;
cerr << "unable to suspend" << endl; cerr << "unable to suspend" << endl;
// FIXME: detach sehment here shmdt(d->my_shm);
d->FreeLocks();
return false; return false;
} }
cerr << "unable to attach" << endl; cerr << "unable to attach" << endl;
d->FreeLocks();
return false; return false;
} }
@ -451,9 +558,11 @@ bool SHMProcess::detach()
d->suspended = false; d->suspended = false;
d->my_shm = 0; d->my_shm = 0;
g_pProcess = 0; g_pProcess = 0;
d->FreeLocks();
return true; return true;
} }
// fail if we can't detach // fail if we can't detach
// FIXME: throw exception here??
perror("failed to detach shared segment"); perror("failed to detach shared segment");
return false; return false;
} }

@ -33,6 +33,8 @@ distribution.
#include <sys/shm.h> #include <sys/shm.h>
#include <sys/types.h> #include <sys/types.h>
#include <sys/ipc.h> #include <sys/ipc.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h> #include <unistd.h>
#include <vector> #include <vector>
#include <string> #include <string>
@ -49,10 +51,14 @@ char *shm = 0;
int shmid = 0; int shmid = 0;
bool inited = 0; bool inited = 0;
int fd_svlock = 0;
int fd_cllock = 0;
/******************************************************************************* /*******************************************************************************
* SHM part starts here * * SHM part starts here *
*******************************************************************************/ *******************************************************************************/
/*
// FIXME: add error checking? // FIXME: add error checking?
bool isValidSHM() bool isValidSHM()
{ {
@ -61,6 +67,25 @@ bool isValidSHM()
//fprintf(stderr,"ID %d, attached: %d\n",shmid, descriptor.shm_nattch); //fprintf(stderr,"ID %d, attached: %d\n",shmid, descriptor.shm_nattch);
return (descriptor.shm_nattch == 2); 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() uint32_t OS_getPID()
{ {
return getpid(); return getpid();
@ -84,6 +109,23 @@ void SHM_Init ( void )
return; return;
} }
inited = true; 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 // name for the segment, an accident waiting to happen
key_t key = SHM_KEY + OS_getPID(); key_t key = SHM_KEY + OS_getPID();
@ -134,6 +176,14 @@ void SHM_Destroy ( void )
shmctl(shmid, IPC_STAT, &descriptor); shmctl(shmid, IPC_STAT, &descriptor);
} }
shmctl(shmid,IPC_RMID,NULL); 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"); fprintf(stderr,"dfhack: destroyed shared segment.\n");
inited = false; inited = false;
} }