1 # -*- coding: utf-8 -*-
2 # Copyright (c) 2009, 2010, 2011, 2013, 2014 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 = '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 """TODO: redundant with startopt cli args controls
133 for op, ftochk in [('log', self.config['log']['logfile']),
134 ('pidfile', self.config['daemon']['pidfile']),]:
137 if not exists(ftochk):
138 # Is parent directory writable then
139 filedir = dirname(ftochk)
140 if not access(filedir, W_OK):
141 self.log.critical('no write access to "{0}" ({1})'.format(filedir, op))
144 if not access(ftochk, W_OK):
145 self.log.critical('no write access to "{0}" ({1}))'.format(ftochk, op))
150 def control_mod(self):
152 Controls conf file permissions.
154 mode = S_IMODE(stat(self.conf_file)[ST_MODE])
155 self.log.debug('file permission is: %o' % mode)
156 if mode & S_IRWXO or mode & S_IRWXG:
157 self.log.warning('File is readable by "other" and/or' +
158 ' "group" (actual permission %o octal).' %
160 self.log.warning('Consider setting permissions' +
163 def supersedes_config_with_cmd_line_options(self):
164 """Updates defaults settings with command line options"""
165 for sec in self.config.sections():
166 for opt in self.config.options(sec):
167 if opt in list(self.startopt.keys()):
168 self.config.set(sec, opt, str(self.startopt.get(opt)))
171 """Use MPD en.var. to set defaults"""
172 mpd_host, mpd_port, passwd = utils.get_mpd_environ()
174 self.log.info('Env. variable MPD_HOST set to "%s"' % mpd_host)
175 self.config['MPD'].update(host=mpd_host)
177 self.log.info('Env. variable MPD_HOST contains password.')
178 self.config['MPD'].update(password=passwd)
180 self.log.info('Env. variable MPD_PORT set to "%s".' % mpd_port)
181 self.config['MPD'].update(port=mpd_port)
183 def init_config(self):
185 Use XDG directory standard if exists
186 else use "HOME/(.config|.local/share)/sima/"
187 http://standards.freedesktop.org/basedir-spec/basedir-spec-0.6.html
190 homedir = environ.get('HOME')
192 if environ.get('XDG_DATA_HOME'):
193 data_dir = join(environ.get('XDG_DATA_HOME'), DIRNAME)
194 elif homedir and isdir(homedir) and homedir not in ['/']:
195 data_dir = join(homedir, '.local', 'share', DIRNAME)
197 self.log.error('Can\'t find a suitable location for data folder (XDG_DATA_HOME)')
198 self.log.error('Please use "--var_dir" to set a proper location')
201 if not isdir(data_dir):
203 chmod(data_dir, 0o700)
205 if self.startopt.get('conf_file'):
206 # No need to handle conf file location
208 elif environ.get('XDG_CONFIG_HOME'):
209 conf_dir = join(environ.get('XDG_CONFIG_HOME'), DIRNAME)
210 elif homedir and isdir(homedir) and homedir not in ['/']:
211 conf_dir = join(homedir, '.config', DIRNAME)
212 # Create conf_dir if necessary
213 if not isdir(conf_dir):
215 chmod(conf_dir, 0o700)
216 self.conf_file = join(conf_dir, CONF_FILE)
218 self.log.critical('Can\'t find a suitable location for config folder (XDG_CONFIG_HOME)')
219 self.log.critical('Please use "--config" to locate the conf file')
223 self.config['sima']['var_dir'] = join(data_dir)
225 # If no conf file present, uses defaults
226 if not isfile(self.conf_file):
229 self.log.info('Loading configuration from: %s' % self.conf_file)
233 self.config.read(self.conf_file)
239 # vim: ai ts=4 sw=4 sts=4 expandtab