Command line: -sessionid, cvars: locksession, (R/O) sessionid
authordivverent <divverent@d7cf8633-e32d-0410-b094-e92efae38249>
Tue, 1 Nov 2011 14:45:57 +0000 (14:45 +0000)
committerdivverent <divverent@d7cf8633-e32d-0410-b094-e92efae38249>
Tue, 1 Nov 2011 14:45:57 +0000 (14:45 +0000)
Allows games to require a session lock. Put "locksession 1" in the game's default config file and users then need to run instances with unique -sessionid parameter.

git-svn-id: svn://svn.icculus.org/twilight/trunk/darkplaces@11513 d7cf8633-e32d-0410-b094-e92efae38249

crypto.c
crypto.h
fs.c
host.c
netconn.c
prvm_edict.c
quakedef.h

index adf5cb9..f9cfbbb 100644 (file)
--- a/crypto.c
+++ b/crypto.c
@@ -356,14 +356,14 @@ void sha256(unsigned char *out, const unsigned char *in, int n)
        qd0_blind_id_util_sha256((char *) out, (const char *) in, n);
 }
 
-static size_t Crypto_LoadFile(const char *path, char *buf, size_t nmax)
+static size_t Crypto_LoadFile(const char *path, char *buf, size_t nmax, qboolean inuserdir)
 {
        char vabuf[1024];
        qfile_t *f = NULL;
        fs_offset_t n;
-       if(*fs_userdir)
-               f = FS_SysOpen(va(vabuf, sizeof(vabuf), "%s%s", fs_userdir, path), "rb", false);
-       if(!f)
+       if(inuserdir)
+               f = FS_SysOpen(va(vabuf, sizeof(vabuf), "%s%s", *fs_userdir ? fs_userdir : fs_basedir, path), "rb", false);
+       else
                f = FS_SysOpen(va(vabuf, sizeof(vabuf), "%s%s", fs_basedir, path), "rb", false);
        if(!f)
                return 0;
@@ -765,13 +765,21 @@ static void Crypto_BuildChallengeAppend(void)
        challenge_append_length = p - challenge_append;
 }
 
