1 # -*- coding: utf-8 -*-
2 # Copyright (c) 2009, 2010, 2011, 2013, 2014, 2015 Jack Kaliko <kaliko@azylum.org>
4 # This file is part of sima
6 # sima is free software: you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation, either version 3 of the License, or
9 # (at your option) any later version.
11 # sima is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
16 # You should have received a copy of the GNU General Public License
17 # along with sima. If not, see <http://www.gnu.org/licenses/>.
22 Deal with configuration and data files.
23 Parse configuration file and set defaults for missing options.
31 from configparser import Error
32 from os import (access, makedirs, environ, stat, chmod, W_OK, R_OK)
33 from os.path import (join, isdir, isfile, dirname, exists)
34 from stat import (S_IMODE, ST_MODE, S_IRWXO, S_IRWXG)
40 CONF_FILE = 'mpd_sima.cfg'
49 'internal': "Crop, Lastfm, RandomFallBack",
52 'history_duration': 8,
55 'musicbrainzid': "true",
69 'queue_mode': "track", #TODO control values
71 'single_album': "false",
77 'queue_mode': "track", #TODO control values
79 'single_album': "false",
86 'flavour': "sensible", # in pure, sensible
93 class ConfMan(object): # CONFIG MANAGER CLASS
95 Configuration manager.
96 Default configuration is stored in DEFAULT_CONF dictionnary.
97 First init_config() run to get config from file.
98 Then control_conf() is run and retrieve configuration from defaults if not
100 These settings are then updated with command line options with
101 supersedes_config_with_cmd_line_options().
103 Order of priority for the origin of an option is then (lowest to highest):
105 * Env. Var for MPD host, port and password
106 * configuration file (overrides previous)
107 * command line options (overrides previous)
110 def __init__(self, options=None):
111 self.log = logging.getLogger('sima')
112 # options settings priority:
113 # defauts < env. var. < conf. file < command line
114 self.conf_file = options.get('conf_file')
115 self.config = configparser.ConfigParser(inline_comment_prefixes='#')
116 self.config.read_dict(DEFAULT_CONF)
117 # update DEFAULT_CONF with env. var.
119 self.startopt = options
123 self.supersedes_config_with_cmd_line_options()
124 # Controls files access
127 self.config['sima']['db_file'] = join(self.config['sima']['var_dir'], 'sima.db')
129 def control_facc(self):
130 """Controls file access.
131 This is relevant only for file provided through the configuration file
132 since files provided on the command line are already checked with
136 for op, ftochk in [('log', self.config['log']['logfile']),
137 ('pidfile', self.config['daemon']['pidfile']),]:
141 self.log.critical('Need a file not a directory: "{}"'.format(ftochk))
143 if not exists(ftochk):
144 # Is parent directory writable then
145 filedir = dirname(ftochk)
146 if not access(filedir, W_OK):
147 self.log.critical('no write access to "{0}" ({1})'.format(filedir, op))
150 if not access(ftochk, W_OK):
151 self.log.critical('no write access to "{0}" ({1})'.format(ftochk, op))
154 if exists(self.conf_file):
155 self.log.warning('Try to check the configuration file: {}'.format(self.conf_file))
158 def control_mod(self):
160 Controls conf file permissions.
162 mode = S_IMODE(stat(self.conf_file)[ST_MODE])
163 self.log.debug('file permission is: %o' % mode)
164 if mode & S_IRWXO or mode & S_IRWXG:
165 self.log.warning('File is readable by "other" and/or' +
166 ' "group" (actual permission %o octal).' %
168 self.log.warning('Consider setting permissions' +
171 def supersedes_config_with_cmd_line_options(self):
172 """Updates defaults settings with command line options"""
173 for sec in self.config.sections():
174 for opt in self.config.options(sec):
175 if opt in list(self.startopt.keys()):
176 self.config.set(sec, opt, str(self.startopt.get(opt)))
179 """Use MPD en.var. to set defaults"""
180 mpd_host, mpd_port, passwd = utils.get_mpd_environ()
182 self.log.info('Env. variable MPD_HOST set to "%s"' % mpd_host)
183 self.config['MPD'].update(host=mpd_host)
185 self.log.info('Env. variable MPD_HOST contains password.')
186 self.config['MPD'].update(password=passwd)
188 self.log.info('Env. variable MPD_PORT set to "%s".' % mpd_port)
189 self.config['MPD'].update(port=mpd_port)
191 def init_config(self):
193 Use XDG directory standard if exists
194 else use "HOME/(.config|.local/share)/sima/"
195 http://standards.freedesktop.org/basedir-spec/basedir-spec-0.6.html
198 homedir = environ.get('HOME')
200 if environ.get('XDG_DATA_HOME'):
201 data_dir = join(environ.get('XDG_DATA_HOME'), DIRNAME)
202 elif homedir and isdir(homedir) and homedir not in ['/']:
203 data_dir = join(homedir, '.local', 'share', DIRNAME)
205 self.log.error('Can\'t find a suitable location for data folder (XDG_DATA_HOME)')
206 self.log.error('Please use "--var_dir" to set a proper location')
209 if not isdir(data_dir):
211 chmod(data_dir, 0o700)
213 if self.startopt.get('conf_file'):
214 # No need to handle conf file location
216 elif environ.get('XDG_CONFIG_HOME'):
217 conf_dir = join(environ.get('XDG_CONFIG_HOME'), DIRNAME)
218 elif homedir and isdir(homedir) and homedir not in ['/']:
219 conf_dir = join(homedir, '.config', DIRNAME)
220 # Create conf_dir if necessary
221 if not isdir(conf_dir):
223 chmod(conf_dir, 0o700)
224 self.conf_file = join(conf_dir, CONF_FILE)
226 self.log.critical('Can\'t find a suitable location for config folder (XDG_CONFIG_HOME)')
227 self.log.critical('Please use "--config" to locate the conf file')
231 self.config['sima']['var_dir'] = join(data_dir)
233 # If no conf file present, uses defaults
234 if not isfile(self.conf_file):
237 self.log.info('Loading configuration from: %s' % self.conf_file)
241 self.config.read(self.conf_file)
247 # vim: ai ts=4 sw=4 sts=4 expandtab