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",
72 'queue_mode': "track", #TODO control values
74 'single_album': "false",
82 'flavour': "sensible", # in pure, sensible
90 class ConfMan(object): # CONFIG MANAGER CLASS
92 Configuration manager.
93 Default configuration is stored in DEFAULT_CONF dictionnary.
94 First init_config() run to get config from file.
95 Then control_conf() is run and retrieve configuration from defaults if not
97 These settings are then updated with command line options with
98 supersedes_config_with_cmd_line_options().
100 Order of priority for the origin of an option is then (lowest to highest):
102 * Env. Var for MPD host, port and password
103 * configuration file (overrides previous)
104 * command line options (overrides previous)
107 def __init__(self, options=None):
108 self.log = logging.getLogger('sima')
109 # options settings priority:
110 # defauts < env. var. < conf. file < command line
111 self.conf_file = options.get('conf_file')
112 self.config = configparser.ConfigParser(inline_comment_prefixes='#')
113 self.config.read_dict(DEFAULT_CONF)
114 # update DEFAULT_CONF with env. var.
116 self.startopt = options
120 self.supersedes_config_with_cmd_line_options()
121 # Controls files access
124 self.config['sima']['db_file'] = join(self.config['sima']['var_dir'], 'sima.db')
127 data_dir = self.config['sima']['var_dir']
128 if not isdir(data_dir):
129 self.log.trace('Creating "{}"'.format(data_dir))
131 chmod(data_dir, 0o700)
133 def control_facc(self):
134 """Controls file access.
135 This is relevant only for file provided through the configuration file
136 since files provided on the command line are already checked with
140 for op, ftochk in [('logfile', self.config.get('log', 'logfile')),
141 ('pidfile', self.config.get('daemon', 'pidfile')),]:
145 self.log.critical('Need a file not a directory: "%s"', ftochk)
147 if not exists(ftochk):
148 # Is parent directory writable then
149 filedir = dirname(ftochk)
150 if not access(filedir, W_OK):
151 self.log.critical('no write access to "%s" (%s)', filedir, op)
154 if not access(ftochk, W_OK):
155 self.log.critical('no write access to "%s" (%s)', ftochk, op)
158 if exists(self.conf_file):
159 self.log.warning('Try to check the configuration file: %s', self.conf_file)
162 def control_mod(self):
164 Controls conf file permissions.
166 mode = S_IMODE(stat(self.conf_file)[ST_MODE])
167 self.log.debug('file permission is: %o', mode)
168 if mode & S_IRWXO or mode & S_IRWXG:
169 self.log.warning('File is readable by "other" and/or' +
170 ' "group" (actual permission %o octal).' %
172 self.log.warning('Consider setting permissions' +
175 def supersedes_config_with_cmd_line_options(self):
176 """Updates defaults settings with command line options"""
177 for sec in self.config.sections():
178 for opt in self.config.options(sec):
179 if opt in list(self.startopt.keys()):
180 self.config.set(sec, opt, str(self.startopt.get(opt)))
183 """Use MPD en.var. to set defaults"""
184 mpd_host, mpd_port, passwd = utils.get_mpd_environ()
186 self.log.info('Env. variable MPD_HOST set to "%s"', mpd_host)
187 self.config['MPD'].update(host=mpd_host)
189 self.log.info('Env. variable MPD_HOST contains password.')
190 self.config['MPD'].update(password=passwd)
192 self.log.info('Env. variable MPD_PORT set to "%s".', mpd_port)
193 self.config['MPD'].update(port=mpd_port)
195 def init_config(self):
197 Use XDG directory standard if exists
198 else use "HOME/(.config|.local/share)/sima/"
199 http://standards.freedesktop.org/basedir-spec/basedir-spec-0.6.html
202 homedir = environ.get('HOME')
204 if environ.get('XDG_DATA_HOME'):
205 data_dir = join(environ.get('XDG_DATA_HOME'), DIRNAME)
206 elif homedir and isdir(homedir) and homedir not in ['/']:
207 data_dir = join(homedir, '.local', 'share', DIRNAME)
209 self.log.critical('Can\'t find a suitable location for data folder (XDG_DATA_HOME)')
210 self.log.critical('Please use "--var-dir" to set a proper location')
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 self.conf_file = join(conf_dir, CONF_FILE)
222 self.log.critical('Can\'t find a suitable location for config folder (XDG_CONFIG_HOME)')
223 self.log.critical('Please use "--config" to locate the conf file')
227 self.config['sima']['var_dir'] = join(data_dir)
229 # If no conf file present, uses defaults
230 if not isfile(self.conf_file):
233 self.log.info('Loading configuration from: %s', self.conf_file)
237 self.config.read(self.conf_file)
243 # vim: ai ts=4 sw=4 sts=4 expandtab