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",
78 'queue_mode': "track", #TODO control values
80 'single_album': "false",
88 'flavour': "sensible", # in pure, sensible
96 class ConfMan(object): # CONFIG MANAGER CLASS
98 Configuration manager.
99 Default configuration is stored in DEFAULT_CONF dictionnary.
100 First init_config() run to get config from file.
101 Then control_conf() is run and retrieve configuration from defaults if not
103 These settings are then updated with command line options with
104 supersedes_config_with_cmd_line_options().
106 Order of priority for the origin of an option is then (lowest to highest):
108 * Env. Var for MPD host, port and password
109 * configuration file (overrides previous)
110 * command line options (overrides previous)
113 def __init__(self, options=None):
114 self.log = logging.getLogger('sima')
115 # options settings priority:
116 # defauts < env. var. < conf. file < command line
117 self.conf_file = options.get('conf_file')
118 self.config = configparser.ConfigParser(inline_comment_prefixes='#')
119 self.config.read_dict(DEFAULT_CONF)
120 # update DEFAULT_CONF with env. var.
122 self.startopt = options
126 self.supersedes_config_with_cmd_line_options()
127 # Controls files access
130 self.config['sima']['db_file'] = join(self.config['sima']['var_dir'], 'sima.db')
132 def control_facc(self):
133 """Controls file access.
134 This is relevant only for file provided through the configuration file
135 since files provided on the command line are already checked with
139 for op, ftochk in [('log', self.config['log']['logfile']),
140 ('pidfile', self.config['daemon']['pidfile']),]:
144 self.log.critical('Need a file not a directory: "{}"'.format(ftochk))
146 if not exists(ftochk):
147 # Is parent directory writable then
148 filedir = dirname(ftochk)
149 if not access(filedir, W_OK):
150 self.log.critical('no write access to "{0}" ({1})'.format(filedir, op))
153 if not access(ftochk, W_OK):
154 self.log.critical('no write access to "{0}" ({1})'.format(ftochk, op))
157 if exists(self.conf_file):
158 self.log.warning('Try to check the configuration file: {}'.format(self.conf_file))
161 def control_mod(self):
163 Controls conf file permissions.
165 mode = S_IMODE(stat(self.conf_file)[ST_MODE])
166 self.log.debug('file permission is: %o' % mode)
167 if mode & S_IRWXO or mode & S_IRWXG:
168 self.log.warning('File is readable by "other" and/or' +
169 ' "group" (actual permission %o octal).' %
171 self.log.warning('Consider setting permissions' +
174 def supersedes_config_with_cmd_line_options(self):
175 """Updates defaults settings with command line options"""
176 for sec in self.config.sections():
177 for opt in self.config.options(sec):
178 if opt in list(self.startopt.keys()):
179 self.config.set(sec, opt, str(self.startopt.get(opt)))
182 """Use MPD en.var. to set defaults"""
183 mpd_host, mpd_port, passwd = utils.get_mpd_environ()
185 self.log.info('Env. variable MPD_HOST set to "%s"' % mpd_host)
186 self.config['MPD'].update(host=mpd_host)
188 self.log.info('Env. variable MPD_HOST contains password.')
189 self.config['MPD'].update(password=passwd)
191 self.log.info('Env. variable MPD_PORT set to "%s".' % mpd_port)
192 self.config['MPD'].update(port=mpd_port)
194 def init_config(self):
196 Use XDG directory standard if exists
197 else use "HOME/(.config|.local/share)/sima/"
198 http://standards.freedesktop.org/basedir-spec/basedir-spec-0.6.html
201 homedir = environ.get('HOME')
203 if environ.get('XDG_DATA_HOME'):
204 data_dir = join(environ.get('XDG_DATA_HOME'), DIRNAME)
205 elif homedir and isdir(homedir) and homedir not in ['/']:
206 data_dir = join(homedir, '.local', 'share', DIRNAME)
208 self.log.error('Can\'t find a suitable location for data folder (XDG_DATA_HOME)')
209 self.log.error('Please use "--var_dir" to set a proper location')
212 if not isdir(data_dir):
214 chmod(data_dir, 0o700)
216 if self.startopt.get('conf_file'):
217 # No need to handle conf file location
219 elif environ.get('XDG_CONFIG_HOME'):
220 conf_dir = join(environ.get('XDG_CONFIG_HOME'), DIRNAME)
221 elif homedir and isdir(homedir) and homedir not in ['/']:
222 conf_dir = join(homedir, '.config', DIRNAME)
223 # Create conf_dir if necessary
224 if not isdir(conf_dir):
226 chmod(conf_dir, 0o700)
227 self.conf_file = join(conf_dir, CONF_FILE)
229 self.log.critical('Can\'t find a suitable location for config folder (XDG_CONFIG_HOME)')
230 self.log.critical('Please use "--config" to locate the conf file')
234 self.config['sima']['var_dir'] = join(data_dir)
236 # If no conf file present, uses defaults
237 if not isfile(self.conf_file):
240 self.log.info('Loading configuration from: %s' % self.conf_file)
244 self.config.read(self.conf_file)
250 # vim: ai ts=4 sw=4 sts=4 expandtab