mirror of
https://bitbucket.org/CPMADevs/cnq3
synced 2025-03-13 22:23:04 +00:00
sv_restartProcess restarts the child process
uptime print child process, parent process and map uptimes
This commit is contained in:
parent
484e9e7e40
commit
9a7ab19245
7 changed files with 199 additions and 1 deletions
|
@ -1,6 +1,8 @@
|
|||
|
||||
DD Mmm 17 - 1.49
|
||||
|
||||
add: uptime command that prints uptimes for the process, the current map and the parent process
|
||||
|
||||
add: keys F13 to F24 are now bindable
|
||||
|
||||
add: new cvar type and range extension for compatible mods like CPMA 1.50
|
||||
|
@ -142,6 +144,7 @@ Linux:
|
|||
add: automatic dedicated server process restarts for crashes and timed reboots (sv_minRebootDelayMins)
|
||||
this means 2 CNQ3 processes run per server: a parent (fixed pid) and a child (new pid after each restart)
|
||||
this behavior can be disabled by passing "nohardreboot" as a command-line argument
|
||||
the new command sv_restartProcess can be used to shut down the child process and start a new one
|
||||
|
||||
fix: color codes (e.g. "^1") don't get printed to the terminal anymore
|
||||
|
||||
|
|
|
@ -1106,6 +1106,9 @@ qbool Sys_LowPhysicalMemory( void );
|
|||
|
||||
qbool Sys_HardReboot(); // qtrue when the server can restart itself
|
||||
|
||||
qbool Sys_HasCNQ3Parent(); // qtrue if a child of CNQ3
|
||||
int Sys_GetUptimeSeconds( qbool parent ); // negative if not available
|
||||
|
||||
// huffman.cpp - id's original code
|
||||
// used for out-of-band (OOB) datagrams with dynamically created trees
|
||||
void DynHuff_Compress( msg_t* buf, int offset );
|
||||
|
|
|
@ -77,6 +77,8 @@ struct server_t {
|
|||
int gameClientSize; // will be > sizeof(playerState_t) due to game private data
|
||||
|
||||
int restartTime;
|
||||
|
||||
int mapLoadTime;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -695,6 +695,65 @@ static void SV_ServerRestart_f()
|
|||
}
|
||||
|
||||
|
||||
static void SV_RestartProcess_f()
|
||||
{
|
||||
if ( !Sys_HasCNQ3Parent() ) {
|
||||
Com_Printf( "this server isn't controlled by a parent CNQ3 process\n" );
|
||||
return;
|
||||
}
|
||||
|
||||
Com_Quit(1);
|
||||
}
|
||||
|
||||
|
||||
static const char* SV_FormatUptime( int seconds )
|
||||
{
|
||||
static char result[32];
|
||||
|
||||
static const int unitCount = 4;
|
||||
static const char* units[unitCount] = { "s", "m", "h", "d" };
|
||||
static const int divisors[unitCount] = { 1, 60, 60, 24 };
|
||||
|
||||
if ( seconds <= 0 )
|
||||
return "nada";
|
||||
|
||||
int uptime[unitCount] = { seconds, 0, 0, 0 };
|
||||
for ( int i = 1; i < unitCount; ++i ) {
|
||||
uptime[i] = uptime[i-1] / divisors[i];
|
||||
uptime[i-1] -= uptime[i] * divisors[i];
|
||||
}
|
||||
|
||||
result[0] = '\0';
|
||||
for ( int i = unitCount - 1; i >= 0; --i ) {
|
||||
if ( uptime[i] <= 0 )
|
||||
continue;
|
||||
|
||||
Q_strcat( result, sizeof( result ), va( "%d%s", uptime[i], units[i] ) );
|
||||
if ( i > 0 && uptime[i-1] > 0 )
|
||||
Q_strcat( result, sizeof( result ), " " );
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
static void SV_Uptime_f()
|
||||
{
|
||||
if (Sys_HasCNQ3Parent()) {
|
||||
const int parentTime = Sys_GetUptimeSeconds( qtrue );
|
||||
Com_Printf( "Parent process : %s\n", parentTime >= 0 ? SV_FormatUptime(parentTime) : "unknown" );
|
||||
}
|
||||
|
||||
const int childTime = Sys_GetUptimeSeconds( qfalse );
|
||||
Com_Printf( "This process : %s\n", childTime >= 0 ? SV_FormatUptime(childTime) : "unknown" );
|
||||
|
||||
int mapTime = -1;
|
||||
if ( Cvar_VariableIntegerValue( "sv_running" ) )
|
||||
mapTime = ( Sys_Milliseconds() - sv.mapLoadTime ) / 1000;
|
||||
Com_Printf( "Current map : %s\n", mapTime >= 0 ? SV_FormatUptime(mapTime) : "no map loaded" );
|
||||
}
|
||||
|
||||
|
||||
static const cmdTableItem_t sv_cmds[] =
|
||||
{
|
||||
{ "heartbeat", SV_Heartbeat_f, NULL, "sends a heartbeat to master servers" },
|
||||
|
@ -711,7 +770,9 @@ static const cmdTableItem_t sv_cmds[] =
|
|||
{ "map", SV_Map_f, SV_CompleteMap_f, "loads a map" },
|
||||
{ "devmap", SV_DevMap_f, SV_CompleteMap_f, "loads a map with cheats enabled" },
|
||||
{ "killserver", SV_KillServer_f, NULL, "shuts the server down" },
|
||||
{ "sv_restart", SV_ServerRestart_f, NULL, "restarts the server" }
|
||||
{ "sv_restart", SV_ServerRestart_f, NULL, "restarts the server" },
|
||||
{ "sv_restartProcess", SV_RestartProcess_f, NULL, "restarts the server's child process" },
|
||||
{ "uptime", SV_Uptime_f, NULL, "prints the server's uptimes" }
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -522,6 +522,8 @@ void SV_SpawnServer( const char* mapname )
|
|||
|
||||
Hunk_SetMark();
|
||||
|
||||
sv.mapLoadTime = Sys_Milliseconds();
|
||||
|
||||
QSUBSYSTEM_INIT_DONE( "Server" );
|
||||
}
|
||||
|
||||
|
|
|
@ -31,6 +31,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|||
#include <sys/ipc.h>
|
||||
#include <sys/shm.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/sysinfo.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <sys/wait.h>
|
||||
|
@ -909,6 +910,96 @@ static void Lin_HardRebootHandler( int argc, char** argv )
|
|||
#endif
|
||||
|
||||
|
||||
static qbool lin_hasParent = qfalse;
|
||||
static pid_t lix_parentPid;
|
||||
|
||||
|
||||
static const char* Lin_GetExeName(const char* path)
|
||||
{
|
||||
const char* lastSlash = strrchr(path, '/');
|
||||
if (lastSlash == NULL)
|
||||
return path;
|
||||
|
||||
return lastSlash + 1;
|
||||
}
|
||||
|
||||
|
||||
static void Lin_TrackParentProcess()
|
||||
{
|
||||
static char cmdLine[1024];
|
||||
|
||||
char fileName[128];
|
||||
Com_sprintf(fileName, sizeof(fileName), "/proc/%d/cmdline", (int)getppid());
|
||||
|
||||
const int fd = open(fileName, O_RDONLY);
|
||||
if (fd == -1)
|
||||
return;
|
||||
|
||||
const qbool hasCmdLine = read(fd, cmdLine, sizeof(cmdLine)) > 0;
|
||||
close(fd);
|
||||
|
||||
if (!hasCmdLine)
|
||||
return;
|
||||
|
||||
cmdLine[sizeof(cmdLine) - 1] = '\0';
|
||||
lin_hasParent = strcmp(Lin_GetExeName(cmdLine), Lin_GetExeName(q_argv[0])) == 0;
|
||||
}
|
||||
|
||||
|
||||
qbool Sys_HasCNQ3Parent()
|
||||
{
|
||||
return lin_hasParent;
|
||||
}
|
||||
|
||||
|
||||
static int Sys_GetProcessUptime( pid_t pid )
|
||||
{
|
||||
// length must be in sync with the fscanf call!
|
||||
static char word[256];
|
||||
|
||||
// The process start time is the 22nd column and
|
||||
// encoded as jiffies after system boot.
|
||||
const int jiffiesPerSec = sysconf(_SC_CLK_TCK);
|
||||
if (jiffiesPerSec <= 0)
|
||||
return -1;
|
||||
|
||||
char fileName[128];
|
||||
Com_sprintf(fileName, sizeof(fileName), "/proc/%ld/stat", (long)pid);
|
||||
FILE* const file = fopen(fileName, "r");
|
||||
if (file == NULL)
|
||||
return -1;
|
||||
|
||||
for (int i = 0; i < 21; ++i) {
|
||||
if (fscanf(file, "%255s", word) != 1) {
|
||||
fclose(file);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
int jiffies;
|
||||
const qbool success = fscanf(file, "%d", &jiffies) == 1;
|
||||
fclose(file);
|
||||
|
||||
if (!success)
|
||||
return -1;
|
||||
|
||||
const int secondsSinceBoot = jiffies / jiffiesPerSec;
|
||||
struct sysinfo info;
|
||||
sysinfo(&info);
|
||||
|
||||
return (int)info.uptime - secondsSinceBoot;
|
||||
}
|
||||
|
||||
|
||||
int Sys_GetUptimeSeconds( qbool parent )
|
||||
{
|
||||
if (!lin_hasParent)
|
||||
return -1;
|
||||
|
||||
return Sys_GetProcessUptime( parent ? getppid() : getpid() );
|
||||
}
|
||||
|
||||
|
||||
int main( int argc, char** argv )
|
||||
{
|
||||
q_argc = argc;
|
||||
|
@ -938,6 +1029,7 @@ int main( int argc, char** argv )
|
|||
Com_Printf( "Working directory: %s\n", Sys_Cwd() );
|
||||
|
||||
Sys_ConsoleInputInit();
|
||||
Lin_TrackParentProcess();
|
||||
|
||||
for (;;) {
|
||||
SIG_Frame();
|
||||
|
|
|
@ -47,3 +47,38 @@ qbool Sys_HardReboot()
|
|||
return qfalse;
|
||||
}
|
||||
|
||||
|
||||
qbool Sys_HasCNQ3Parent()
|
||||
{
|
||||
return qfalse;
|
||||
}
|
||||
|
||||
|
||||
int Sys_GetUptimeSeconds( qbool parent )
|
||||
{
|
||||
if (parent)
|
||||
return -1;
|
||||
|
||||
FILETIME startFileTime;
|
||||
FILETIME trash[3];
|
||||
if (GetProcessTimes(GetCurrentProcess(), &startFileTime, &trash[0], &trash[1], &trash[2]) == 0)
|
||||
return -1;
|
||||
|
||||
SYSTEMTIME endSystemTime;
|
||||
GetSystemTime(&endSystemTime);
|
||||
|
||||
FILETIME endFileTime;
|
||||
if (SystemTimeToFileTime(&endSystemTime, &endFileTime) == 0)
|
||||
return -1;
|
||||
|
||||
// 1 FILETIME unit is 100-nanoseconds
|
||||
ULARGE_INTEGER start, end;
|
||||
start.LowPart = startFileTime.dwLowDateTime;
|
||||
start.HighPart = startFileTime.dwHighDateTime;
|
||||
end.LowPart = endFileTime.dwLowDateTime;
|
||||
end.HighPart = endFileTime.dwHighDateTime;
|
||||
const int seconds = (int)((end.QuadPart - start.QuadPart) / 1e7);
|
||||
|
||||
return seconds;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue