scomd.py 49 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522
  1. #!/usr/bin/python
  2. # -*- coding: utf-8 -*-
  3. #
  4. # Sulin boot and initialization system
  5. # Copyright (C) 2017, Suleyman POYRAZ (AquilaNipalensis)
  6. #
  7. # This program is free software; you can redistribute it and/or modify it
  8. # under the terms of the GNU General Public License as published by the
  9. # Free Software Foundation; either version 2 of the License, or (at your
  10. # option) any later version. Please read the COPYING file.
  11. #
  12. """
  13. Pardus booting and initialization system written in Python.
  14. """
  15. import os
  16. import re
  17. import sys
  18. import stat
  19. import time
  20. import signal
  21. import gettext
  22. import subprocess
  23. from scomd_cgroupfs import Cgroupfs
  24. ########
  25. # i18n #
  26. ########
  27. __trans = gettext.translation('scomd', fallback=True)
  28. _ = __trans.gettext
  29. ##############
  30. # Decorators #
  31. ##############
  32. def skip_for_lxc_guests(function):
  33. """Ignores the function call if lxc_guest is set."""
  34. def wrapped():
  35. """Checks whether lxc_guest is set."""
  36. if CONFIG.get("lxc_guest") == "no":
  37. return function()
  38. return wrapped
  39. def plymouth_update_milestone(function):
  40. """Updates plymouth milestones."""
  41. def wrapped():
  42. """Calls the function and updates plymouth milestone."""
  43. function()
  44. SPLASH.update(function.__name__)
  45. return wrapped
  46. #######################
  47. # Convenience methods #
  48. #######################
  49. def wait_bus(unix_name, timeout=5, wait=0.1, stream=True):
  50. """Waits over a AF_UNIX socket for a given duration."""
  51. import socket
  52. itimeout = timeout
  53. if stream:
  54. sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
  55. else:
  56. sock = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM)
  57. while timeout > 0:
  58. try:
  59. sock.connect(unix_name)
  60. LOGGER.debug("Waited %.2f sec for '%s'" \
  61. % (itimeout-timeout, unix_name))
  62. return True
  63. except socket.error:
  64. timeout -= wait
  65. time.sleep(wait)
  66. LOGGER.debug("Waited %.2f seconds for '%s'" % (itimeout-timeout, unix_name))
  67. return False
  68. def load_file(path):
  69. """Reads the contents of a file and returns it."""
  70. try:
  71. with open(path, "r") as _file:
  72. data = _file.read()
  73. # Ignore comments and empty lines
  74. data = "\n".join([line for line in data.split("\n") \
  75. if line and not line.startswith("#")])
  76. except IOError:
  77. return ""
  78. else:
  79. return data
  80. def load_config(path):
  81. """Reads key=value formatted config files and returns a dictionary."""
  82. data = {}
  83. for key, value in [_line.split("=", 1) for _line in
  84. open(path, "r").readlines() if "=" in _line
  85. and not _line.startswith("#")]:
  86. key = key.strip()
  87. value = value.strip()
  88. data[key] = value.strip("'").strip('"')
  89. return data
  90. def write_to_file(filename, data=""):
  91. """Write data to file."""
  92. with open(filename, "w") as _file:
  93. _file.write(data)
  94. def create_directory(path):
  95. """Create missing directories in the path."""
  96. if not os.path.exists(path):
  97. os.makedirs(path)
  98. def create_link(src, dest, hardLink=False):
  99. """Create link from src to dest. """
  100. if hardLink:
  101. os.link(src, dest)
  102. else:
  103. os.symlink(src, dest)
  104. def mtime(filename):
  105. """Returns the last modification time of a file."""
  106. m_time = 0
  107. if os.path.exists(filename):
  108. m_time = os.path.getmtime(filename)
  109. return m_time
  110. def mdirtime(dirname):
  111. """Returns the last modification date of a directory."""
  112. # Directory mdate is not updated for file updates, so we check each file
  113. # Note that we dont recurse into subdirs, modules.d, env.d etc are all flat
  114. mtime_dir = mtime(dirname)
  115. for _file in os.listdir(dirname):
  116. mtime_file = mtime(os.path.join(dirname, _file))
  117. if mtime_file > mtime_dir:
  118. mtime_dir = mtime_file
  119. return mtime_dir
  120. def touch(filename):
  121. """Updates file modification date, create file if necessary"""
  122. try:
  123. if os.path.exists(filename):
  124. os.utime(filename, None)
  125. else:
  126. open(filename, "w").close()
  127. except IOError as error:
  128. if error.errno != 13:
  129. raise
  130. else:
  131. return False
  132. except OSError as error:
  133. if error.errno != 13:
  134. raise
  135. else:
  136. return False
  137. return True
  138. def get_kernel_option(option):
  139. """Get a dictionary of args for the given kernel command line option"""
  140. args = {}
  141. try:
  142. cmdline = open("/proc/cmdline").read().split()
  143. except IOError:
  144. return args
  145. for cmd in cmdline:
  146. if "=" in cmd:
  147. optname, optargs = cmd.split("=", 1)
  148. else:
  149. optname = cmd
  150. optargs = ""
  151. if optname == option:
  152. for arg in optargs.split(","):
  153. if ":" in arg:
  154. key, value = arg.split(":", 1)
  155. args[key] = value
  156. else:
  157. args[arg] = ""
  158. return args
  159. ##########################################
  160. # Reboot/shutdown related methods #
  161. ##########################################
  162. @skip_for_lxc_guests
  163. def load_kexec_image():
  164. """Attempts to load a kexec image if configured."""
  165. kexec_conf = "/etc/conf.d/kexec"
  166. loaded = False
  167. # Check for grubonce, inhibit kexec if grub has set
  168. # a default entry.
  169. grub_default = load_file("/boot/grub/default")
  170. if grub_default:
  171. grub_default = int(grub_default.rstrip("\x00"))
  172. grub_default = (grub_default ^ 0x4000 < 0x4000)
  173. if not grub_default and \
  174. os.path.exists("/usr/sbin/kexec") and \
  175. os.path.exists(kexec_conf):
  176. conf = load_config(kexec_conf)
  177. # Read config
  178. kexec_reboot = conf.get("KEXEC_REBOOT", "no") == "yes"
  179. kexec_shutdown = conf.get("KEXEC_SHUTDOWN", "no") == "yes"
  180. stage = sys.argv[1]
  181. if kexec_reboot and stage == "reboot" or \
  182. (kexec_shutdown and stage == "shutdown"):
  183. kernel_ver = os.uname()[2]
  184. kernel_suffix = None
  185. if "-" in kernel_ver:
  186. # e.g. -pae
  187. kernel_suffix = kernel_ver.split("-")[-1]
  188. default_kernel = "/boot/latest-kernel"
  189. default_initrd = "/boot/latest-initramfs"
  190. if not os.path.exists(default_kernel):
  191. # default to running kernel if no latest
  192. default_kernel = "/boot/kernel-%s" % kernel_ver
  193. elif kernel_suffix:
  194. default_kernel += "-%s" % kernel_suffix
  195. if not os.path.exists(default_initrd):
  196. default_initrd = "/boot/initramfs-%s" % kernel_ver
  197. elif kernel_suffix:
  198. default_initrd += "-%s" % kernel_suffix
  199. # Get relevant parameters
  200. append_params = conf.get("APPEND_CMDLINE_%s" % stage.upper())
  201. owrite_params = conf.get("OVERWRITE_CMDLINE_%s" % stage.upper())
  202. # Override the images if provided in conf
  203. kernel_image = conf.get("KERNEL_IMAGE", default_kernel)
  204. initrd_image = conf.get("INITRD_IMAGE", default_initrd)
  205. if owrite_params:
  206. run_quiet("/usr/sbin/kexec", "--load", kernel_image,
  207. "--initrd", initrd_image, "--command-line",
  208. owrite_params)
  209. elif append_params:
  210. run_quiet("/usr/sbin/kexec", "--load", kernel_image,
  211. "--initrd", initrd_image, "--reuse-cmdline",
  212. "--append", append_params)
  213. else:
  214. run_quiet("/usr/sbin/kexec", "--load", kernel_image,
  215. "--initrd", initrd_image, "--reuse-cmdline")
  216. try:
  217. loaded = open("/sys/kernel/kexec_loaded").read().strip() == "1"
  218. except OSError:
  219. pass
  220. return loaded
  221. @skip_for_lxc_guests
  222. def kexec_halt():
  223. """Try to reboot using kexec."""
  224. run_quiet("/usr/sbin/kexec", "-e")
  225. ####################################
  226. # Process spawning related methods #
  227. ####################################
  228. def capture(*cmd):
  229. """Captures the output of a command without running a shell."""
  230. process = subprocess.Popen(cmd,
  231. stdout=subprocess.PIPE,
  232. stderr=subprocess.PIPE)
  233. return process.communicate()
  234. def run_async(cmd, stdout=None, stderr=None):
  235. """Runs a command in background and redirects the outputs optionally."""
  236. fstdout = stdout if stdout else "/dev/null"
  237. fstderr = stderr if stderr else "/dev/null"
  238. return subprocess.Popen(cmd,
  239. stdout=open(fstdout, "w"),
  240. stderr=open(fstderr, "w")).pid
  241. def run(*cmd):
  242. """Runs a command without running a shell, only output errors."""
  243. return subprocess.call(cmd, stdout=open("/dev/null", "w"))
  244. def run_full(*cmd):
  245. """Runs a command without running a shell, with full output."""
  246. return subprocess.call(cmd)
  247. def run_quiet(*cmd):
  248. """Runs a command without running a shell and no output."""
  249. _file = open("/dev/null", "w")
  250. return subprocess.call(cmd, stdout=_file, stderr=_file)
  251. ################
  252. # Logger class #
  253. ################
  254. class Logger:
  255. """Logger class for dumping into /var/log/scomd.log."""
  256. def __init__(self):
  257. self.lines = ["\n"]
  258. def log(self, msg):
  259. """Logs the given message."""
  260. stamp = time.strftime("%b %d %H:%M:%S")
  261. # Strip color characters
  262. msg = re.sub("(\033.*?m)", "", msg)
  263. self.lines.append("[%.3f] %s %s\n" % (time.time(), stamp, msg))
  264. def debug(self, msg):
  265. """Log the message if debug is enabled."""
  266. if CONFIG.get("debug"):
  267. self.log(msg)
  268. def flush(self):
  269. """Flushes the log buffer."""
  270. try:
  271. self.lines.append("\n")
  272. with open("/var/log/scomd.log", "a") as _file:
  273. _file.writelines(self.lines)
  274. except IOError:
  275. UI.error(_("Cannot write scomd.log, read-only file system"))
  276. ################
  277. # Config class #
  278. ################
  279. class Config:
  280. """Configuration class which parses /proc/cmdline to get scomd options."""
  281. def __init__(self):
  282. self.fstab = None
  283. # Parse kernel version
  284. vers = os.uname()[2].replace("_", ".").replace("-", ".")
  285. self.kernel = vers.split(".")
  286. # Default options for scomd= in /proc/cmdline
  287. self.options = {
  288. "language" : "en",
  289. "clock" : "local",
  290. "clock_adjust" : "no",
  291. "tty_number" : "6",
  292. "lxc_guest" : "no",
  293. "keymap" : None,
  294. "debug" : True,
  295. "live" : False,
  296. "safe" : False,
  297. "forcefsck" : False,
  298. "head_start" : "",
  299. "services" : "",
  300. }
  301. # Load config file if exists
  302. if os.path.exists("/etc/conf.d/scomd"):
  303. self.options.update(load_config("/etc/conf.d/scomd"))
  304. # File system check can be requested with a file
  305. self.options["forcefsck"] = os.path.exists("/forcefsck")
  306. # First try
  307. self.parse_kernel_options()
  308. def parse_kernel_options(self):
  309. """Parse scomd= from kernel boot parameters."""
  310. # We need to mount /proc before accessing kernel options
  311. # This function is called after that, and finish parsing options
  312. # We dont print any messages before, cause language is not known
  313. options = get_kernel_option("scomd")
  314. # Fill in the options
  315. self.options["live"] = "thin" in options or \
  316. os.path.exists("/run/sulin/livemedia")
  317. for k in [_k for _k in list(options.keys()) if _k not in ("thin")]:
  318. self.options[k] = options[k] if options[k] else True
  319. # Normalize options
  320. # If language is unknown, default to English
  321. # Default language is Turkish, so this only used if someone
  322. # selected a language which isn't Turkish or English, and
  323. # in that case it is more likely they'll prefer English.
  324. lang = self.options["language"]
  325. if lang not in LANGUAGES:
  326. print("Unknown language option '%s'" % lang)
  327. lang = "en"
  328. self.options["language"] = lang
  329. # If no keymap is given, use the language's default
  330. if not self.options["keymap"]:
  331. self.options["keymap"] = LANGUAGES[lang].keymap
  332. def get(self, key):
  333. """Custom dictionary getter method."""
  334. try:
  335. return self.options[key]
  336. except KeyError:
  337. print("Unknown option '%s' requested" % key)
  338. time.sleep(3)
  339. def get_fstab_entry_with_mountpoint(self, mountpoint):
  340. """Returns /etc/fstab entry corresponding to the given mountpoint."""
  341. if not self.fstab:
  342. data = load_file("/etc/fstab").split("\n")
  343. self.fstab = [line.split() for line in data]
  344. for entry in self.fstab:
  345. if entry and len(entry) > 3 and entry[1] == mountpoint:
  346. return entry
  347. ##################
  348. # Plymouth class #
  349. ##################
  350. class Plymouth:
  351. """Plymouth class for visualizing init messages and plymouth SPLASH."""
  352. def __init__(self):
  353. """Plymouth constructor."""
  354. self.client = "/bin/plymouth"
  355. self.daemon = "/sbin/plymouthd"
  356. self.available = CONFIG.get("lxc_guest") != "yes" \
  357. and os.path.exists(self.client)
  358. self.running = self.available and not run_quiet(self.client, "--ping")
  359. def send_cmd(self, *cmd):
  360. """Send the client a command to pass to the daemon."""
  361. if self.running:
  362. return run_quiet(self.client, *cmd)
  363. def start_daemon(self):
  364. """Starts plymouth daemon."""
  365. if self.available:
  366. self.running = not run_quiet(self.daemon, "--mode=shutdown")
  367. def show_splash(self):
  368. """Shows splash screen."""
  369. self.send_cmd("show-splash")
  370. def hide_splash(self):
  371. """Hides splash screen."""
  372. self.send_cmd("hide-splash")
  373. def report_error(self):
  374. """Reports error."""
  375. self.send_cmd("report-error")
  376. def update(self, milestone):
  377. """Updates status milestones."""
  378. self.send_cmd("update", "--status=%s" % milestone)
  379. def rootfs_is_now_rw(self):
  380. """Notifies that rootfs is now rw."""
  381. self.send_cmd("update-root-fs", "--read-write")
  382. def quit(self, retain_splash=False):
  383. """Quits the daemon."""
  384. self.send_cmd("quit", "--retain-splash" if retain_splash else "")
  385. ############
  386. # Ui class #
  387. ############
  388. class Ui:
  389. """User Interface class to settle the console and fonts."""
  390. UNICODE_MAGIC = "\x1b%G"
  391. # constants from linux/kd.h
  392. KDSKBMODE = 0x4B45
  393. K_UNICODE = 0x03
  394. def __init__(self):
  395. self.colors = {'red' : '\x1b[31;01m', # BAD
  396. 'blue' : '\x1b[34;01m',
  397. 'cyan' : '\x1b[36;01m',
  398. 'gray' : '\x1b[30;01m',
  399. 'green' : '\x1b[32;01m', # GOOD
  400. 'light' : '\x1b[37;01m',
  401. 'yellow' : '\x1b[33;01m', # WARN
  402. 'magenta' : '\x1b[35;01m',
  403. 'reddark' : '\x1b[31;0m',
  404. 'bluedark' : '\x1b[34;0m',
  405. 'cyandark' : '\x1b[36;0m',
  406. 'graydark' : '\x1b[30;0m',
  407. 'greendark' : '\x1b[32;0m',
  408. 'magentadark': '\x1b[35;0m',
  409. 'normal' : '\x1b[0m'} # NORMAL
  410. def greet(self):
  411. """Dump release information, sets unicode mode."""
  412. print(self.UNICODE_MAGIC)
  413. if os.path.exists("/etc/sulin-release"):
  414. release = load_file("/etc/sulin-release").rstrip("\n")
  415. sys.stdout.write("\x1b[1m %s \x1b[0;36mhttp://www.sulin.org\x1b[0m" \
  416. % release)
  417. else:
  418. self.error(_("Cannot find /etc/sulin-release"))
  419. sys.stdout.write("\n")
  420. def info(self, msg):
  421. """Print the given message and log if debug enabled."""
  422. if CONFIG.get("debug"):
  423. LOGGER.log(msg)
  424. sys.stdout.write(" %s*%s %s\n" % (self.colors['green'],
  425. self.colors['normal'], msg.encode("utf-8")))
  426. def warn(self, msg):
  427. """Print the given message as a warning and log if debug enabled."""
  428. LOGGER.log(msg)
  429. sys.stdout.write(" %s*%s %s\n" % (self.colors['yellow'],
  430. self.colors['normal'], msg.encode("utf-8")))
  431. def error(self, msg):
  432. """Print the given message as an error and log if debug enabled."""
  433. SPLASH.report_error()
  434. LOGGER.log(msg)
  435. sys.stdout.write(" %s*%s %s\n" % (self.colors['red'],
  436. self.colors['normal'], msg.encode("utf-8")))
  437. def colorize(self, uicolor, msg):
  438. """Colorizes the given message."""
  439. return "%s%s%s" % (self.colors[uicolor], msg, self.colors['normal'])
  440. ##################
  441. # Language class #
  442. ##################
  443. class Language:
  444. """Dummy class to hold language informations."""
  445. def __init__(self, keymap, font, trans, locale):
  446. self.keymap = keymap
  447. self.font = font
  448. self.trans = trans
  449. self.locale = locale
  450. ########################################
  451. # Language and console related methods #
  452. ########################################
  453. LANGUAGES = {
  454. "ca" : Language("es", "iso01.16", "8859-1", "ca_ES.UTF-8"),
  455. "de" : Language("de", "iso01.16", "8859-1", "de_DE.UTF-8"),
  456. #"el" : Language("gr", "iso07u-16", "", "el_GR.UTF-8"),
  457. "en" : Language("us", "iso01.16", "8859-1", "en_US.UTF-8"),
  458. "es" : Language("es", "iso01.16", "8859-1", "es_ES.UTF-8"),
  459. "fr" : Language("fr", "iso01.16", "8859-1", "fr_FR.UTF-8"),
  460. "hu" : Language("hu", "lat2a-16", "8859-2", "hu_HU.UTF-8"),
  461. "it" : Language("it", "iso01.16", "8859-1", "it_IT.UTF-8"),
  462. "nl" : Language("nl", "iso01.16", "8859-1", "nl_NL.UTF-8"),
  463. "pl" : Language("pl", "iso02.16", "8859-2", "pl_PL.UTF-8"),
  464. "pt_BR" : Language("br-abnt2", "iso01.16", "8859-1", "pt_BR.UTF-8"),
  465. "ru" : Language("ru", "Cyr_a8x16", "8859-5", "ru_RU.UTF-8"),
  466. "sv" : Language("sv-latin1", "lat0-16", "8859-1", "sv_SE.UTF-8"),
  467. "tr" : Language("trq", "lat5u-16", "8859-9", "tr_TR.UTF-8"),
  468. }
  469. def set_console_parameters():
  470. """Setups encoding, font and mapping for console."""
  471. lang = CONFIG.get("language")
  472. keymap = CONFIG.get("keymap")
  473. language = LANGUAGES[lang]
  474. # Now actually set the values
  475. run("/usr/bin/kbd_mode", "-u")
  476. run_quiet("/bin/loadkeys", keymap)
  477. run("/usr/bin/setfont", "-f", language.font, "-m", language.trans)
  478. def set_system_language():
  479. """Sets the system language."""
  480. lang = CONFIG.get("language")
  481. keymap = CONFIG.get("keymap")
  482. language = LANGUAGES[lang]
  483. # Put them in /etc, so other programs like kdm can use them
  484. # without duplicating default->scomd.conf->kernel-option logic
  485. # we do here. Note that these are system-wide not per user,
  486. # and only for reading.
  487. create_directory("/etc/scomd")
  488. write_to_file("/etc/scomd/language", "%s\n" % lang)
  489. write_to_file("/etc/scomd/keymap", "%s\n" % keymap)
  490. write_to_file("/etc/scomd/locale", "%s\n" % language.locale)
  491. # Update environment if necessary
  492. content = "LANG=%s\nLC_ALL=%s\n" % (language.locale, language.locale)
  493. try:
  494. if content != load_file("/etc/env.d/03locale"):
  495. write_to_file("/etc/env.d/03locale", content)
  496. except IOError:
  497. UI.warn(_("/etc/env.d/03locale cannot be updated"))
  498. def load_translations():
  499. """Loads the translation catalogue for scomd."""
  500. global __trans
  501. global _
  502. lang = CONFIG.get("language")
  503. __trans = gettext.translation('scomd', languages=[lang], fallback=True)
  504. _ = __trans.gettext
  505. def set_unicode_mode():
  506. """Makes TTYs unicode compatible."""
  507. import fcntl
  508. lang = CONFIG.get("language")
  509. language = LANGUAGES[lang]
  510. for i in range(1, int(CONFIG.get("tty_number")) + 1):
  511. try:
  512. if os.path.exists("/dev/tty%s" % i):
  513. with open("/dev/tty%s" % i, "w") as _file:
  514. fcntl.ioctl(_file, UI.KDSKBMODE, UI.K_UNICODE)
  515. _file.write(UI.UNICODE_MAGIC)
  516. run("/usr/bin/setfont", "-f",
  517. language.font, "-m",
  518. language.trans, "-C", "/dev/tty%s" %i)
  519. except:
  520. UI.error(_("Could not set unicode mode on tty %d") % i)
  521. ######################################
  522. # Service management related methods #
  523. ######################################
  524. def fork_handler():
  525. """Callback which is passed to Popen as preexec_fn."""
  526. import termios
  527. import fcntl
  528. # Set umask to a sane value
  529. # (other and group has no write permission by default)
  530. os.umask(0o22)
  531. # Detach from controlling terminal
  532. try:
  533. tty_fd = os.open("/dev/tty", os.O_RDWR)
  534. fcntl.ioctl(tty_fd, termios.TIOCNOTTY)
  535. os.close(tty_fd)
  536. except OSError:
  537. pass
  538. # Close IO channels
  539. devnull_fd = os.open("/dev/null", os.O_RDWR)
  540. os.dup2(devnull_fd, 0)
  541. os.dup2(devnull_fd, 1)
  542. os.dup2(devnull_fd, 2)
  543. # Detach from process group
  544. os.setsid()
  545. def manage_service(service, command):
  546. """Starts/Stops the given service."""
  547. cmd = ["/bin/service", "--quiet", service, command]
  548. LOGGER.debug("%s service %s.." % (command, service))
  549. subprocess.Popen(cmd, close_fds=True, preexec_fn=fork_handler,
  550. stdout=subprocess.PIPE, stderr=subprocess.PIPE)
  551. LOGGER.debug("%s service %s..done" % (command, service))
  552. SPLASH.update(service)
  553. def get_service_list(bus, _all=False):
  554. """Requests and returns the list of system services through COMAR."""
  555. obj = bus.get_object("tr.org.sulin.scom", "/", introspect=False)
  556. services = obj.listModelApplications("System.Service",
  557. dbus_interface="tr.org.sulin.scom")
  558. if _all:
  559. return services
  560. else:
  561. enabled = set(os.listdir("/etc/scomd/services/enabled"))
  562. conditional = set(os.listdir("/etc/scomd/services/conditional"))
  563. return enabled.union(conditional).intersection(set(services))
  564. def start_services(extras=None):
  565. """Sends start signals to the required services through D-Bus."""
  566. import dbus
  567. os.setuid(0)
  568. try:
  569. bus = dbus.SystemBus()
  570. except dbus.exceptions.DBusException:
  571. UI.error(_("Cannot connect to DBus, services won't be started"))
  572. return
  573. if extras:
  574. # Start only the services given in extras
  575. for service in extras:
  576. try:
  577. manage_service(service, "start")
  578. except dbus.exceptions.DBusException:
  579. pass
  580. else:
  581. # Start network service first
  582. try:
  583. manage_service("NetworkManager", "ready")
  584. except Exception as error:
  585. UI.warn(_("Unable to start network:\n %s") % error)
  586. # Almost everything depends on logger, so start manually
  587. manage_service("rsyslog", "start")
  588. if not wait_bus("/dev/log", stream=False, timeout=15):
  589. UI.warn(_("Cannot start system logger"))
  590. # Mount remote filesystems if any
  591. mount_remote_filesystems()
  592. if not CONFIG.get("safe"):
  593. UI.info(_("Starting services"))
  594. services = get_service_list(bus)
  595. # Remove already started services
  596. services = set(services).difference(["rsyslog", "NetworkManager"])
  597. # Give login screen a headstart
  598. head_start = CONFIG.get("head_start")
  599. run_head_start = head_start and head_start in services
  600. # Decide whether we'll stop plymouth or not
  601. stop_plymouth = "off" in get_kernel_option("xorg") or \
  602. not run_head_start
  603. if run_head_start:
  604. manage_service(head_start, "ready")
  605. services.remove(head_start)
  606. # Run other services
  607. for service in services:
  608. manage_service(service, "ready")
  609. if stop_plymouth:
  610. # Stop plymouth
  611. SPLASH.quit(retain_splash=False)
  612. # Close the handle
  613. bus.close()
  614. @plymouth_update_milestone
  615. def stop_services():
  616. """Sends stop signals to all available services through D-Bus."""
  617. import dbus
  618. UI.info(_("Stopping services"))
  619. try:
  620. bus = dbus.SystemBus()
  621. except dbus.exceptions.DBusException:
  622. return
  623. for service in get_service_list(bus, _all=True):
  624. manage_service(service, "stop")
  625. # Close the handle
  626. bus.close()
  627. def prune_needs_action_package_list():
  628. """Clears the lists to hold needsServiceRestart and needsReboot updates."""
  629. for _file in ("/var/lib/spam/info/needsrestart",
  630. "/var/lib/spam/info/needsreboot"):
  631. if os.path.exists(_file):
  632. os.unlink(_file)
  633. ############################
  634. # D-Bus start/stop methods #
  635. ############################
  636. @plymouth_update_milestone
  637. def start_dbus():
  638. """Starts the D-Bus service."""
  639. os.setuid(0)
  640. UI.info(_("Starting %s") % "DBus")
  641. if not os.path.exists("/var/lib/dbus/machine-id"):
  642. run("/usr/bin/dbus-uuidgen", "--ensure")
  643. if not os.path.exists("/run/dbus"):
  644. create_directory("/run/dbus")
  645. run("/sbin/start-stop-daemon", "-b", "--start", "--quiet",
  646. "--pidfile", "/run/dbus/pid", "--exec", "/usr/bin/dbus-daemon",
  647. "--", "--system")
  648. wait_bus("/run/dbus/system_bus_socket")
  649. @plymouth_update_milestone
  650. def stop_dbus():
  651. """Stops the D-Bus service."""
  652. UI.info(_("Stopping %s") % "DBus")
  653. run("/sbin/start-stop-daemon", "--stop", "--quiet",
  654. "--pidfile", "/run/dbus/pid")
  655. #############################
  656. # UDEV management functions #
  657. #############################
  658. @skip_for_lxc_guests
  659. @plymouth_update_milestone
  660. def wait_for_udev_events():
  661. """Waits for udev events."""
  662. run("/sbin/udevadm", "settle", "--timeout=60")
  663. @skip_for_lxc_guests
  664. def trigger_failed_udev_events():
  665. """Trigger only the events which are failed during a previous run."""
  666. if os.path.exists("/dev/.udev/failed"):
  667. run("/sbin/udevadm", "trigger", "--type=failed", "--action=add")
  668. @skip_for_lxc_guests
  669. def copy_udev_rules():
  670. """Copies persistent udev rules from /dev into /etc/udev/rules."""
  671. import glob
  672. import shutil
  673. # Copy udevtrigger log file to /var/log
  674. if os.path.exists("/dev/.udevmonitor.log"):
  675. try:
  676. shutil.move("/dev/.udevmonitor.log", "/var/log/udevmonitor.log")
  677. except IOError:
  678. # Can't move it, no problem.
  679. pass
  680. # Moves any persistent rules from /dev/.udev to /etc/udev/rules.d
  681. for rule in glob.glob("/dev/.udev/tmp-rules--*"):
  682. dest = "/etc/udev/rules.d/%s" % \
  683. os.path.basename(rule).split("tmp-rules--")[1]
  684. try:
  685. shutil.move(rule, dest)
  686. except IOError:
  687. UI.warn(_("Can't move persistent udev rules from /dev/.udev"))
  688. @skip_for_lxc_guests
  689. @plymouth_update_milestone
  690. def start_udev():
  691. """Prepares the startup of udev daemon and starts it."""
  692. # When these files are missing, lots of trouble happens
  693. # so we double check their existence
  694. ######
  695. # we don't need that; scomd_tmpfiles and baselayout.conf creates this dir and symlink
  696. #create_directory("/run/shm")
  697. #create_link("/run/shm", "/dev/shm")
  698. # Start udev daemon
  699. UI.info(_("Starting udev"))
  700. run("/sbin/start-stop-daemon",
  701. "--start", "--quiet",
  702. "--exec", "/sbin/udevd", "--", "--daemon")
  703. # Create needed queue directory
  704. create_directory("/dev/.udev/queue/")
  705. # Log things that trigger does
  706. pid = run_async(["/sbin/udevadm", "monitor", "--env"],
  707. stdout="/dev/.udevmonitor.log")
  708. # Filling up /dev by triggering uevents
  709. UI.info(_("Populating /dev"))
  710. # Trigger events for all devices
  711. run("/sbin/udevadm", "trigger", "--type=subsystems", "--action=add")
  712. run("/sbin/udevadm", "trigger", "--type=devices", "--action=add")
  713. run("/sbin/udevadm", "trigger", "--type=devices", "--action=change")
  714. # Stop udevmonitor
  715. os.kill(pid, 15)
  716. @skip_for_lxc_guests
  717. @plymouth_update_milestone
  718. def stop_udev():
  719. """Stops udev daemon."""
  720. run("/sbin/start-stop-daemon",
  721. "--stop", "--exec", "/sbin/udevd")
  722. ##############################
  723. # Filesystem related methods #
  724. ##############################
  725. def update_mtab_for_root():
  726. """Calls mount -f to update mtab for a previous mount."""
  727. mount_failed_lock = 16
  728. if os.path.exists("/etc/mtab~"):
  729. try:
  730. UI.warn(_("Removing stale lock file /etc/mtab~"))
  731. os.unlink("/etc/mtab~")
  732. except OSError:
  733. UI.warn(_("Failed removing stale lock file /etc/mtab~"))
  734. return (run_quiet("/bin/mount", "-f", "/") != mount_failed_lock)
  735. @skip_for_lxc_guests
  736. @plymouth_update_milestone
  737. def check_root_filesystem():
  738. """Checks root filesystem with fsck if required."""
  739. if not CONFIG.get("live"):
  740. entry = CONFIG.get_fstab_entry_with_mountpoint("/")
  741. if not entry:
  742. UI.warn(_("/etc/fstab doesn't contain an entry "
  743. "for the root filesystem"))
  744. return
  745. if CONFIG.get("forcefsck") or (len(entry) > 5 and entry[5] != "0"):
  746. # Remount root filesystem ro for fsck without writing to mtab (-n)
  747. UI.info(_("Remounting root filesystem read-only"))
  748. run_quiet("/bin/mount", "-n", "-o", "remount,ro", "/")
  749. if CONFIG.get("forcefsck"):
  750. SPLASH.hide_splash()
  751. UI.info(_("Checking root filesystem (full check forced)"))
  752. # -y: Fix whatever the error is without user's intervention
  753. ret = run_full("/sbin/fsck", "-C", "-y", "-f", "/")
  754. # /forcefsck isn't deleted because check_filesystems needs it.
  755. # it'll be deleted in that function.
  756. else:
  757. UI.info(_("Checking root filesystem"))
  758. ret = run_full("/sbin/fsck", "-C", "-T", "-a", "/")
  759. if ret == 0:
  760. # No errors,just go on
  761. pass
  762. elif ret == 2 or ret == 3:
  763. # Actually 2 means that a reboot is required, fsck man page
  764. # doesn't mention about 3 but let's leave it as it's harmless.
  765. SPLASH.hide_splash()
  766. UI.warn(_("Filesystem repaired, but reboot needed!"))
  767. i = 0
  768. while i < 4:
  769. sys.stdout.write("\07")
  770. time.sleep(1)
  771. i += 1
  772. UI.warn(_("Rebooting in 10 seconds..."))
  773. time.sleep(10)
  774. UI.warn(_("Rebooting..."))
  775. run("/sbin/reboot", "-f")
  776. # Code should never reach here
  777. else:
  778. UI.error(_("Filesystem could not be repaired"))
  779. run_full("/sbin/sulogin")
  780. # Code should never reach here
  781. else:
  782. UI.info(_("Skipping root filesystem check (fstab's passno == 0)"))
  783. @skip_for_lxc_guests
  784. @plymouth_update_milestone
  785. def mount_root_filesystem():
  786. """Mounts root filesystem."""
  787. # Let's remount read/write again.
  788. UI.info(_("Remounting root filesystem read/write"))
  789. # We remount here without writing to mtab (-n)
  790. if run_quiet("/bin/mount", "-n", "-o", "remount,rw", "/") != 0:
  791. UI.error(_("Root filesystem could not be mounted read/write\n\
  792. You can either login below and manually check your filesytem(s) OR\n\
  793. restart your system, press F3 and select 'FS check' from boot menu\n"))
  794. # Fail if can't remount r/w
  795. run_full("/sbin/sulogin")
  796. # Fix mtab as we didn't update it yet
  797. try:
  798. # Double guard against IO exceptions
  799. write_to_file("/etc/mtab")
  800. except IOError:
  801. UI.warn(_("Couldn't synchronize /etc/mtab from /proc/mounts"))
  802. # This will actually try to update mtab for /. If it fails because
  803. # of a stale lock file, it will clear it and return.
  804. update_mtab_for_root()
  805. # Update mtab
  806. for entry in load_file("/proc/mounts").split("\n"):
  807. try:
  808. devpath = entry.split()[1]
  809. except IndexError:
  810. continue
  811. if CONFIG.get_fstab_entry_with_mountpoint(devpath):
  812. run("/bin/mount", "-f", "-o", "remount", devpath)
  813. @skip_for_lxc_guests
  814. @plymouth_update_milestone
  815. def check_filesystems():
  816. """Checks all the filesystems with fsck if required."""
  817. if not CONFIG.get("live"):
  818. UI.info(_("Checking all filesystems"))
  819. if CONFIG.get("forcefsck"):
  820. SPLASH.hide_splash()
  821. UI.info(_("A full fsck has been forced"))
  822. # -C: Display completion bars
  823. # -R: Skip the root file system
  824. # -A: Check all filesystems found in /etc/fstab
  825. # -a: Automatically repair without any questions
  826. # -f: Force checking even it's clean (e2fsck)
  827. ret = run_full("/sbin/fsck", "-C", "-R", "-A", "-a", "-f")
  828. # remove forcefsck file if it exists
  829. if os.path.exists("/forcefsck"):
  830. os.unlink("/forcefsck")
  831. else:
  832. # -T: Don't show the title on startup
  833. ret = run_full("/sbin/fsck", "-C", "-T", "-R", "-A", "-a")
  834. if ret == 0:
  835. pass
  836. elif ret >= 2 and ret <= 3:
  837. UI.warn(_("Filesystem errors corrected"))
  838. else:
  839. UI.error(_("Fsck could not correct all errors, "\
  840. "manual repair needed"))
  841. run_full("/sbin/sulogin")
  842. @skip_for_lxc_guests
  843. @plymouth_update_milestone
  844. def mount_local_filesystems():
  845. """Mounts local filesystems."""
  846. UI.info(_("Mounting local filesystems"))
  847. run("/bin/mount", "-at", "noproc,nocifs,nonfs,nonfs4,noncpfs")
  848. @skip_for_lxc_guests
  849. @plymouth_update_milestone
  850. def mount_tmpfs_run():
  851. df = os.popen('df 2>/dev/null', 'r')
  852. for line in df.readlines():
  853. line = line.strip()
  854. if line.startswith("tmpfs") and line.endswith("/run"):
  855. UI.info(_("Unmounting /run"))
  856. run("/bin/umount", "/run")
  857. break
  858. df.close()
  859. run_full("/bin/mount", "-t", "tmpfs", "-o", "nodev,nosuid,size=10%,mode=755", "tmpfs", "/run")
  860. c = Cgroupfs()
  861. def mount_remote_filesystems():
  862. """Mounts remote filesystems."""
  863. from sulin.fstabutils import Fstab
  864. fstab = Fstab()
  865. if fstab.contains_remote_mounts():
  866. UI.info(_("Mounting remote filesystems"))
  867. manage_service("netfs", "start")
  868. ################################################################################
  869. # Other system related methods for hostname setting, modules autoloading, etc. #
  870. ################################################################################
  871. @skip_for_lxc_guests
  872. def minimize_printk_log_level():
  873. """Decrease kernel console log level for cleaner boot."""
  874. write_to_file("/proc/sys/kernel/printk", "1")
  875. @skip_for_lxc_guests
  876. def run_sysctl():
  877. """Applies sysctl.conf rules."""
  878. run("/sbin/sysctl", "-q", "-p", "/etc/sysctl.conf")
  879. def set_hostname():
  880. """Sets the system's hostname."""
  881. khost = capture("/bin/hostname")[0].rstrip("\n")
  882. uhost = None
  883. if os.path.exists("/etc/env.d/01hostname"):
  884. data = load_file("/etc/env.d/01hostname")
  885. i = data.find('HOSTNAME="')
  886. if i != -1:
  887. j = data.find('"', i+10)
  888. if j != -1:
  889. uhost = data[i+10:j]
  890. if khost != "" and khost != "(none)":
  891. # kernel already got a hostname (pxeboot or something)
  892. host = khost
  893. else:
  894. # if nothing found, use the default hostname 'pisilinux'
  895. host = uhost if uhost else "pisilinux"
  896. if uhost and host != uhost:
  897. i = data.find('HOSTNAME="')
  898. if i != -1:
  899. j = data.find('"', i+10)
  900. if j != -1:
  901. data = data[:i+10] + host + data[j:]
  902. else:
  903. data = 'HOSTNAME="' + host + '"\n' + data
  904. write_to_file("/etc/env.d/01hostname", data)
  905. UI.info(_("Setting up hostname as '%s'") % UI.colorize("light", host))
  906. run("/bin/hostname", host)
  907. @skip_for_lxc_guests
  908. @plymouth_update_milestone
  909. def autoload_modules():
  910. """Traverses /etc/modules.autoload.d to autoload kernel modules if any."""
  911. if os.path.exists("/proc/modules"):
  912. import glob
  913. for _file in glob.glob("/etc/modules.autoload.d/kernel-%s*" \
  914. % CONFIG.kernel[0]):
  915. data = load_file(_file).split("\n")
  916. for module in data:
  917. run("/sbin/modprobe", "-q", "-b", module)
  918. @skip_for_lxc_guests
  919. def set_disk_parameters():
  920. """Sets disk parameters if hdparm is available."""
  921. if CONFIG.get("safe") or not os.path.exists("/etc/conf.d/hdparm"):
  922. return
  923. conf = load_config("/etc/conf.d/hdparm")
  924. if len(conf) > 0:
  925. UI.info(_("Setting disk parameters"))
  926. if "all" in conf:
  927. for name in os.listdir("/sys/block/"):
  928. if name.startswith("sd") and \
  929. len(name) == 3 and name not in conf:
  930. run_quiet("/sbin/hdparm", "%s" % conf["all"].split(),
  931. "/dev/%s" % name)
  932. for key, value in conf:
  933. if key != "all":
  934. run_quiet("/sbin/hdparm", "%s" % value.split(), "/dev/%s" % key)
  935. ################
  936. # Swap methods #
  937. ################
  938. @skip_for_lxc_guests
  939. @plymouth_update_milestone
  940. def enable_swap():
  941. """Calls swapon for all swaps in /etc/fstab."""
  942. UI.info(_("Activating swap space"))
  943. run("/sbin/swapon", "-a")
  944. @skip_for_lxc_guests
  945. @plymouth_update_milestone
  946. def disable_swap():
  947. """Calls swapoff after unmounting tmpfs."""
  948. # unmount unused tmpfs filesystems before swap
  949. # (tmpfs can be swapped and you can get a deadlock)
  950. run_quiet("/bin/umount", "-at", "tmpfs")
  951. UI.info(_("Deactivating swap space"))
  952. run_quiet("/sbin/swapoff", "-a")
  953. ##############################
  954. # Filesystem cleanup methods #
  955. ##############################
  956. def cleanup_run():
  957. """Cleans up /run upon boot."""
  958. UI.info(_("Cleaning up /run"))
  959. blacklist = ["utmp", "random-seed", "livemedia"]
  960. for root, dirs, files in os.walk("/run"):
  961. for _file in files:
  962. if _file not in blacklist:
  963. try:
  964. os.unlink(os.path.join(root, _file))
  965. except OSError:
  966. pass
  967. # Prune needsrestart and needsreboot files if any
  968. prune_needs_action_package_list()
  969. @plymouth_update_milestone
  970. def cleanup_tmp():
  971. """Cleans up /tmp upon boot."""
  972. UI.info(_("Cleaning up /tmp"))
  973. cleanup_list = (
  974. "/tmp/gpg-*",
  975. "/tmp/kde-*",
  976. "/tmp/kde4-*",
  977. "/tmp/kio*",
  978. "/tmp/ksocket-*",
  979. "/tmp/ksocket4-*",
  980. "/tmp/mc-*",
  981. "/tmp/pisi-*",
  982. "/tmp/pulse-*",
  983. "/tmp/quilt.*",
  984. "/tmp/ssh-*",
  985. "/tmp/.*-unix",
  986. "/tmp/.X*-lock"
  987. )
  988. # Remove directories
  989. os.system("rm -rf %s" % " ".join(cleanup_list))
  990. create_directory("/tmp/.ICE-unix")
  991. os.chown("/tmp/.ICE-unix", 0, 0)
  992. os.chmod("/tmp/.ICE-unix", 0o1777)
  993. create_directory("/tmp/.X11-unix")
  994. os.chown("/tmp/.X11-unix", 0, 0)
  995. os.chmod("/tmp/.X11-unix", 0o1777)
  996. ########################################
  997. # System time/Clock management methods #
  998. ########################################
  999. @skip_for_lxc_guests
  1000. @plymouth_update_milestone
  1001. def set_clock():
  1002. """Sets the system time according to /etc."""
  1003. UI.info(_("Setting system clock to hardware clock"))
  1004. # Default is UTC
  1005. options = "--utc"
  1006. if CONFIG.get("clock") != "UTC":
  1007. options = "--localtime"
  1008. # Default is no
  1009. if CONFIG.get("clock_adjust") == "yes":
  1010. adj = "--adjust"
  1011. if not touch("/etc/adjtime"):
  1012. adj = "--noadjfile"
  1013. elif os.stat("/etc/adjtime").st_size == 0:
  1014. write_to_file("/etc/adjtime", "0.0 0 0.0\n")
  1015. ret = capture("/sbin/hwclock", adj, options)
  1016. if ret[1] != '':
  1017. UI.error(_("Failed to adjust systematic "\
  1018. "drift of the hardware clock"))
  1019. ret = capture("/sbin/hwclock", "--hctosys", options)
  1020. if ret[1] != '':
  1021. UI.error(_("Failed to set system clock to hardware clock"))
  1022. @skip_for_lxc_guests
  1023. @plymouth_update_milestone
  1024. def save_clock():
  1025. """Saves the system time for further boots."""
  1026. if not CONFIG.get("live"):
  1027. options = "--utc"
  1028. if CONFIG.get("clock") != "UTC":
  1029. options = "--localtime"
  1030. UI.info(_("Syncing system clock to hardware clock"))
  1031. ret = capture("/sbin/hwclock", "--systohc", options)
  1032. if ret[1] != '':
  1033. UI.error(_("Failed to synchronize clocks"))
  1034. def stop_system():
  1035. """Stops the system."""
  1036. import shutil
  1037. def get_fs_entries():
  1038. """Parses and returns /proc/mounts entries."""
  1039. entries = []
  1040. # Igore API filesystems
  1041. vfs = ["proc", "devpts", "sysfs", "devtmpfs", "squashfs", \
  1042. "tmpfs", "rootfs", "debugfs", "cgroup", "configfs"]
  1043. for entry in load_file("/proc/mounts").split("\n"):
  1044. fields = entry.split()
  1045. if len(fields) > 2 and fields[2] not in vfs and \
  1046. fields[0] != "none" and fields[1] != "/":
  1047. entries.append(fields)
  1048. entries.sort(key=lambda x: x[1], reverse=True)
  1049. return entries
  1050. def remount_ro(force=False):
  1051. """Remounts the root filesystem read/only."""
  1052. rootfs = None
  1053. for entry in load_file("/proc/mounts").split("\n"):
  1054. fields = entry.split()
  1055. if len(fields) > 2 and fields[0] != "rootfs" \
  1056. and fields[1] == "/":
  1057. rootfs = fields
  1058. break
  1059. SPLASH.update("remount_ro")
  1060. SPLASH.quit(retain_splash=True)
  1061. ret = 0
  1062. if force:
  1063. ret += run_quiet("/bin/umount", "-n", "-r", rootfs[1])
  1064. else:
  1065. ret += run_quiet("/bin/mount", "-n", "-o",
  1066. "remount,ro", rootfs[1])
  1067. if ret:
  1068. run_quiet("/sbin/killall5", "-9")
  1069. return ret
  1070. # Stopping system
  1071. stop_services()
  1072. stop_dbus()
  1073. stop_udev()
  1074. save_hwclock()
  1075. disable_swap()
  1076. # write a reboot record to /var/log/wtmp before unmounting
  1077. run("/sbin/halt", "-w")
  1078. if CONFIG.get("lxc_guest") != "yes":
  1079. SPLASH.update("unmount_filesystems")
  1080. UI.info(_("Unmounting filesystems"))
  1081. for dev in get_fs_entries():
  1082. if run_quiet("/bin/umount", dev[1]) != 0:
  1083. # kill processes still using this mount
  1084. run_quiet("/bin/fuser", "-k", "-9", "-m", dev[1])
  1085. time.sleep(1)
  1086. run_quiet("/bin/umount", "-f", "-r", dev[1])
  1087. UI.info(_("Remounting remaining filesystems read-only"))
  1088. # We parse /proc/mounts but use umount, so this have to agree
  1089. shutil.copy("/proc/mounts", "/etc/mtab")
  1090. if remount_ro():
  1091. if remount_ro():
  1092. remount_ro(True)
  1093. ##################
  1094. # Exception hook #
  1095. ##################
  1096. def except_hook(e_type, e_value, e_trace):
  1097. """Hook that intercepts and handles exceptions."""
  1098. import traceback
  1099. sys.stdout.write("\n")
  1100. sys.stdout.write(_("An internal error occured. Please report to the bugs.pisilinux.org"
  1101. "with following information:"))
  1102. sys.stdout.write(str(e_type)); sys.stdout.write(str(e_value))
  1103. traceback.print_tb(e_trace)
  1104. sys.stdout.write("\n")
  1105. run_full("/sbin/sulogin")
  1106. ##################
  1107. # Global objects #
  1108. ##################
  1109. CONFIG = Config()
  1110. LOGGER = Logger()
  1111. SPLASH = Plymouth()
  1112. UI = Ui()
  1113. def main():
  1114. """Main entry point."""
  1115. signal.signal(signal.SIGINT, signal.SIG_IGN)
  1116. signal.signal(signal.SIGQUIT, signal.SIG_IGN)
  1117. signal.signal(signal.SIGTSTP, signal.SIG_IGN)
  1118. sys.excepthook = except_hook
  1119. os.umask(0o22)
  1120. # Setup path just in case
  1121. os.environ["PATH"] = "/bin:/sbin:/usr/bin:/usr/sbin:" + os.environ["PATH"]
  1122. # We can log the event with uptime information now
  1123. LOGGER.log("/sbin/scomd.py %s" % sys.argv[1])
  1124. # Activate i18n, we can print localized messages from now on
  1125. load_translations()
  1126. ### SYSINIT ###
  1127. if sys.argv[1] == "sysinit":
  1128. # This is who we are...
  1129. UI.greet()
  1130. # Now we know which language and keymap to use
  1131. set_console_parameters()
  1132. # Minimize dmesg noise
  1133. minimize_printk_log_level()
  1134. # Check root file system
  1135. check_root_filesystem()
  1136. # Mount root file system
  1137. mount_root_filesystem()
  1138. SPLASH.rootfs_is_now_rw()
  1139. # Set hostname
  1140. set_hostname()
  1141. # Load modules manually written in /etc/modules.autoload.d/kernel-x.y
  1142. autoload_modules()
  1143. # Check all filesystems
  1144. check_filesystems()
  1145. # Mount local filesystems
  1146. mount_local_filesystems()
  1147. mount_tmpfs_run()
  1148. # Activate swap space
  1149. enable_swap()
  1150. # Set disk parameters using hdparm
  1151. set_disk_parameters()
  1152. # Set the clock
  1153. load_hwclock()
  1154. # Set the system language
  1155. set_system_language()
  1156. # When we exit this runlevel, init will write a boot record to utmp
  1157. write_to_file("/run/utmp")
  1158. touch("/var/log/wtmp")
  1159. #create_directory("/run/lock")
  1160. #create_directory("/run/lock/subsys")
  1161. #create_directory("/run/pisilinux")
  1162. run("/bin/chgrp", "utmp", "/run/utmp", "/var/log/wtmp")
  1163. os.chmod("/run/utmp", 0o664)
  1164. os.chmod("/var/log/wtmp", 0o664)
  1165. # Create tmpfiles
  1166. UI.info(_("Creating tmpfiles"))
  1167. if not os.path.isdir("/run/tmpfiles.d"): create_directory("/run/tmpfiles.d")
  1168. run("/usr/bin/kmod", "static-nodes", "--format=tmpfiles", "--output=/run/tmpfiles.d/kmod.conf")
  1169. out = [line for line in capture("/sbin/scomd_tmpfiles.py", "--boot")[0].split("\n") if line.strip()]
  1170. if out: LOGGER.log("Errors during tmpfiles creation.\n\t%s" % "\n\t".join(out))
  1171. run("mount", "-t", "tmpfs", "tmpfs", "/dev/shm")
  1172. # Start udev and event triggering
  1173. start_udev()
  1174. # Grab persistent rules and udev.log file from /dev
  1175. #copy_udev_rules()
  1176. ### BOOT ###
  1177. elif sys.argv[1] == "boot":
  1178. SPLASH.update("boot_runlevel")
  1179. UI.info(_("Setting up localhost"))
  1180. run("/sbin/ifconfig", "lo", "127.0.0.1", "up")
  1181. run("/sbin/route", "add", "-net", "127.0.0.0",
  1182. "netmask", "255.0.0.0", "gw", "127.0.0.1", "dev", "lo")
  1183. run_sysctl()
  1184. # Prune needsrestart and needsreboot files if any
  1185. prune_needs_action_package_list()
  1186. #cleanup_run()
  1187. # Update environment variables according to the modification
  1188. # time of the relevant files
  1189. if mdirtime("/etc/env.d") > mtime("/etc/profile.env"):
  1190. UI.info(_("Updating environment variables"))
  1191. run("/sbin/update-environment")
  1192. # Cleanup /tmp
  1193. cleanup_tmp()
  1194. # Start DBUS
  1195. start_dbus()
  1196. # Set unicode properties for ttys
  1197. set_unicode_mode()
  1198. # Call udev settle
  1199. # trigger_failed_udev_events()
  1200. wait_for_udev_events()
  1201. ### DEFAULT ###
  1202. elif sys.argv[1] == "default":
  1203. SPLASH.update("default_runlevel")
  1204. # Source local.start
  1205. if not CONFIG.get("safe") and os.path.exists("/etc/conf.d/local.start"):
  1206. run("/bin/bash", "/etc/conf.d/local.start")
  1207. # Start services
  1208. start_services()
  1209. ### SINGLE ###
  1210. elif sys.argv[1] == "single":
  1211. stop_services()
  1212. ### REBOOT/SHUTDOWN ###
  1213. elif sys.argv[1] == "reboot" or sys.argv[1] == "shutdown":
  1214. SPLASH.start_daemon()
  1215. SPLASH.rootfs_is_now_rw()
  1216. SPLASH.show_splash()
  1217. # Log the operation before unmounting file systems
  1218. LOGGER.flush()
  1219. # Source local.stop
  1220. if not CONFIG.get("safe") and os.path.exists("/etc/conf.d/local.stop"):
  1221. run("/bin/bash", "/etc/conf.d/local.stop")
  1222. # Load kexec image if any before unmounting filesystems
  1223. should_kexec = load_kexec_image()
  1224. # Stop the system
  1225. stop_system()
  1226. # Try to reboot/shutdown using kexec
  1227. if should_kexec:
  1228. kexec_halt()
  1229. if sys.argv[1] == "reboot":
  1230. # Shut down all network interfaces just before halt or reboot,
  1231. # When halting the system do a poweroff. This is the default
  1232. # when halt is called as powerof. Don't write the wtmp record.
  1233. run("/sbin/reboot", "-idp")
  1234. # Force halt or reboot, don't call shutdown
  1235. run("/sbin/reboot", "-f")
  1236. else:
  1237. run("/sbin/halt", "-ihdp")
  1238. run("/sbin/halt", "-f")
  1239. # Control never reaches here
  1240. try:
  1241. LOGGER.flush()
  1242. except IOError:
  1243. pass
  1244. ############################
  1245. # Main program starts here #
  1246. ############################
  1247. if __name__ == "__main__":
  1248. if "profile" in get_kernel_option("scomd"):
  1249. import cProfile
  1250. cProfile.run("main()", "/dev/.scomd-%s.log" % sys.argv[1])
  1251. else:
  1252. main()