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