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/>.
19 # pylint: disable=bad-continuation
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)
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, Random",
52 'history_duration': 8,
55 'musicbrainzid': "true",
70 'queue_mode': "track", #TODO control values
72 'single_album': "false",
79 'queue_mode': "track", #TODO control values
81 'single_album': "false",
89 'flavour': "sensible", # in pure, sensible
97 class ConfMan(object): # CONFIG MANAGER CLASS
99 Configuration manager.
100 Default configuration is stored in DEFAULT_CONF dictionnary.
101 First init_config() run to get config from file.
102 Then control_conf() is run and retrieve configuration from defaults if not
104 These settings are then updated with command line options with
105 supersedes_config_with_cmd_line_options().
107 Order of priority for the origin of an option is then (lowest to highest):
109 * Env. Var for MPD host, port and password
110 * configuration file (overrides previous)
111 * command line options (overrides previous)
114 def __init__(self, options=None):
115 self.log = logging.getLogger('sima')
116 # options settings priority:
117 # defauts < env. var. < conf. file < command line
118 self.conf_file = options.get('conf_file')
119 self.config = configparser.ConfigParser(inline_comment_prefixes='#')
120 self.config.read_dict(DEFAULT_CONF)
121 # update DEFAULT_CONF with env. var.
123 self.startopt = options
127 self.supersedes_config_with_cmd_line_options()
128 # Controls files access
131 self.config['sima']['db_file'] = join(self.config['sima']['var_dir'], 'sima.db')
134 data_dir = self.config['sima']['var_dir']
135 if not isdir(data_dir):
136 self.log.trace('Creating "{}"'.format(data_dir))
138 chmod(data_dir, 0o700)
140 def control_facc(self):
141 """Controls file access.
142 This is relevant only for file provided through the configuration file
143 since files provided on the command line are already checked with
147 for op, ftochk in [('logfile', self.config.get('log', 'logfile')),
148 ('pidfile', self.config.get('daemon', 'pidfile')),]:
152 self.log.critical('Need a file not a directory: "%s"', ftochk)
154 if not exists(ftochk):
155 # Is parent directory writable then
156 filedir = dirname(ftochk)
157 if not access(filedir, W_OK):
158 self.log.critical('no write access to "%s" (%s)', filedir, op)
161 if not access(ftochk, W_OK):
162 self.log.critical('no write access to "%s" (%s)', ftochk, op)
165 if exists(self.conf_file):
166 self.log.warning('Try to check the configuration file: %s', self.conf_file)
169 def control_mod(self):
171 Controls conf file permissions.
173 mode = S_IMODE(stat(self.conf_file)[ST_MODE])
174 self.log.debug('file permission is: %o', mode)
175 if mode & S_IRWXO or mode & S_IRWXG:
176 self.log.warning('File is readable by "other" and/or' +
177 ' "group" (actual permission %o octal).' %
179 self.log.warning('Consider setting permissions' +
182 def supersedes_config_with_cmd_line_options(self):
183 """Updates defaults settings with command line options"""
184 for sec in self.config.sections():
185 for opt in self.config.options(sec):
186 if opt in list(self.startopt.keys()):
187 self.config.set(sec, opt, str(self.startopt.get(opt)))
190 """Use MPD en.var. to set defaults"""
191 mpd_host, mpd_port, passwd = utils.get_mpd_environ()
193 self.log.info('Env. variable MPD_HOST set to "%s"', mpd_host)
194 self.config['MPD'].update(host=mpd_host)
196 self.log.info('Env. variable MPD_HOST contains password.')
197 self.config['MPD'].update(password=passwd)
199 self.log.info('Env. variable MPD_PORT set to "%s".', mpd_port)
200 self.config['MPD'].update(port=mpd_port)
202 def init_config(self):
204 Use XDG directory standard if exists
205 else use "HOME/(.config|.local/share)/sima/"
206 http://standards.freedesktop.org/basedir-spec/basedir-spec-0.6.html
209 homedir = environ.get('HOME')
211 if environ.get('XDG_DATA_HOME'):
212 data_dir = join(environ.get('XDG_DATA_HOME'), DIRNAME)
213 elif homedir and isdir(homedir) and homedir not in ['/']:
214 data_dir = join(homedir, '.local', 'share', DIRNAME)
216 self.log.critical('Can\'t find a suitable location for data folder (XDG_DATA_HOME)')
217 self.log.critical('Please use "--var-dir" to set a proper location')
220 if self.startopt.get('conf_file'):
221 # No need to handle conf file location
223 elif environ.get('XDG_CONFIG_HOME'):
224 conf_dir = join(environ.get('XDG_CONFIG_HOME'), DIRNAME)
225 elif homedir and isdir(homedir) and homedir not in ['/']:
226 conf_dir = join(homedir, '.config', DIRNAME)
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