From b044526ddb82cbe38fa2d3ff3cbbe114f47df265 Mon Sep 17 00:00:00 2001 From: Daniel Gibson Date: Sun, 20 Jan 2013 03:41:09 +0100 Subject: [PATCH] Implement Sys_ReLaunch() for Linux, refactor it It now works on Linux so executing it doesn't freeze the game like described in https://github.com/RobertBeckebans/RBDOOM-3-BFG/issues/33 Furthermore, this function doesn't have any parameters anymore (on any platform) because the only thing supplied was the original program arguments +"+set com_skipIntroVideos 1" anyway - this is now done in Sys_ReLaunch() (also on Windows). Having the program arguments as one string is bad on Linux/POSIX because there it's expected that the program arguments are one C-string per argument. --- .../menus/MenuScreen_Shell_Stereoscopics.cpp | 10 +-- .../menus/MenuScreen_Shell_SystemOptions.cpp | 10 +-- neo/sys/linux/linux_main.cpp | 88 ++++++++++++++++++- neo/sys/sys_public.h | 4 +- neo/sys/win32/win_main.cpp | 14 ++- 5 files changed, 108 insertions(+), 18 deletions(-) diff --git a/neo/d3xp/menus/MenuScreen_Shell_Stereoscopics.cpp b/neo/d3xp/menus/MenuScreen_Shell_Stereoscopics.cpp index 52d39c10..9c46b582 100644 --- a/neo/d3xp/menus/MenuScreen_Shell_Stereoscopics.cpp +++ b/neo/d3xp/menus/MenuScreen_Shell_Stereoscopics.cpp @@ -219,12 +219,10 @@ void idMenuScreen_Shell_Stereoscopics::HideScreen( const mainMenuTransition_t tr common->Dialog().ClearDialog( msg ); if( restart ) { - idStr cmdLine = Sys_GetCmdLine(); - if( cmdLine.Find( "com_skipIntroVideos" ) < 0 ) - { - cmdLine.Append( " +set com_skipIntroVideos 1" ); - } - Sys_ReLaunch( ( void* )cmdLine.c_str(), cmdLine.Length() ); + // DG: Sys_ReLaunch() doesn't need any options anymore + // (the old way would have been unnecessarily painful on POSIX systems) + Sys_ReLaunch(); + // DG end } return idSWFScriptVar(); } diff --git a/neo/d3xp/menus/MenuScreen_Shell_SystemOptions.cpp b/neo/d3xp/menus/MenuScreen_Shell_SystemOptions.cpp index b11ca75e..4646b96c 100644 --- a/neo/d3xp/menus/MenuScreen_Shell_SystemOptions.cpp +++ b/neo/d3xp/menus/MenuScreen_Shell_SystemOptions.cpp @@ -232,12 +232,10 @@ void idMenuScreen_Shell_SystemOptions::HideScreen( const mainMenuTransition_t tr common->Dialog().ClearDialog( msg ); if( restart ) { - idStr cmdLine = Sys_GetCmdLine(); - if( cmdLine.Find( "com_skipIntroVideos" ) < 0 ) - { - cmdLine.Append( " +set com_skipIntroVideos 1" ); - } - Sys_ReLaunch( ( void* )cmdLine.c_str(), cmdLine.Length() ); + // DG: Sys_ReLaunch() doesn't need any options anymore + // (the old way would have been unnecessarily painful on POSIX systems) + Sys_ReLaunch(); + // DG end } return idSWFScriptVar(); } diff --git a/neo/sys/linux/linux_main.cpp b/neo/sys/linux/linux_main.cpp index 4132b537..d913ca35 100644 --- a/neo/sys/linux/linux_main.cpp +++ b/neo/sys/linux/linux_main.cpp @@ -38,6 +38,13 @@ If you have questions concerning this license or the applicable additional terms #include #include +// DG: needed for Sys_ReLaunch() +#include + +static const char** cmdargv = NULL; +static int cmdargc = 0; +// DG end + #ifdef ID_MCHECK #include #endif @@ -46,7 +53,6 @@ static idStr basepath; static idStr savepath; - /* ============== Sys_DefaultSavePath @@ -645,6 +651,7 @@ Sys_GetCmdLine */ const char* Sys_GetCmdLine() { + // DG: don't use this, use cmdargv and cmdargc instead! return "TODO Sys_GetCmdLine"; } @@ -653,9 +660,80 @@ const char* Sys_GetCmdLine() Sys_ReLaunch ======================== */ -void Sys_ReLaunch( void* data, const unsigned int dataSize ) +void Sys_ReLaunch() { - idLib::Error( "Could not start process: TODO Sys_ReLaunch() " ); + // DG: implementing this... basic old fork() exec() (+ setsid()) routine.. + // NOTE: this function used to have parameters: the commandline arguments, but as one string.. + // for Linux/Unix we want one char* per argument so we'll just add the friggin' + // " +set com_skipIntroVideos 1" to the other commandline arguments in this function. + + int ret = fork(); + if( ret < 0 ) + idLib::Error( "Sys_ReLaunch(): Couldn't fork(), reason: %s ", strerror( errno ) ); + + if( ret == 0 ) + { + // child process + + // get our own session so we don't depend on the (soon to be killed) + // parent process anymore - else we'll freeze + pid_t sId = setsid(); + if( sId == ( pid_t ) - 1 ) + { + idLib::Error( "Sys_ReLaunch(): setsid() failed! Reason: %s ", strerror( errno ) ); + } + + // close all FDs (except for stdin/out/err) so we don't leak FDs + DIR* devfd = opendir( "/dev/fd" ); + if( devfd != NULL ) + { + struct dirent entry; + struct dirent* result; + while( readdir_r( devfd, &entry, &result ) == 0 ) + { + const char* filename = result->d_name; + char* endptr = NULL; + long int fd = strtol( filename, &endptr, 0 ); + if( endptr != filename && fd > STDERR_FILENO ) + close( fd ); + } + } + else + { + idLib::Warning( "Sys_ReLaunch(): Couldn't open /dev/fd/ - will leak file descriptors. Reason: %s", strerror( errno ) ); + } + + // + 3 because "+set" "com_skipIntroVideos" "1" - and note that while we'll skip + // one (the first) cmdargv argument, we need one more pointer for NULL at the end. + int argc = cmdargc + 3; + const char** argv = ( const char** )calloc( argc, sizeof( char* ) ); + + int i; + for( i = 0; i < cmdargc - 1; ++i ) + argv[i] = cmdargv[i + 1]; // ignore cmdargv[0] == executable name + + // add +set com_skipIntroVideos 1 + argv[i++] = "+set"; + argv[i++] = "com_skipIntroVideos"; + argv[i++] = "1"; + // execv expects NULL terminated array + argv[i] = NULL; + + const char* exepath = Sys_EXEPath(); + + errno = 0; + execv( exepath, ( char** )argv ); + // we only get here if execv() fails, else the executable is restarted + idLib::Error( "Sys_ReLaunch(): WTF exec() failed! Reason: %s ", strerror( errno ) ); + + } + else + { + // original process + // just do a clean shutdown + cmdSystem->AppendCommandText( "quit\n" ); + } + // DG end } /* @@ -665,6 +743,10 @@ main */ int main( int argc, const char** argv ) { + // DG: needed for Sys_ReLaunch() + cmdargc = argc; + cmdargv = argv; + // DG end #ifdef ID_MCHECK // must have -lmcheck linkage mcheck( abrt_func ); diff --git a/neo/sys/sys_public.h b/neo/sys/sys_public.h index 5e77a4d3..8fc66034 100644 --- a/neo/sys/sys_public.h +++ b/neo/sys/sys_public.h @@ -432,7 +432,9 @@ void Sys_Init(); void Sys_Shutdown(); void Sys_Error( const char* error, ... ); const char* Sys_GetCmdLine(); -void Sys_ReLaunch( void* launchData, unsigned int launchDataSize ); +// DG: Sys_ReLaunch() doesn't need any options (and the old way is painful for POSIX systems) +void Sys_ReLaunch(); +// DG end void Sys_Launch( const char* path, idCmdArgs& args, void* launchData, unsigned int launchDataSize ); void Sys_SetLanguageFromSystem(); const char* Sys_DefaultLanguage(); diff --git a/neo/sys/win32/win_main.cpp b/neo/sys/win32/win_main.cpp index c14d60de..19ab553c 100644 --- a/neo/sys/win32/win_main.cpp +++ b/neo/sys/win32/win_main.cpp @@ -290,7 +290,7 @@ const char * Sys_GetCmdLine() { Sys_ReLaunch ======================== */ -void Sys_ReLaunch( void * data, const unsigned int dataSize ) { +void Sys_ReLaunch() { TCHAR szPathOrig[MAX_PRINT_MSG]; STARTUPINFO si; PROCESS_INFORMATION pi; @@ -298,7 +298,17 @@ void Sys_ReLaunch( void * data, const unsigned int dataSize ) { ZeroMemory( &si, sizeof(si) ); si.cb = sizeof(si); - strcpy( szPathOrig, va( "\"%s\" %s", Sys_EXEPath(), (const char *)data ) ); + // DG: we don't have function arguments in Sys_ReLaunch() anymore, everyone only passed + // the command-line +" +set com_skipIntroVideos 1" anyway and it was painful on POSIX systems + // so let's just add it here. + idStr cmdLine = Sys_GetCmdLine(); + if( cmdLine.Find( "com_skipIntroVideos" ) < 0 ) + { + cmdLine.Append( " +set com_skipIntroVideos 1" ); + } + + strcpy( szPathOrig, va( "\"%s\" %s", Sys_EXEPath(), cmdLine.c_str() ) ); + // DG end CloseHandle( hProcessMutex );