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",
56 'repeat_disable_queue': "true",
57 'single_disable_queue': "true",
58 'mopidy_compat': "false",
73 'queue_mode': "track", #TODO control values
75 'single_album': "false",
83 'flavour': "sensible", # in pure, sensible
91 class ConfMan(object): # CONFIG MANAGER CLASS
93 Configuration manager.
94 Default configuration is stored in DEFAULT_CONF dictionnary.
95 First init_config() run to get config from file.
96 Then control_conf() is run and retrieve configuration from defaults if not
98 These settings are then updated with command line options with
99 supersedes_config_with_cmd_line_options().
101 Order of priority for the origin of an option is then (lowest to highest):
103 * Env. Var for MPD host, port and password
104 * configuration file (overrides previous)
105 * command line options (overrides previous)
108 def __init__(self, options=None):
109 self.log = logging.getLogger('sima')
110 # options settings priority:
111 # defauts < env. var. < conf. file < command line
112 self.conf_file = options.get('conf_file')
113 self.config = configparser.ConfigParser(inline_comment_prefixes='#')
114 self.config.read_dict(DEFAULT_CONF)
115 # update DEFAULT_CONF with env. var.
117 self.startopt = options
121 self.supersedes_config_with_cmd_line_options()
122 # Controls files access
125 self.config['sima']['db_file'] = join(self.config['sima']['var_dir'], 'sima.db')
128 data_dir = self.config['sima']['var_dir']
129 if not isdir(data_dir):
130 self.log.trace('Creating "{}"'.format(data_dir))
132 chmod(data_dir, 0o700)
134 def control_facc(self):
135 """Controls file access.
136 This is relevant only for file provided through the configuration file
137 since files provided on the command line are already checked with
141 for op, ftochk in [('logfile', self.config.get('log', 'logfile')),
142 ('pidfile', self.config.get('daemon', 'pidfile')),]:
146 self.log.critical('Need a file not a directory: "%s"', ftochk)
148 if not exists(ftochk):
149 # Is parent directory writable then
150 filedir = dirname(ftochk)
151 if not access(filedir, W_OK):
152 self.log.critical('no write access to "%s" (%s)', filedir, op)
155 if not access(ftochk, W_OK):
156 self.log.critical('no write access to "%s" (%s)', ftochk, op)
159 if exists(self.conf_file):
160 self.log.warning('Try to check the configuration file: %s', self.conf_file)
163 def control_mod(self):
165 Controls conf file permissions.
167 mode = S_IMODE(stat(self.conf_file)[ST_MODE])
168 self.log.debug('file permission is: %o', mode)
169 if mode & S_IRWXO or mode & S_IRWXG:
170 self.log.warning('File is readable by "other" and/or' +
171 ' "group" (actual permission %o octal).' %
173 self.log.warning('Consider setting permissions' +
176 def supersedes_config_with_cmd_line_options(self):
177 """Updates defaults settings with command line options"""
178 for sec in self.config.sections():
179 for opt in self.config.options(sec):
180 if opt in list(self.startopt.keys()):
181 self.config.set(sec, opt, str(self.startopt.get(opt)))
182 # honor MPD_HOST format as in mpc(1) for command line option --host
183 if self.startopt.get('host'):
184 if '@' in self.startopt.get('host'):
185 print(self.startopt.get('host').split('@'))
186 passwd, host = self.startopt.get('host').split('@')
187 self.config.set('MPD', 'password', passwd)
188 self.config.set('MPD', 'host', host)
191 """Use MPD en.var. to set defaults"""
192 mpd_host, mpd_port, passwd = utils.get_mpd_environ()
194 self.log.info('Env. variable MPD_HOST set to "%s"', mpd_host)
195 self.config['MPD'].update(host=mpd_host)
197 self.log.info('Env. variable MPD_HOST contains password.')
198 self.config['MPD'].update(password=passwd)
200 self.log.info('Env. variable MPD_PORT set to "%s".', mpd_port)
201 self.config['MPD'].update(port=mpd_port)
203 def init_config(self):
205 Use XDG directory standard if exists
206 else use "HOME/(.config|.local/share)/sima/"
207 http://standards.freedesktop.org/basedir-spec/basedir-spec-0.6.html
210 homedir = environ.get('HOME')
212 if environ.get('XDG_DATA_HOME'):
213 data_dir = join(environ.get('XDG_DATA_HOME'), DIRNAME)
214 elif homedir and isdir(homedir) and homedir not in ['/']:
215 data_dir = join(homedir, '.local', 'share', DIRNAME)
217 self.log.critical('Can\'t find a suitable location for data folder (XDG_DATA_HOME)')
218 self.log.critical('Please use "--var-dir" to set a proper location')
221 if self.startopt.get('conf_file'):
222 # No need to handle conf file location
224 elif environ.get('XDG_CONFIG_HOME'):
225 conf_dir = join(environ.get('XDG_CONFIG_HOME'), DIRNAME)
226 elif homedir and isdir(homedir) and homedir not in ['/']:
227 conf_dir = join(homedir, '.config', DIRNAME)
228 self.conf_file = join(conf_dir, CONF_FILE)
230 self.log.critical('Can\'t find a suitable location for config folder (XDG_CONFIG_HOME)')
231 self.log.critical('Please use "--config" to locate the conf file')
235 self.config['sima']['var_dir'] = join(data_dir)
237 # If no conf file present, uses defaults
238 if not isfile(self.conf_file):
241 self.log.info('Loading configuration from: %s', self.conf_file)
245 self.config.read(self.conf_file)
251 # vim: ai ts=4 sw=4 sts=4 expandtab