1
2 '''
3 # DESCRIPTION:
4 # Entropy Object Oriented Interface
5
6 Copyright (C) 2007-2009 Fabio Erculiani
7
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
12
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 '''
22
23 from __future__ import with_statement
24 import os
25 from entropy.exceptions import IncorrectParameter, SystemDatabaseError
26 from entropy.const import etpConst, etpUi, etpSys, const_setup_perms, \
27 const_secure_config_file, const_set_nice_level, \
28 const_extract_cli_repo_params, etpCache
29 from entropy.i18n import _
30
32
33 """
34 If your class wants to become a sexy Singleton,
35 subclass this and replace __init__ with init_singleton
36 """
37
38 __is_destroyed = False
39 __is_singleton = True
41 instance = cls.__dict__.get("__it__")
42 if instance != None:
43 if not instance.is_destroyed():
44 return instance
45 cls.__it__ = instance = object.__new__(cls)
46 instance.init_singleton(*args, **kwds)
47 return instance
48
50 """
51 In our world, Singleton instances may be destroyed,
52 this is done by setting a private bool var __is_destroyed
53
54 @rtype: bool
55 @return: instance status, if destroyed or not
56 """
57 return self.__is_destroyed
58
60 """
61 Return if the instance is a singleton
62
63 @rtype: bool
64 @return: class singleton property, if singleton or not
65 """
66 return self.__is_singleton
67
69
70 """
71 This is a plugin base class for all SystemSettings plugins.
72 It allows to add extra parsers (though metadata) to
73 SystemSettings.
74 Just inherit from this class and call add_parser to add
75 your custom parsers.
76 SystemSettings will call the parse method, as explained below.
77 """
78
79 - def __init__(self, plugin_id, helper_interface):
80 """
81 SystemSettingsPlugin constructor.
82
83 @param plugin_id -- plugin identifier, must be unique
84 @type plugin_id basestring
85 @param helper_interface -- any Python instance that could
86 be of help to your parsers
87 @type handler_instance instance
88 """
89 self.__parsers = []
90 self.__plugin_id = plugin_id
91 self._helper = helper_interface
92 parser_postfix = "_parser"
93 for method in sorted(dir(self)):
94 if method == "add_parser":
95 continue
96 elif method.endswith(parser_postfix) and (method != parser_postfix):
97 parser_id = method[:len(parser_postfix)*-1]
98 self.__parsers.append((parser_id, getattr(self, method),))
99
101 """
102 Returns the unique plugin id passed at construction time.
103
104 @return plugin identifier
105 """
106 return self.__plugin_id
107
108 - def add_parser(self, parser_id, parser_callable):
109 """
110 You must call this method in order to add your custom
111 parsers to the plugin.
112 Please note, if your parser method ends with "_parser"
113 it will be automatically added this way:
114
115 method: foo_parser
116 parser_id => foo
117 method: another_fabulous_parser
118 parser_id => another_fabulous
119
120 @param parser_id -- parser identifier, must be unique
121 @type parser_id basestring
122 @param parser_callable -- any callable function which has
123 the following signature: callable(system_settings_instance)
124 can return True to stop further parsers calls
125 @type parser_callable callable
126 """
127 self.__parsers.append((parser_id, parser_callable,))
128
129 - def parse(self, system_settings_instance):
130 """
131 This method is called by SystemSettings instance
132 when building its settings metadata.
133
134 Returned data from parser will be put into the SystemSettings
135 dict using plugin_id and parser_id keys.
136 If returned data is None, SystemSettings dict won't be changed.
137
138 @param system_settings_instance -- SystemSettings instance
139 @type system_settings_instance SystemSettings instance
140 """
141 plugin_id = self.get_id()
142 for parser_id, parser in self.__parsers:
143 data = parser(system_settings_instance)
144 if data == None:
145 continue
146 if not system_settings_instance.has_key(plugin_id):
147 system_settings_instance[plugin_id] = {}
148 system_settings_instance[plugin_id][parser_id] = data
149
151
152 """
153 This is the place where all the Entropy settings are stored if
154 they are not considered instance constants (etpConst).
155 For example, here we store package masking cache information and
156 settings.
157 Also, this class mimics a dictionary (even if not inheriting it
158 due to issues with the Singleton class).
159 """
160
161 import entropy.tools as entropyTools
163
164 """
165 Replaces __init__ because SystemSettings is a Singleton.
166 see Singleton API reference for more information.
167
168 """
169
170 from entropy.cache import EntropyCacher
171 self.__cacher = EntropyCacher()
172 self.__data = {}
173 self.__is_destroyed = False
174
175 self.__plugins = {}
176 self.__setting_files_order = []
177 self.__setting_files_pre_run = []
178 self.__setting_files = {}
179 self.__mtime_files = {}
180 self.__persistent_settings = {
181 'pkg_masking_reasons': {
182 0: _('reason not available'),
183 1: _('user package.mask'),
184 2: _('system keywords'),
185 3: _('user package.unmask'),
186 4: _('user repo package.keywords (all packages)'),
187 5: _('user repo package.keywords'),
188 6: _('user package.keywords'),
189 7: _('completely masked'),
190 8: _('repository general packages.db.mask'),
191 10: _('user license.mask'),
192 11: _('user live unmask'),
193 12: _('user live mask'),
194 },
195 'pkg_masking_reference': {
196 'reason_not_avail': 0,
197 'user_package_mask': 1,
198 'system_keyword': 2,
199 'user_package_unmask': 3,
200 'user_repo_package_keywords_all': 4,
201 'user_repo_package_keywords': 5,
202 'user_package_keywords': 6,
203 'completely_masked': 7,
204 'repository_packages_db_mask': 8,
205 'repository_in_branch_pacakges_db_mask': 9,
206 'user_license_mask': 10,
207 'user_live_unmask': 11,
208 'user_live_mask': 12,
209 },
210 'backed_up': {},
211
212 'live_packagemasking': {
213 'unmask_matches': set(),
214 'mask_matches': set(),
215 },
216 }
217
218 self.__setup_const()
219 self.__scan()
220
222 """
223 Overloaded method from Singleton.
224 "Destroys" the instance.
225
226 @return None
227 """
228 self.__is_destroyed = True
229
230 - def add_plugin(self, system_settings_plugin_instance):
231 """
232 This method lets you add custom parsers to SystemSettings.
233 Mind that you are responsible of handling your plugin instance
234 and remove it before it is destroyed. You can remove the plugin
235 instance at any time by issuing remove_plugin.
236 Every add_plugin or remove_plugin method will also issue clear()
237 for you. This could be bad and it might be removed in future.
238
239 @param plugin_id -- plugin identifier
240 @type plugin_id basestring
241 @param system_settings_plugin_instance -- valid SystemSettingsPlugin
242 instance
243 @type system_settings_plugin_instance SystemSettingsPlugin instance
244 """
245 inst = system_settings_plugin_instance
246 if not isinstance(inst,SystemSettingsPlugin):
247 raise AttributeError("SystemSettings: expected valid " + \
248 "SystemSettingsPlugin instance")
249 self.__plugins[inst.get_id()] = inst
250 self.clear()
251
253 """
254 This method lets you remove previously added custom parsers from
255 SystemSettings through its plugin identifier. If plugin_id is not
256 available, KeyError exception will be raised.
257 Every add_plugin or remove_plugin method will also issue clear()
258 for you. This could be bad and it might be removed in future.
259
260 @param plugin_id -- plugin identifier
261 @type plugin_id basestring
262 """
263 del self.__plugins[plugin_id]
264 self.clear()
265
267
268 """
269 Internal method. Does the constants initialization.
270
271 @return None
272 """
273
274 del self.__setting_files_order[:]
275 del self.__setting_files_pre_run[:]
276 self.__setting_files.clear()
277 self.__mtime_files.clear()
278
279 self.__setting_files.update({
280
281 'keywords': etpConst['confpackagesdir']+"/package.keywords",
282
283 'unmask': etpConst['confpackagesdir']+"/package.unmask",
284
285 'mask': etpConst['confpackagesdir']+"/package.mask",
286
287 'license_mask': etpConst['confpackagesdir']+"/license.mask",
288 'repos_system_mask': {},
289 'system_mask': etpConst['confpackagesdir']+"/system.mask",
290 'repos_mask': {},
291 'repos_license_whitelist': {},
292 'system_package_sets': {},
293 'conflicting_tagged_packages': {},
294 'system_dirs': etpConst['confdir']+"/fsdirs.conf",
295 'system_dirs_mask': etpConst['confdir']+"/fsdirsmask.conf",
296 'system_rev_symlinks': etpConst['confdir']+"/fssymlinks.conf",
297 'hw_hash': etpConst['confdir']+"/.hw.hash",
298 'socket_service': etpConst['socketconf'],
299 'system': etpConst['entropyconf'],
300 'repositories': etpConst['repositoriesconf'],
301 })
302 self.__setting_files_order.extend([
303 'keywords', 'unmask', 'mask', 'license_mask',
304 'repos_system_mask', 'system_mask', 'repos_mask',
305 'repos_license_whitelist', 'system_package_sets',
306 'conflicting_tagged_packages', 'system_dirs',
307 'system_dirs_mask', 'socket_service', 'system',
308 'system_rev_symlinks', 'hw_hash'
309 ])
310 self.__setting_files_pre_run.extend(['repositories'])
311
312
313 trunk_fsdirs_conf = "../conf/fsdirs.conf"
314 trunk_fsdirsmask_conf = "../conf/fsdirsmask.conf"
315 trunk_fssymlinks_conf = "../conf/fssymlinks.conf"
316 if os.path.isfile(trunk_fsdirs_conf):
317 self.__setting_files['system_dirs'] = trunk_fsdirs_conf
318 if os.path.isfile(trunk_fsdirsmask_conf):
319 self.__setting_files['system_dirs_mask'] = trunk_fsdirsmask_conf
320 if os.path.isfile(trunk_fssymlinks_conf):
321 self.__setting_files['system_rev_symlinks'] = trunk_fssymlinks_conf
322
323 dmp_dir = etpConst['dumpstoragedir']
324 self.__mtime_files.update({
325 'keywords_mtime': dmp_dir+"/keywords.mtime",
326 'unmask_mtime': dmp_dir+"/unmask.mtime",
327 'mask_mtime': dmp_dir+"/mask.mtime",
328 'license_mask_mtime': dmp_dir+"/license_mask.mtime",
329 'system_mask_mtime': dmp_dir+"/system_mask.mtime",
330 'repos_system_mask': {},
331 'repos_mask': {},
332 'repos_license_whitelist': {},
333 })
334
335
337
338 """
339 Internal method. Scan settings and fill variables.
340
341 @return None
342 """
343
344 self.__parse()
345
346 for keyword in self.__data['keywords']['universal']:
347 etpConst['keywords'].add(keyword)
348
349
350 for plugin_id in sorted(self.__plugins):
351 self.__plugins[plugin_id].parse(self)
352
353
354 self.__data.update(self.__persistent_settings)
355
356 self.__data.update(self.__persistent_settings['backed_up'].copy())
357
359 """
360 dict method. See Python dict API reference.
361 """
362 if self.__persistent_settings.has_key(mykey):
363 self.__persistent_settings[mykey] = myvalue
364 self.__data[mykey] = myvalue
365
367 """
368 dict method. See Python dict API reference.
369 """
370 return self.__data[mykey]
371
373 """
374 dict method. See Python dict API reference.
375 """
376 del self.__data[mykey]
377
379 """
380 dict method. See Python dict API reference.
381 """
382 return iter(self.__data)
383
385 """
386 dict method. See Python dict API reference.
387 """
388 return item in self.__data
389
391 """
392 dict method. See Python dict API reference.
393 """
394 return cmp(self.__data, other)
395
397 """
398 dict method. See Python dict API reference.
399 """
400 return hash(self.__data)
401
403 """
404 dict method. See Python dict API reference.
405 """
406 return len(self.__data)
407
408 - def get(self, mykey):
409 """
410 dict method. See Python dict API reference.
411 """
412 return self.__data.get(mykey)
413
415 """
416 dict method. See Python dict API reference.
417 """
418 return self.__data.has_key(mykey)
419
421 """
422 dict method. See Python dict API reference.
423 """
424 return self.__data.copy()
425
427 """
428 dict method. See Python dict API reference.
429 """
430 return self.__data.fromkeys(seq, val)
431
433 """
434 dict method. See Python dict API reference.
435 """
436 return self.__data.items()
437
439 """
440 dict method. See Python dict API reference.
441 """
442 return self.__data.iteritems()
443
445 """
446 dict method. See Python dict API reference.
447 """
448 return self.__data.iterkeys()
449
451 """
452 dict method. See Python dict API reference.
453 """
454 return self.__data.keys()
455
456 - def pop(self, mykey, default = None):
457 """
458 dict method. See Python dict API reference.
459 """
460 return self.__data.pop(mykey, default)
461
463 """
464 dict method. See Python dict API reference.
465 """
466 return self.__data.popitem()
467
469 """
470 dict method. See Python dict API reference.
471 """
472 return self.__data.setdefault(mykey, default)
473
475 """
476 dict method. See Python dict API reference.
477 """
478 return self.__data.update(kwargs)
479
481 """
482 dict method. See Python dict API reference.
483 """
484 return self.__data.values()
485
487 """
488 dict method. See Python dict API reference.
489 Settings are also re-initialized here.
490
491 @return None
492 """
493 self.__data.clear()
494 self.__setup_const()
495 self.__scan()
496
498 """
499 Make metadata persistent, the input dict will be merged
500 with the base one at every reset call (clear()).
501
502 @param persistent_dict -- dictionary to merge
503 @type persistent_dict dict
504
505 @return None
506 """
507 self.__persistent_settings.update(persistent_dict)
508
510 """
511 Remove dict key from persistent dictionary
512
513 @param persistent_key -- key to remove
514 @type persistent_dict dict
515
516 @return None
517 """
518 del self.__persistent_settings[persistent_key]
519 del self.__data[persistent_key]
520
522
523 """
524 This function setups the *mtimes* and *files* dictionaries
525 that will be read and parsed afterwards by respective
526 internal parsers.
527
528 @return None
529 """
530
531 dmp_dir = etpConst['dumpstoragedir']
532 for repoid in self['repositories']['order']:
533
534 repos_mask_setting = {}
535 repos_mask_mtime = {}
536 repos_lic_wl_setting = {}
537 repos_lic_wl_mtime = {}
538 repo_data = self['repositories']['available'][repoid]
539 repos_sm_mask_setting = {}
540 repos_sm_mask_mtime = {}
541 confl_tagged = {}
542
543 maskpath = os.path.join(repo_data['dbpath'],
544 etpConst['etpdatabasemaskfile'])
545 wlpath = os.path.join(repo_data['dbpath'],
546 etpConst['etpdatabaselicwhitelistfile'])
547 sm_path = os.path.join(repo_data['dbpath'],
548 etpConst['etpdatabasesytemmaskfile'])
549 ct_path = os.path.join(repo_data['dbpath'],
550 etpConst['etpdatabaseconflictingtaggedfile'])
551
552 if os.path.isfile(maskpath) and os.access(maskpath, os.R_OK):
553 repos_mask_setting[repoid] = maskpath
554 repos_mask_mtime[repoid] = dmp_dir + "/repo_" + \
555 repoid + "_" + etpConst['etpdatabasemaskfile'] + ".mtime"
556
557 if os.path.isfile(wlpath) and os.access(wlpath, os.R_OK):
558 repos_lic_wl_setting[repoid] = wlpath
559 repos_lic_wl_mtime[repoid] = dmp_dir + "/repo_" + \
560 repoid + "_" + etpConst['etpdatabaselicwhitelistfile'] + \
561 ".mtime"
562
563 if os.path.isfile(sm_path) and os.access(sm_path, os.R_OK):
564 repos_sm_mask_setting[repoid] = sm_path
565 repos_sm_mask_mtime[repoid] = dmp_dir + "/repo_" + \
566 repoid + "_" + etpConst['etpdatabasesytemmaskfile'] + \
567 ".mtime"
568 if os.path.isfile(ct_path) and os.access(ct_path, os.R_OK):
569 confl_tagged[repoid] = ct_path
570
571 self.__setting_files['repos_mask'].update(repos_mask_setting)
572 self.__mtime_files['repos_mask'].update(repos_mask_mtime)
573
574 self.__setting_files['repos_license_whitelist'].update(
575 repos_lic_wl_setting)
576 self.__mtime_files['repos_license_whitelist'].update(
577 repos_lic_wl_mtime)
578
579 self.__setting_files['repos_system_mask'].update(
580 repos_sm_mask_setting)
581 self.__mtime_files['repos_system_mask'].update(
582 repos_sm_mask_mtime)
583
584 self.__setting_files['conflicting_tagged_packages'].update(
585 confl_tagged)
586
588
589 """
590 This function setups the *files* dictionary about package sets
591 that will be read and parsed afterwards by the respective
592 internal parser.
593
594 @return None
595 """
596
597
598 sets_dir = etpConst['confsetsdir']
599 pkg_set_data = {}
600 if (os.path.isdir(sets_dir) and os.access(sets_dir, os.R_OK)):
601 set_files = [x for x in os.listdir(sets_dir) if \
602 (os.path.isfile(os.path.join(sets_dir, x)) and \
603 os.access(os.path.join(sets_dir, x),os.R_OK))]
604 for set_file in set_files:
605 try:
606 set_file = str(set_file)
607 except (UnicodeDecodeError, UnicodeEncodeError,):
608 continue
609 pkg_set_data[set_file] = os.path.join(sets_dir, set_file)
610 self.__setting_files['system_package_sets'].update(pkg_set_data)
611
613 """
614 This is the main internal parsing method.
615 *files* and *mtimes* dictionaries are prepared and
616 parsed just a few lines later.
617
618 @return dict settings metadata
619 """
620
621 for item in self.__setting_files_pre_run:
622 myattr = '%s_parser' % (item,)
623 if not hasattr(self, myattr):
624 continue
625 func = getattr(self, myattr)
626 self.__data[item] = func()
627
628
629 self.__setup_setting_vars()
630 self.__setup_package_sets_vars()
631
632 data = {}
633 for item in self.__setting_files_order:
634 myattr = '%s_parser' % (item,)
635 if not hasattr(self, myattr):
636 continue
637 func = getattr(self, myattr)
638 self.__data[item] = func()
639
641 """
642 Return a copy of the internal *files* dictionary.
643 This dict contains config file paths and their identifiers.
644
645 @return dict __setting_files
646 """
647 return self.__setting_files.copy()
648
650 """
651 Return a copy of the internal *mtime* dictionary.
652 This dict contains config file paths and their current mtime.
653
654 @return dict __mtime_files
655 """
656 return self.__mtime_files.copy()
657
659 """
660 Parser returning package keyword masking metadata
661 read from package.keywords file.
662 This file contains package mask or unmask directives
663 based on package keywords.
664
665 @return dict data
666 """
667 data = {
668 'universal': set(),
669 'packages': {},
670 'repositories': {},
671 }
672
673 self.__validate_entropy_cache(self.__setting_files['keywords'],
674 self.__mtime_files['keywords_mtime'])
675 content = [x.split() for x in \
676 self.__generic_parser(self.__setting_files['keywords']) \
677 if len(x.split()) < 4]
678 for keywordinfo in content:
679
680 if len(keywordinfo) > 3:
681 continue
682
683 if len(keywordinfo) == 1:
684 if keywordinfo[0].startswith("repo="):
685 continue
686
687 if keywordinfo[0] == "**":
688 keywordinfo[0] = ""
689 data['universal'].add(keywordinfo[0])
690 continue
691
692 if len(keywordinfo) in (2, 3,):
693
694 if keywordinfo[0].startswith("repo="):
695 continue
696
697 items = keywordinfo[1:]
698
699 if keywordinfo[0] == "**":
700 keywordinfo[0] = ""
701 reponame = [x for x in items if x.startswith("repo=") \
702 and (len(x.split("=")) == 2)]
703 if reponame:
704 reponame = reponame[0].split("=")[1]
705 if reponame not in data['repositories']:
706 data['repositories'][reponame] = {}
707
708 if keywordinfo[0] not in data['repositories'][reponame]:
709 data['repositories'][reponame][keywordinfo[0]] = set()
710 if len(items) == 1:
711
712 data['repositories'][reponame][keywordinfo[0]].add('*')
713 elif "*" not in \
714 data['repositories'][reponame][keywordinfo[0]]:
715
716 item = [x for x in items if not x.startswith("repo=")]
717 data['repositories'][reponame][keywordinfo[0]].add(
718 item[0])
719 elif len(items) == 2:
720
721
722 continue
723 else:
724
725 if keywordinfo[0] not in data['packages']:
726 data['packages'][keywordinfo[0]] = set()
727 data['packages'][keywordinfo[0]].add(items[0])
728 return data
729
730
732 """
733 Parser returning package unmasking metadata read from
734 package.unmask file.
735 This file contains package unmask directives, allowing
736 to enable experimental or *secret* packages.
737
738 @return list parsed data
739 """
740 self.__validate_entropy_cache(self.__setting_files['unmask'],
741 self.__mtime_files['unmask_mtime'])
742 return self.__generic_parser(self.__setting_files['unmask'])
743
745 """
746 Parser returning package masking metadata read from
747 package.mask file.
748 This file contains package mask directives, allowing
749 to disable experimental or *secret* packages.
750
751 @return list parsed data
752 """
753 self.__validate_entropy_cache(self.__setting_files['mask'],
754 self.__mtime_files['mask_mtime'])
755 return self.__generic_parser(self.__setting_files['mask'])
756
758 """
759 Parser returning system packages mask metadata read from
760 package.system_mask file.
761 This file contains packages that should be always kept
762 installed, extending the already defined (in repository database)
763 set of atoms.
764
765 @return list parsed data
766 """
767 self.__validate_entropy_cache(self.__setting_files['system_mask'],
768 self.__mtime_files['system_mask_mtime'])
769 return self.__generic_parser(self.__setting_files['system_mask'])
770
772 """
773 Parser returning packages masked by license metadata read from
774 license.mask file.
775 Packages shipped with licenses listed there will be masked.
776
777 @return list parsed data
778 """
779 self.__validate_entropy_cache(self.__setting_files['license_mask'],
780 self.__mtime_files['license_mask_mtime'])
781 return self.__generic_parser(self.__setting_files['license_mask'])
782
784 """
785 Parser returning licenses considered accepted by default
786 (= GPL compatibles) read from package.lic_whitelist.
787
788 @return dict parsed data
789 """
790 data = {}
791 for repoid in self.__setting_files['repos_license_whitelist']:
792 self.__validate_entropy_cache(
793 self.__setting_files['repos_license_whitelist'][repoid],
794 self.__mtime_files['repos_license_whitelist'][repoid],
795 repoid = repoid)
796 data[repoid] = self.__generic_parser(
797 self.__setting_files['repos_license_whitelist'][repoid])
798 return data
799
801 """
802 Parser returning packages masked at repository level read from
803 packages.db.mask inside the repository database directory.
804
805 @return dict parsed data
806 """
807 data = {}
808 for repoid in self.__setting_files['repos_mask']:
809 self.__validate_entropy_cache(
810 self.__setting_files['repos_mask'][repoid],
811 self.__mtime_files['repos_mask'][repoid], repoid = repoid)
812 data[repoid] = self.__generic_parser(
813 self.__setting_files['repos_mask'][repoid])
814
815 return data
816
818 """
819 Parser returning system packages mask metadata read from
820 packages.db.system_mask file inside the repository directory.
821 This file contains packages that should be always kept
822 installed, extending the already defined (in repository database)
823 set of atoms.
824
825 @return dict parsed data
826 """
827 data = []
828 for repoid in self.__setting_files['repos_system_mask']:
829 self.__validate_entropy_cache(
830 self.__setting_files['repos_system_mask'][repoid],
831 self.__mtime_files['repos_system_mask'][repoid],
832 repoid = repoid)
833 data += [x for x in self.__generic_parser(
834 self.__setting_files['repos_system_mask'][repoid]) if x \
835 not in data]
836
837 return data
838
840 """
841 Parser returning system defined package sets read from
842 /etc/entropy/packages/sets.
843
844 @return dict parsed data
845 """
846 data = {}
847 for set_name in self.__setting_files['system_package_sets']:
848 set_filepath = self.__setting_files['system_package_sets'][set_name]
849 set_elements = self.entropyTools.extract_packages_from_set_file(
850 set_filepath)
851 if set_elements:
852 data[set_name] = set_elements.copy()
853 return data
854
856 """
857 Parser returning directories considered part of the base system.
858
859 @return list parsed data
860 """
861 return self.__generic_parser(self.__setting_files['system_dirs'])
862
864 """
865 Parser returning directories NOT considered part of the base system.
866 Settings here overlay system_dirs_parser.
867
868 @return list parsed data
869 """
870 return self.__generic_parser(self.__setting_files['system_dirs_mask'])
871
873 """
874 Hardware hash metadata parser and generator. It returns a theorically
875 unique SHA256 hash bound to the computer running this Framework.
876
877 @return basestring containing SHA256 hexdigest
878 """
879 hw_hash_file = self.__setting_files['hw_hash']
880 if os.access(hw_hash_file, os.R_OK | os.F_OK):
881 hash_f = open(hw_hash_file, "r")
882 hash_data = hash_f.readline().strip()
883 hash_f.close()
884 return hash_data
885
886 hash_file_dir = os.path.dirname(hw_hash_file)
887 hw_hash_exec = etpConst['etp_hw_hash_gen']
888 if os.access(hash_file_dir, os.W_OK) and \
889 os.access(hw_hash_exec, os.X_OK | os.F_OK | os.R_OK):
890 pipe = os.popen('{ ' + hw_hash_exec + '; } 2>&1', 'r')
891 hash_data = pipe.read().strip()
892 sts = pipe.close()
893 if sts != None:
894 return None
895 hash_f = open(hw_hash_file, "w")
896 hash_f.write(hash_data)
897 hash_f.flush()
898 hash_f.close()
899 return hash_data
900
902 """
903 Parser returning important system symlinks mapping. For example:
904 {'/usr/lib': ['/usr/lib64']}
905 Useful for reverse matching files belonging to packages.
906
907 @return dict containing the mapping
908 """
909 setting_file = self.__setting_files['system_rev_symlinks']
910 raw_data = self.__generic_parser(setting_file)
911 data = {}
912 for line in raw_data:
913 line = line.split()
914 if len(line) < 2:
915 continue
916 data[line[0]] = frozenset(line[1:])
917 return data
918
920 """
921 Parser returning packages that could have been installed because
922 they aren't in the same scope, but ending up creating critical
923 issues. You can see it as a configurable conflict map.
924
925 @return dict parsed data
926 """
927 data = {}
928
929 repoids = [x for x in self['repositories']['order'] if x in \
930 self.__setting_files['conflicting_tagged_packages']]
931 for repoid in repoids:
932 filepath = self.__setting_files['conflicting_tagged_packages'].get(
933 repoid)
934 if os.path.isfile(filepath) and os.access(filepath, os.R_OK):
935 confl_f = open(filepath,"r")
936 content = confl_f.readlines()
937 confl_f.close()
938 content = [x.strip().rsplit("#", 1)[0].strip().split() for x \
939 in content if not x.startswith("#") and x.strip()]
940 for mydata in content:
941 if len(mydata) < 2:
942 continue
943 data[mydata[0]] = mydata[1:]
944 return data
945
947 """
948 Parses socket service configuration file.
949 This file contains information about Entropy remote service ports
950 and SSL.
951
952 @return dict data
953 """
954
955 data = etpConst['socket_service'].copy()
956
957 sock_conf = self.__setting_files['socket_service']
958 if not (os.path.isfile(sock_conf) and \
959 os.access(sock_conf,os.R_OK)):
960 return data
961
962 socket_f = open(sock_conf,"r")
963 socketconf = [x.strip() for x in socket_f.readlines() if \
964 x.strip() and not x.strip().startswith("#")]
965 socket_f.close()
966
967 for line in socketconf:
968
969 split_line = line.split("|")
970 split_line_len = len(split_line)
971
972 if line.startswith("listen|") and (split_line_len > 1):
973
974 item = split_line[1].strip()
975 if item:
976 data['hostname'] = item
977
978 elif line.startswith("listen-port|") and \
979 (split_line_len > 1):
980
981 item = split_line[1].strip()
982 try:
983 item = int(item)
984 data['port'] = item
985 except ValueError:
986 continue
987
988 elif line.startswith("listen-timeout|") and \
989 (split_line_len > 1):
990
991 item = split_line[1].strip()
992 try:
993 item = int(item)
994 data['timeout'] = item
995 except ValueError:
996 continue
997
998 elif line.startswith("listen-threads|") and \
999 (split_line_len > 1):
1000
1001 item = split_line[1].strip()
1002 try:
1003 item = int(item)
1004 data['threads'] = item
1005 except ValueError:
1006 continue
1007
1008 elif line.startswith("session-ttl|") and \
1009 (split_line_len > 1):
1010
1011 item = split_line[1].strip()
1012 try:
1013 item = int(item)
1014 data['session_ttl'] = item
1015 except ValueError:
1016 continue
1017
1018 elif line.startswith("max-connections|") and \
1019 (split_line_len > 1):
1020
1021 item = split_line[1].strip()
1022 try:
1023 item = int(item)
1024 data['max_connections'] = item
1025 except ValueError:
1026 continue
1027
1028 elif line.startswith("ssl-port|") and \
1029 (split_line_len > 1):
1030
1031 item = split_line[1].strip()
1032 try:
1033 item = int(item)
1034 data['ssl_port'] = item
1035 except ValueError:
1036 continue
1037
1038 elif line.startswith("disabled-commands|") and \
1039 (split_line_len > 1):
1040
1041 disabled_cmds = split_line[1].strip().split()
1042 for disabled_cmd in disabled_cmds:
1043 data['disabled_cmds'].add(disabled_cmd)
1044
1045 elif line.startswith("ip-blacklist|") and \
1046 (split_line_len > 1):
1047
1048 ips_blacklist = split_line[1].strip().split()
1049 for ip_blacklist in ips_blacklist:
1050 data['ip_blacklist'].add(ip_blacklist)
1051
1052 return data
1053
1055
1056 """
1057 Parses Entropy system configuration file.
1058
1059 @return dict data
1060 """
1061
1062 data = {}
1063 data['proxy'] = etpConst['proxy'].copy()
1064 data['name'] = etpConst['systemname']
1065 data['log_level'] = etpConst['entropyloglevel']
1066
1067 etp_conf = self.__setting_files['system']
1068 if not os.path.isfile(etp_conf) and \
1069 os.access(etp_conf,os.R_OK):
1070 return data
1071
1072 const_secure_config_file(etp_conf)
1073 entropy_f = open(etp_conf,"r")
1074 entropyconf = [x.strip() for x in entropy_f.readlines() if \
1075 x.strip() and not x.strip().startswith("#")]
1076 entropy_f.close()
1077
1078 for line in entropyconf:
1079
1080 split_line = line.split("|")
1081 split_line_len = len(split_line)
1082
1083 if line.startswith("loglevel|") and \
1084 (len(line.split("loglevel|")) == 2):
1085
1086 loglevel = line.split("loglevel|")[1]
1087 try:
1088 loglevel = int(loglevel)
1089 except ValueError:
1090 pass
1091 if (loglevel > -1) and (loglevel < 3):
1092 data['log_level'] = loglevel
1093
1094 elif line.startswith("ftp-proxy|") and \
1095 (split_line_len == 2):
1096
1097 ftpproxy = split_line[1].strip().split()
1098 if ftpproxy:
1099 data['proxy']['ftp'] = ftpproxy[-1]
1100
1101 elif line.startswith("http-proxy|") and \
1102 (split_line_len == 2):
1103
1104 httpproxy = split_line[1].strip().split()
1105 if httpproxy:
1106 data['proxy']['http'] = httpproxy[-1]
1107
1108 elif line.startswith("proxy-username|") and \
1109 (split_line_len == 2):
1110
1111 httpproxy = split_line[1].strip().split()
1112 if httpproxy:
1113 data['proxy']['username'] = httpproxy[-1]
1114
1115 elif line.startswith("proxy-password|") and \
1116 (split_line_len == 2):
1117
1118 httpproxy = split_line[1].strip().split()
1119 if httpproxy:
1120 data['proxy']['password'] = httpproxy[-1]
1121
1122 elif line.startswith("system-name|") and \
1123 (split_line_len == 2):
1124
1125 data['name'] = split_line[1].strip()
1126
1127 elif line.startswith("nice-level|") and \
1128 (split_line_len == 2):
1129
1130 mylevel = split_line[1].strip()
1131 try:
1132 mylevel = int(mylevel)
1133 if (mylevel >= -19) and (mylevel <= 19):
1134 const_set_nice_level(mylevel)
1135 except (ValueError,):
1136 continue
1137
1138 return data
1139
1141
1142 """
1143 Setup Entropy Client repository settings reading them from
1144 the relative config file specified in etpConst['repositoriesconf']
1145
1146 @return None
1147 """
1148
1149 data = {
1150 'available': {},
1151 'excluded': {},
1152 'order': [],
1153 'product': etpConst['product'],
1154 'branch': etpConst['branch'],
1155 'default_repository': etpConst['officialrepositoryid'],
1156 'transfer_limit': etpConst['downloadspeedlimit'],
1157 'security_advisories_url': etpConst['securityurl'],
1158 }
1159
1160 repo_conf = etpConst['repositoriesconf']
1161 if not (os.path.isfile(repo_conf) and os.access(repo_conf, os.R_OK)):
1162 return data
1163
1164 repo_f = open(repo_conf,"r")
1165 repositoriesconf = [x.strip() for x in repo_f.readlines() if x.strip()]
1166 repo_f.close()
1167
1168
1169 for line in repositoriesconf:
1170
1171 split_line = line.split("|")
1172 split_line_len = len(split_line)
1173
1174 if (line.find("product|") != -1) and \
1175 (not line.startswith("#")) and (split_line_len == 2):
1176
1177 data['product'] = split_line[1]
1178
1179 elif (line.find("branch|") != -1) and \
1180 (not line.startswith("#")) and (split_line_len == 2):
1181
1182 branch = split_line[1].strip()
1183 data['branch'] = branch
1184 if not os.path.isdir(etpConst['packagesbindir']+"/"+branch) \
1185 and (etpConst['uid'] == 0):
1186
1187 try:
1188 os.makedirs(etpConst['packagesbindir']+"/"+branch)
1189 except (OSError, IOError,):
1190 continue
1191
1192 for line in repositoriesconf:
1193
1194 split_line = line.split("|")
1195 split_line_len = len(split_line)
1196
1197
1198 if (line.find("repository|") != -1) and (split_line_len == 5):
1199
1200 excluded = False
1201 my_repodata = data['available']
1202 if line.startswith("##"):
1203 continue
1204 elif line.startswith("#"):
1205 excluded = True
1206 my_repodata = data['excluded']
1207 line = line[1:]
1208
1209 reponame, repodata = const_extract_cli_repo_params(line,
1210 data['branch'], data['product'])
1211 if my_repodata.has_key(reponame):
1212
1213 my_repodata[reponame]['plain_packages'].extend(
1214 repodata['plain_packages'])
1215 my_repodata[reponame]['packages'].extend(
1216 repodata['packages'])
1217
1218 if (not my_repodata[reponame]['plain_database']) and \
1219 repodata['plain_database']:
1220
1221 my_repodata[reponame]['plain_database'] = \
1222 repodata['plain_database']
1223 my_repodata[reponame]['database'] = \
1224 repodata['database']
1225 my_repodata[reponame]['dbrevision'] = \
1226 repodata['dbrevision']
1227 my_repodata[reponame]['dbcformat'] = \
1228 repodata['dbcformat']
1229 else:
1230
1231 my_repodata[reponame] = repodata.copy()
1232 if not excluded:
1233 data['order'].append(reponame)
1234
1235 elif (line.find("officialrepositoryid|") != -1) and \
1236 (not line.startswith("#")) and (split_line_len == 2):
1237
1238 officialreponame = split_line[1]
1239 data['default_repository'] = officialreponame
1240
1241 elif (line.find("downloadspeedlimit|") != -1) and \
1242 (not line.startswith("#")) and (split_line_len == 2):
1243
1244 try:
1245 myval = int(split_line[1])
1246 if myval > 0:
1247 data['transfer_limit'] = myval
1248 else:
1249 data['transfer_limit'] = None
1250 except (ValueError, IndexError,):
1251 data['transfer_limit'] = None
1252
1253 elif (line.find("securityurl|") != -1) and \
1254 (not line.startswith("#")) and (split_line_len == 2):
1255
1256 try:
1257 data['security_advisories_url'] = split_line[1]
1258 except (IndexError, ValueError, TypeError,):
1259 continue
1260
1261 return data
1262
1264 """
1265 Internal method, go away!
1266 """
1267 self.__cacher.discard()
1268 self._clear_dump_cache(etpCache['world_available'])
1269 self._clear_dump_cache(etpCache['world_update'])
1270 self._clear_dump_cache(etpCache['check_package_update'])
1271 self._clear_dump_cache(etpCache['filter_satisfied_deps'])
1272 self._clear_dump_cache(etpCache['atomMatch'])
1273 self._clear_dump_cache(etpCache['dep_tree'])
1274 if repoid != None:
1275 self._clear_dump_cache("%s/%s%s/" % (
1276 etpCache['dbMatch'],etpConst['dbnamerepoprefix'],repoid,))
1277 self._clear_dump_cache("%s/%s%s/" % (
1278 etpCache['dbSearch'],etpConst['dbnamerepoprefix'],repoid,))
1279
1281 """
1282 Internal method, go away!
1283 """
1284 dump_path = os.path.join(etpConst['dumpstoragedir'],dump_name)
1285 dump_dir = os.path.dirname(dump_path)
1286
1287 for currentdir, subdirs, files in os.walk(dump_dir):
1288 path = os.path.join(dump_dir,currentdir)
1289 if skip:
1290 found = False
1291 for myskip in skip:
1292 if path.find(myskip) != -1:
1293 found = True
1294 break
1295 if found: continue
1296 for item in files:
1297 if item.endswith(etpConst['cachedumpext']):
1298 item = os.path.join(path,item)
1299 try:
1300 os.remove(item)
1301 except (OSError, IOError,):
1302 pass
1303 try:
1304 if not os.listdir(path):
1305 os.rmdir(path)
1306 except (OSError, IOError,):
1307 pass
1308
1310 """
1311 Internal method. This is the generic file parser here.
1312
1313 @param filepath valid path
1314 @type filepath basestring
1315 @return list parsed data
1316 """
1317 return self.entropyTools.generic_file_content_parser(filepath)
1318
1320 """
1321 Internal method. Remove repository cache, because not valid anymore.
1322
1323 @param repoid repository identifier or None
1324 @type repoid basestring or None
1325 @return None
1326 """
1327 if os.path.isdir(etpConst['dumpstoragedir']):
1328 if repoid:
1329 self._clear_repository_cache(repoid = repoid)
1330 return
1331 for repoid in self['repositories']['order']:
1332 self._clear_repository_cache(repoid = repoid)
1333 else:
1334 try:
1335 os.makedirs(etpConst['dumpstoragedir'])
1336 except IOError, e:
1337 if e.errno == 30:
1338 etpUi['pretend'] = True
1339 return
1340 except OSError:
1341 return
1342
1344 """
1345 Internal method. Save mtime of a file to another file.
1346
1347 @param toread file path to read
1348 @type toread basestring
1349 @param tosaveinto path where to save retrieved mtime information
1350 @type tosaveinto basestring
1351 @return None
1352 """
1353 if not os.path.isfile(toread):
1354 currmtime = 0.0
1355 else:
1356 currmtime = os.path.getmtime(toread)
1357
1358 if not os.path.isdir(etpConst['dumpstoragedir']):
1359 try:
1360 os.makedirs(etpConst['dumpstoragedir'], 0775)
1361 const_setup_perms(etpConst['dumpstoragedir'],
1362 etpConst['entropygid'])
1363 except IOError, e:
1364 if e.errno == 30:
1365 etpUi['pretend'] = True
1366 return
1367 except (OSError,), e:
1368
1369
1370 return
1371
1372 try:
1373 mtime_f = open(tosaveinto,"w")
1374 except IOError, e:
1375 if e.errno == 30:
1376 etpUi['pretend'] = True
1377 return
1378 else:
1379 mtime_f.write(str(currmtime))
1380 mtime_f.flush()
1381 mtime_f.close()
1382 os.chmod(tosaveinto, 0664)
1383 if etpConst['entropygid'] != None:
1384 os.chown(tosaveinto, 0, etpConst['entropygid'])
1385
1386
1388 """
1389 Internal method. Validates Entropy Cache
1390
1391 @param maskfile path of the setting file
1392 @type maskfile basestring
1393 @param mtimefile path where to save retrieved mtime information
1394 @type mtimefile basestring
1395 @param repoid repository identifier or None
1396 @type repoid basestring or None
1397 @return None
1398 """
1399
1400
1401
1402 if os.getuid() != 0:
1403 return
1404
1405
1406
1407
1408 if not os.path.isfile(mtimefile):
1409
1410
1411 self.__remove_repo_cache(repoid = repoid)
1412 self.__save_file_mtime(maskfile, mtimefile)
1413 else:
1414
1415 try:
1416 mtime_f = open(mtimefile,"r")
1417 mtime = mtime_f.readline().strip()
1418 mtime_f.close()
1419
1420 try:
1421 currmtime = str(os.path.getmtime(maskfile))
1422 except OSError:
1423 currmtime = "0.0"
1424 if mtime != currmtime:
1425 self.__remove_repo_cache(repoid = repoid)
1426 self.__save_file_mtime(maskfile, mtimefile)
1427 except (OSError, IOError,):
1428 self.__remove_repo_cache(repoid = repoid)
1429 self.__save_file_mtime(maskfile, mtimefile)
1430