-static void Crypto_LoadKeys(void)
+void Crypto_LoadKeys(void)
 {
        char buf[8192];
        size_t len, len2;
        int i;
        char vabuf[1024];
 
+       if(!d0_blind_id_dll) // don't if we can't
+               return;
+
+       if(crypto_idstring) // already loaded? then not
+               return;
+
+       Host_LockSession(); // we use the session ID here
+
        // load keys
        // note: we are just a CLIENT
        // so we load:
@@ -785,14 +793,14 @@ static void Crypto_LoadKeys(void)
                memset(pubkeys_fp64[i], 0, sizeof(pubkeys_fp64[i]));
                memset(pubkeys_priv_fp64[i], 0, sizeof(pubkeys_fp64[i]));
                pubkeys_havepriv[i] = false;
-               len = Crypto_LoadFile(va(vabuf, sizeof(vabuf), "key_%d.d0pk", i), buf, sizeof(buf));
+               len = Crypto_LoadFile(va(vabuf, sizeof(vabuf), "key_%d.d0pk", i), buf, sizeof(buf), false);
                if((pubkeys[i] = Crypto_ReadPublicKey(buf, len)))
                {
                        len2 = FP64_SIZE;
                        if(qd0_blind_id_fingerprint64_public_key(pubkeys[i], pubkeys_fp64[i], &len2)) // keeps final NUL
                        {
                                Con_Printf("Loaded public key key_%d.d0pk (fingerprint: %s)\n", i, pubkeys_fp64[i]);
-                               len = Crypto_LoadFile(va(vabuf, sizeof(vabuf), "key_%d.d0si", i), buf, sizeof(buf));
+                               len = Crypto_LoadFile(va(vabuf, sizeof(vabuf), "key_%d.d0si%s", i, sessionid.string), buf, sizeof(buf), true);
                                if(len)
                                {
                                        if(Crypto_AddPrivateKey(pubkeys[i], buf, len))
@@ -800,7 +808,7 @@ static void Crypto_LoadKeys(void)
                                                len2 = FP64_SIZE;
                                                if(qd0_blind_id_fingerprint64_public_id(pubkeys[i], pubkeys_priv_fp64[i], &len2)) // keeps final NUL
                                                {
-                                                       Con_Printf("Loaded private ID key_%d.d0si for key_%d.d0pk (public key fingerprint: %s)\n", i, i, pubkeys_priv_fp64[i]);
+                                                       Con_Printf("Loaded private ID key_%d.d0si%s for key_%d.d0pk (public key fingerprint: %s)\n", i, sessionid.string, i, pubkeys_priv_fp64[i]);
                                                        pubkeys_havepriv[i] = true;
                                                        strlcat(crypto_idstring_buf, va(vabuf, sizeof(vabuf), " %s@%s", pubkeys_priv_fp64[i], pubkeys_fp64[i]), sizeof(crypto_idstring_buf));
                                                }
@@ -885,7 +893,6 @@ static void Crypto_UnloadKeys(void)
        crypto_idstring = NULL;
 }
 
-
 static mempool_t *cryptomempool;
 
 #ifdef __cplusplus
@@ -983,7 +990,6 @@ void Crypto_Init(void)
        Crypto_Rijndael_OpenLibrary(); // if this fails, it's uncritical
 
        Crypto_InitHostKeys();
-       Crypto_LoadKeys();
 }
 // end
 
@@ -1163,19 +1169,11 @@ static void Crypto_KeyGen_Finished(int code, size_t length_received, unsigned ch
                return;
        }
 
-       if(*fs_userdir)
-       {
-               FS_CreatePath(va(vabuf, sizeof(vabuf), "%skey_%d.d0si", fs_userdir, keygen_i));
-               f = FS_SysOpen(va(vabuf, sizeof(vabuf), "%skey_%d.d0si", fs_userdir, keygen_i), "wb", false);
-       }
+       FS_CreatePath(va(vabuf, sizeof(vabuf), "%skey_%d.d0si%s", *fs_userdir ? fs_userdir : fs_basedir, keygen_i, sessionid.string));
+       f = FS_SysOpen(va(vabuf, sizeof(vabuf), "%skey_%d.d0si%s", *fs_userdir ? fs_userdir : fs_basedir, keygen_i, sessionid.string), "wb", false);
        if(!f)
        {
-               FS_CreatePath(va(vabuf, sizeof(vabuf), "%skey_%d.d0si", fs_basedir, keygen_i));
-               f = FS_SysOpen(va(vabuf, sizeof(vabuf), "%skey_%d.d0si", fs_basedir, keygen_i), "wb", false);
-       }
-       if(!f)
-       {
-               Con_Printf("Cannot open key_%d.d0si\n", keygen_i);
+               Con_Printf("Cannot open key_%d.d0si%s\n", keygen_i, sessionid.string);
                keygen_i = -1;
                if (crypto_mutex) Thread_UnlockMutex(crypto_mutex);
                return;
@@ -1183,7 +1181,7 @@ static void Crypto_KeyGen_Finished(int code, size_t length_received, unsigned ch
        FS_Write(f, buf2, buf2size);
        FS_Close(f);
 
-       Con_Printf("Saved to key_%d.d0si\n", keygen_i);
+       Con_Printf("Saved to key_%d.d0si%s\n", keygen_i, sessionid.string);
        keygen_i = -1;
        if (crypto_mutex) Thread_UnlockMutex(crypto_mutex);
 }
@@ -1207,6 +1205,7 @@ static void Crypto_KeyGen_f(void)
                return;
        }
        if (crypto_mutex) Thread_LockMutex(crypto_mutex);
+       Crypto_LoadKeys();
        i = atoi(Cmd_Argv(1));
        if(!pubkeys[i])
        {
@@ -1295,7 +1294,7 @@ static void Crypto_Keys_f(void)
                {
                        Con_Printf("%2d: public key key_%d.d0pk (fingerprint: %s)\n", i, i, pubkeys_fp64[i]);
                        if(pubkeys_havepriv[i])
-                               Con_Printf("    private ID key_%d.d0si (public key fingerprint: %s)\n", i, pubkeys_priv_fp64[i]);
+                               Con_Printf("    private ID key_%d.d0si%s (public key fingerprint: %s)\n", i, sessionid.string, pubkeys_priv_fp64[i]);
                }
        }
 }
index 2cff5c2..7b2b992 100644 (file)
--- a/crypto.h
+++ b/crypto.h
@@ -31,6 +31,7 @@ crypto_t;
 
 void Crypto_Init(void);
 void Crypto_Init_Commands(void);
+void Crypto_LoadKeys(void);
 void Crypto_Shutdown(void);
 qboolean Crypto_Available(void);
 void sha256(unsigned char *out, const unsigned char *in, int n); // may ONLY be called if Crypto_Available()
diff --git a/fs.c b/fs.c
index ff6c0ee..e5c7015 100644 (file)
--- a/fs.c
+++ b/fs.c
@@ -775,11 +775,7 @@ static pack_t *FS_LoadPackPK3FromFD (const char *packfile, int packhandle, qbool
 static pack_t *FS_LoadPackPK3 (const char *packfile)
 {
        int packhandle;
-#if _MSC_VER >= 1400
-       _sopen_s(&packhandle, packfile, O_RDONLY | O_BINARY, _SH_DENYNO, _S_IREAD | _S_IWRITE);
-#else
-       packhandle = open (packfile, O_RDONLY | O_BINARY);
-#endif
+       packhandle = FS_SysOpenFD (packfile, "rb", false);
        if (packhandle < 0)
                return NULL;
        return FS_LoadPackPK3FromFD(packfile, packhandle, false);
@@ -949,11 +945,7 @@ static pack_t *FS_LoadPackPAK (const char *packfile)
        pack_t *pack;
        dpackfile_t *info;
 
-#if _MSC_VER >= 1400
-       _sopen_s(&packhandle, packfile, O_RDONLY | O_BINARY, _SH_DENYNO, _S_IREAD | _S_IWRITE);
-#else
-       packhandle = open (packfile, O_RDONLY | O_BINARY);
-#endif
+       packhandle = FS_SysOpenFD(packfile, "rb", false);
        if (packhandle < 0)
                return NULL;
        if(read (packhandle, (void *)&header, sizeof(header)) != sizeof(header))
@@ -1890,11 +1882,8 @@ static int FS_ChooseUserDir(userdirmode_t userdirmode, char *userdir, size_t use
 
        // see if we can write to this path (note: won't create path)
 #ifdef WIN32
-# if _MSC_VER >= 1400
-       _sopen_s(&fd, va(vabuf, sizeof(vabuf), "%s%s/config.cfg", userdir, gamedirname1), O_WRONLY | O_CREAT, _SH_DENYNO, _S_IREAD | _S_IWRITE); // note: no O_TRUNC here!
-# else
-       fd = open (va(vabuf, sizeof(vabuf), "%s%s/config.cfg", userdir, gamedirname1), O_WRONLY | O_CREAT, 0666); // note: no O_TRUNC here!
-# endif
+       // no access() here, we must try to open the file for appending
+       fd = Sys_OpenFD(va(vabuf, sizeof(vabuf), "%s%s/config.cfg", userdir, gamedirname1), "a", false);
        if(fd >= 0)
                close(fd);
 #else
@@ -2119,9 +2108,10 @@ void FS_Shutdown (void)
 
 int FS_SysOpenFD(const char *filepath, const char *mode, qboolean nonblocking)
 {
-       int handle;
+       int handle = -1;
        int mod, opt;
        unsigned int ind;
+       qboolean dolock = false;
 
        // Parse the mode string
        switch (mode[0])
@@ -2152,6 +2142,9 @@ int FS_SysOpenFD(const char *filepath, const char *mode, qboolean nonblocking)
                        case 'b':
                                opt |= O_BINARY;
                                break;
+                       case 'l':
+                               dolock = true;
+                               break;
                        default:
                                Con_Printf ("FS_SysOpen(%s, %s): unknown character in mode (%c)\n",
                                                        filepath, mode, mode[ind]);
@@ -2161,11 +2154,29 @@ int FS_SysOpenFD(const char *filepath, const char *mode, qboolean nonblocking)
        if (nonblocking)
                opt |= O_NONBLOCK;
 
-#if _MSC_VER >= 1400
-       _sopen_s(&handle, filepath, mod | opt, _SH_DENYNO, _S_IREAD | _S_IWRITE);
+#ifdef WIN32
+# if _MSC_VER >= 1400
+       _sopen_s(&handle, filepath, mod | opt, (dolock ? ((mod == O_RDONLY) ? _SH_DENYRD : _SH_DENYRW) : _SH_DENYNO), _S_IREAD | _S_IWRITE);
+# else
+       handle = _sopen (filepath, mod | opt, (dolock ? ((mod == O_RDONLY) ? _SH_DENYRD : _SH_DENYRW) : _SH_DENYNO), _S_IREAD | _S_IWRITE);
+# endif
 #else
        handle = open (filepath, mod | opt, 0666);
+       if(handle >= 0 && dolock)
+       {
+               struct flock l;
+               l.l_type = ((mod == O_RDONLY) ? F_RDLCK : F_WRLCK);
+               l.l_whence = SEEK_SET;
+               l.l_start = 0;
+               l.l_len = 0;
+               if(fcntl(handle, F_SETLK, &l) == -1)
+               {
+                       close(handle);
+                       handle = -1;
+               }
+       }
 #endif
+
        return handle;
 }
 
diff --git a/host.c b/host.c
index 729e998..a8a8e0d 100644 (file)
--- a/host.c
+++ b/host.c
@@ -85,6 +85,9 @@ cvar_t developer_entityparsing = {0, "developer_entityparsing", "0", "prints det
 cvar_t timestamps = {CVAR_SAVE, "timestamps", "0", "prints timestamps on console messages"};
 cvar_t timeformat = {CVAR_SAVE, "timeformat", "[%Y-%m-%d %H:%M:%S] ", "time format to use on timestamped console messages"};
 
+cvar_t sessionid = {CVAR_READONLY, "sessionid", "", "ID of the current session (use the -sessionid parameter to set it); this is always either empty or begins with a dot (.)"};
+cvar_t locksession = {0, "locksession", "0", "Lock the session? 0 = no, 1 = yes and abort on failure, 2 = yes and continue on failure"};
+
 /*
 ================
 Host_AbortCurrentFrame
@@ -1071,6 +1074,63 @@ qboolean sys_nostdout = false;
 
 extern qboolean host_stuffcmdsrun;
 
+static qfile_t *locksession_fh = NULL;
+static qboolean locksession_run = false;
+static void Host_InitSession(void)
+{
+       int i;
+       Cvar_RegisterVariable(&sessionid);
+       Cvar_RegisterVariable(&locksession);
+
+       // load the session ID into the read-only cvar
+       if ((i = COM_CheckParm("-sessionid")) && (i + 1 < com_argc))
+       {
+               char vabuf[1024];
+               if(com_argv[i+1][0] == '.')
+                       Cvar_SetQuick(&sessionid, com_argv[i+1]);
+               else
+                       Cvar_SetQuick(&sessionid, va(vabuf, sizeof(vabuf), ".%s", com_argv[i+1]));
+       }
+}
+void Host_LockSession(void)
+{
+       if(locksession_run)
+               return;
+       locksession_run = true;
+       if(locksession.integer != 0)
+       {
+               char vabuf[1024];
+               locksession_fh = FS_SysOpen(va(vabuf, sizeof(vabuf), "%slock%s", *fs_userdir ? fs_userdir : fs_basedir, sessionid.string), "wl", false);
+               // TODO maybe write the pid into the lockfile, while we are at it? may help server management tools
+               if(!locksession_fh)
+               {
+                       if(locksession.integer == 2)
+                       {
+                               Con_Printf("WARNING: session lock %slock%s could not be acquired. Please run with -sessionid and an unique session name. Continuing anyway.\n", *fs_userdir ? fs_userdir : fs_basedir, sessionid.string);
+                       }
+                       else
+                       {
+                               Sys_Error("session lock %slock%s could not be acquired. Please run with -sessionid and an unique session name.\n", *fs_userdir ? fs_userdir : fs_basedir, sessionid.string);
+                       }
+               }
+       }
+}
+void Host_UnlockSession(void)
+{
+       if(!locksession_run)
+               return;
+       locksession_run = false;
+
+       if(locksession_fh)
+       {
+               FS_Close(locksession_fh);
+               // NOTE: we can NOT unlink the lock here, as doing so would
+               // create a race condition if another process created it
+               // between our close and our unlink
+               locksession_fh = NULL;
+       }
+}
+
 /*
 ====================
 Host_Init
@@ -1168,6 +1228,9 @@ static void Host_Init (void)
        // initialize filesystem (including fs_basedir, fs_gamedir, -game, scr_screenshot_name)
        FS_Init();
 
+       // register the cvars for session locking
+       Host_InitSession();
+
        // must be after FS_Init
        Crypto_Init();
        Crypto_Init_Commands();
@@ -1356,7 +1419,10 @@ void Host_Shutdown(void)
        Sys_Shutdown();
        Log_Close();
        Crypto_Shutdown();
-       FS_Shutdown();
+
+       Host_UnlockSession();
+
+       S_Shutdown();
        Con_Shutdown();
        Memory_Shutdown();
 }
index aa839ee..2e1e9ad 100755 (executable)
--- a/netconn.c
+++ b/netconn.c
@@ -922,6 +922,9 @@ void NetConn_OpenClientPorts(void)
 {
        int port;
        NetConn_CloseClientPorts();
+
+       Crypto_LoadKeys(); // client sockets
+
        port = bound(0, cl_netport.integer, 65535);
        if (cl_netport.integer != port)
                Cvar_SetValueQuick(&cl_netport, port);
@@ -987,6 +990,9 @@ void NetConn_OpenServerPorts(int opennetports)
 {
        int port;
        NetConn_CloseServerPorts();
+
+       Crypto_LoadKeys(); // server sockets
+
        NetConn_UpdateSockets();
        port = bound(0, sv_netport.integer, 65535);
        if (port == 0)
index e60c14d..25f9899 100644 (file)
@@ -1896,6 +1896,9 @@ void PRVM_Prog_Load(prvm_prog_t *prog, const char * filename, int numrequiredfun
        if (prog->loaded)
                prog->error_cmd("PRVM_LoadProgs: there is already a %s program loaded!", prog->name );
 
+       Host_LockSession(); // all progs can use the session cvar
+       Crypto_LoadKeys(); // all progs might use the keys at init time
+
        dprograms = (dprograms_t *)FS_LoadFile (filename, prog->progs_mempool, false, &filesize);
        if (dprograms == NULL || filesize < (fs_offset_t)sizeof(dprograms_t))
                prog->error_cmd("PRVM_LoadProgs: couldn't load %s for %s", filename, prog->name);
index f656b26..65f2415 100644 (file)
@@ -405,6 +405,8 @@ extern cvar_t developer_insane;
 extern cvar_t developer_loadfile;
 extern cvar_t developer_loading;
 
+extern cvar_t sessionid;
+
 #define STARTCONFIGFILENAME "quake.rc"
 #define CONFIGFILENAME "config.cfg"
 
@@ -515,6 +517,8 @@ void Host_ClientCommands(const char *fmt, ...) DP_FUNC_PRINTF(1);
 void Host_ShutdownServer(void);
 void Host_Reconnect_f(void);
 void Host_NoOperation_f(void);
+void Host_LockSession(void);
+void Host_UnlockSession(void);
 
 void Host_AbortCurrentFrame(void);