OSDN Git Service

2020.05.10 update
[rebornos/cnchi-gnome-osdn.git] / Cnchi / check.py
1 #!/usr/bin/env python
2 # -*- coding: utf-8 -*-
3 #
4 #  check.py
5 #
6 #  Copyright © 2013-2019 RebornOS
7 #
8 #  This file is part of Cnchi.
9 #
10 #  Cnchi is free software; you can redistribute it and/or modify
11 #  it under the terms of the GNU General Public License as published by
12 #  the Free Software Foundation; either version 3 of the License, or
13 #  (at your option) any later version.
14 #
15 #  Cnchi is distributed in the hope that it will be useful,
16 #  but WITHOUT ANY WARRANTY; without even the implied warranty of
17 #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18 #  GNU General Public License for more details.
19 #
20 #  The following additional terms are in effect as per Section 7 of the license:
21 #
22 #  The preservation of all legal notices and author attributions in
23 #  the material or in the Appropriate Legal Notices displayed
24 #  by works containing it is required.
25 #
26 #  You should have received a copy of the GNU General Public License
27 #  along with Cnchi; If not, see <http://www.gnu.org/licenses/>.
28
29
30 """ Check screen (detects if Reborn prerequisites are meet) """
31
32 import dbus
33 import logging
34 import os
35 import subprocess
36
37 from gi.repository import GLib
38
39 import info
40
41 import misc.extra as misc
42 from misc.run_cmd import call
43 from pages.gtkbasebox import GtkBaseBox
44
45 import show_message as show
46
47 # Constants
48 NM = 'org.freedesktop.NetworkManager'
49 NM_STATE_CONNECTED_GLOBAL = 70
50 UPOWER = 'org.freedesktop.UPower'
51 UPOWER_PATH = '/org/freedesktop/UPower'
52 MIN_ROOT_SIZE = 8000000000
53
54 class Check(GtkBaseBox):
55     """ Check class """
56
57     def __init__(self, params, prev_page="language", next_page="location"):
58         """ Init class ui """
59         super().__init__(self, params, "check", prev_page, next_page)
60
61         self.remove_timer = False
62
63         self.prepare_power_source = None
64         self.prepare_network_connection = None
65         self.prepare_enough_space = None
66         self.timeout_id = None
67         self.prepare_best_results = None
68         self.updated = None
69         self.packaging_issues = None
70         self.remote_version = None
71
72         self.label_space = self.gui.get_object("label_space")
73
74         if 'checks_are_optional' in params:
75             self.checks_are_optional = params['checks_are_optional']
76         else:
77             self.checks_are_optional = False
78
79     def translate_ui(self):
80         """ Translates all ui elements """
81         txt = _("System Check")
82         self.header.set_subtitle(txt)
83
84         self.updated = self.gui.get_object("updated")
85         txt = _("Cnchi is up to date")
86         self.updated.set_property("label", txt)
87
88         self.prepare_enough_space = self.gui.get_object("prepare_enough_space")
89         txt = _("has at least {0}GB available storage space. (*)")
90         txt = txt.format(MIN_ROOT_SIZE / 1000000000)
91         self.prepare_enough_space.set_property("label", txt)
92
93         txt = _("This highly depends on which desktop environment you choose, "
94                 "so you might need more space.")
95         txt = "(*) <i>{0}</i>".format(txt)
96         self.label_space.set_markup(txt)
97         self.label_space.set_hexpand(False)
98         self.label_space.set_line_wrap(True)
99         self.label_space.set_max_width_chars(80)
100
101         self.prepare_power_source = self.gui.get_object("prepare_power_source")
102         txt = _("is plugged in to a power source")
103         self.prepare_power_source.set_property("label", txt)
104
105         self.prepare_network_connection = self.gui.get_object(
106             "prepare_network_connection")
107         txt = _("is connected to the Internet")
108         self.prepare_network_connection.set_property("label", txt)
109
110         self.packaging_issues = self.gui.get_object("packaging_issues")
111         txt = _(
112             "There are no temporary packaging issues that would interfere with installation.")
113         self.packaging_issues.set_property("label", txt)
114
115         self.prepare_best_results = self.gui.get_object("prepare_best_results")
116         txt = _("For best results, please ensure that this computer:")
117         txt = '<span weight="bold" size="large">{0}</span>'.format(txt)
118         self.prepare_best_results.set_markup(txt)
119         self.prepare_best_results.set_hexpand(False)
120         self.prepare_best_results.set_line_wrap(True)
121         self.prepare_best_results.set_max_width_chars(80)
122
123     def check_all(self):
124         """ Check that all requirements are meet """
125         if os.path.exists("/tmp/.cnchi_partitioning_completed"):
126             msg = "You must reboot before retrying again."
127             logging.error(msg)
128             msg = _("You must reboot before retrying again.")
129             show.fatal_error(self.main_window, msg)
130             return False
131
132         has_internet = misc.has_connection()
133         self.prepare_network_connection.set_state(has_internet)
134
135         on_power = not self.on_battery()
136         self.prepare_power_source.set_state(on_power)
137
138         space = self.has_enough_space()
139         self.prepare_enough_space.set_state(space)
140
141         packaging_issues = os.path.exists('/tmp/.packaging_issue')
142         self.packaging_issues.set_state(not packaging_issues)
143
144         if has_internet:
145             updated = self.is_updated()
146         else:
147             updated = False
148
149         self.updated.set_state(updated)
150
151         if self.checks_are_optional:
152             return True
153
154         if has_internet and space and not packaging_issues:
155             return True
156
157         return False
158
159     def on_battery(self):
160         """ Checks if we are on battery power """
161         if self.has_battery():
162             bus = dbus.SystemBus()
163             upower = bus.get_object(UPOWER, UPOWER_PATH)
164             result = misc.get_prop(upower, UPOWER_PATH, 'OnBattery')
165             if result is None:
166                 # Cannot read property, something is wrong.
167                 logging.warning("Cannot read %s/%s dbus property",
168                                 UPOWER_PATH, 'OnBattery')
169                 # We will assume we are connected to a power supply
170                 result = False
171             return result
172
173         return False
174
175     def has_battery(self):
176         """ Checks if latptop is connected to a power supply """
177         # UPower doesn't seem to have an interface for this.
178         path = '/sys/class/power_supply'
179         if os.path.exists(path):
180             for folder in os.listdir(path):
181                 type_path = os.path.join(path, folder, 'type')
182                 if os.path.exists(type_path):
183                     with open(type_path) as power_file:
184                         if power_file.read().startswith('Battery'):
185                             self.settings.set('laptop', 'True')
186                             return True
187         return False
188
189     @staticmethod
190     def has_enough_space():
191         """ Check that we have a disk or partition with enough space """
192
193         output = call(cmd=["lsblk", "-lnb"], debug=False).split("\n")
194
195         max_size = 0
196
197         for item in output:
198             col = item.split()
199             if len(col) >= 5:
200                 if col[5] == "disk" or col[5] == "part":
201                     size = int(col[3])
202                     if size > max_size:
203                         max_size = size
204
205         return max_size >= MIN_ROOT_SIZE
206
207     def is_updated(self):
208         """ Checks that we're running the latest stable cnchi version """
209         remote_version = info.CNCHI_VERSION
210         local_version = info.CNCHI_VERSION
211
212         if remote_version:
213             return self.compare_versions(remote_version, local_version)
214         else:
215             return False
216
217     def compare_versions(self, remote, local):
218         """ Compares Cnchi versions (local vs remote) and returns true
219             if local is at least as new as remote """
220         
221         remote = remote.split('.')
222         local = local.split('.')
223
224         for i, remote_val in enumerate(remote):
225             remote[i] = info.CNCHI_VERSION
226
227         for i, local_val in enumerate(local):
228             local[i] = info.CNCHI_VERSION
229
230         if remote[0] < local[0]:
231             return True
232         
233         if remote[0] > local[0]:
234             return False
235
236         if remote[1] < local[1]:
237             return True        
238         
239         if remote[1] > local[1]:
240             return False
241         
242         if remote[2] >  local[2]:
243             return False
244         
245         return True
246
247     def get_cnchi_version_in_repo(self):
248         """ Checks cnchi version in the RebornOS repository """
249         if not self.remote_version:
250             try:
251                 cmd = ["pacman", "-Ss", "cnchi"]
252                 line = subprocess.check_output(cmd).decode().split()
253                 version = line[1]
254                 if '-' in version:
255                     version = version.split('-')[0]
256                 logging.debug(
257                     'Cnchi version in the repository is: CNCHI_VERSION', version)
258             except subprocess.CalledProcessError as err:
259                 logging.debug(err)
260                 version = None
261             return version
262         else:
263             return self.remote_version
264
265     def on_timer(self):
266         """ If all requirements are meet, enable forward button """
267         if not self.remove_timer:
268             self.forward_button.set_sensitive(self.check_all())
269         return not self.remove_timer
270
271     def store_values(self):
272         """ Continue """
273         # Remove timer
274         self.remove_timer = True
275
276         logging.info("We have Internet connection.")
277         logging.info("We're connected to a power source.")
278         logging.info("We have enough disk space.")
279
280         # Enable forward button
281         self.forward_button.set_sensitive(True)
282         return True
283
284     def prepare(self, direction):
285         """ Load screen """
286         self.translate_ui()
287         self.show_all()
288
289         self.forward_button.set_sensitive(self.check_all())
290
291         # Set timer
292         self.timeout_id = GLib.timeout_add(5000, self.on_timer)
293
294
295 # When testing, no _() is available
296 try:
297     _("")
298 except NameError as err:
299     def _(message):
300         return message
301
302 if __name__ == '__main__':
303     from test_screen import _, run
304
305     run('Check')
306