2 # -*- coding: utf-8 -*-
6 # Copyright © 2013-2018 Antergos
8 # This file is part of Cnchi.
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 2 of the License, or
13 # (at your option) any later version.
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.
20 # You should have received a copy of the GNU General Public License
21 # along with Cnchi; if not, write to the Free Software
22 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
25 """ Post-Installation process module. """
35 from installation import mkinitcpio
36 from installation import systemd_networkd
37 from installation.boot import loader
39 from installation.post_fstab import PostFstab
40 from installation.post_features import PostFeatures
41 from installation import services as srv
43 from misc.events import Events
44 import misc.gocryptfs as gocryptfs
45 from misc.run_cmd import call, chroot_call
47 import parted3.fs_module as fs
49 from lembrame.lembrame import Lembrame
51 # When testing, no _() is available
54 except NameError as err:
60 class PostInstallation():
61 """ Post-Installation process thread class """
62 POSTINSTALL_SCRIPT = 'postinstall.sh'
63 LOG_FOLDER = '/var/log/cnchi'
66 self, settings, callback_queue, mount_devices, fs_devices, ssd=None, blvm=False):
68 """ Initialize installation class """
69 self.settings = settings
70 self.events = Events(callback_queue)
72 self.pacman_conf_updated = False
74 self.method = self.settings.get('partition_mode')
75 self.desktop = self.settings.get('desktop').lower()
77 # This flag tells us if there is a lvm partition (from advanced install)
78 # If it's true we'll have to add the 'lvm2' hook to mkinitcpio
86 self.mount_devices = mount_devices
87 self.fs_devices = fs_devices
88 self.virtual_box = self.settings.get('is_vbox')
91 """ Copy Cnchi logs to new installation """
92 log_dest_dir = os.path.join(DEST_DIR, "var/log/cnchi")
93 os.makedirs(log_dest_dir, mode=0o755, exist_ok=True)
95 datetime = "{0}-{1}".format(time.strftime("%Y%m%d"),
96 time.strftime("%H%M%S"))
99 'cnchi', 'cnchi-alpm', 'postinstall', 'pacman']
101 for name in file_names:
102 src = os.path.join(PostInstallation.LOG_FOLDER, "{0}.log".format(name))
104 log_dest_dir, "{0}-{1}.log".format(name, datetime))
106 shutil.copy(src, dst)
107 except FileNotFoundError as err:
108 logging.warning("Can't copy %s log to %s: %s", src, dst, str(err))
109 except FileExistsError:
112 # Store install id for later use by antergos-pkgstats
113 with open(os.path.join(log_dest_dir, 'install_id'), 'w') as install_record:
114 install_id = self.settings.get('install_id')
117 install_record.write(install_id)
120 def copy_network_config():
121 """ Copies Network Manager configuration """
122 source_nm = "/etc/NetworkManager/system-connections/"
123 target_nm = os.path.join(
124 DEST_DIR, "etc/NetworkManager/system-connections")
126 # Sanity checks. We don't want to do anything if a network
127 # configuration already exists on the target
128 if os.path.exists(source_nm) and os.path.exists(target_nm):
129 for network in os.listdir(source_nm):
131 if network == "LTSP":
134 source_network = os.path.join(source_nm, network)
135 target_network = os.path.join(target_nm, network)
137 if os.path.exists(target_network):
141 shutil.copy(source_network, target_network)
142 except FileNotFoundError:
144 "Can't copy network configuration files, file %s not found", source_network)
145 except FileExistsError:
148 def set_scheduler(self):
149 """ Copies udev rule for SSDs """
150 rule_src = os.path.join(
151 self.settings.get('cnchi'),
152 'scripts/60-schedulers.rules')
153 rule_dst = os.path.join(
155 "etc/udev/rules.d/60-schedulers.rules")
157 shutil.copy2(rule_src, rule_dst)
158 os.chmod(rule_dst, 0o755)
159 except FileNotFoundError:
161 "Cannot copy udev rule for SSDs, file %s not found.",
163 except FileExistsError:
167 def change_user_password(user, new_password):
168 """ Changes the user's password """
169 shadow_password = crypt.crypt(new_password, crypt.mksalt())
170 chroot_call(['usermod', '-p', shadow_password, user])
173 def auto_timesetting():
174 """ Set hardware clock """
175 cmd = ["hwclock", "--systohc", "--utc"]
178 shutil.copy2("/etc/adjtime", os.path.join(DEST_DIR, "etc/"))
179 except FileNotFoundError:
180 logging.warning("File /etc/adjtime not found!")
181 except FileExistsError:
185 def update_pacman_conf():
186 """ Add Antergos and multilib repos """
187 """ Now, remove antergos repo, and add Reborn-OS repo (Rafael) """
188 path = os.path.join(DEST_DIR, "etc/pacman.conf")
189 if os.path.exists(path):
190 with open(path) as pacman_file:
191 paclines = pacman_file.readlines()
193 mode = os.uname()[-1]
194 multilib_open = False
196 with open(path, 'w') as pacman_file:
197 for pacline in paclines:
198 if mode == "x86_64" and pacline == '#[multilib]\n':
200 pacline = '[multilib]\n'
201 elif mode == 'x86_64' and multilib_open and pacline.startswith('#Include ='):
202 pacline = pacline[1:]
203 multilib_open = False
204 elif pacline == '#[testing]\n':
205 antlines = '\n#[Reborn-OS-staging]\n'
206 antlines += '#SigLevel = Optional TrustAll\n'
207 antlines += '#Server = http://repo-de.rebornos.org/Reborn-OS/\n\n'
208 antlines += '[Reborn-OS]\n'
209 antlines += 'SigLevel = Optional TrustAll\n'
210 antlines += 'Include = /etc/pacman.d/reborn-mirrorlist\n\n'
211 pacman_file.write(antlines)
213 pacman_file.write(pacline)
215 logging.warning("Can't find pacman configuration file")
218 def uncomment_locale_gen(locale):
219 """ Uncomment selected locale in /etc/locale.gen """
221 path = os.path.join(DEST_DIR, "etc/locale.gen")
223 if os.path.exists(path):
224 with open(path) as gen:
225 text = gen.readlines()
227 with open(path, "w") as gen:
229 if locale in line and line[0] == "#":
230 # remove trailing '#'
234 logging.error("Can't find locale.gen file")
237 def fix_thermald_service():
238 """ Adds --ignore-cpuid-check to thermald service file """
239 path = os.path.join(DEST_DIR, "usr/lib/systemd/system/thermald.service")
240 if os.path.exists(path):
241 with open(path, 'r') as fin:
242 lines = fin.readlines()
243 for index, line in enumerate(lines):
244 if line.startswith("ExecStart") and "--ignore-cpuid-check" not in line:
245 lines[index] += " --ignore-cpuid-check"
246 with open(path, 'w') as fout:
247 fout.writelines(lines)
249 def setup_display_manager(self):
250 """ Configures LightDM desktop manager, including autologin. """
251 txt = _("Configuring LightDM desktop manager...")
252 self.events.add('info', txt)
254 if self.desktop in desktop_info.SESSIONS:
255 session = desktop_info.SESSIONS[self.desktop]
259 username = self.settings.get('user_name')
260 autologin = not self.settings.get('require_password')
262 lightdm_greeter = "lightdm-webkit2-greeter"
264 lightdm_conf_path = os.path.join(DEST_DIR, "etc/lightdm/lightdm.conf")
266 # Setup LightDM as Desktop Manager
267 with open(lightdm_conf_path) as lightdm_conf:
268 text = lightdm_conf.readlines()
270 with open(lightdm_conf_path, "w") as lightdm_conf:
273 # Enable automatic login
274 if '#autologin-user=' in line:
275 line = 'autologin-user={0}\n'.format(username)
276 if '#autologin-user-timeout=0' in line:
277 line = 'autologin-user-timeout=0\n'
278 # Set correct DE session
279 if '#user-session=default' in line:
280 line = 'user-session={0}\n'.format(session)
281 # Set correct greeter
282 if '#greeter-session=example-gtk-gnome' in line:
283 line = 'greeter-session={0}\n'.format(lightdm_greeter)
284 if 'session-wrapper' in line:
285 line = 'session-wrapper=/etc/lightdm/Xsession\n'
286 lightdm_conf.write(line)
287 txt = _("LightDM display manager configuration completed.")
289 except FileNotFoundError:
290 txt = _("Error while trying to configure the LightDM display manager")
294 def alsa_mixer_setup():
295 """ Sets ALSA mixer settings """
298 "Master 70% unmute", "Front 70% unmute", "Side 70% unmute", "Surround 70% unmute",
299 "Center 70% unmute", "LFE 70% unmute", "Headphone 70% unmute", "Speaker 70% unmute",
300 "PCM 70% unmute", "Line 70% unmute", "External 70% unmute", "FM 50% unmute",
301 "Master Mono 70% unmute", "Master Digital 70% unmute", "Analog Mix 70% unmute",
302 "Aux 70% unmute", "Aux2 70% unmute", "PCM Center 70% unmute", "PCM Front 70% unmute",
303 "PCM LFE 70% unmute", "PCM Side 70% unmute", "PCM Surround 70% unmute",
304 "Playback 70% unmute", "PCM,1 70% unmute", "DAC 70% unmute", "DAC,0 70% unmute",
305 "DAC,1 70% unmute", "Synth 70% unmute", "CD 70% unmute", "Wave 70% unmute",
306 "Music 70% unmute", "AC97 70% unmute", "Analog Front 70% unmute",
307 "VIA DXS,0 70% unmute", "VIA DXS,1 70% unmute", "VIA DXS,2 70% unmute",
308 "VIA DXS,3 70% unmute", "Mic 70% mute", "IEC958 70% mute",
309 "Master Playback Switch on", "Master Surround on",
310 "SB Live Analog/Digital Output Jack off", "Audigy Analog/Digital Output Jack off"]
312 for alsa_command in alsa_commands:
313 cmd = ["amixer", "-q", "-c", "0", "sset"]
314 cmd.extend(alsa_command.split())
318 logging.debug("Saving ALSA settings...")
319 chroot_call(['alsactl', 'store'])
320 logging.debug("ALSA settings saved.")
323 def set_fluidsynth():
324 """ Sets fluidsynth configuration file """
325 fluid_path = os.path.join(DEST_DIR, "etc/conf.d/fluidsynth")
326 if os.path.exists(fluid_path):
327 audio_system = "alsa"
328 pulseaudio_path = os.path.join(DEST_DIR, "usr/bin/pulseaudio")
329 if os.path.exists(pulseaudio_path):
330 audio_system = "pulse"
331 with open(fluid_path, "w") as fluid_conf:
332 fluid_conf.write('# Created by Cnchi, Antergos installer\n')
333 txt = 'SYNTHOPTS="-is -a {0} -m alsa_seq -r 48000"\n\n'
334 txt = txt.format(audio_system)
335 fluid_conf.write(txt)
338 def patch_user_dirs_update_gtk():
339 """ Patches user-dirs-update-gtk.desktop so it is run in
340 XFCE, MATE and Cinnamon """
341 path = os.path.join(DEST_DIR, "etc/xdg/autostart/user-dirs-update-gtk.desktop")
342 if os.path.exists(path):
343 with open(path, 'r') as user_dirs:
344 lines = user_dirs.readlines()
345 with open(path, 'w') as user_dirs:
347 if "OnlyShowIn=" in line:
348 line = "OnlyShowIn=GNOME;LXDE;Unity;XFCE;MATE;Cinnamon\n"
349 user_dirs.write(line)
351 def set_keymap(self):
352 """ Set X11 and console keymap """
353 keyboard_layout = self.settings.get("keyboard_layout")
354 keyboard_variant = self.settings.get("keyboard_variant")
355 # localectl set-x11-keymap es cat
356 cmd = ['localectl', 'set-x11-keymap', keyboard_layout]
358 cmd.append(keyboard_variant)
359 # Systemd based tools like localectl do not work inside a chroot
360 # This will set correct keymap to live media, we will copy
361 # the created files to destination
363 # Copy 00-keyboard.conf and vconsole.conf files to destination
364 path = os.path.join(DEST_DIR, "etc/X11/xorg.conf.d")
365 os.makedirs(path, mode=0o755, exist_ok=True)
366 files = ["/etc/X11/xorg.conf.d/00-keyboard.conf", "/etc/vconsole.conf"]
369 if os.path.exists(src):
370 dst = os.path.join(DEST_DIR, src[1:])
371 shutil.copy(src, dst)
372 logging.debug("%s copied.", src)
373 except FileNotFoundError:
374 logging.error("File %s not found in live media", src)
375 except FileExistsError:
377 except shutil.Error as err:
381 def get_installed_zfs_version():
382 """ Get installed zfs version """
383 zfs_version = "0.6.5.4"
384 path = "/install/usr/src"
385 for file_name in os.listdir(path):
386 if file_name.startswith("zfs") and not file_name.startswith("zfs-utils"):
388 zfs_version = file_name.split("-")[1]
390 "Installed zfs module's version: %s", zfs_version)
392 logging.warning("Can't get zfs version from %s", file_name)
396 def get_installed_kernel_versions():
397 """ Get installed kernel versions """
399 path = "/install/usr/lib/modules"
400 for file_name in os.listdir(path):
401 if not file_name.startswith("extramodules"):
402 kernel_versions.append(file_name)
403 return kernel_versions
405 def set_desktop_settings(self):
406 """ Runs postinstall.sh that sets DE settings
407 Postinstall script uses arch-chroot, so we don't have to worry
408 about /proc, /dev, ... """
409 logging.debug("Running Cnchi post-install script")
410 keyboard_layout = self.settings.get("keyboard_layout")
411 keyboard_variant = self.settings.get("keyboard_variant")
412 # Call post-install script to fine tune our setup
413 script_path_postinstall = os.path.join(
414 self.settings.get('cnchi'),
416 PostInstallation.POSTINSTALL_SCRIPT)
419 script_path_postinstall,
420 self.settings.get('user_name'),
423 self.settings.get("locale"),
424 str(self.virtual_box),
427 # Keyboard variant is optional
429 cmd.append(keyboard_variant)
431 call(cmd, timeout=300)
432 logging.debug("Post install script completed successfully.")
435 def modify_makepkg():
436 """ Modify the makeflags to allow for threading.
437 Use threads for xz compression. """
438 makepkg_conf_path = os.path.join(DEST_DIR, 'etc/makepkg.conf')
439 if os.path.exists(makepkg_conf_path):
440 with open(makepkg_conf_path, 'r') as makepkg_conf:
441 contents = makepkg_conf.readlines()
442 with open(makepkg_conf_path, 'w') as makepkg_conf:
443 for line in contents:
444 if '#MAKEFLAGS' in line:
445 line = 'MAKEFLAGS="-j$(nproc)"\n'
446 elif 'COMPRESSXZ' in line:
447 line = 'COMPRESSXZ=(xz -c -z - --threads=0)\n'
448 makepkg_conf.write(line)
450 def setup_user(self):
451 """ Set user parameters """
452 username = self.settings.get('user_name')
453 fullname = self.settings.get('user_fullname')
454 password = self.settings.get('user_password')
455 hostname = self.settings.get('hostname')
457 sudoers_dir = os.path.join(DEST_DIR, "etc/sudoers.d")
458 if not os.path.exists(sudoers_dir):
459 os.mkdir(sudoers_dir, 0o710)
460 sudoers_path = os.path.join(sudoers_dir, "10-installer")
462 with open(sudoers_path, "w") as sudoers:
463 sudoers.write('{0} ALL=(ALL) ALL\n'.format(username))
464 os.chmod(sudoers_path, 0o440)
465 logging.debug("Sudo configuration for user %s done.", username)
466 except IOError as io_error:
467 # Do not fail if can't write 10-installer file.
468 # Something bad must be happening, though.
469 logging.error(io_error)
473 default_groups = 'wheel'
476 # Why there is no vboxusers group? Add it ourselves.
477 chroot_call(['groupadd', 'vboxusers'])
478 default_groups += ',vboxusers,vboxsf'
479 srv.enable_services(["vboxservice"])
481 if self.settings.get('require_password') is False:
482 # Prepare system for autologin.
483 # LightDM needs the user to be in the autologin group.
484 chroot_call(['groupadd', 'autologin'])
485 default_groups += ',autologin'
488 'useradd', '--create-home',
489 '--shell', '/bin/bash',
490 '--groups', default_groups,
493 logging.debug("User %s added.", username)
495 self.change_user_password(username, password)
497 chroot_call(['chfn', '-f', fullname, username])
498 home_dir = os.path.join("/home", username)
499 cmd = ['chown', '-R', '{0}:{0}'.format(username), home_dir]
503 hostname_path = os.path.join(DEST_DIR, "etc/hostname")
504 if not os.path.exists(hostname_path):
505 with open(hostname_path, "w") as hostname_file:
506 hostname_file.write(hostname)
508 logging.debug("Hostname set to %s", hostname)
510 # User password is the root password
511 self.change_user_password('root', password)
512 logging.debug("Set the same password to root.")
515 avatar = self.settings.get('user_avatar')
516 if avatar and os.path.exists(avatar):
520 'var/lib/AccountsService/icons',
522 shutil.copy(avatar, dst)
523 except FileNotFoundError:
524 logging.warning("Can't copy %s log to %s", avatar, dst)
525 except FileExistsError:
528 ## Encrypt user's home directory if requested
529 if self.settings.get('encrypt_home'):
530 self.events.add('info', _("Encrypting user home dir..."))
531 gocryptfs.setup(username, "users", DEST_DIR, password)
532 logging.debug("User home dir encrypted")
536 """ Enable colors and syntax highlighting in nano editor """
537 nanorc_path = os.path.join(DEST_DIR, 'etc/nanorc')
538 if os.path.exists(nanorc_path):
540 "Enabling colors and syntax highlighting in nano editor")
541 with open(nanorc_path, 'a') as nanorc:
543 nanorc.write('# Added by Cnchi (Antergos Installer)\n')
544 nanorc.write('set titlecolor brightwhite,blue\n')
545 nanorc.write('set statuscolor brightwhite,green\n')
546 nanorc.write('set numbercolor cyan\n')
547 nanorc.write('set keycolor cyan\n')
548 nanorc.write('set functioncolor green\n')
549 nanorc.write('include "/usr/share/nano/*.nanorc"\n')
551 def rebuild_zfs_modules(self):
552 """ Sometimes dkms tries to build the zfs module before spl. """
553 self.events.add('info', _("Building zfs modules..."))
554 zfs_version = self.get_installed_zfs_version()
555 spl_module = 'spl/{}'.format(zfs_version)
556 zfs_module = 'zfs/{}'.format(zfs_version)
557 kernel_versions = self.get_installed_kernel_versions()
559 for kernel_version in kernel_versions:
561 "Installing zfs v%s modules for kernel %s", zfs_version, kernel_version)
563 ['dkms', 'install', spl_module, '-k', kernel_version])
565 ['dkms', 'install', zfs_module, '-k', kernel_version])
567 # No kernel version found, try to install for current kernel
569 "Installing zfs v%s modules for current kernel.", zfs_version)
570 chroot_call(['dkms', 'install', spl_module])
571 chroot_call(['dkms', 'install', zfs_module])
573 def pamac_setup(self):
574 """ Enable AUR in pamac if AUR feature selected """
575 pamac_conf = os.path.join(DEST_DIR, 'etc/pamac.conf')
576 if os.path.exists(pamac_conf) and self.settings.get('feature_aur'):
577 logging.debug("Enabling AUR options in pamac")
578 with open(pamac_conf, 'r') as pamac_conf_file:
579 file_data = pamac_conf_file.read()
580 file_data = file_data.replace("#EnableAUR", "EnableAUR")
581 file_data = file_data.replace(
582 "#SearchInAURByDefault", "SearchInAURByDefault")
583 file_data = file_data.replace(
584 "#CheckAURUpdates", "CheckAURUpdates")
585 with open(pamac_conf, 'w') as pamac_conf_file:
586 pamac_conf_file.write(file_data)
589 def setup_timesyncd():
590 """ Setups and enables time sync service """
591 timesyncd_path = os.path.join(DEST_DIR, "etc/systemd/timesyncd.conf")
593 with open(timesyncd_path, 'w') as timesyncd:
594 timesyncd.write("[Time]\n")
595 timesyncd.write("NTP=0.arch.pool.ntp.org 1.arch.pool.ntp.org "
596 "2.arch.pool.ntp.org 3.arch.pool.ntp.org\n")
597 timesyncd.write("FallbackNTP=0.pool.ntp.org 1.pool.ntp.org "
598 "0.fr.pool.ntp.org\n")
599 except FileNotFoundError as err:
600 logging.warning("Can't find %s file: %s", timesyncd_path, err)
601 chroot_call(['systemctl', '-fq', 'enable',
602 'systemd-timesyncd.service'])
604 def check_btrfs(self):
605 """ Checks if any device will be using btrfs """
606 for mount_point in self.mount_devices:
607 partition_path = self.mount_devices[mount_point]
608 uuid = fs.get_uuid(partition_path)
609 if uuid and partition_path in self.fs_devices:
610 myfmt = self.fs_devices[partition_path]
615 def configure_system(self, hardware_install):
616 """ Final install steps.
617 Set clock, language, timezone, run mkinitcpio,
618 populate pacman keyring, setup systemd services, ... """
620 self.events.add('pulse', 'start')
621 self.events.add('info', _("Configuring your new system"))
623 auto_fstab = PostFstab(
624 self.method, self.mount_devices, self.fs_devices, self.ssd, self.settings)
626 if auto_fstab.root_uuid:
627 self.settings.set('ruuid', auto_fstab.root_uuid)
628 logging.debug("fstab file generated.")
630 # Check if we have any btrfs device
631 if self.check_btrfs():
632 self.settings.set('btrfs', True)
634 # If SSD was detected copy udev rule for deadline scheduler
637 logging.debug("SSD udev rule copied successfully")
639 # Copy configured networks in Live medium to target system
640 if self.settings.get("network_manager") == "NetworkManager":
641 self.copy_network_config()
643 if self.desktop == "base":
644 # Setup systemd-networkd for systems that won't use the
645 # networkmanager or connman daemons (atm it's just base install)
646 # Enable systemd_networkd services
647 # https://github.com/Antergos/Cnchi/issues/332#issuecomment-108745026
648 srv.enable_services(["systemd-networkd", "systemd-resolved"])
649 # Setup systemd_networkd
650 # TODO: Ask user for SSID and passphrase if a wireless link is
651 # found (here or inside systemd_networkd.setup() ?)
652 systemd_networkd.setup()
654 logging.debug("Network configuration done.")
657 mirrorlist_src_path = '/etc/pacman.d/mirrorlist'
658 mirrorlist_dst_path = os.path.join(DEST_DIR, 'etc/pacman.d/mirrorlist')
660 shutil.copy2(mirrorlist_src_path, mirrorlist_dst_path)
661 logging.debug("Mirror list copied.")
662 except FileNotFoundError:
664 "Can't copy mirrorlist file. File %s not found",
666 except FileExistsError:
667 logging.warning("File %s already exists.", mirrorlist_dst_path)
669 # Add Antergos repo to /etc/pacman.conf
670 self.update_pacman_conf()
671 self.pacman_conf_updated = True
672 logging.debug("pacman.conf has been created successfully")
674 # Enable some useful services
676 if self.desktop != "base":
677 # In base there's no desktop manager ;)
678 services.append(self.settings.get("desktop_manager"))
679 # In base we use systemd-networkd (setup already done above)
680 services.append(self.settings.get("network_manager"))
681 # If bumblebee (optimus cards) is installed, enable it
682 if os.path.exists(os.path.join(DEST_DIR, "usr/lib/systemd/system/bumblebeed.service")):
683 services.extend(["bumblebee"])
684 services.extend(["ModemManager", "haveged"])
685 if self.method == "zfs":
686 # Beginning with ZOL version 0.6.5.8 the ZFS service unit files have
687 # been changed so that you need to explicitly enable any ZFS services
689 services.extend(["zfs.target", "zfs-import-cache", "zfs-mount"])
690 srv.enable_services(services)
692 # Enable timesyncd service
693 if self.settings.get("use_timesyncd"):
694 self.setup_timesyncd()
697 zone = self.settings.get("timezone_zone")
699 zoneinfo_path = os.path.join("/usr/share/zoneinfo", zone)
700 localtime_path = "/etc/localtime"
701 chroot_call(['ln', '-sf', zoneinfo_path, localtime_path])
702 logging.debug("Timezone set to %s", zoneinfo_path)
705 "Can't read selected timezone! Will leave it to UTC.")
707 # Configure detected hardware
708 # NOTE: Because hardware can need extra repos, this code must run
709 # always after having called the update_pacman_conf method
710 if self.pacman_conf_updated and hardware_install:
712 logging.debug("Running hardware drivers post-install jobs...")
713 hardware_install.post_install(DEST_DIR)
714 except Exception as ex:
715 template = "Error in hardware module. " \
716 "An exception of type {0} occured. Arguments:\n{1!r}"
717 message = template.format(type(ex).__name__, ex.args)
718 logging.error(message)
723 locale = self.settings.get("locale")
724 self.events.add('info', _("Generating locales..."))
725 self.uncomment_locale_gen(locale)
726 chroot_call(['locale-gen'])
727 locale_conf_path = os.path.join(DEST_DIR, "etc/locale.conf")
728 with open(locale_conf_path, "w") as locale_conf:
729 locale_conf.write('LANG={0}\n'.format(locale))
730 locale_conf.write('LC_COLLATE={0}\n'.format(locale))
732 # environment_path = os.path.join(DEST_DIR, "etc/environment")
733 # with open(environment_path, "w") as environment:
734 # environment.write('LANG={0}\n'.format(locale))
736 self.events.add('info', _("Adjusting hardware clock..."))
737 self.auto_timesetting()
739 self.events.add('info', _("Configuring keymap..."))
742 # Install configs for root
743 chroot_call(['cp', '-av', '/etc/skel/.', '/root/'])
745 self.events.add('info', _("Configuring hardware..."))
747 # Copy generated xorg.conf to target
748 if os.path.exists("/etc/X11/xorg.conf"):
749 src = "/etc/X11/xorg.conf"
750 dst = os.path.join(DEST_DIR, 'etc/X11/xorg.conf')
751 shutil.copy2(src, dst)
754 # self.alsa_mixer_setup()
755 #logging.debug("Updated Alsa mixer settings")
758 # if os.path.exists(os.path.join(DEST_DIR, "usr/bin/pulseaudio-ctl")):
759 # chroot_run(['pulseaudio-ctl', 'normal'])
761 # Set fluidsynth audio system (in our case, pulseaudio)
762 self.set_fluidsynth()
763 logging.debug("Updated fluidsynth configuration file")
765 # Workaround for pacman-key bug FS#45351
766 # https://bugs.archlinux.org/task/45351
767 # We have to kill gpg-agent because if it stays around we can't
768 # reliably unmount the target partition.
769 logging.debug("Stopping gpg agent...")
770 chroot_call(['killall', '-9', 'gpg-agent'])
772 # FIXME: Temporary workaround for spl and zfs packages
773 if self.method == "zfs":
774 self.rebuild_zfs_modules()
776 # Let's start without using hwdetect for mkinitcpio.conf.
777 # It should work out of the box most of the time.
778 # This way we don't have to fix deprecated hooks.
779 # NOTE: With LUKS or LVM maybe we'll have to fix deprecated hooks.
780 self.events.add('info', _("Configuring System Startup..."))
781 mkinitcpio.run(DEST_DIR, self.settings, self.mount_devices, self.blvm)
783 # Patch user-dirs-update-gtk.desktop
784 self.patch_user_dirs_update_gtk()
785 logging.debug("File user-dirs-update-gtk.desktop patched.")
787 # Set lightdm config including autologin if selected
788 if self.desktop != "base":
789 self.setup_display_manager()
791 # Configure user features (firewall, libreoffice language pack, ...)
792 #self.setup_features()
793 post_features = PostFeatures(DEST_DIR, self.settings)
794 post_features.setup()
796 # Install boot loader (always after running mkinitcpio)
797 if self.settings.get('bootloader_install'):
799 self.events.add('info', _("Installing bootloader..."))
800 boot_loader = loader.Bootloader(
804 boot_loader.install()
805 except Exception as ex:
806 template = "Cannot install bootloader. " \
807 "An exception of type {0} occured. Arguments:\n{1!r}"
808 message = template.format(type(ex).__name__, ex.args)
809 logging.error(message)
811 # Create an initial database for mandb (slow)
812 #self.events.add('info', _("Updating man pages..."))
813 #chroot_call(["mandb", "--quiet"])
815 # Initialise pkgfile (pacman .files metadata explorer) database
816 logging.debug("Updating pkgfile database")
817 chroot_call(["pkgfile", "--update"])
819 if self.desktop != "base":
820 # avahi package seems to fail to create its user and group in some cases (¿?)
821 cmd = ["groupadd", "-r", "-g", "84", "avahi"]
823 cmd = ["useradd", "-r", "-u", "84", "-g", "avahi", "-d", "/", "-s",
824 "/bin/nologin", "-c", "avahi", "avahi"]
827 # Install sonar (a11y) gsettings if present in the ISO (and a11y is on)
828 src = "/usr/share/glib-2.0/schemas/92_antergos_sonar.gschema.override"
829 if self.settings.get('a11y') and os.path.exists(src):
830 dst = os.path.join(DEST_DIR, 'usr/share/glib-2.0/schemas')
831 shutil.copy2(src, dst)
833 # Enable AUR in pamac if AUR feature selected
836 # Apply makepkg tweaks upon install (issue #871)
837 self.modify_makepkg()
841 logging.debug("Setting .bashrc to load .bashrc.aliases")
842 bashrc_files = ["etc/skel/.bashrc"]
843 username = self.settings.get('user_name')
844 bashrc_files.append("home/{}/.bashrc".format(username))
845 for bashrc_file in bashrc_files:
846 bashrc_file = os.path.join(DEST_DIR, bashrc_file)
847 if os.path.exists(bashrc_file):
848 with open(bashrc_file, 'a') as bashrc:
850 bashrc.write('if [ -e ~/.bashrc.aliases ] ; then\n')
851 bashrc.write(' source ~/.bashrc.aliases\n')
854 # Fixes thermald service file
855 self.fix_thermald_service()
857 # Overwrite settings with Lembrame if enabled
858 # TODO: Rethink this function because we need almost everything but some things for Lembrame
859 if self.settings.get("feature_lembrame"):
860 logging.debug("Overwriting configs from Lembrame")
861 self.events.add('info', _("Overwriting configs from Lembrame"))
863 lembrame = Lembrame(self.settings)
864 lembrame.overwrite_content()
866 # This must be done at the end of the installation when using zfs
867 if self.method == "zfs":
868 logging.debug("Installation done, exporting ZFS pool")
869 pool_name = self.settings.get("zfs_pool_name")
870 cmd = ["zpool", "export", "-f", pool_name]