2010-11-18 23:52:26 +03:00
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
/***
This file is part of systemd .
Copyright 2010 Lennart Poettering
systemd is free software ; you can redistribute it and / or modify it
2012-04-12 02:20:58 +04:00
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation ; either version 2.1 of the License , or
2010-11-18 23:52:26 +03:00
( at your option ) any later version .
systemd is distributed in the hope that it will be useful , but
WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the GNU
2012-04-12 02:20:58 +04:00
Lesser General Public License for more details .
2010-11-18 23:52:26 +03:00
2012-04-12 02:20:58 +04:00
You should have received a copy of the GNU Lesser General Public License
2010-11-18 23:52:26 +03:00
along with systemd ; If not , see < http : //www.gnu.org/licenses/>.
* * */
# include <stdlib.h>
# include <stdbool.h>
# include <errno.h>
# include <string.h>
# include <stdio.h>
# include <limits.h>
2011-07-12 05:36:17 +04:00
# include <getopt.h>
2010-11-18 23:52:26 +03:00
# include "log.h"
2011-04-25 22:41:47 +04:00
# include "strv.h"
2010-11-18 23:52:26 +03:00
# include "util.h"
2012-08-03 18:20:31 +04:00
# include "hashmap.h"
2012-05-07 23:36:12 +04:00
# include "path-util.h"
2012-05-07 20:55:45 +04:00
# include "conf-files.h"
2013-02-14 15:26:13 +04:00
# include "fileio.h"
2013-11-06 21:28:39 +04:00
# include "build.h"
2015-03-11 12:37:45 +03:00
# include "sysctl-util.h"
2010-11-18 23:52:26 +03:00
2013-02-12 02:48:36 +04:00
static char * * arg_prefixes = NULL ;
2010-11-18 23:52:26 +03:00
2014-10-29 15:02:56 +03:00
static const char conf_file_dirs [ ] = CONF_DIRS_NULSTR ( " sysctl " ) ;
2013-02-12 02:48:36 +04:00
static int apply_all ( Hashmap * sysctl_options ) {
2012-08-03 18:20:31 +04:00
char * property , * value ;
Iterator i ;
2015-04-24 20:55:16 +03:00
int r = 0 ;
2013-02-12 02:48:36 +04:00
2012-08-03 18:20:31 +04:00
HASHMAP_FOREACH_KEY ( value , property , sysctl_options , i ) {
int k ;
2015-03-11 12:37:45 +03:00
k = sysctl_write ( property , value ) ;
if ( k < 0 ) {
2015-08-04 16:46:34 +03:00
log_full_errno ( k = = - ENOENT ? LOG_INFO : LOG_WARNING , k ,
2015-08-05 11:02:24 +03:00
" Couldn't write '%s' to '%s', ignoring: %m " , value , property ) ;
2015-03-11 12:37:45 +03:00
2015-04-24 20:55:16 +03:00
if ( r = = 0 & & k ! = - ENOENT )
2015-03-11 12:37:45 +03:00
r = k ;
}
2012-08-03 18:20:31 +04:00
}
2015-04-24 20:55:16 +03:00
2012-08-03 18:20:31 +04:00
return r ;
}
2013-02-12 02:48:36 +04:00
static int parse_file ( Hashmap * sysctl_options , const char * path , bool ignore_enoent ) {
_cleanup_fclose_ FILE * f = NULL ;
int r ;
2010-11-18 23:52:26 +03:00
assert ( path ) ;
2014-03-14 08:32:12 +04:00
r = search_and_fopen_nulstr ( path , " re " , NULL , conf_file_dirs , & f ) ;
2013-02-12 02:48:36 +04:00
if ( r < 0 ) {
2013-03-27 16:41:59 +04:00
if ( ignore_enoent & & r = = - ENOENT )
2011-03-04 00:17:09 +03:00
return 0 ;
2014-11-28 21:13:53 +03:00
return log_error_errno ( r , " Failed to open file '%s', ignoring: %m " , path ) ;
2010-11-18 23:52:26 +03:00
}
2015-03-15 05:56:01 +03:00
log_debug ( " Parsing %s " , path ) ;
2010-11-18 23:52:26 +03:00
while ( ! feof ( f ) ) {
2013-02-12 02:48:36 +04:00
char l [ LINE_MAX ] , * p , * value , * new_value , * property , * existing ;
2013-08-15 20:35:03 +04:00
void * v ;
2013-02-12 02:48:36 +04:00
int k ;
2010-11-18 23:52:26 +03:00
if ( ! fgets ( l , sizeof ( l ) , f ) ) {
if ( feof ( f ) )
break ;
2014-11-28 21:29:59 +03:00
log_error_errno ( errno , " Failed to read file '%s', ignoring: %m " , path ) ;
2013-02-12 02:48:36 +04:00
return - errno ;
2010-11-18 23:52:26 +03:00
}
p = strstrip ( l ) ;
if ( ! * p )
continue ;
2013-04-14 04:22:53 +04:00
if ( strchr ( COMMENTS " \n " , * p ) )
2010-11-18 23:52:26 +03:00
continue ;
2012-08-03 18:20:31 +04:00
value = strchr ( p , ' = ' ) ;
if ( ! value ) {
2010-11-18 23:52:26 +03:00
log_error ( " Line is not an assignment in file '%s': %s " , path , value ) ;
2011-03-04 00:17:09 +03:00
if ( r = = 0 )
r = - EINVAL ;
2010-11-18 23:52:26 +03:00
continue ;
}
* value = 0 ;
value + + ;
2015-03-11 12:37:45 +03:00
p = sysctl_normalize ( strstrip ( p ) ) ;
2013-02-12 02:48:36 +04:00
value = strstrip ( value ) ;
2015-02-07 16:12:41 +03:00
if ( ! strv_isempty ( arg_prefixes ) ) {
char * * i , * t ;
STRV_FOREACH ( i , arg_prefixes ) {
t = path_startswith ( * i , " /proc/sys/ " ) ;
if ( t = = NULL )
t = * i ;
if ( path_startswith ( p , t ) )
goto found ;
}
/* not found */
continue ;
}
found :
2013-08-15 20:35:03 +04:00
existing = hashmap_get2 ( sysctl_options , p , & v ) ;
2013-02-12 02:48:36 +04:00
if ( existing ) {
2013-08-15 20:35:03 +04:00
if ( streq ( value , existing ) )
continue ;
2013-02-12 02:48:36 +04:00
2015-02-27 03:00:11 +03:00
log_debug ( " Overwriting earlier assignment of %s in file '%s'. " , p , path ) ;
2013-08-15 20:35:03 +04:00
free ( hashmap_remove ( sysctl_options , p ) ) ;
free ( v ) ;
2012-08-03 18:20:31 +04:00
}
2013-02-12 02:48:36 +04:00
property = strdup ( p ) ;
if ( ! property )
return log_oom ( ) ;
new_value = strdup ( value ) ;
2012-08-03 18:20:31 +04:00
if ( ! new_value ) {
free ( property ) ;
2013-02-12 02:48:36 +04:00
return log_oom ( ) ;
2012-08-03 18:20:31 +04:00
}
2013-02-12 02:48:36 +04:00
k = hashmap_put ( sysctl_options , property , new_value ) ;
if ( k < 0 ) {
2014-11-28 15:19:16 +03:00
log_error_errno ( k , " Failed to add sysctl variable %s to hashmap: %m " , property ) ;
2012-08-03 18:20:31 +04:00
free ( property ) ;
free ( new_value ) ;
2013-02-12 02:48:36 +04:00
return k ;
2012-08-03 18:20:31 +04:00
}
2010-11-18 23:52:26 +03:00
}
2011-03-04 00:17:09 +03:00
return r ;
2010-11-18 23:52:26 +03:00
}
2014-08-02 19:12:21 +04:00
static void help ( void ) {
2011-07-12 05:36:17 +04:00
printf ( " %s [OPTIONS...] [CONFIGURATION FILE...] \n \n "
" Applies kernel sysctl settings. \n \n "
" -h --help Show this help \n "
2013-11-06 21:28:39 +04:00
" --version Show package version \n "
2014-09-17 11:06:49 +04:00
" --prefix=PATH Only apply rules with the specified prefix \n "
2014-08-02 19:12:21 +04:00
, program_invocation_short_name ) ;
2011-07-12 05:36:17 +04:00
}
static int parse_argv ( int argc , char * argv [ ] ) {
enum {
2013-11-06 21:28:39 +04:00
ARG_VERSION = 0x100 ,
2011-07-12 05:36:17 +04:00
ARG_PREFIX
} ;
static const struct option options [ ] = {
{ " help " , no_argument , NULL , ' h ' } ,
2013-11-06 21:28:39 +04:00
{ " version " , no_argument , NULL , ARG_VERSION } ,
2011-07-12 05:36:17 +04:00
{ " prefix " , required_argument , NULL , ARG_PREFIX } ,
2013-11-06 21:28:39 +04:00
{ }
2011-07-12 05:36:17 +04:00
} ;
int c ;
assert ( argc > = 0 ) ;
assert ( argv ) ;
2014-08-02 19:12:21 +04:00
while ( ( c = getopt_long ( argc , argv , " h " , options , NULL ) ) > = 0 )
2011-07-12 05:36:17 +04:00
switch ( c ) {
case ' h ' :
2014-08-02 19:12:21 +04:00
help ( ) ;
return 0 ;
2013-11-06 21:28:39 +04:00
case ARG_VERSION :
puts ( PACKAGE_STRING ) ;
puts ( SYSTEMD_FEATURES ) ;
2011-07-12 05:36:17 +04:00
return 0 ;
case ARG_PREFIX : {
char * p ;
2014-09-17 11:06:49 +04:00
/* We used to require people to specify absolute paths
* in / proc / sys in the past . This is kinda useless , but
* we need to keep compatibility . We now support any
* sysctl name available . */
2015-03-11 12:37:45 +03:00
sysctl_normalize ( optarg ) ;
2015-04-24 20:55:16 +03:00
2014-09-17 11:06:49 +04:00
if ( startswith ( optarg , " /proc/sys " ) )
p = strdup ( optarg ) ;
else
p = strappend ( " /proc/sys/ " , optarg ) ;
if ( ! p )
return log_oom ( ) ;
2015-04-24 20:55:16 +03:00
2014-09-17 11:06:49 +04:00
if ( strv_consume ( & arg_prefixes , p ) < 0 )
2012-07-26 01:55:59 +04:00
return log_oom ( ) ;
2011-07-15 04:01:31 +04:00
2011-07-12 05:36:17 +04:00
break ;
}
case ' ? ' :
return - EINVAL ;
default :
2013-11-06 21:28:39 +04:00
assert_not_reached ( " Unhandled option " ) ;
2011-07-12 05:36:17 +04:00
}
return 1 ;
}
2010-11-18 23:52:26 +03:00
int main ( int argc , char * argv [ ] ) {
2012-09-21 19:01:39 +04:00
int r = 0 , k ;
2013-02-12 02:48:36 +04:00
Hashmap * sysctl_options ;
2010-11-18 23:52:26 +03:00
2011-07-12 05:36:17 +04:00
r = parse_argv ( argc , argv ) ;
if ( r < = 0 )
return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS ;
2010-11-18 23:52:26 +03:00
log_set_target ( LOG_TARGET_AUTO ) ;
log_parse_environment ( ) ;
log_open ( ) ;
2011-08-01 22:52:18 +04:00
umask ( 0022 ) ;
2014-08-13 03:00:18 +04:00
sysctl_options = hashmap_new ( & string_hash_ops ) ;
2012-08-03 18:20:31 +04:00
if ( ! sysctl_options ) {
r = log_oom ( ) ;
goto finish ;
}
2012-09-21 19:01:39 +04:00
r = 0 ;
2012-03-20 18:31:09 +04:00
if ( argc > optind ) {
int i ;
for ( i = optind ; i < argc ; i + + ) {
2013-02-12 02:48:36 +04:00
k = parse_file ( sysctl_options , argv [ i ] , false ) ;
if ( k < 0 & & r = = 0 )
2012-03-20 18:31:09 +04:00
r = k ;
}
} else {
2013-02-12 02:48:36 +04:00
_cleanup_strv_free_ char * * files = NULL ;
char * * f ;
2011-03-04 00:17:09 +03:00
2013-02-12 02:48:36 +04:00
r = conf_files_list_nulstr ( & files , " .conf " , NULL , conf_file_dirs ) ;
2011-04-29 01:51:24 +04:00
if ( r < 0 ) {
2014-11-28 15:19:16 +03:00
log_error_errno ( r , " Failed to enumerate sysctl.d files: %m " ) ;
2011-04-29 01:51:24 +04:00
goto finish ;
}
2011-04-25 22:41:47 +04:00
2013-02-12 02:48:36 +04:00
STRV_FOREACH ( f , files ) {
k = parse_file ( sysctl_options , * f , true ) ;
if ( k < 0 & & r = = 0 )
2011-04-25 22:41:47 +04:00
r = k ;
}
2010-11-18 23:52:26 +03:00
}
2012-08-03 18:20:31 +04:00
2013-02-12 02:48:36 +04:00
k = apply_all ( sysctl_options ) ;
if ( k < 0 & & r = = 0 )
2012-09-21 19:01:39 +04:00
r = k ;
2011-04-29 01:51:24 +04:00
finish :
2013-02-12 02:48:36 +04:00
hashmap_free_free_free ( sysctl_options ) ;
2011-07-15 04:01:31 +04:00
strv_free ( arg_prefixes ) ;
2011-03-04 00:17:09 +03:00
return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS ;
2010-11-18 23:52:26 +03:00
}