From: divverent Date: Tue, 1 Nov 2011 14:45:57 +0000 (+0000) Subject: Command line: -sessionid, cvars: locksession, (R/O) sessionid X-Git-Tag: xonotic-v0.6.0~190 X-Git-Url: https://git.rm.cloudns.org/?a=commitdiff_plain;h=85bcf1b0c54087b658f33995b2e5461ccf2a6998;p=xonotic%2Fdarkplaces.git Command line: -sessionid, cvars: locksession, (R/O) sessionid 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 ::stable-branch::merge=927bd7b13c9a18fb07a8681c7483f632c7ea32f3 --- diff --git a/crypto.c b/crypto.c index 7bd36d12..6333d181 100644 --- a/crypto.c +++ b/crypto.c @@ -340,12 +340,12 @@ 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) { qfile_t *f = NULL; fs_offset_t n; if(*fs_userdir) - f = FS_SysOpen(va("%s%s", fs_userdir, path), "rb", false); + f = FS_SysOpen(va("%s%s", *fs_userdir ? fs_userdir : fs_basedir, path), "rb", false); if(!f) f = FS_SysOpen(va("%s%s", fs_basedir, path), "rb", false); if(!f) @@ -748,12 +748,20 @@ 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; + 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: @@ -767,14 +775,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("key_%d.d0pk", i), buf, sizeof(buf)); + len = Crypto_LoadFile(va("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("key_%d.d0si", i), buf, sizeof(buf)); + len = Crypto_LoadFile(va("key_%d.d0si%s", i, sessionid.string), buf, sizeof(buf), true); if(len) { if(Crypto_AddPrivateKey(pubkeys[i], buf, len)) @@ -782,7 +790,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(" %s@%s", pubkeys_priv_fp64[i], pubkeys_fp64[i]), sizeof(crypto_idstring_buf)); } @@ -910,7 +918,6 @@ void Crypto_Init(void) Crypto_Rijndael_OpenLibrary(); // if this fails, it's uncritical Crypto_InitHostKeys(); - Crypto_LoadKeys(); } // end @@ -1072,26 +1079,18 @@ static void Crypto_KeyGen_Finished(int code, size_t length_received, unsigned ch return; } - if(*fs_userdir) - { - FS_CreatePath(va("%skey_%d.d0si", fs_userdir, keygen_i)); - f = FS_SysOpen(va("%skey_%d.d0si", fs_userdir, keygen_i), "wb", false); - } - if(!f) - { - FS_CreatePath(va("%skey_%d.d0si", fs_basedir, keygen_i)); - f = FS_SysOpen(va("%skey_%d.d0si", fs_basedir, keygen_i), "wb", false); - } + FS_CreatePath(va("%skey_%d.d0si%s", *fs_userdir ? fs_userdir : fs_basedir, keygen_i, sessionid.string)); + f = FS_SysOpen(va("%skey_%d.d0si%s", *fs_userdir ? fs_userdir : fs_basedir, keygen_i, sessionid.string), "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; return; } 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; } @@ -1113,6 +1112,7 @@ static void Crypto_KeyGen_f(void) Con_Printf("usage:\n%s id url\n", Cmd_Argv(0)); return; } + Crypto_LoadKeys(); i = atoi(Cmd_Argv(1)); if(!pubkeys[i]) { @@ -1192,7 +1192,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]); } } } diff --git a/crypto.h b/crypto.h index 2cff5c2d..7b2b9921 100644 --- 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 4288ab5d..1917fd2f 100644 --- a/fs.c +++ b/fs.c @@ -773,11 +773,7 @@ pack_t *FS_LoadPackPK3FromFD (const char *packfile, int packhandle, qboolean sil 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); @@ -947,11 +943,7 @@ 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)) @@ -1884,11 +1876,8 @@ int FS_ChooseUserDir(userdirmode_t userdirmode, char *userdir, size_t userdirsiz // see if we can write to this path (note: won't create path) #ifdef WIN32 -# if _MSC_VER >= 1400 - _sopen_s(&fd, va("%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("%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("%s%s/config.cfg", userdir, gamedirname1), "a", false); if(fd >= 0) close(fd); #else @@ -2105,9 +2094,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]) @@ -2138,6 +2128,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]); @@ -2147,11 +2140,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 3f70370e..be123bf9 100644 --- a/host.c +++ b/host.c @@ -83,6 +83,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 @@ -1051,6 +1054,63 @@ extern void COM_Init_Commands(void); extern void FS_Init_Commands(void); 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 @@ -1147,6 +1207,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(); @@ -1325,7 +1388,10 @@ void Host_Shutdown(void) Sys_Shutdown(); Log_Close(); Crypto_Shutdown(); - FS_Shutdown(); + + Host_UnlockSession(); + + S_Shutdown(); Con_Shutdown(); Memory_Shutdown(); } diff --git a/netconn.c b/netconn.c index c6b3ea03..3a8b8beb 100755 --- 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) diff --git a/prvm_edict.c b/prvm_edict.c index ec094fb8..2aa49200 100644 --- a/prvm_edict.c +++ b/prvm_edict.c @@ -1954,6 +1954,9 @@ void PRVM_LoadProgs (const char * filename, int numrequiredfunc, const char **re if (prog->loaded) PRVM_ERROR ("PRVM_LoadProgs: there is already a %s program loaded!", PRVM_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)) PRVM_ERROR ("PRVM_LoadProgs: couldn't load %s for %s", filename, PRVM_NAME); diff --git a/quakedef.h b/quakedef.h index e90be22e..684e23fa 100644 --- a/quakedef.h +++ b/quakedef.h @@ -404,6 +404,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" @@ -512,6 +514,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);