Initial 0.1 commit

This is a small daemon/libary to enable games to request a
performance "game mode" from the system.

Currently only implemented to upgrade the CPU governor and
automatically downgrade it once done.

A game only needs to load libgamemodeauto to activate the request.
Which means an LD_PRELOAD can be applied to any game to activate
the mode as needed.

However gamemode_client.h can be used to manually activate/deactivate
the mode as needed, and also perform error checking.

The libs are miminal loaders to call into an installed libgamemode
which, if the daemon is installed and running, will send through
the equivelant game mode request, and magic will happen.

See the README.md for more details.

Currently licensed under the BSD 3 clause license.
This commit is contained in:
Marc Di Luzio
2017-12-11 11:26:59 +00:00
commit c459c05076
23 changed files with 1621 additions and 0 deletions

193
daemon/cpugovctl.c Normal file
View File

@@ -0,0 +1,193 @@
/*
Copyright (c) 2017, Feral Interactive
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of Feral Interactive nor the names of its contributors
may be used to endorse or promote products derived from this software
without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
#include "logging.h"
#include <sys/types.h>
#include <dirent.h>
#include <ctype.h>
static const int max_governors = 128;
static const int max_governor_length = PATH_MAX+1;
// Governers are located at /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor
static int fetch_governors( char governors[max_governors][max_governor_length] )
{
const char* cpu_base_path = "/sys/devices/system/cpu/";
DIR* dir = opendir( cpu_base_path );
if( !dir )
FATAL_ERRORNO( "cpu device path not found" );
int num_governors = 0;
// Explore the directory
struct dirent* ent;
while( ( ent = readdir(dir) ) && num_governors < max_governors )
{
// CPU directories all start with "cpu"
if( strncmp( ent->d_name, "cpu", 3 ) == 0 )
{
// Check if this matches "cpu\d+"
const int len = strlen( ent->d_name );
if( len > 3 && len < 5
&& isdigit( ent->d_name[3] ) )
{
// Construct the full path
char path[PATH_MAX] = {};
snprintf( path, sizeof(path), "%s%s/cpufreq/scaling_governor", cpu_base_path, ent->d_name );
// Get the real path to the file
// Traditionally cpufreq symlinks to a policy directory that can be shared
// So let's prevent duplicates
char fullpath[PATH_MAX] = {};
const char* ptr = realpath( path, fullpath );
if( fullpath != ptr )
continue;
// Only add if unique
for( int i = 0; i < num_governors; i++ )
{
if( strncmp( fullpath, governors[i], max_governor_length ) == 0 )
continue;
}
strncpy( governors[num_governors], fullpath, max_governor_length );
num_governors++;
}
}
}
closedir(dir);
return num_governors;
}
// Get the current governor state
const char* get_gov_state()
{
// To be returned
static char governor[64];
memset( governor, 0, sizeof(governor) );
// State of all the overnors
char governors[max_governors][max_governor_length];
memset( governors, 0, sizeof(governors) );
int num = fetch_governors( governors );
// Check the list
for( int i = 0; i < num; i++ )
{
const char* gov = governors[i];
FILE* f = fopen( gov, "r" );
if( !f )
{
LOG_ERROR( "Failed to open file for read %s\n", gov );
continue;
}
// Pull out the file contents
fseek( f, 0, SEEK_END );
int length = ftell(f);
fseek( f, 0, SEEK_SET );
char contents[length];
if( fread(contents, 1, length, f) > 0 )
{
// Files have a newline
strtok(contents, "\n");
if( strlen(governor) > 0 && strncmp( governor, contents, 64 ) != 0 )
{
// Don't handle the mixed case, this shouldn't ever happen
// But it is a clear sign we shouldn't carry on
LOG_ERROR( "Governors malformed: got \"%s\", expected \"%s\"", contents, governor );
return "malformed";
}
strncpy( governor, contents, sizeof(governor) );
}
else
{
LOG_ERROR( "Failed to read contents of %s\n", gov );
}
fclose( f );
}
return governor;
}
// Sets all governors to a value
void set_gov_state( const char* value )
{
char governors[max_governors][max_governor_length];
memset( governors, 0, sizeof(governors) );
int num = fetch_governors( governors );
LOG_MSG( "Setting governors to %s\n", value );
for( int i = 0; i < num; i++ )
{
const char* gov = governors[i];
FILE* f = fopen( gov, "w" );
if( !f )
{
LOG_ERROR( "Failed to open file for write %s\n", gov );
continue;
}
fprintf( f, "%s\n", value );
fclose( f );
}
}
// Main entry point
int main( int argc, char *argv[] )
{
if( argc < 2 )
{
fprintf( stderr, "usage: cpugovctl [get] [set VALUE]\n" );
exit( EXIT_FAILURE );
}
if( strncmp( argv[1], "get", 3 ) == 0 )
{
printf( "%s", get_gov_state() );
}
else if( strncmp( argv[1], "set", 3 ) == 0 )
{
const char* value = argv[2];
set_gov_state( value );
}
else
{
exit( EXIT_FAILURE );
}
}

69
daemon/daemonize.c Normal file
View File

@@ -0,0 +1,69 @@
/*
Copyright (c) 2017, Feral Interactive
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of Feral Interactive nor the names of its contributors
may be used to endorse or promote products derived from this software
without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
#include "daemonize.h"
#include "logging.h"
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
// function to daemonize the process
void daemonize( char* name )
{
// Fork once
pid_t pid = fork();
if( pid < 0 )
FATAL_ERRORNO( "Failed to fork" );
if( pid != 0 )
{
LOG_MSG( "Daemon launched...\n" );
exit( EXIT_SUCCESS );
}
// Fork a second time
pid = fork();
if( pid < 0 )
FATAL_ERRORNO( "Failed to fork" );
else if( pid > 0 )
exit( EXIT_SUCCESS );
// Continue to set up as a daemon
umask(0);
if ( setsid() < 0 )
FATAL_ERRORNO( "Failed to create process group\n" );
if ( chdir( "/" ) < 0 )
FATAL_ERRORNO( "Failed to change to root directory\n" );
close( STDIN_FILENO );
close( STDOUT_FILENO );
close( STDERR_FILENO );
}

38
daemon/daemonize.h Normal file
View File

@@ -0,0 +1,38 @@
/*
Copyright (c) 2017, Feral Interactive
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of Feral Interactive nor the names of its contributors
may be used to endorse or promote products derived from this software
without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef _DAEMONIZE_GAMEMODE_H_
#define _DAEMONIZE_GAMEMODE_H_
// Function to daemonize the process
// Exits with error in case of failure
void daemonize( char* name );
#endif // _DAEMONIZE_GAMEMODE_H_

154
daemon/dbus_messaging.c Normal file
View File

@@ -0,0 +1,154 @@
/*
Copyright (c) 2017, Feral Interactive
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of Feral Interactive nor the names of its contributors
may be used to endorse or promote products derived from this software
without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
#include "dbus_messaging.h"
#include "logging.h"
#include "gamemode.h"
#include "governors.h"
#include "daemonize.h"
#include <stdlib.h>
#include <systemd/sd-bus.h>
// sd-bus tracker values
static sd_bus* bus = NULL;
static sd_bus_slot* slot = NULL;
// Clean up any resources as needed
static void clean_up()
{
if( slot )
sd_bus_slot_unref( slot );
slot = NULL;
if( bus )
sd_bus_unref( bus );
bus = NULL;
}
// Callback for RegisterGame
static int method_register_game( sd_bus_message *m,
void *userdata,
sd_bus_error *ret_error )
{
int pid = 0;
int ret = sd_bus_message_read( m, "i", &pid );
if( ret < 0 )
{
LOG_ERROR( "Failed to parse input parameters: %s\n", strerror(-ret) );
return ret;
}
register_game( pid );
return sd_bus_reply_method_return( m, "i", 0 );
}
// Callback for UnregisterGame
static int method_unregister_game( sd_bus_message *m,
void *userdata,
sd_bus_error *ret_error )
{
int pid = 0;
int ret = sd_bus_message_read( m, "i", &pid );
if( ret < 0 )
{
LOG_ERROR( "Failed to parse input parameters: %s\n", strerror(-ret) );
return ret;
}
unregister_game( pid );
return sd_bus_reply_method_return( m, "i", 0 );
}
// Vtable for function dispatch
static const sd_bus_vtable gamemode_vtable[] = {
SD_BUS_VTABLE_START( 0 ),
SD_BUS_METHOD( "RegisterGame", "i", "i", method_register_game, SD_BUS_VTABLE_UNPRIVILEGED ),
SD_BUS_METHOD( "UnregisterGame", "i", "i", method_unregister_game, SD_BUS_VTABLE_UNPRIVILEGED ),
SD_BUS_VTABLE_END
};
// Main loop, will not return until something request a quit
void run_dbus_main_loop( bool system_dbus )
{
// Set up function to handle clean up of resources
atexit( clean_up );
int ret = 0;
// Connec to the desired bus
if( system_dbus )
ret = sd_bus_open_system( &bus );
else
ret = sd_bus_open_user( &bus );
if( ret < 0 )
FATAL_ERROR( "Failed to connect to the bus: %s", strerror(-ret) );
// Create the object to allow connections
ret = sd_bus_add_object_vtable( bus,
&slot,
"/com/feralinteractive/GameMode",
"com.feralinteractive.GameMode",
gamemode_vtable,
NULL );
if( ret < 0 )
FATAL_ERROR( "Failed to install GameMode object: %s", strerror(-ret) );
// Request our name
ret = sd_bus_request_name( bus, "com.feralinteractive.GameMode", 0 );
if( ret < 0 )
FATAL_ERROR( "Failed to acquire service name: %s", strerror(-ret) );
LOG_MSG( "Successfully initialised bus with name [%s]...\n", "com.feralinteractive.GameMode" );
// Now loop, waiting for callbacks
for(;;)
{
ret = sd_bus_process( bus, NULL );
if( ret < 0 )
FATAL_ERROR( "Failure when processing the bus: %s", strerror(-ret) );
// We're done processing
if( ret > 0 )
continue;
// Wait for more
ret = sd_bus_wait( bus, (uint64_t)-1 );
if( ret < 0 && -ret != EINTR )
FATAL_ERROR( "Failure when waiting on bus: %s", strerror(-ret) );
}
}

40
daemon/dbus_messaging.h Normal file
View File

@@ -0,0 +1,40 @@
/*
Copyright (c) 2017, Feral Interactive
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of Feral Interactive nor the names of its contributors
may be used to endorse or promote products derived from this software
without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef _DBUS_MESSAGING_GAMEMODE_H_
#define _DBUS_MESSAGING_GAMEMODE_H_
#include <stdbool.h>
// Run the main dbus loop
// Will not return until finished
void run_dbus_main_loop( bool system_dbus );
#endif // _DBUS_MESSAGING_GAMEMODE_H_

177
daemon/gamemode.c Normal file
View File

@@ -0,0 +1,177 @@
/*
Copyright (c) 2017, Feral Interactive
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of Feral Interactive nor the names of its contributors
may be used to endorse or promote products derived from this software
without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
#include "gamemode.h"
#include "logging.h"
#include "governors.h"
#include <signal.h>
#include <string.h>
// Storage for game PIDs
#define MAX_GAMES 16
static int game_pids[MAX_GAMES];
static int num_games = 0;
// Constant to control how often we'll wake up and check process ids
static const int wakeup_timer = 5;
static void start_alarm_timer();
// Called once to enter game mode
// Must be followed by a call to leave_game_mode
// Must be called after init_game_mode
static void enter_game_mode()
{
LOG_MSG( "Entering Game Mode...\n" );
set_governors( "performance" );
// Set up the alarm callback
start_alarm_timer();
}
// Called once to leave game mode
// Must be called after an equivelant call to enter_game_mode
static void leave_game_mode()
{
LOG_MSG( "Leaving Game Mode...\n" );
set_governors( NULL );
}
// Set up an alarm callback to check for current process ids
static void alarm_handler( int sig )
{
// Quick return if no games, and don't register another callback
if( num_games == 0 )
return;
// Check if games are alive at all
for( int i = 0; i < num_games; )
{
int game = game_pids[i];
if( kill( game, 0 ) != 0 )
{
LOG_MSG( "Removing expired game [%i]...\n", game );
memmove( &game_pids[i], &game_pids[i+1], MAX_GAMES - (i+1) );
num_games--;
}
else
i++;
}
// Either trigger another alarm, or reset the governors
if( num_games )
start_alarm_timer();
else
leave_game_mode();
}
// Call to trigger starting the alarm timer for pid checks
static void start_alarm_timer()
{
// Set up process check alarms
signal( SIGALRM, alarm_handler );
alarm( wakeup_timer );
}
// Intialise any game mode needs
void init_game_mode()
{
// Read current governer state before setting up any message handling
update_initial_gov_state();
LOG_MSG( "governor is set to [%s]\n", get_initial_governor() );
}
// Called on exit to clean up the governors if appropriate
void term_game_mode()
{
if( num_games )
leave_game_mode();
}
// Register a game pid with the game mode
// Will trigger enter game mode if appropriate
void register_game( int pid )
{
// Check for duplicates
for( int i = 0; i < num_games; i++ )
{
if( game_pids[i] == pid )
{
LOG_ERROR( "Addition requested for already known process [%i]\n", pid );
return;
}
}
// Check we've not already hit max
if( num_games == MAX_GAMES )
{
LOG_ERROR( "Max games (%i) reached, could not add [%i]\n", MAX_GAMES, pid );
return;
}
// Add the game to the database
LOG_MSG( "Adding game: %i\n", pid );
game_pids[num_games] = pid;
num_games++;
if( num_games == 1 )
enter_game_mode();
}
// Remove a game from game mode
// Will exit game mode if appropriate
void unregister_game( int pid )
{
bool found = false;
// Check list even contains this entry
for( int i = 0; i < num_games; i++ )
{
if( game_pids[i] == pid )
{
LOG_MSG( "Removing game: %i\n", pid );
memmove( &game_pids[i], &game_pids[i+1], MAX_GAMES - (i+1) );
num_games--;
found = true;
}
}
if( !found )
{
LOG_ERROR( "Removal requested for unknown process [%i]\n", pid );
return;
}
// Leave game mode if needed
if( num_games == 0 )
leave_game_mode();
}

43
daemon/gamemode.h Normal file
View File

@@ -0,0 +1,43 @@
/*
Copyright (c) 2017, Feral Interactive
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of Feral Interactive nor the names of its contributors
may be used to endorse or promote products derived from this software
without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef _GAME_MODE_GAMEMODE_H_
#define _GAME_MODE_GAMEMODE_H_
// Initialise or terminate the game mode system
void init_game_mode();
void term_game_mode();
// Add or remove games to the tracker
// Tracker will automatically start and stop game mode as appropriate
void register_game( int pid );
void unregister_game( int pid );
#endif // _GAME_MODE_GAMEMODE_H_

78
daemon/governors.c Normal file
View File

@@ -0,0 +1,78 @@
/*
Copyright (c) 2017, Feral Interactive
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of Feral Interactive nor the names of its contributors
may be used to endorse or promote products derived from this software
without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
#include "governors.h"
#include "logging.h"
#include <stdio.h>
#include <unistd.h>
#include <linux/limits.h>
static char initial[32];
// Store the initial governor state to be referenced later
void update_initial_gov_state()
{
static char* command = "cpugovctl get";
FILE* f = popen( command, "r" );
if( !f )
FATAL_ERRORNO( "Failed to launch \"%s\" script", command );
if( !fgets( initial, sizeof(initial)-1, f ) )
FATAL_ERROR( "Failed to get output from \"%s\"", command );
pclose(f);
strtok( initial, "\n" );
}
// Sets all governors to a value, if NULL argument provided, will reset them back
void set_governors( const char* value )
{
const char* newval = value ? value : initial;
LOG_MSG("Setting governors to %s\n", newval ? newval : "initial values");
char command[PATH_MAX] = {};
snprintf( command, sizeof(command), "cpugovctl set %s", newval );
FILE* f = popen( command, "r" );
if( !f )
FATAL_ERRORNO( "Failed to launch %s script", command );
pclose(f);
}
// Return the initial governor
const char* get_initial_governor()
{
return initial;
}

43
daemon/governors.h Normal file
View File

@@ -0,0 +1,43 @@
/*
Copyright (c) 2017, Feral Interactive
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of Feral Interactive nor the names of its contributors
may be used to endorse or promote products derived from this software
without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef _GOVERNORS_GAMEMODE_H_
#define _GOVERNORS_GAMEMODE_H_
// Store the initial governor state to be referenced later
void update_initial_gov_state();
// Get the initial governor state
const char* get_initial_governor();
// Sets all governors to a value, if null argument provided, will reset them back
void set_governors( const char* value );
#endif // _GOVERNORS_GAMEMODE_H_

48
daemon/logging.c Normal file
View File

@@ -0,0 +1,48 @@
/*
Copyright (c) 2017, Feral Interactive
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of Feral Interactive nor the names of its contributors
may be used to endorse or promote products derived from this software
without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
#include "logging.h"
#include "syslog.h"
static bool use_syslog = false;
// Control if we want to use the system logger
void set_use_syslog( const char* name )
{
// Open the syslog
openlog( name, LOG_PID, LOG_DAEMON );
use_syslog = true;
}
// Simple getter for the syslog var
bool get_use_syslog()
{
return use_syslog;
}

59
daemon/logging.h Normal file
View File

@@ -0,0 +1,59 @@
/*
Copyright (c) 2017, Feral Interactive
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of Feral Interactive nor the names of its contributors
may be used to endorse or promote products derived from this software
without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef _LOGGING_GAMEMODE_H_
#define _LOGGING_GAMEMODE_H_
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
#include <unistd.h>
#include <stdbool.h>
// Logging helpers
#define PLOG_MSG( msg, ... ) printf( msg, ##__VA_ARGS__ )
#define SYSLOG_MSG( msg, ... ) syslog( LOG_INFO, msg, ##__VA_ARGS__ )
#define LOG_MSG( msg, ... ) do { if( get_use_syslog() ) SYSLOG_MSG( msg, ##__VA_ARGS__ ); else PLOG_MSG( msg, ##__VA_ARGS__ ); } while(0)
#define PLOG_ERROR( msg, ... ) fprintf( stderr, msg, ##__VA_ARGS__ )
#define SYSLOG_ERROR( msg, ... ) syslog( LOG_ERR, msg, ##__VA_ARGS__ )
#define LOG_ERROR( msg, ... ) do { if( get_use_syslog() ) SYSLOG_MSG( msg, ##__VA_ARGS__ ); else PLOG_MSG( msg, ##__VA_ARGS__ ); } while(0)
// Fatal errors trigger an exit
#define FATAL_ERRORNO( msg, ... ) do { LOG_ERROR( msg " (%s)\n", ##__VA_ARGS__, strerror(errno) ); exit(EXIT_FAILURE); } while(0)
#define FATAL_ERROR( msg, ... ) do { LOG_ERROR( msg, ##__VA_ARGS__ ); exit(EXIT_FAILURE); } while(0)
// Control if we want to use the system logger
void set_use_syslog( const char* name );
bool get_use_syslog();
#endif //_LOGGING_GAMEMODE_H_

99
daemon/main.c Normal file
View File

@@ -0,0 +1,99 @@
/*
Copyright (c) 2017, Feral Interactive
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of Feral Interactive nor the names of its contributors
may be used to endorse or promote products derived from this software
without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
// Simple daemon to allow user space programs to control the CPU governors
#include "gamemode.h"
#include "dbus_messaging.h"
#include "logging.h"
#include "daemonize.h"
#include <string.h>
#include <unistd.h>
#include <signal.h>
static void sigint_handler(int signo)
{
LOG_MSG( "Quitting by request...\n" );
// Terminate the game mode
term_game_mode();
exit( EXIT_SUCCESS );
}
// Main entry point
int main( int argc, char *argv[] )
{
// Gather command line options
bool daemon = false;
bool system_dbus = false;
bool use_syslog = false;
int opt = 0;
while( ( opt = getopt( argc, argv, "dsl") ) != -1 )
{
switch( opt )
{
case 'd':
daemon = true;
break;
case 's':
system_dbus = true;
break;
case 'l':
use_syslog = true;
break;
default:
fprintf( stderr, "Usage: %s [-d] [-s] [-l]\n", argv[0] );
exit( EXIT_FAILURE );
break;
}
}
// Use syslog if requested
if( use_syslog )
set_use_syslog( argv[0] );
// Daemonize ourselves first if asked
if ( daemon )
daemonize( argv[0] );
// Set up the game mode
init_game_mode();
// Set up the SIGINT handler
if( signal( SIGINT, sigint_handler ) == SIG_ERR )
FATAL_ERRORNO( "Could not catch SIGINT" );
// Run the main dbus message loop
run_dbus_main_loop( system_dbus );
// Log we're finished
LOG_MSG( "Quitting naturally...\n" );
}