├── .gitignore ├── lib ├── fs_proc.pl ├── fs_tmpfs.pl ├── fs_exfat.pl ├── fs_nilfs2.pl ├── linux_luks.pl ├── fs_vfat.pl ├── cli_common.pl ├── tui_common.pl ├── list_common.pl ├── atom_common.pl ├── linux_prop_fs.pl ├── tui_ext.pl ├── fs_xfs.pl ├── linux_lvm.pl ├── fs_btrfs.pl ├── fs_bcachefs.pl ├── fs_f2fs.pl ├── os_grub.pl ├── unix_common.pl ├── fs_extfs.pl ├── os_common.pl ├── linux_dracut.pl ├── fs_zfs.pl └── linux_common.pl └── module ├── void_gummiboot.pl ├── void_btrfs.pl ├── void_state.pl ├── void_cmd_arg.pl ├── void_info.pl ├── void_limine.pl ├── void_refind.pl ├── void_efistub.pl ├── void_syslinux.pl ├── void_dracut.pl ├── void_luks.pl ├── void_zfsbootmenu.pl ├── void_grub.pl ├── void_fstab.pl ├── void_menu_fs.pl ├── void_soft.pl ├── void_menu_dev.pl ├── void_zfs.pl ├── void_net.pl ├── void_bootloader.pl ├── void_fs.pl └── void_menu_linux.pl /.gitignore: -------------------------------------------------------------------------------- 1 | void-pi 2 | .projections.json 3 | *.tgz 4 | *.tgz.* 5 | -------------------------------------------------------------------------------- /lib/fs_proc.pl: -------------------------------------------------------------------------------- 1 | % vi: noexpandtab:tabstop=4:ft=gprolog 2 | % Copyright (c) 2024 Sergey Sikorskiy, released under the GNU GPLv2 license. 3 | 4 | % man 5 proc 5 | 6 | prop_info(mnt_proc_opt, opt3s('--options', ','), [ 7 | opt3(hidepid, enum(0, 1, 2), 0) 8 | , opt3(gid, str, 0) % "str" is a correct value here. 9 | ]). 10 | 11 | -------------------------------------------------------------------------------- /module/void_gummiboot.pl: -------------------------------------------------------------------------------- 1 | % vi: noexpandtab:tabstop=4:ft=gprolog 2 | % Copyright (c) 2023-2024 Sergey Sikorskiy, released under the GNU GPLv2 license. 3 | 4 | gummiboot_install(RD) :- 5 | tui_progressbox_safe([chroot, RD, gummiboot, install, '2>&1'], '', [sz([6, 60])]), 6 | true. 7 | 8 | gummiboot_configure(TL, RD) :- 9 | os_mkdir_p(RD + '/boot/loader/'), 10 | FN = '/boot/loader/void-options.conf', 11 | atom_concat(RD, FN, CF), 12 | open(CF, write, S), 13 | % It should contain, on a single line, only the command-line options you want passed to the kernel. 14 | bootloader_write_cmdline(TL, S), nl(S), 15 | close(S), 16 | true. 17 | 18 | -------------------------------------------------------------------------------- /lib/fs_tmpfs.pl: -------------------------------------------------------------------------------- 1 | % vi: noexpandtab:tabstop=4:ft=gprolog 2 | % Copyright (c) 2024 Sergey Sikorskiy, released under the GNU GPLv2 license. 3 | 4 | % man 5 tmpfs 5 | 6 | prop_info(mnt_tmpfs_opt, opt3s('--options', ','), [ 7 | opt3(size, size, 0) 8 | , opt3(nr_blocks, size, 0) 9 | , opt3(nr_inodes, size, 0) 10 | , opt3(mode, int, 0) 11 | , opt3(gid, int, 0) 12 | , opt3(uid, int, 0) 13 | , opt3(huge, enum([never, always, within_size, advise, deny, force]), never) 14 | , opt3(mpol, enum([ 15 | default, 16 | % prefer:node, 17 | % bind:nodelist, 18 | interleave, 19 | % interleave:nodelist, 20 | local 21 | ]), default) 22 | ]). 23 | 24 | -------------------------------------------------------------------------------- /lib/fs_exfat.pl: -------------------------------------------------------------------------------- 1 | % vi: noexpandtab:tabstop=4:ft=gprolog 2 | % Copyright (c) 2024 Sergey Sikorskiy, released under the GNU GPLv2 license. 3 | 4 | % man mkfs.exfat 5 | 6 | prop_info(exfat_rw, opt4s(' '), [ 7 | opt4('boundary-align', size, '16k', '-b') 8 | , opt4('cluster-size', size, '4k', '-c') 9 | , opt4('full-format', enable, no, '-f') 10 | , opt4(label, str, '', '-L') 11 | , opt4(guid, guid, '', '-U') 12 | , opt4('pack-bitmap', enable, no, '--pack-bitmap') 13 | , opt4(quite, enable, no, '-q') 14 | ]). 15 | 16 | prop_info(mnt_exfat_opt, opt3s('--options', ','), [ 17 | opt3(umask, octal, 0) 18 | , opt3(dmask, octal, 0) 19 | , opt3(fmask, octal, 0) 20 | , opt3(uid, int, 0) 21 | , opt3(gid, int, 0) 22 | ]). 23 | 24 | -------------------------------------------------------------------------------- /lib/fs_nilfs2.pl: -------------------------------------------------------------------------------- 1 | % vi: noexpandtab:tabstop=4:ft=gprolog 2 | % Copyright (c) 2024 Sergey Sikorskiy, released under the GNU GPLv2 license. 3 | 4 | % man mount.nilfs2 5 | % man mkfs.nilfs2 6 | 7 | prop_info(nilfs2_feat, feat4s('-O', ',', pref1('^')), [ 8 | prop_feat4(block_count, off, std, none) 9 | ]). 10 | 11 | prop_info(nilfs2_rw, opt4s(' '), [ 12 | opt4('block-size', enum([1024, 2048, 4096, 8192]), 4096, '-b') 13 | , opt4('blocks-per-segment', int, 2048, '-B') 14 | , opt4(force, enable, no, '-f') 15 | , opt4('check-for-bad-blocks', enable, no, '-c') 16 | , opt4(discard, enable, yes, '-K') 17 | , opt4(label, str, '', '-L') 18 | , opt4(quite, enable, no, '-q') 19 | , opt4('gc-reserved-segments', percentage, 5, '-m') 20 | ]). 21 | 22 | prop_info(mnt_nilfs2_feat, feat4s('--options', ',', on_off), [ 23 | prop_feat4(barrier, off, pref1(no), none) 24 | , prop_feat4(discard, off, pref1(no), none) 25 | , prop_feat4(nogc, off, std, none) 26 | , prop_feat4(norecovery, off, std, none) 27 | ]). 28 | 29 | prop_info(mnt_nilfs2_opt, opt3s('--options', ','), [ 30 | opt3(cp, int, 0) 31 | , opt3(errors, enum([continue, panic, 'remount-ro']), 'remount-ro') 32 | , opt3(pp, int, 0) 33 | , opt3(order, enum([relaxed, strict]), relaxed) 34 | ]). 35 | 36 | -------------------------------------------------------------------------------- /module/void_btrfs.pl: -------------------------------------------------------------------------------- 1 | % vi: noexpandtab:tabstop=4:ft=gprolog 2 | % Copyright (c) 2023-2024 Sergey Sikorskiy, released under the GNU GPLv2 license. 3 | 4 | % https://wiki.archlinux.org/title/Btrfs 5 | 6 | btrfs_mkfs_multi(PTL, RD) :- 7 | forall(member(subv(S, _, _, _), PTL), (format_to_atom(SA, '~w/~w', [RD, S]), os_shell2l([btrfs, subvolume, create, SA]))), 8 | forall(member(subv(S, _, _, nodatacow), PTL), (format_to_atom(SA, '~w/~w', [RD, S]), os_shell2l([chattr, '-R', '+C', SA]))), 9 | true. 10 | 11 | btrfs_mount_multi(BD, PTL, RD) :- 12 | forall(member(subv(SV, MP, MOL, _), PTL), btrfs_mount_muli_1(BD, SV, MP, MOL, RD)), 13 | os_mkdir_p(RD + '/mnt/btr_pool'), 14 | true. 15 | 16 | btrfs_mount_muli_1(BD, SV, MP, MOL, RD) :- 17 | atom_concat(RD, MP, DA), 18 | os_mkdir_p(DA), 19 | attr_list_set(MOL, mnt_btrfs_opt, subvol, SV, MOL1), 20 | get_fstab_attrs(MOL1, O), 21 | CL = [mount, o(o, lc(O)), BD, DA, '2>&1'], 22 | % os_call2(CL), 23 | tui_shell2_safe(CL), 24 | true. 25 | 26 | btrfs_mkfs(Title, DL, PTL, COL, RD) :- 27 | tui_progressbox_safe(['mkfs.btrfs', COL, DL, '2>&1'], '', [title(Title), sz([12, 80])]), 28 | DL = [D| _], 29 | CL = [mount, D, RD, '2>&1'], 30 | % os_call2(CL), 31 | tui_shell2_safe(CL), 32 | % create_btrfs_subv(RD), 33 | btrfs_mkfs_multi(PTL, RD), 34 | os_call2([umount, RD]), 35 | true. 36 | 37 | -------------------------------------------------------------------------------- /lib/linux_luks.pl: -------------------------------------------------------------------------------- 1 | % vi: noexpandtab:tabstop=4:ft=gprolog 2 | % Copyright (c) 2023-2024 Sergey Sikorskiy, released under the GNU GPLv2 license. 3 | 4 | is_luks(D) :- 5 | os_call2([cryptsetup, isLuks, D]), 6 | true. 7 | 8 | lx_luks_dev_name(N, LUKS_PD) :- 9 | atom_concat('/dev/mapper/', N, LUKS_PD), 10 | true. 11 | 12 | lx_luks_format(Type, PD, PWD) :- 13 | CL = [cryptsetup, '--type', Type, '--key-file=-', '--force-password', luksFormat, PD, '2>&1', '1>/dev/null'], 14 | os_scmdl(CL, CA), 15 | popen(CA, write, WS), !, 16 | write(WS, PWD), % no nl(WS) should be here. 17 | close(WS), 18 | true. 19 | 20 | lx_luks_open(Type, Name, PD, PWD) :- 21 | CL = [cryptsetup, '--key-file=-', open, '--type', Type, PD, Name], 22 | os_scmdl(CL, CA), 23 | exec(CA, SI, SO, SE, Pid), 24 | write(SI, PWD), % no nl(SI) should be here. 25 | close(SI), 26 | close(SO), 27 | close(SE), 28 | wait(Pid, _RC), 29 | true. 30 | 31 | lx_luks_close(Name) :- 32 | os_call2([cryptsetup, close, Name]), 33 | true. 34 | 35 | lx_luks_add_keyfile(DISK_PUUID, KF, PWD) :- 36 | % CL = [cryptsetup, '--key-file=-', '--force-password', luksAddKey, DISK_PUUID, KF], 37 | CL = [cryptsetup, '-q', '--force-password', luksAddKey, DISK_PUUID, KF], 38 | os_scmdl(CL, CA), 39 | popen(CA, write, WS), !, 40 | write(WS, PWD), % no nl(WS) should be here. 41 | close(WS), 42 | true. 43 | 44 | -------------------------------------------------------------------------------- /module/void_state.pl: -------------------------------------------------------------------------------- 1 | % vi: noexpandtab:tabstop=4:ft=gprolog 2 | % Copyright (c) 2023-2024 Sergey Sikorskiy, released under the GNU GPLv2 license. 3 | 4 | st_root_fs(TL, FS) :- 5 | memberchk(state(root_fs, ctx_rfs(_PTT, FS)), TL), 6 | !. 7 | st_root_fs(_TL, ext4). 8 | 9 | st_part_tmpl(TL, PTN) :- 10 | memberchk(state(make_part_tmpl, ctx_part4(_PTT, _FS, PTN, _D4L)), TL), 11 | !. 12 | st_part_tmpl(_TL, root). 13 | 14 | st_used_d7(TL, UL) :- 15 | memberchk(state(used_d7, ctx_used(UL)), TL). 16 | 17 | st_bootloader(TL, B) :- 18 | memberchk(state(bootloader, ctx_bl(B)), TL). 19 | 20 | st_skip_till([state(C, CTX)|T], C, [state(C, CTX)|T]) :- !. 21 | st_skip_till([_|T], C, L) :- !, 22 | st_skip_till(T, C, L), !. 23 | st_skip_till([], _C, []). 24 | 25 | st_retrieve_ctx(state(bootloader, ctx_bl(B)), L, [bl(B)|L]) :- !. 26 | st_retrieve_ctx(state(template, ctx_tmpl(_B, TT)), L, [tmpl(TT)|L]) :- !. 27 | st_retrieve_ctx(state(used_d7, ctx_used(UL)), L, [used(UL)|L]) :- !. 28 | st_retrieve_ctx(state(bootloader_dev, ctx_bld7(_B, BD, _D7L)), L, [bld7(BD)|L]) :- !. 29 | st_retrieve_ctx(state(root_fs, ctx_rfs(_PTT, FS)), L, [fs(FS)|L]) :- !. 30 | st_retrieve_ctx(state(make_part_tmpl, ctx_part4(_PTT, _FS, PTN, _D4L)), L, [part_tmpl(PTN)|L]) :- !. 31 | st_retrieve_ctx(state(soft, ctx_soft(_FS, _B, SL)), L, [soft(SL)|L]) :- !. 32 | st_retrieve_ctx(_, L, L). 33 | 34 | -------------------------------------------------------------------------------- /module/void_cmd_arg.pl: -------------------------------------------------------------------------------- 1 | % vi: noexpandtab:tabstop=4:ft=gprolog 2 | % Copyright (c) 2023-2024 Sergey Sikorskiy, released under the GNU GPLv2 license. 3 | 4 | usage :- 5 | cmd_arg_usage_short(SAL), 6 | surround_atoms('[', ']', SAL, SAL1), 7 | join_atoms(['Usage: void-pi'| SAL1], ' ', SAL2), 8 | writenl(SAL2), nl, 9 | writenl('Void Linux installer implemented in GNU Prolog.'), nl, 10 | cmd_arg_usage_long(S), 11 | writenl(S), nl, 12 | writenl('Copyright (c) 2023-2024 Sergey Sikorskiy, released under the GNU GPLv2 license'), 13 | true. 14 | 15 | % cmd_arg_info(alias, short, long, value_descr, descr) 16 | % short argument is a character code. 17 | cmd_arg_info(help, 0'h, help, '', 'Show this help and exit.'). 18 | cmd_arg_info(version, 0'v, version, '', 'Show the version and exit.'). 19 | cmd_arg_info(rootdir, 0'r, rootdir, rootdir, 'Use an alternative rootdir. Acts like xbps\'s -r flag. Default is /mnt.'). 20 | cmd_arg_info(config, 0, config, 'file_name', 'Set configuration file. Default is settings.pl.'). 21 | 22 | % first argument is an alias. 23 | on_cmd_arg(help, _, _) :- !, 24 | usage, 25 | fail. 26 | on_cmd_arg(version, _, _) :- !, 27 | version, 28 | fail. 29 | on_cmd_arg(rootdir, LI, T) :- !, 30 | ( LI = [RD|T] 31 | ; writenl('rootdir value expected'), 32 | fail 33 | ), !, 34 | retractall(inst_setting(root_dir, _)), 35 | assertz(inst_setting(root_dir, RD)), 36 | true. 37 | on_cmd_arg(config, LI, T) :- !, 38 | ( LI = [CF|T] 39 | ; writenl('file name expected'), 40 | fail 41 | ), !, 42 | retractall(inst_setting(config_file, _)), 43 | assertz(inst_setting(config_file, CF)), 44 | true. 45 | 46 | -------------------------------------------------------------------------------- /module/void_info.pl: -------------------------------------------------------------------------------- 1 | % vi: noexpandtab:tabstop=4:ft=gprolog 2 | % Copyright (c) 2023-2024 Sergey Sikorskiy, released under the GNU GPLv2 license. 3 | 4 | version :- 5 | writenl('version 0.21.3'). 6 | 7 | fs_info(bcachefs, 'Linux Bcachefs'). 8 | fs_info(btrfs, 'Oracle\'s Btrfs'). 9 | fs_info(ext2, 'Linux ext2 (no journaling)'). 10 | fs_info(ext3, 'Linux ext3 (journal)'). 11 | fs_info(ext4, 'Linux ext4 (journal)'). 12 | fs_info(f2fs, 'Flash-Friendly Filesystem'). 13 | fs_info(swap, 'Linux swap'). 14 | fs_info(vfat, 'FAT32'). 15 | fs_info(xfs, 'SGI\'s XFS'). 16 | fs_info(zfs, 'OpenZFS'). 17 | fs_info(nilfs2, 'Linux NILFS2'). 18 | fs_info(exfat, 'Microsoft exFAT'). 19 | 20 | source_dep_module('void-live', filesystem(bcachefs), ['bcachefs-tools', lz4]). 21 | source_dep_module('void-live', filesystem(zfs), [zfs, lz4]). 22 | source_dep_module('void-live', filesystem(f2fs), [lz4]). 23 | source_dep_module('void-live', filesystem(exfat), [exfatprogs]). 24 | source_dep_module('void-live', filesystem(nilfs2), ['nilfs-utils']). 25 | source_dep_module('void-live', template(_), [gptfdisk]). 26 | source_dep_module('void-live', template(gpt_luks), [lz4]). 27 | % source_dep_module('void-live', template(gpt_luks_lvm), [lz4, curl]). 28 | source_dep_module('void-live', template(gpt_luks_lvm), [lz4]). 29 | source_dep_module('void-live', template(gpt_lvm_luks), [lz4]). 30 | source_dep_module('void-live', template(gpt_raid), [mdadm]). 31 | source_dep_module('void-live', inst_method(rootfs), [xz]). 32 | 33 | source_dep_module(hrmpf, filesystem(zfs), [lz4]). 34 | source_dep_module(hrmpf, filesystem(f2fs), [lz4]). 35 | source_dep_module(hrmpf, filesystem(exfat), [exfatprogs]). 36 | source_dep_module(hrmpf, filesystem(nilfs2), ['nilfs-utils']). 37 | source_dep_module(hrmpf, template(gpt_luks), [lz4]). 38 | source_dep_module(hrmpf, template(gpt_luks_lvm), [lz4]). 39 | source_dep_module(hrmpf, template(gpt_lvm_luks), [lz4]). 40 | 41 | -------------------------------------------------------------------------------- /module/void_limine.pl: -------------------------------------------------------------------------------- 1 | % vi: noexpandtab:tabstop=4:ft=gprolog 2 | % Copyright (c) 2023-2024 Sergey Sikorskiy, released under the GNU GPLv2 license. 3 | 4 | limine_install(BD, RD) :- 5 | os_mkdir_p(RD + '/boot/limine'), 6 | ( \+ inst_setting(system(efi), _) 7 | ; limine_install_efi(RD) 8 | ), !, 9 | ( \+ inst_setting(system(bios), _) 10 | ; limine_install_bios(BD, RD) 11 | ), !, 12 | true. 13 | 14 | limine_install_efi(RD) :- 15 | os_mkdir_p(RD + '/boot/efi/EFI/BOOT'), 16 | inst_setting(system(arch), ARCH), 17 | arch2limine(ARCH, BL), 18 | os_call2([cp, '-f', RD + '/usr/share/limine/' + BL, RD + '/boot/efi/EFI/BOOT/' + BL]), 19 | true. 20 | 21 | limine_install_bios(BD, RD) :- 22 | tui_progressbox_safe([chroot, RD, 'limine', 'bios-install', BD, '2>&1'], '', [title(' Installing Limine '), sz([6, 60])]), 23 | os_call2([cp, '-f', RD + '/usr/share/limine/limine-bios.sys', RD + '/boot/limine/limine-bios.sys']), 24 | true. 25 | 26 | arch2limine('x86_64', 'BOOTX64.EFI'). 27 | arch2limine('x86_64-musl', 'BOOTX64.EFI'). 28 | arch2limine('i686', 'BOOTIA32.EFI'). 29 | arch2limine('aarch64', 'BOOTAA64.EFI'). 30 | 31 | limine_configure(TL, RD) :- 32 | atom_concat(RD, '/boot/vmlinuz-', P0), 33 | atom_concat(P0, '*', P1), 34 | os_shell2_line([ls, P1], A0), 35 | atom_concat(P0, V, A0), 36 | 37 | atom_concat(RD, '/boot/limine/limine.cfg', CF), 38 | open(CF, write, S), 39 | limine_write_cfg(TL, V, S), 40 | close(S), 41 | true. 42 | 43 | limine_write_cfg(TL, V, S) :- 44 | boot_pref(TL, Pref), 45 | 46 | write(S, 'INTERFACE_BRANDING=Void Linux'), nl(S), 47 | write(S, 'TIMEOUT=5'), nl(S), nl(S), 48 | write(S, ':Boot with standard options'), nl(S), 49 | write(S, ' PROTOCOL=linux'), nl(S), 50 | write(S, ' KERNEL_PATH=boot:///'), write(S, Pref), write(S, 'vmlinuz-'), write(S, V), nl(S), 51 | write(S, ' MODULE_PATH=boot:///'), write(S, Pref), write(S, 'initramfs-'), write(S, V), write(S, '.img'), nl(S), 52 | write(S, ' CMDLINE='), bootloader_write_cmdline(TL, S), nl(S), 53 | true. 54 | 55 | -------------------------------------------------------------------------------- /module/void_refind.pl: -------------------------------------------------------------------------------- 1 | % vi: noexpandtab:tabstop=4:ft=gprolog 2 | % Copyright (c) 2023-2024 Sergey Sikorskiy, released under the GNU GPLv2 license. 3 | 4 | refind_install(TL, RD) :- 5 | ( memberchk(p4(sys_efi, bd1([EFI_PD| _]), _CK, _SZ), TL) 6 | ; tui_msgbox('efi system partition was not found'), 7 | fail 8 | ), !, 9 | CL1 = [chroot, RD, 'refind-install', oo(usedefault), EFI_PD, '2>&1'], 10 | tui_progressbox_safe(CL1, '', [title(' Installing bootloader '), sz([6, 60])]), 11 | % os_shell2(CL1), 12 | true. 13 | 14 | refind_configure(TL, RD) :- 15 | atom_concat(RD, '/boot/refind_linux.conf', FN), 16 | open(FN, write, S), 17 | 18 | write(S, '"Boot with standard options" "'), 19 | refind_write_cfg(TL, S, []), 20 | write(S, '"'), nl(S), 21 | 22 | write(S, '"Boot to single-user mode" "'), 23 | refind_write_cfg(TL, S, [single]), 24 | write(S, '"'), nl(S), 25 | 26 | close(S), 27 | true. 28 | 29 | % It is different from bootloader_kernel_params. 30 | refind_kernel_params(TL, L) :- 31 | % Device 32 | root_pd(TL, ROOT_PD), 33 | ( atom_concat('/dev/mapper/', _, ROOT_PD) -> 34 | L = [root=ROOT_PD] 35 | ; lx_get_dev_partuuid(ROOT_PD, RPID), 36 | L = [root=v('PARTUUID', RPID)] 37 | ), 38 | true. 39 | refind_kernel_params(TL, L) :- 40 | % LUKS 41 | ( uses_luks(TL) -> 42 | bootloader_kernel_params_luks(TL, L) 43 | ; L = ['rd.luks'=0] 44 | ), 45 | true. 46 | refind_kernel_params(TL, ['rd.lvm'=0]) :- 47 | \+ memberchk(bdev(lvm, _Value), TL), 48 | true. 49 | refind_kernel_params(_TL, [ 50 | 'rd.md'=0 51 | , 'rd.dm'=0 52 | , loglevel=4 53 | , gpt 54 | , 'vconsole.unicode'=1 55 | , 'vconsole.keymap'=KB 56 | , 'locale.LANG'=LC 57 | ]) :- 58 | inst_setting(keymap, KB), 59 | inst_setting(locale, LC), 60 | true. 61 | refind_kernel_params(TL, L) :- 62 | ( root_fs(TL, btrfs), \+ has_boot_part(TL) -> 63 | L = [rootflags=v(subvol, '@'), initrd='@\\boot\\initramfs-%v.img'] 64 | ; boot_pref(TL, Pref), 65 | atom_concat(Pref, 'initramfs-%v.img', BI), 66 | L = [initrd=BI] 67 | ). 68 | 69 | refind_write_cfg(TL, S, L) :- 70 | findall(P0, ((refind_kernel_params(TL, PL0); PL0 = L), member(P0, PL0)), AL), 71 | os_wcmdl(AL, S), 72 | true. 73 | 74 | -------------------------------------------------------------------------------- /module/void_efistub.pl: -------------------------------------------------------------------------------- 1 | % vi: noexpandtab:tabstop=4:ft=gprolog 2 | % Copyright (c) 2023-2024 Sergey Sikorskiy, released under the GNU GPLv2 license. 3 | 4 | % https://wiki.archlinux.org/title/EFISTUB 5 | % https://mth.st/blog/void-efistub 6 | % EFI Stub - booting without a bootloader (https://forums.gentoo.org/viewtopic-p-8805827.html#8805827) 7 | 8 | efistub_install(RD) :- 9 | lx_kernel_ver(RD, LV), 10 | atom_concat('Void Linux with kernel ', LV, Name), 11 | efibootmgr_get(MgrL), 12 | memberchk(boot(Num, Name, _), MgrL), 13 | memberchk(order(OL), MgrL), 14 | delete(OL, Num, OL1), 15 | efibootmgr_set_order([Num|OL1]), 16 | true. 17 | 18 | efistub_configure(TL, RD) :- 19 | atom_concat(RD, '/etc/default/efibootmgr-kernel-hook', CF), 20 | open(CF, write, S), 21 | efistub_write_cfg(TL, S), 22 | close(S), 23 | true. 24 | 25 | efistub_write_cfg(TL, S) :- 26 | % fs6(Name, MountPoint, Dev, [CreateOptList], [MountOptList], create/keep) 27 | member(fs6(vfat, '/boot', PD, _COL, _MOL, _CK), TL), 28 | lx_parent_dev_name(PD, N, PD0), 29 | write(S, 'MODIFY_EFI_ENTRIES="1"'), nl(S), 30 | write(S, 'OPTIONS="'), bootloader_write_cmdline(TL, S), write(S, '"'), nl(S), 31 | format(S, 'DISK="~w"', [PD0]), nl(S), 32 | format(S, 'PART=~w', [N]), nl(S), 33 | true. 34 | 35 | efibootmgr_get(L) :- 36 | os_shell_lines_codes(efibootmgr, CL), 37 | maplist(efibootmgr_get_, CL, L), 38 | true. 39 | 40 | efibootmgr_get_(L, timeout(A)) :- 41 | append("Timeout: ", L0, L), !, 42 | split_list_ne(L0, " ", [L1| _]), 43 | atom_codes(A, L1), 44 | true. 45 | efibootmgr_get_(L, current(A)) :- 46 | append("BootCurrent: ", L0, L), !, 47 | atom_codes(A, L0), 48 | true. 49 | efibootmgr_get_(L, next(A)) :- 50 | append("BootNext: ", L0, L), !, 51 | atom_codes(A, L0), 52 | true. 53 | efibootmgr_get_(L, order(L2)) :- 54 | append("BootOrder: ", L0, L), !, 55 | split_list_ne(L0, ",", L1), 56 | maplist(codes_atom, L1, L2), 57 | true. 58 | efibootmgr_get_(L, boot(A1, A2, F)) :- 59 | % Boot0000* 60 | split_list(L, "\t", [L0| _]), 61 | split_list(L0, " ", [L1| T]), 62 | append("Boot", L11, L1), 63 | ( append(L12, [0'*], L11), 64 | F = active 65 | ; L12 = L11, 66 | F = inactive 67 | ), 68 | atom_codes(A1, L12), 69 | join(T, " ", L2), 70 | flatten(L2, L3), 71 | atom_codes(A2, L3), 72 | true. 73 | 74 | efibootmgr_set_order(L) :- 75 | os_call2([efibootmgr, '-o', lc(L)]). 76 | -------------------------------------------------------------------------------- /lib/fs_vfat.pl: -------------------------------------------------------------------------------- 1 | % vi: noexpandtab:tabstop=4:ft=gprolog 2 | % Copyright (c) 2024 Sergey Sikorskiy, released under the GNU GPLv2 license. 3 | 4 | % man mkfs.vfat 5 | prop_info(vfat_rw, opt4s(' '), [ 6 | opt4(align, enable, no, '-a') 7 | , opt4(atary, enable, no, '-A') 8 | , opt4('sector-of-backup', int, 6, '-b') 9 | , opt4('check for bad blocks', enable, no, '-c') 10 | , opt4('drive number', hex, '0x80', '-D') 11 | , opt4('number-of-fats', int, 2, '-f') 12 | , opt4('fat-size', enum([12, 16, 32]), 12, '-F') 13 | % , opt4('heads/sectors-per-track', int, 0, '-g') 14 | , opt4('number-of-hidden-sectors', int, 0, '-h') 15 | , opt4('volume-id', hex, '', '-i') 16 | , opt4('ignore and disable safety checks', enable, no, '-I') 17 | , opt4('bad blocks list file', file, '', '-l') 18 | , opt4('message-file', file, '', '-m') 19 | , opt4('fat-media-type', hex, '0xF8', '-M') 20 | % , opt4(mbr, enum([yes, no, auto]), auto, '--mbr') 21 | , opt4(label, str_upper, '', '-n') 22 | % , opt4(codepage, str, '850', '--codepage') 23 | , opt4('root-dir-entries', int, 512, '-r') 24 | , opt4('number-of-reserved-sectors', int, 32, '-R') 25 | , opt4('sectors-per-cluster', enum([1, 2, 4, 8, 16, 32, 64, 128]), 2, '-s') 26 | , opt4('logical-sector-size', enum([512, 1024, 2048, 4096, 8192, 16384, 32768]), 512, '-S') 27 | , opt4(offset, int, 0, '--offset') 28 | , opt4(variant, enum([standard, atari]), standard, '--variant') 29 | , opt4(invariant, enable, no, '--invariant') 30 | ]). 31 | 32 | prop_info(mnt_vfat_feat, feat4s('--options', ',', on_off), [ 33 | prop_feat4(debug, off, std, none) 34 | , prop_feat4(discard, off, std, none) 35 | , prop_feat4(dos1xfloppy, off, std, none) 36 | , prop_feat4(quiet, off, std, none) 37 | , prop_feat4(rodir, off, std, none) 38 | , prop_feat4(showexec, off, std, none) 39 | , prop_feat4(sys_immutable, off, std, none) 40 | , prop_feat4(flush, off, std, none) 41 | , prop_feat4(usefree, off, std, none) 42 | 43 | , prop_feat4(uni_xlate, off, std, none) 44 | , prop_feat4(posix, off, std, none) 45 | , prop_feat4(nonumtail, off, std, none) 46 | , prop_feat4(utf8, off, std, none) 47 | ]). 48 | 49 | prop_info(mnt_vfat_opt, opt3s('--options', ','), [ 50 | opt3(uid, int, 0) 51 | , opt3(gid, int, 0) 52 | , opt3(umask, octal, 0) 53 | , opt3(dmask, octal, '0022') 54 | , opt3(fmask, octal, '0022') 55 | , opt3(allow_utime, int, 0) 56 | , opt3(check, enum([relaxed, normal, strict]), normal) 57 | , opt3(codepage, int, 437) 58 | , opt3(errors, enum([panic, continue, 'remount-ro']), 'remount-ro') 59 | , opt3(fat, enum([12, 16, 32]), 12) 60 | , opt3(iocharset, str, 'iso8859-1') 61 | , opt3(nfs, enum([stale_rw, nostale_ro]), stale_rw) 62 | , opt3(tz, str, '') 63 | , opt3(time_offset, int, 0) 64 | % , opt3(dotsOK, enable, yes) 65 | 66 | , opt3(shortname, enum([lower, win95, winnt, mixed]), mixed) 67 | ]). 68 | 69 | -------------------------------------------------------------------------------- /lib/cli_common.pl: -------------------------------------------------------------------------------- 1 | % vi: noexpandtab:tabstop=4:ft=gprolog 2 | % Copyright (c) 2023-2024 Sergey Sikorskiy, released under the GNU GPLv2 license. 3 | 4 | handle_cmd_args([A|T]) :- 5 | atom_codes(A, CL), 6 | handle_cmd_arg_cl(CL,T,T1), !, 7 | handle_cmd_args(T1). 8 | handle_cmd_args([]). 9 | 10 | % First argument is a code list. 11 | handle_cmd_arg_short_cl([], L, L) :- !. 12 | handle_cmd_arg_short_cl([C|T], LI, LO) :- 13 | cmd_arg_info(Alias, C, _, _, _), !, 14 | on_cmd_arg(Alias, LI, LO1), 15 | handle_cmd_arg_short_cl(T, LO1, LO). 16 | handle_cmd_arg_short_cl([C|_], _, _) :- !, 17 | write('Unknown argument -'), 18 | atom_codes(A, [C]), 19 | writenl(A), 20 | fail. 21 | 22 | % First argument is a code list. 23 | handle_cmd_arg_long_cl(CL, LI, LO) :- 24 | split_list(CL, "=", [H|T1]), 25 | atom_codes(A1, H), 26 | cmd_arg_info(Alias, _, A1, _, _), !, 27 | ( T1 = [] -> 28 | on_cmd_arg(Alias, LI, LO) 29 | ; append(H, [0'=|T2], CL), 30 | atom_codes(V, T2), 31 | on_cmd_arg(Alias, [V|LI], LO) 32 | ), 33 | true. 34 | handle_cmd_arg_long_cl(CL, _, _) :- 35 | write('Unknown argument --'), 36 | atom_codes(A, CL), 37 | writenl(A), 38 | fail. 39 | 40 | % First argument is a code list. 41 | handle_cmd_arg_cl([0'-,0'-|T], LI, LO) :- !, 42 | handle_cmd_arg_long_cl(T, LI, LO), 43 | true. 44 | handle_cmd_arg_cl([0'-|T], LI, LO) :- 45 | handle_cmd_arg_short_cl(T, LI, LO), 46 | true. 47 | 48 | cmd_arg_usage_short(S) :- 49 | % arguments without value 50 | findall(SA, (cmd_arg_info(_, SA, _, '', _), SA \= 0), AL1), 51 | % short arguments with value 52 | findall(A, (cmd_arg_info(_, SA, _, V, _), SA \= 0, V \= '', format_to_atom(A, '-~c ~w', [SA, V])), AL2), 53 | % long only arguments with value 54 | findall(A, (cmd_arg_info(_, 0, LA, V, _), V \= '', format_to_atom(A, '--~w ~w', [LA, V])), AL3), 55 | append(AL2, AL3, CL1), 56 | ( AL1 = [] -> 57 | S = AL2 58 | ; atom_codes(A1, [0'-|AL1]), 59 | S = [A1|CL1] 60 | ), 61 | true. 62 | 63 | cmd_arg_usage_long(S) :- 64 | findall(Line, format_cmd_arg_long0(Line), LL), 65 | maplist(format_cmd_arg_len, LL, LLen), 66 | max_list(LLen, Max), 67 | maplist(format_cmd_arg_long1(Max), LL, LL1), 68 | join_atoms(LL1, '\n', S), 69 | true. 70 | 71 | format_cmd_arg_len(a0(Line, _), Len) :- 72 | atom_length(Line, Len). 73 | 74 | format_cmd_arg_long1(Max, a0(Line, D), S) :- 75 | atom_length(Line, Len), 76 | N is Max - Len + 3, 77 | make_offset(N, O), 78 | atom_concat(Line, O, S0), 79 | atom_concat(S0, D, S), 80 | true. 81 | 82 | format_cmd_arg_long0(a0(Line, D)) :- 83 | cmd_arg_info(_, SA, LA, _, D), 84 | format_cmd_arg_long0(SA, LA, Line), 85 | true. 86 | 87 | format_cmd_arg_long0(0, LA, Line) :- !, 88 | format_to_atom(Line, ' --~w', [LA]). 89 | format_cmd_arg_long0(SA, '', Line) :- !, 90 | format_to_atom(Line, ' -~c', [SA]). 91 | format_cmd_arg_long0(SA, LA, Line) :- !, 92 | format_to_atom(Line, ' -~c, --~w', [SA, LA]). 93 | 94 | -------------------------------------------------------------------------------- /lib/tui_common.pl: -------------------------------------------------------------------------------- 1 | % vi: noexpandtab:tabstop=4:ft=gprolog 2 | % Copyright (c) 2023-2024 Sergey Sikorskiy, released under the GNU GPLv2 license. 3 | 4 | tui_menu_on_off([T], [T, T]) :- !. 5 | tui_menu_on_off([T, V|_], [T, V]) :- !. 6 | tui_menu_on_off(T, [T, T]) :- !. 7 | 8 | tui_radiolist_on_off(ON, [T], [T, T, I]) :- !, 9 | ( T = ON -> 10 | I = on 11 | ; I = off 12 | ). 13 | tui_radiolist_on_off(ON, [T, V|_], [T, V, I]) :- !, 14 | ( T = ON -> 15 | I = on 16 | ; I = off 17 | ). 18 | tui_radiolist_on_off(ON, T, [T, T, I]) :- !, 19 | ( T = ON -> 20 | I = on 21 | ; I = off 22 | ). 23 | 24 | tui_checklist_on_off(ONL, [T], [T, T, I]) :- !, 25 | ( memberchk(T, ONL) -> 26 | I = on 27 | ; I = off 28 | ). 29 | tui_checklist_on_off(ONL, [T, V|_], [T, V, I]) :- !, 30 | ( memberchk(T, ONL) -> 31 | I = on 32 | ; I = off 33 | ). 34 | tui_checklist_on_off(ONL, T, [T, T, I]) :- !, 35 | ( memberchk(T, ONL) -> 36 | I = on 37 | ; I = off 38 | ). 39 | 40 | tui_add_num_pref(N, [H|T], [A, H1|T1]) :- 41 | number_atom(N, A), 42 | N1 is N + 1, 43 | add_dquote(H, H1), 44 | tui_add_num_pref(N1, T, T1). 45 | tui_add_num_pref(_, [], []). 46 | 47 | tui_make_ind_param([H, S], H1, S) :- !, 48 | add_dquote(H, H1). 49 | tui_make_ind_param([H], H1, off) :- !, 50 | add_dquote(H, H1). 51 | tui_make_ind_param(A, A1, off) :- !, 52 | add_dquote(A, A1). 53 | 54 | tui_make_tag_param([A1, A2, S], [A11, A12, S]) :- !, 55 | add_dquote(A1, A11), 56 | add_dquote(A2, A12). 57 | tui_make_tag_param([A1, A2], [A11, A12, off]) :- !, 58 | add_dquote(A1, A11), 59 | add_dquote(A2, A12). 60 | tui_make_tag_param([A], [A1, A1, off]) :- !, 61 | add_dquote(A, A1). 62 | tui_make_tag_param(A, [A1, A1, off]) :- !, 63 | add_dquote(A, A1). 64 | 65 | tui_add_check_num_pref(N, [H|T], [NA, I, S|T1]) :- 66 | tui_make_ind_param(H, I, S), 67 | number_atom(N, NA), 68 | N1 is N + 1, 69 | tui_add_check_num_pref(N1, T, T1). 70 | tui_add_check_num_pref(_, [], []). 71 | 72 | tui_add_check_tag_suf(LI, LO) :- 73 | maplist(tui_make_tag_param, LI, LO). 74 | 75 | % +def_args, +user_args, -result 76 | tui_merge_args([H|T], U, T2) :- !, 77 | tui_merge_args(T, U, T1), 78 | tui_member_args(H, U, T1, T2). 79 | tui_merge_args([], U, U). 80 | 81 | tui_args_mask(sz(_), sz(_)) :- !. 82 | tui_args_mask(V, V) :- !. 83 | 84 | tui_member_args(M, U, T, T) :- 85 | tui_args_mask(M, MM), 86 | member(MM, U), !. 87 | tui_member_args(M, _, T, [M|T]). 88 | 89 | tui_make_args2([sz(V)|T], SZA, AL) :- !, 90 | tui_make_sz2(sz(V), SZA), 91 | tui_make_args2(T, _, AL). 92 | tui_make_args2([H|T], SZ, [H1|AL]) :- 93 | tui_arg_map(H, H1), 94 | tui_make_args2(T, SZ, AL). 95 | tui_make_args2([], _, []). 96 | 97 | tui_make_args3([sz(V)|T], SZA, AL) :- !, 98 | tui_make_sz3(sz(V), SZA), 99 | tui_make_args3(T, _, AL). 100 | tui_make_args3([H|T], SZ, [H1|AL]) :- 101 | tui_arg_map(H, H1), 102 | tui_make_args3(T, SZ, AL). 103 | tui_make_args3([], _, []). 104 | 105 | -------------------------------------------------------------------------------- /lib/list_common.pl: -------------------------------------------------------------------------------- 1 | % vi: noexpandtab:tabstop=4:ft=gprolog 2 | % Copyright (c) 2023-2024 Sergey Sikorskiy, released under the GNU GPLv2 license. 3 | 4 | % S - separator. 5 | join([H1,H2|T], S, [H1,S|T1]) :- !, 6 | join([H2|T], S, T1). 7 | join([H], _, [H]) :- !. 8 | join_atoms([], _, []). 9 | 10 | % S - separator. 11 | join_atoms([H1,H2|T], S, T2) :- 12 | atom_concat(H1, S, H3), 13 | join_atoms([H2|T], S, T1), 14 | atom_concat(H3, T1, T2). 15 | join_atoms([H], _, H). 16 | join_atoms([], _, ''). 17 | 18 | join_atoms([H|T], A2) :- 19 | join_atoms(T, A1), 20 | atom_concat(H, A1, A2). 21 | join_atoms([], ''). 22 | 23 | dedup(IL, OL) :- 24 | dedup_(IL, [], OL), 25 | true. 26 | 27 | dedup_([H|T], L, OL) :- 28 | ( memberchk(H, L) -> 29 | dedup_(T, L, OL) 30 | ; dedup_(T, [H|L], OL) 31 | ). 32 | dedup_([], L, RL) :- 33 | reverse(L, RL). 34 | 35 | % SEP - separator. 36 | write_atoms([H1,H2|T], SEP, S) :- !, 37 | write(S, H1), write(S, SEP), 38 | write_atoms([H2|T], SEP, S). 39 | write_atoms([H], _, S) :- !, 40 | write(S, H). 41 | write_atoms([], _, _). 42 | 43 | % Split list, no empty elements. 44 | split_list_ne(I, SL, L) :- 45 | split_list_ne_(I, SL, H, T), 46 | split_list_combine(H, T, L). 47 | 48 | split_list_ne_([H|T], SL, [], T3) :- 49 | member(H, SL), !, 50 | split_list_ne_(T, SL, T1, T2), 51 | split_list_combine(T1, T2, T3). 52 | split_list_ne_([H|T], SL, [H|T1], T2) :- !, 53 | split_list_ne_(T, SL, T1, T2). 54 | split_list_ne_([], _, [], []). 55 | 56 | split_list(I, SL, [H|T]) :- 57 | split_list_(I, SL, H, T). 58 | 59 | split_list_([H|T], SL, [], [T1|T2]) :- 60 | member(H, SL), !, 61 | split_list_(T, SL, T1, T2). 62 | split_list_([H|T], SL, [H|T1], T2) :- !, 63 | split_list_(T, SL, T1, T2). 64 | split_list_([], _, [], []). 65 | 66 | split_list_combine([], T, T) :- !. 67 | split_list_combine(H, T, [H|T]). 68 | 69 | split_line_chars(I, L) :- 70 | split_list_ne(I, ['\n'], L). 71 | 72 | split_line_codes(I, L) :- 73 | split_list(I, "\n", L). 74 | 75 | % No empty lines. 76 | split_line_codes_ne(I, L) :- 77 | split_list_ne(I, "\n", L). 78 | 79 | split_atom_chars(I, L) :- 80 | split_list(I, [' ', '\t', '\n'], L). 81 | 82 | % No empty atoms. 83 | split_atom_chars_ne(I, L) :- 84 | split_list_ne(I, [' ', '\t', '\n'], L). 85 | 86 | split_atom_codes(I, L) :- 87 | split_list(I, " \t\n", L). 88 | 89 | % No empty atoms. 90 | split_atom_codes_ne(I, L) :- 91 | split_list_ne(I, " \t\n", L). 92 | 93 | % SL - split list. 94 | split_atom(SL, IL, OL) :- 95 | split_list(IL, SL, LL), 96 | maplist(codes_atom, LL, OL). 97 | 98 | % No empty atoms. 99 | % SL - split list. 100 | split_atom_ne(SL, IL, OL) :- 101 | split_list_ne(IL, SL, LL), 102 | maplist(codes_atom, LL, OL). 103 | 104 | % E - first element of a suffix in a list 105 | % L - list 106 | % S - suffix 107 | % N - 1-based index of E. 108 | suffix(E, L, N, S) :- 109 | suffix_(E, L, 1, N, S), 110 | true. 111 | 112 | suffix_(H, [H|T], IN, IN, [H|T]) :- !. 113 | suffix_(E, [_|T], IN, ON, S) :- 114 | N1 is IN + 1, 115 | suffix_(E, T, N1, ON, S), 116 | true. 117 | 118 | -------------------------------------------------------------------------------- /module/void_syslinux.pl: -------------------------------------------------------------------------------- 1 | % vi: noexpandtab:tabstop=4:ft=gprolog 2 | % Copyright (c) 2023-2024 Sergey Sikorskiy, released under the GNU GPLv2 license. 3 | 4 | % https://wiki.archlinux.org/title/Syslinux 5 | 6 | syslinux_install(TL, BD, RD) :- 7 | ( \+ inst_setting(system(efi), _) 8 | ; syslinux_install_efi(BD, RD) 9 | ), !, 10 | ( \+ inst_setting(system(bios), _) 11 | ; syslinux_install_bios(TL, BD, RD) 12 | ), !. 13 | 14 | syslinux_install_efi(BD, RD) :- 15 | os_mkdir_p(RD + '/boot/EFI/syslinux'), 16 | os_shell2([cp, '-r', RD + '/usr/lib/syslinux/efi64/*', RD + '/boot/EFI/syslinux']), 17 | % os_shell2([chroot, RD, efibootmgr, '--create', oo(disk, BD), oo(part, 1), oo(loader, '/EFI/syslinux/syslinux.efi'), oo(label, '"Syslinux"'), '--unicode']), 18 | tui_progressbox_safe([chroot, RD, efibootmgr, '--create', oo(disk, BD), oo(part, 1), oo(loader, '/EFI/syslinux/syslinux.efi'), oo(label, '"Syslinux"'), '2>&1'], 'efibootmgr', [sz([12, 80])]), 19 | true. 20 | 21 | syslinux_install_bios(TL, BD, RD) :- 22 | % Install the boot sector provided by Syslinux. 23 | tui_progressbox_safe([dd, bs=440, count=1, conv=notrunc, if=concat(RD, '/usr/lib/syslinux/gptmbr.bin'), of=BD, '2>&1'], '', [sz([6, 60])]), 24 | 25 | % p4(sys_bios_boot, bd1([PD, D]), create, MBR_SZ) 26 | get_boot_part(TL, BPD), 27 | lx_parent_dev_name(BPD, PN, _PD), 28 | 29 | % Enable the legacy BIOS bootable attribute 30 | % This attribute is required. 31 | format_to_atom(A, '--attributes=~w:set:2', [PN]), 32 | os_shell2([sgdisk, BD, A]), 33 | 34 | os_mkdir_p(RD + '/boot/syslinux'), 35 | 36 | % Copy modules. 37 | os_shell2([cp, RD + '/usr/lib/syslinux/*.c32', RD + '/boot/syslinux']), 38 | os_shell2([cp, RD + '/usr/lib/syslinux/memdisk', RD + '/boot/syslinux']), 39 | 40 | % Use the extlinux command to install the necessary files 41 | tui_progressbox_safe([chroot, RD, extlinux, '--install', '/boot/syslinux', '2>&1'], '', [sz([6, 60])]), 42 | true. 43 | 44 | syslinux_configure(TL, RD) :- 45 | ( \+ inst_setting(system(efi), _) 46 | ; syslinux_configure(efi, TL, RD) 47 | ), !, 48 | ( \+ inst_setting(system(bios), _) 49 | ; syslinux_configure(bios, TL, RD) 50 | ), !, 51 | true. 52 | 53 | % BE - bios/efi 54 | syslinux_configure(BE, TL, RD) :- 55 | ( BE = efi -> 56 | FN = '/boot/EFI/syslinux/syslinux.cfg' 57 | ; FN = '/boot/syslinux/syslinux.cfg' 58 | ), 59 | atom_concat(RD, FN, CF), 60 | atom_concat(RD, '/boot/vmlinuz-', P0), 61 | atom_concat(P0, '*', P1), 62 | os_shell2_line([ls, P1], A0), 63 | atom_concat(P0, V, A0), 64 | syslinux_configure_(BE, TL, V, CF). 65 | 66 | syslinux_configure_(BE, TL, V, CF) :- 67 | open(CF, write, S), 68 | syslinux_write_cfg(BE, TL, V, S), 69 | close(S). 70 | 71 | % BE - bios/efi 72 | syslinux_write_cfg(BE, TL, V, S) :- 73 | ( BE = efi -> 74 | Pref = '' 75 | ; boot_pref(TL, Pref) 76 | ), 77 | write(S, 'PROMPT 1'), nl(S), 78 | write(S, 'TIMEOUT 30'), nl(S), 79 | write(S, 'DEFAULT Void'), nl(S), nl(S), 80 | write(S, 'LABEL Void'), nl(S), 81 | write(S, ' LINUX /'), write(S, Pref), write(S, 'vmlinuz-'), write(S, V), nl(S), 82 | write(S, ' INITRD /'), write(S, Pref), write(S, 'initramfs-'), write(S, V), write(S, '.img'), nl(S), 83 | write(S, ' APPEND '), bootloader_write_cmdline(TL, S), nl(S), 84 | true. 85 | 86 | -------------------------------------------------------------------------------- /module/void_dracut.pl: -------------------------------------------------------------------------------- 1 | % vi: noexpandtab:tabstop=4:ft=gprolog 2 | % Copyright (c) 2023-2024 Sergey Sikorskiy, released under the GNU GPLv2 license. 3 | 4 | dracut_conf(TL, _B, RD) :- 5 | member(C, [common, luks, fs(zfs)]), 6 | dracut_enable(C, TL), 7 | findall(V, (dracut_conf(C, TL, RD, L), member(V, L)), VL), 8 | dracut_key(C, K), 9 | lx_dracut_conf(VL, K, RD), 10 | fail. 11 | dracut_conf(TL, efistub, RD) :- 12 | efistub_configure(TL, RD), 13 | fail. 14 | dracut_conf(_TL, _B, _RD). 15 | 16 | dracut_enable(luks, TL) :- !, 17 | uses_luks(TL). 18 | dracut_enable(fs(zfs), TL) :- !, 19 | uses_zfs(TL). 20 | dracut_enable(_, _TL) :- !. 21 | 22 | dracut_key(fs(FS), FS) :- !. 23 | dracut_key(K, K) :- !. 24 | 25 | dracut_conf(common, _TL, _RD, [ 26 | v(add_dracutmodules, [dm, 'kernel-modules']) 27 | , v(add_drivers, [ahci, i915]) 28 | , v(omit_dracutmodules, ['dracut-systemd', plymouth, systemd, 'systemd-initrd', usrmount]) 29 | , v(persistent_policy, 'by-uuid') 30 | , v(tmpdir, '/tmp') 31 | , v(hostonly, HOSTONLY) 32 | ]) :- !, 33 | inst_setting(hostonly, HOSTONLY), 34 | true. 35 | 36 | dracut_conf(luks, _TL, _RD, [ 37 | v(add_dracutmodules, [crypt]) 38 | , v(add_drivers, [lz4, lz4hc, xxhash_generic]) 39 | , v(compress, lz4) 40 | % , v(kernel_cmdline, ['rd.lvm'=0, 'rd.md'=0, 'rd.dm'=0]) 41 | ]). 42 | dracut_conf(luks, TL, RD, [v(install_items, ['/boot/volume.key', '/etc/crypttab'])]) :- !, 43 | % In case of a dedicated boot partition we do not need a key-file. 44 | ( has_boot_part(TL) -> 45 | setup_crypt_none(TL, RD) 46 | ; setup_crypt(TL, RD) 47 | ). 48 | 49 | dracut_conf(lvm, _TL, _RD, [v(add_dracutmodules, [lvm]), v(add_drivers, [lvm])]) :- !. 50 | dracut_conf(mdraid, _TL, _RD, [v(add_dracutmodules, [mdraid]), v(add_drivers, [mdraid])]) :- !. 51 | 52 | dracut_conf(fs(zfs), _TL, _RD, [ 53 | v(nofscks, yes) 54 | , v(add_dracutmodules, [zfs]) 55 | , v(omit_dracutmodules, [btrfs, resume]) 56 | ]). 57 | dracut_conf(fs(zfs), TL, RD, L) :- 58 | zfs_uses_encr(TL), !, 59 | dracut_conf_zfs_encr(TL, RD, L). 60 | 61 | dracut_conf(fs(bcachefs), _TL, _RD, [ 62 | v(nofscks, yes) 63 | , v(add_dracutmodules, [bcachefs]) 64 | , v(add_drivers, [bcachefs]) 65 | ]) :- !. 66 | dracut_conf(fs(btrfs), _TL, _RD, [v(add_dracutmodules, [btrfs]), v(add_drivers, [btrfs])]) :- !. 67 | dracut_conf(fs(cifs), _TL, _RD, [v(add_dracutmodules, [cifs]), v(add_drivers, [cifs])]) :- !. 68 | dracut_conf(fs(nfs), _TL, _RD, [v(add_dracutmodules, [nfs]), v(add_drivers, [nfs])]) :- !. 69 | 70 | dracut_conf_zfs_encr(TL, _RD, []) :- 71 | has_boot_part(TL), 72 | !. 73 | dracut_conf_zfs_encr(TL, RD, [ 74 | v(install_items, [FN]) 75 | % , v(add_dracutmodules, [crypt]) % dracut module 'crypt' cannot be found or installed. 76 | ]) :- 77 | zfs_pool_keylocation_file(TL, FN), 78 | zfs_setup_encr(FN, RD). 79 | 80 | dracut_run(RD) :- 81 | % DL = [chroot, RD, dracut, '--no-hostonly', '--force', '2>&1'], 82 | % DL = [chroot, RD, dracut, '--regenerate-all', '--hostonly', '--force', '2>&1'], 83 | DL = [chroot, RD, dracut, '--regenerate-all', '--force', '2>&1'], 84 | tui_progressbox_safe(DL, '', [title(' Rebuilding initramfs for target '), sz(max)]), 85 | true. 86 | 87 | dracut_setup(TL, B, RD) :- 88 | dracut_conf(TL, B, RD), 89 | dracut_run(RD), 90 | true. 91 | 92 | -------------------------------------------------------------------------------- /lib/atom_common.pl: -------------------------------------------------------------------------------- 1 | % vi: noexpandtab:tabstop=4:ft=gprolog 2 | % Copyright (c) 2023-2024 Sergey Sikorskiy, released under the GNU GPLv2 license. 3 | 4 | writenl(A) :- 5 | write(A), nl. 6 | 7 | upper(I, O) :- 8 | atom_chars(I, IL), 9 | maplist(lower_upper, IL, OL), 10 | atom_chars(O, OL). 11 | 12 | chars_atom(C, A) :- 13 | atom_chars(A, C). 14 | 15 | codes_atom(C, A) :- 16 | atom_codes(A, C). 17 | 18 | codes_number(C, N) :- 19 | number_codes(N, C). 20 | 21 | % LA - left atom 22 | % RA - right atom 23 | surround_atoms(LA, RA, LI, LO) :- 24 | maplist(surround_atom(LA, RA), LI, LO). 25 | 26 | % LA - left atom 27 | % RA - right atom 28 | surround_atom(LA, RA, I, O) :- 29 | atom_concat(LA, I, LA1), 30 | atom_concat(LA1, RA, O). 31 | 32 | add_dquote(I, O) :- 33 | format_to_atom(O, '"~w"', [I]). 34 | % concat_atoms(['"', I, '"'], O), 35 | 36 | add_dquote_list(I, O) :- 37 | maplist(add_dquote_list_, I, O). 38 | 39 | add_dquote_list_(I, O) :- 40 | maplist(add_dquote, I, O). 41 | 42 | offset1s(0, ''). 43 | offset1s(1, ' '). 44 | offset1s(2, ' '). 45 | offset1s(3, ' '). 46 | offset1s(4, ' '). 47 | offset1s(5, ' '). 48 | offset1s(6, ' '). 49 | offset1s(7, ' '). 50 | offset1s(8, ' '). 51 | offset1s(9, ' '). 52 | offset1s(10, ' '). 53 | 54 | make_offset(N, O) :- 55 | M is min(N, 10), 56 | N1 is N - M, 57 | offset1s(M, O1), 58 | ( N1 =< 10 -> 59 | O = O1 60 | ; make_offset(N1, O2), 61 | atom_concat(O1, O2, O) 62 | ), !, 63 | true. 64 | 65 | read_file_codes(S, [C|T]) :- 66 | get_code(S, C), 67 | % writenl(C), 68 | C =\= -1, !, 69 | read_file_codes(S, T). 70 | read_file_codes(_, []). 71 | 72 | % Reads till a separator or EOS. 73 | % (Reads only ONE line) 74 | % SL - List of separators (codes) 75 | read_file_codes(S, SL, [C|T]) :- 76 | get_code(S, C), 77 | % writenl(C), 78 | C =\= -1, 79 | \+ member(C, SL), !, 80 | read_file_codes(S, SL, T). 81 | read_file_codes(_, _, []). 82 | 83 | read_file_codes_line(S, L) :- 84 | read_file_codes(S, [10], L). 85 | 86 | % Reads ALL lines. 87 | read_file_codes_lines(S, LL) :- 88 | read_file_codes_line(S, L1), 89 | ( L1 = [] -> 90 | LL = L1 91 | ; read_file_codes_lines(S, L2), 92 | LL = [L1|L2] 93 | ). 94 | 95 | % Reads lines, skip comments. 96 | read_file_codes_lines_nc(S, C, LL) :- 97 | read_file_codes_line(S, L1), 98 | ( L1 = [] -> 99 | LL = L1 100 | ; append(C, _, L1) -> 101 | read_file_codes_lines_nc(S, C, LL) 102 | ; read_file_codes_lines_nc(S, C, L2), 103 | LL = [L1|L2] 104 | ). 105 | 106 | read_file_chars(S, [C|T]) :- 107 | get_char(S, C), 108 | % writenl(C), 109 | C \= end_of_file, !, 110 | read_file_chars(S, T). 111 | read_file_chars(_, []). 112 | 113 | read_stream_codes(S, []) :- 114 | at_end_of_stream(S), !. 115 | read_stream_codes(S, [C|T]) :- 116 | get_code(S, C), 117 | read_stream_codes(S, T). 118 | 119 | read_stream_codes(S, _, []) :- 120 | at_end_of_stream(S), !. 121 | read_stream_codes(S, SL, [C|T]) :- 122 | get_code(S, C), 123 | \+ member(C, SL), !, 124 | read_stream_codes(S, T). 125 | read_stream_codes(_, _, []). 126 | 127 | read_stream_codes_line(S, L) :- 128 | read_file_codes(S, "\n", L). 129 | 130 | % Reads ALL lines 131 | read_stream_codes_lines(S, LL) :- 132 | read_stream_codes_line(S, L1), 133 | ( L1 = [] 134 | -> LL = L1 135 | ; read_stream_codes_lines(S, L2), 136 | LL = [L1|L2] 137 | ). 138 | 139 | -------------------------------------------------------------------------------- /lib/linux_prop_fs.pl: -------------------------------------------------------------------------------- 1 | % vi: noexpandtab:tabstop=4:ft=gprolog 2 | % Copyright (c) 2024 Sergey Sikorskiy, released under the GNU GPLv2 license. 3 | 4 | % :- multifile(prop_info/3). 5 | :- discontiguous([prop_info/3]). 6 | 7 | fs_set_prop(Tag, P, V, IPL, OPL) :- 8 | fs_set_prop0(IPL, Tag, P, V, OPL). 9 | 10 | fs_set_prop0([attr(Tag, L)| T], Tag, P, V, [attr(Tag, L1)| T]) :- !, 11 | fs_set_prop1(L, P, V, L1). 12 | fs_set_prop0([H| T], Tag, P, V, [H| T1]) :- !, 13 | fs_set_prop0(T, Tag, P, V, T1). 14 | fs_set_prop0([], Tag, P, V, [attr(Tag, [P=V])]). 15 | 16 | fs_set_prop1([P=_V0| T], P, V, [P=V| T]) :- !. 17 | fs_set_prop1([H| T], P, V, [H| T1]) :- !, 18 | fs_set_prop1(T, P, V, T1). 19 | fs_set_prop1([], P, V, [P=V]). 20 | 21 | fs_get_prop(Tag, P, IPL, V) :- 22 | memberchk(attr(Tag, PL), IPL), 23 | memberchk(P=V, PL). 24 | 25 | fs_get_label(zfs, _IPL, '') :- !, % zfs pool doesn't have a label. 26 | true. 27 | fs_get_label(FS, IPL, V) :- 28 | prop_info_label(FS, Tag), 29 | fs_get_prop(Tag, label, IPL, V), !, 30 | true. 31 | fs_get_label(_FS, _IPL, ''). 32 | 33 | fs_set_label(FS, _Label, IPL, IPL) :- 34 | memberchk(FS, [zfs, tmpfs, proc]), !. % zfs pool doesn't have a label. 35 | fs_set_label(FS, Label, IPL, OPL) :- 36 | prop_info_label(FS, Tag), 37 | ( FS = vfat -> 38 | upper(Label, Label0) 39 | ; Label0 = Label 40 | ), 41 | fs_set_prop(Tag, label, Label0, IPL, OPL), 42 | true. 43 | 44 | prop_info_label(ext2, extfs_rw) :- !. 45 | prop_info_label(ext3, extfs_rw) :- !. 46 | prop_info_label(ext4, extfs_rw) :- !. 47 | prop_info_label(btrfs, btrfs_rw) :- !. 48 | prop_info_label(f2fs, f2fs_rw) :- !. 49 | prop_info_label(vfat, vfat_rw) :- !. 50 | prop_info_label(xfs, xfs_rw) :- !. 51 | prop_info_label(bcachefs, bcachefs_rw) :- !. 52 | prop_info_label(nilfs2, nilfs2_rw) :- !. 53 | prop_info_label(exfat, exfat_rw) :- !. 54 | 55 | % default options: rw, suid, dev, exec, auto, nouser, async, (relatime). 56 | % "Do you need to specify the "defaults" option in fstab?" (https://unix.stackexchange.com/questions/191405/do-you-need-to-specify-the-defaults-option-in-fstab) 57 | prop_info(mnt_indpn_feat, feat4s('--options', ',', on_off), [ 58 | % prop_feat4(defaults, off, std, none) 59 | prop_feat4(atime, on, pref1(no), none) 60 | , prop_feat4(auto, on, pref1(no), none) 61 | , prop_feat4(dev, on, pref1(no), none) 62 | , prop_feat4(diratime, off, pref1(no), none) 63 | , prop_feat4(dirsync, off, std, none) 64 | , prop_feat4(exec, on, pref1(no), none) 65 | , prop_feat4(group, off, std, none) 66 | , prop_feat4(iversion, off, pref1(no), none) 67 | , prop_feat4(lazytime, off, pref1(no), none) 68 | , prop_feat4(mand, off, pref1(no), none) 69 | , prop_feat4(nofail, off, std, none) 70 | , prop_feat4(nosymfollow, off, std, none) 71 | , prop_feat4(owner, off, std, none) 72 | , prop_feat4(relatime, on, pref1(no), none) 73 | , prop_feat4(remount, off, std, none) 74 | , prop_feat4(ro, off, v2(ro, rw), none) 75 | , prop_feat4(silent, off, v2(silent, loud), none) 76 | , prop_feat4(strictatime, off, pref1(no), none) 77 | , prop_feat4(suid, on, pref1(no), none) 78 | , prop_feat4(sync, off, v2(sync, async), none) 79 | , prop_feat4(user, off, pref1(no), none) 80 | , prop_feat4('_netdev', off, std, none) 81 | ]). 82 | 83 | prop_info(mnt_indpn_opt, opt3s('--options', ','), [ 84 | opt3(context, str, '') 85 | , opt3(fscontext, str, '') 86 | , opt3(defcontext, str, '') 87 | , opt3(rootcontext, str, '') 88 | ]). 89 | 90 | % X-* 91 | % x-* 92 | % X-mount.auto-fstypes 93 | % X-mount.mkdir 94 | % X-mount.subdir 95 | % X-mount.owner 96 | % X-mount.group 97 | % X-mount.mode 98 | % X-mount.idmap 99 | 100 | -------------------------------------------------------------------------------- /module/void_luks.pl: -------------------------------------------------------------------------------- 1 | % vi: noexpandtab:tabstop=4:ft=gprolog 2 | % Copyright (c) 2023-2024 Sergey Sikorskiy, released under the GNU GPLv2 license. 3 | 4 | % Automatic LUKS unlock using keyfile on boot partition: https://unix.stackexchange.com/questions/666770/automatic-luks-unlock-using-keyfile-on-boot-partition 5 | 6 | uses_luks(TL) :- 7 | memberchk(bdev(luks, _), TL), 8 | !. 9 | 10 | create_keyfile(ROOT_PD, RD) :- 11 | VK = '/boot/volume.key', 12 | atom_concat(RD, VK, KF), 13 | os_shell2([dd, v(bs, 512), v(count, 4), v(if, '/dev/urandom'), v(of, KF)]), 14 | % os_shell2([dd, v(bs, 1), v(count, 64), v(if, '/dev/urandom'), v(of, KF)]), 15 | 16 | inst_setting_tmp(passwd('$_luks_$'), RPWD), 17 | lx_get_dev_disk_uuid(ROOT_PD, DISK_PUUID), 18 | tui_infobox('Adding crypto-key.', [sz([4, 40])]), 19 | ( lx_luks_add_keyfile(DISK_PUUID, KF, RPWD) 20 | ; tui_msgbox('luks_add_keyfile has failed'), 21 | fail 22 | ), !, 23 | 24 | % os_shell2([chmod, '000', KF]), 25 | os_shell2([chroot, RD, chmod, '000', VK]), 26 | os_shell2([chroot, RD, chmod, '-R', 'g-rwx,o-rwx', '/boot']), 27 | true. 28 | 29 | setup_cryptab(PDL, RD) :- 30 | atom_concat(RD, '/etc/crypttab', KF), 31 | open(KF, write, S), 32 | setup_cryptab_(S, PDL, RD), 33 | close(S), 34 | true. 35 | 36 | setup_cryptab_none(PD, RD) :- 37 | atom_concat(RD, '/etc/crypttab', KF), 38 | open(KF, write, S), 39 | setup_cryptab_kf_none(S, PD), 40 | close(S), 41 | true. 42 | 43 | setup_cryptab_(S, [PD], _RD) :- 44 | setup_cryptab_kf(S, PD), 45 | true. 46 | setup_cryptab_(S, [PD|T], RD) :- 47 | % Download decrypt_keyctl 48 | os_mkdir_p(RD + '/lib/cryptsetup/scripts'), 49 | % tui_progressbox_safe is causes weird problems. 50 | % tui_progressbox_safe([curl, 'https://raw.githubusercontent.com/gebi/keyctl_keyscript/master/decrypt_keyctl', o(o, RD + '/lib/cryptsetup/scripts/decrypt_keyctl')], '', [title(' Downloading decrypt_keyctl '), sz([6, 80])]), 51 | tui_progressbox_unsafe([curl, 'https://raw.githubusercontent.com/gebi/keyctl_keyscript/master/decrypt_keyctl', o(o, RD + '/lib/cryptsetup/scripts/decrypt_keyctl')], '', [title(' Downloading decrypt_keyctl '), sz([6, 80])]), 52 | os_shell2([chroot, RD, chmod, '711', '/lib/cryptsetup/scripts/decrypt_keyctl']), 53 | 54 | setup_cryptab_ks(S, 'key_1:', PD), 55 | maplist(setup_cryptab_ks(S, 'key_1'), T), 56 | true. 57 | 58 | setup_cryptab_kf(S, PD) :- 59 | lx_get_dev_uuid(PD, PUUID), 60 | lx_split_dev(PD, _P, SDN), 61 | luks_dev_name_short(SDN, LUKS_PD), 62 | format(S, '~w UUID=~w /boot/volume.key luks', [LUKS_PD, PUUID]), 63 | nl(S), 64 | true. 65 | 66 | setup_cryptab_kf_none(S, PD) :- 67 | lx_get_dev_uuid(PD, PUUID), 68 | lx_split_dev(PD, _P, SDN), 69 | luks_dev_name_short(SDN, LUKS_PD), 70 | format(S, '~w UUID=~w none luks', [LUKS_PD, PUUID]), 71 | nl(S), 72 | true. 73 | 74 | setup_cryptab_ks(S, K, PD) :- 75 | lx_get_dev_uuid(PD, PUUID), 76 | lx_split_dev(PD, _P, SDN), 77 | luks_dev_name_short(SDN, LUKS_PD), 78 | format(S, '~w UUID=~w ~w luks,keyscript=/lib/cryptsetup/scripts/decrypt_keyctl', [LUKS_PD, PUUID, K]), 79 | nl(S), 80 | true. 81 | 82 | % SDN - short device name. 83 | luks_dev_name(SDN, LUKS_PD) :- 84 | inst_setting(luks, luks(Name)), 85 | % lx_luks_dev_name(Name, LUKS_PD), 86 | format_to_atom(LUKS_PD, '/dev/mapper/~w_~w', [Name, SDN]), 87 | true. 88 | 89 | luks_dev_name_short(SDN, LUKS_PD) :- 90 | inst_setting(luks, luks(Name)), 91 | format_to_atom(LUKS_PD, '~w_~w', [Name, SDN]), 92 | true. 93 | 94 | setup_crypt(TL, RD) :- 95 | findall(PD, member(bdev(luks, luks(_, PD)), TL), PDL), 96 | PDL = [PD| _], 97 | % Create the keyfile 98 | create_keyfile(PD, RD), 99 | % Setup crypttab 100 | setup_cryptab(PDL, RD), 101 | true. 102 | 103 | setup_crypt_none(TL, RD) :- 104 | findall(PD, member(bdev(luks, luks(_, PD)), TL), PDL), 105 | PDL = [PD| _], 106 | % Setup crypttab 107 | setup_cryptab_none(PD, RD), 108 | true. 109 | 110 | -------------------------------------------------------------------------------- /lib/tui_ext.pl: -------------------------------------------------------------------------------- 1 | % vi: noexpandtab:tabstop=4:ft=gprolog 2 | % Copyright (c) 2024 Sergey Sikorskiy, released under the GNU GPLv2 license. 3 | 4 | :- dynamic(tui_setting_tmp/2). 5 | :- discontiguous([menu_list_on_init/4, menu_list_on_pre/5, menu_list_on_post/7]). 6 | 7 | % Vertical form. 8 | % FLen - edit field lenght 9 | % ILen - input field lenght 10 | % L - list of items 11 | % M - message 12 | % AL - atom list 13 | tui_form_v(FLen, ILen, L, M, UA, AL) :- 14 | tui_make_form_list(FLen, ILen, L, 1, SO), 15 | tui_box3(form, M, SO, UA, OutL, ok), 16 | split_line_codes(OutL, LL), 17 | maplist(codes_atom, LL, AL). 18 | 19 | tui_passwordform_v(FLen, ILen, L, M, UA, AL) :- 20 | tui_make_form_list(FLen, ILen, L, 1, SO), 21 | tui_box3(passwordform, M, SO, UA, OutL, ok), 22 | split_line_codes(OutL, LL), 23 | maplist(codes_atom, LL, AL). 24 | 25 | % Fields have a type: 0, 1 - hidden/password, 2 - readonly, 3. 26 | tui_mixedform_v(FLen, ILen, L, M, UA, AL) :- 27 | tui_make_form_list_t(FLen, ILen, L, 1, SO), 28 | tui_box3(mixedform, M, SO, UA, OutL, ok), 29 | split_line_codes(OutL, LL), 30 | maplist(codes_atom, LL, AL). 31 | 32 | % IL - input list 33 | % OL - output list or a value 34 | % EV - external value 35 | % UA - user attributes 36 | menu_list_2(TAG, EV, IL, OL, UA) :- 37 | dialog_msg(menu, LABEL), 38 | % LV - local value 39 | menu_list_on_init(TAG, EV, IL, LV), 40 | % R - reset/keep stored settings 41 | R = keep, 42 | ( tui_setting_tmp(menu_list(TAG), v(_, _IL0)), R \= reset 43 | ; retractall(tui_setting_tmp(menu_list(TAG), _)), 44 | assertz(tui_setting_tmp(menu_list(TAG), v(none, IL))) 45 | ), !, 46 | repeat, 47 | % VL - value list. 48 | retract(tui_setting_tmp(menu_list(TAG), v(DI, VL))), 49 | menu_list_on_pre(TAG, EV, LV, VL, PL), 50 | tui_menu_tag_rc(PL, LABEL, [default-item(DI)| UA], Tag, RC), 51 | ( memberchk(RC, [cancel, esc]) -> 52 | % Accept 53 | % Tag is invalid here. 54 | menu_list_on_post(TAG, get, EV, LV, VL, DI, OL), 55 | assertz(tui_setting_tmp(menu_list(TAG), v(DI, VL))) 56 | ; menu_list_on_post(TAG, set, EV, LV, VL, Tag, OL1), 57 | assertz(tui_setting_tmp(menu_list(TAG), v(Tag, OL1))), 58 | fail 59 | ), !. 60 | 61 | % IL - input list 62 | % OL - output list or a value 63 | % EV - external value 64 | % UA - user attributes 65 | menu_list_3(TAG, Label, EV, IL, OL, UA) :- 66 | % LV - local value 67 | menu_list_on_init(TAG, EV, IL, LV), 68 | ( tui_setting_tmp(menu_list(TAG), v(DI0, IL0)) 69 | ; IL0 = IL, 70 | DI0 = none, 71 | assertz(tui_setting_tmp(menu_list(TAG), v(DI0, IL0))) 72 | ), !, 73 | repeat, 74 | % VL - value list. 75 | retract(tui_setting_tmp(menu_list(TAG), v(DI, VL))), 76 | menu_list_on_pre(TAG, EV, LV, VL, PL), 77 | tui_menu_tag_rc(PL, Label, [default-item(DI), extra-button| UA], Tag, RC), 78 | ( RC = extra -> 79 | % Accept 80 | menu_list_on_post(TAG, get, EV, LV, VL, Tag, OL), 81 | assertz(tui_setting_tmp(menu_list(TAG), v(DI, VL))) 82 | ; memberchk(RC, [cancel, esc]) -> 83 | % Tag is invalid here. 84 | % Restore initial state. 85 | menu_list_on_post(TAG, cancel, EV, LV, IL0, DI0, OL), 86 | assertz(tui_setting_tmp(menu_list(TAG), v(DI, IL0))) 87 | ; menu_list_on_post(TAG, set, EV, LV, VL, Tag, OL1), 88 | assertz(tui_setting_tmp(menu_list(TAG), v(Tag, OL1))), 89 | false 90 | ), !. 91 | 92 | % EV - external value 93 | checklist_2(TAG, Title, EV, IL, OL) :- 94 | dialog_msg(checklist, LABEL), 95 | % LV - local value 96 | checklist_on_all(TAG, EV, LV), 97 | ( retract(tui_setting_tmp(check_list(TAG), VL)) 98 | ; VL = IL 99 | ), !, 100 | % Convert prop-list into checklist format. 101 | checklist_on_make(TAG, EV, LV, VL, PL), 102 | tui_checklist_tag_rc(PL, LABEL, [no-tags, title(Title)], OL1, RC), 103 | ( RC = cancel -> 104 | assertz(tui_setting_tmp(check_list(TAG), VL)), 105 | OL = VL 106 | ; checklist_on_set(TAG, EV, LV, OL1, OL), 107 | assertz(tui_setting_tmp(check_list(TAG), OL)) 108 | ), !. 109 | 110 | 111 | -------------------------------------------------------------------------------- /lib/fs_xfs.pl: -------------------------------------------------------------------------------- 1 | % vi: noexpandtab:tabstop=4:ft=gprolog 2 | % Copyright (c) 2024 Sergey Sikorskiy, released under the GNU GPLv2 license. 3 | 4 | % man xfs 5 | % man mkfs.xfs 6 | % man xfs_admin 7 | % man xfs_growfs 8 | 9 | prop_info(xfs_block_size_rw, opt3s('-b', ','), [ 10 | opt3(size, int, 4096) 11 | ]). 12 | 13 | prop_info(xfs_global_metadata_rw, opt3s('-m', ','), [ 14 | opt3(bigtime, enum([0, 1]), 1) 15 | , opt3(crc, enum([0, 1]), 1) 16 | , opt3(finobt, enum([0, 1]), 1) 17 | , opt3(inobtcount, enum([0, 1]), 1) 18 | , opt3(uuid, guid, '') 19 | , opt3(rmapbt, enum([0, 1]), 1) 20 | , opt3(reflink, enum([0, 1]), 1) 21 | ]). 22 | 23 | prop_info(xfs_data_section_rw, opt3s('-d', ','), [ 24 | opt3(agcount, int, 1) 25 | , opt3(agsize, size, '16m') 26 | , opt3(cowextsize, int, 0) 27 | , opt3(name, str, '') 28 | , opt3(file, enum([0, 1]), 1) 29 | , opt3(size, size, 0) 30 | , opt3(sunit, int, 1) 31 | , opt3(su, size, 0) 32 | , opt3(swidth, int, 0) 33 | , opt3(sw, size, 0) 34 | , opt3(noalign, enable, no) 35 | , opt3(rtinherit, enum([0, 1]), 0) 36 | , opt3(projinherit, int, 0) 37 | , opt3(extszinherit, size, 0) 38 | , opt3(daxinherit, enum([0, 1]), 0) 39 | ]). 40 | 41 | prop_info(xfs_inode_rw, opt3s('-i', ','), [ 42 | opt3(size, size, 512) 43 | , opt3(perblock, int, 0) 44 | , opt3(maxpct, percentage, 5) 45 | , opt3(align, enum([0, 1]), 1) 46 | , opt3(attr, int, 2) 47 | , opt3(projid32bit, enum([0, 1]), 1) 48 | , opt3(sparse, enum([0, 1]), 1) 49 | , opt3(nrext64, int, 1) 50 | ]). 51 | 52 | prop_info(xfs_log_rw, opt3s('-l', ','), [ 53 | opt3(agnum, int, 0) 54 | , opt3(internal, enum([0, 1]), 1) 55 | , opt3(logdev, str, '') 56 | , opt3(size, size, 0) 57 | , opt3(version, int, 2) 58 | , opt3(sunit, int, 1) 59 | , opt3(su, size, 0) 60 | , opt3('lazy-count', enum([0, 1]), 1) 61 | ]). 62 | 63 | prop_info(xfs_naming_rw, opt3s('-n', ','), [ 64 | opt3(size, size, 4096) 65 | , opt3(version, enum([2, ci]), 2) 66 | , opt3(ftype, enum([0, 1]), 1) 67 | ]). 68 | 69 | prop_info(xfs_protofile_rw, opt3s('-p', ','), [ 70 | opt3(file, file, '') 71 | , opt3(slashes_are_spaces, enum([0, 1]), 0) 72 | ]). 73 | 74 | prop_info(xfs_realtime_rw, opt3s('-r', ','), [ 75 | opt3(rtdev, str, '') 76 | , opt3(extsize, size, '64k') 77 | , opt3(size, size, 0) 78 | , opt3(noalign, enable, no) 79 | ]). 80 | 81 | prop_info(xfs_sector_size_rw, opt3s('-s', ','), [ 82 | opt3(size, size, 512) 83 | ]). 84 | 85 | prop_info(xfs_rw, opt4s(' '), [ 86 | opt4(label, str, '', '-L') 87 | , opt4(discard, enable, yes, '-K') 88 | , opt4(quite, enable, no, '-q') 89 | , opt4(force, enable, no, '-f') 90 | ]). 91 | 92 | prop_info(mnt_xfs_feat, feat4s('--options', ',', on_off), [ 93 | prop_feat4(attr2, off, pref1(no), none) 94 | , prop_feat4(bsdgroups, off, std, none) 95 | , prop_feat4(discard, off, pref1(no), none) 96 | , prop_feat4(filestreams, off, std, none) 97 | , prop_feat4(gqnoenforce, off, std, none) 98 | , prop_feat4(gquota, off, std, none) 99 | , prop_feat4(grpid, off, pref1(no), none) 100 | , prop_feat4(grpquota, off, std, none) 101 | , prop_feat4(inode64, on, v2(inode64, inode32), none) 102 | , prop_feat4(largeio, off, pref1(no), none) 103 | , prop_feat4(noalign, off, std, none) 104 | , prop_feat4(noquota, off, std, none) 105 | , prop_feat4(norecovery, off, std, none) 106 | , prop_feat4(nouuid, off, std, none) 107 | , prop_feat4(pqnoenforce, off, std, none) 108 | , prop_feat4(pquota, off, std, none) 109 | , prop_feat4(prjquota, off, std, none) 110 | , prop_feat4(qnoenforce, off, std, none) 111 | , prop_feat4(quota, off, std, none) 112 | , prop_feat4(swalloc, off, std, none) 113 | , prop_feat4(sysvgroups, off, std, none) 114 | , prop_feat4(uqnoenforce, off, std, none) 115 | , prop_feat4(uquota, off, std, none) 116 | , prop_feat4(usrquota, off, std, none) 117 | , prop_feat4(wsync, off, std, none) 118 | ]). 119 | 120 | prop_info(mnt_xfs_opt, opt3s('--options', ','), [ 121 | opt3(allocsize, size, '4KB') 122 | , opt3(dax, enum([inode, never, always]), inode) 123 | , opt3(logbufs, enum([2, 3, 4, 5, 6, 7, 8]), 8) 124 | , opt3(logbsize, size, 32768) 125 | , opt3(logdev, str, '') 126 | , opt3(rtdev, str, '') 127 | , opt3(sunit, int, 0) 128 | , opt3(swidth, int, 0) 129 | ]). 130 | 131 | 132 | -------------------------------------------------------------------------------- /lib/linux_lvm.pl: -------------------------------------------------------------------------------- 1 | % vi: noexpandtab:tabstop=4:ft=gprolog 2 | % Copyright (c) 2023-2024 Sergey Sikorskiy, released under the GNU GPLv2 license. 3 | 4 | % pvs -o pv_name --no-heading 5 | lvm_pvs(L) :- 6 | os_shell_lines_codes('pvs -o pv_name,vg_name --readonly --noheading --separator "\t" --units b --nosuffix 2>/dev/null', CL), 7 | % os_shell_lines_codes('pvs --readonly --noheading --separator "\t" --units b --nosuffix 2>/dev/null', CL), 8 | % CL \= [], 9 | maplist(lvm_pvs_convert_, CL, L), 10 | true. 11 | 12 | lvm_lvs(L) :- 13 | os_shell_lines_codes('lvs -o vg_name,lv_name,lv_dm_path --noheading --separator "\t" 2>/dev/null', CL), 14 | CL \= [], 15 | maplist(lvm_lvs_convert_, CL, L), 16 | true. 17 | 18 | lvm_pvs_convert_(L, pv(PV,VG)) :- 19 | split_list(L, "\t", SL0), 20 | SL0 = [[32,32|H]|T], 21 | SL = [H|T], 22 | maplist(codes_atom, SL, [PV,VG]), 23 | true. 24 | 25 | lvm_lvs_convert_(L, lv(VG,LV,Path)) :- 26 | split_list(L, "\t", SL0), 27 | SL0 = [[32,32|H]|T], 28 | SL = [H|T], 29 | maplist(codes_atom, SL, [VG,LV,Path]), 30 | true. 31 | 32 | % PV - physical volume. 33 | lvm_pvcreate_unsafe(PV) :- 34 | % tui_msgbox_w([pvcreate, '--yes', PV, '1>/dev/null', '2>/dev/null']), 35 | os_shell2([pvcreate, '--yes', PV, '1>/dev/null', '2>/dev/null']), 36 | % os_shell2([pvcreate, PV, '1>/dev/null']), 37 | % os_call2([pvcreate, '--yes', PV]), 38 | true. 39 | 40 | lvm_pvremove(PV) :- 41 | % Remove from a VG. 42 | os_shell2([pvremove, '-y', PV, '1>/dev/null', '2>/dev/null']), 43 | true. 44 | 45 | lvm_vgcreate(VG, PVL) :- 46 | % os_shell2([vgcreate, VG, PVL]), 47 | os_shell2([vgcreate, VG, PVL, '1>/dev/null', '2>/dev/null']), 48 | true. 49 | 50 | lvm_vgremove('') :- !. % There is no VG. 51 | lvm_vgremove(VG) :- 52 | os_shell2([vgremove, VG, '1>/dev/null', '2>/dev/null']), 53 | true. 54 | 55 | lvm_vgremove_unsafe('') :- !. % There is no VG. 56 | lvm_vgremove_unsafe(VG) :- 57 | os_shell2([vgremove, '-y', VG, '1>/dev/null', '2>/dev/null']), 58 | true. 59 | 60 | /* Is it correct? 61 | % Remove PV from VG. Remove VG if there is no more PVs left in it. 62 | % PV - long device name 63 | lvm_pvremove_unsafe(VG, PV) :- 64 | % list all PVs 65 | lvm_pvs(L), 66 | % get all PVs in VG 67 | findall(PV1, member(pv(PV1,VG), L), PVL), 68 | lvm_vgremove_unsafe_(VG, PV, PVL), 69 | true. 70 | 71 | % PV - long device name 72 | lvm_vgremove_unsafe_(_VG, _PV, []) :- !. % There are no PVs in VG 73 | lvm_vgremove_unsafe_(VG, PV, [PV]) :- !, % This is the last device in VG. 74 | % Remove all LV in VG. 75 | lvm_lvremove_unsafe(VG), 76 | % Remove whole VG. 77 | lvm_vgremove(VG). 78 | lvm_vgremove_unsafe_(VG, PV, _) :- !, 79 | lvm_vgreduce(VG, PV). 80 | 81 | lvm_vgreduce('', _PV) :- !. 82 | lvm_vgreduce(VG, PV) :- 83 | os_call2([vgreduce, VG, PV]), 84 | true. 85 | */ 86 | 87 | lvm_pvremove_unsafe(PV) :- 88 | % list all PVs 89 | lvm_pvs(L), 90 | lvm_pvremove_unsafe_1(PV, L), 91 | true. 92 | 93 | lvm_pvremove_unsafe_1(_PV, []) :- !. 94 | lvm_pvremove_unsafe_1(PV, L) :- 95 | % Get a VG. 96 | memberchk(pv(PV, VG), L), 97 | ( VG = '' -> 98 | lvm_pvremove(PV) 99 | ; % Get a list of all PV in VG. 100 | findall(P, member(pv(P, VG), L), PVL), 101 | ( PVL = [_] -> 102 | lvm_vgremove(VG) 103 | ; os_call2([vgreduce, '--yes', '--force', VG, PV]), 104 | lvm_pvremove(PV) 105 | ) 106 | ), 107 | true. 108 | 109 | % with the rest of capacity 110 | lvm_lvcreate_unsafe(VG, LV) :- 111 | lvm_lvcreate_unsafe(VG, LV, '100%FREE'). 112 | 113 | lvm_lvcreate_unsafe(VG, LV, '') :- !, 114 | os_shell2([lvcreate, '--yes', '--extents', '100%FREE', '--name', LV, VG, '1>/dev/null', '2>/dev/null']), 115 | true. 116 | lvm_lvcreate_unsafe(VG, LV, SZ) :- 117 | % os_shell2([lvcreate, '--yes', '--extents', SZ, VG, '-n', LV]), 118 | os_shell2([lvcreate, '--yes', '--size', SZ, '--name', LV, VG, '1>/dev/null', '2>/dev/null']), 119 | true. 120 | 121 | % Remove all LVs the specified VG. 122 | lvm_lvremove_unsafe('') :- !. % There is no LV. 123 | lvm_lvremove_unsafe(VG) :- 124 | % os_call2([lvremove, '--yes', VG]), 125 | os_shell2([lvremove, '--yes', '--force', VG, '1>/dev/null', '2>/dev/null']), 126 | true. 127 | 128 | lvm_lvremove_unsafe(VG, LV) :- 129 | format_to_atom(A, '~w/~w', [VG, LV]), 130 | os_shell2([lvremove, '--yes', '--force', A, '1>/dev/null', '2>/dev/null']), 131 | true. 132 | 133 | -------------------------------------------------------------------------------- /module/void_zfsbootmenu.pl: -------------------------------------------------------------------------------- 1 | % vi: noexpandtab:tabstop=4:ft=gprolog 2 | % Copyright (c) 2023-2024 Sergey Sikorskiy, released under the GNU GPLv2 license. 3 | 4 | /* 5 | % Original config.yaml 6 | % /etc/zfsbootmenu/config.yaml 7 | Global: 8 | ManageImages: false 9 | BootMountPoint: /boot/efi 10 | DracutConfDir: /etc/zfsbootmenu/dracut.conf.d 11 | PreHooksDir: /etc/zfsbootmenu/generate-zbm.pre.d 12 | PostHooksDir: /etc/zfsbootmenu/generate-zbm.post.d 13 | InitCPIOConfig: /etc/zfsbootmenu/mkinitcpio.conf 14 | Components: 15 | ImageDir: /boot/efi/EFI/zbm 16 | Versions: 3 17 | Enabled: true 18 | syslinux: 19 | Config: /boot/syslinux/syslinux.cfg 20 | Enabled: false 21 | EFI: 22 | ImageDir: /boot/efi/EFI/zbm 23 | Versions: false 24 | Enabled: false 25 | Kernel: 26 | CommandLine: ro quiet loglevel=0 27 | */ 28 | 29 | zfsbootmenu_install(TL, BD, RD) :- 30 | os_call2([zfs, set, 'org.zfsbootmenu:commandline="quiet loglevel=4"', 'zroot/ROOT']), 31 | ( \+ inst_setting(system(efi), _) 32 | ; zfsbootmenu_install_efi(BD, RD) 33 | ), !, 34 | ( \+ inst_setting(system(bios), _) 35 | ; zfsbootmenu_install_bios(TL, BD, RD) 36 | ), !, 37 | true. 38 | 39 | zfsbootmenu_install_efi(BD, RD) :- 40 | tui_progressbox_safe([chroot, RD, 'generate-zbm', '2>&1'], '', [title(' generate-zbm '), sz([6, 60])]), 41 | tui_progressbox_safe([chroot, RD, efibootmgr, '--create', oo(disk, BD), oo(part, 1), oo(loader, '/EFI/zbm/vmlinuz-backup.EFI'), oo(label, '"ZFSBootMenu (Backup)"'), '2>&1'], 'efibootmgr', [sz([12, 80])]), 42 | tui_progressbox_safe([chroot, RD, efibootmgr, '--create', oo(disk, BD), oo(part, 1), oo(loader, '/EFI/zbm/vmlinuz.EFI'), oo(label, '"ZFSBootMenu"'), '2>&1'], 'efibootmgr', [sz([12, 80])]), 43 | true. 44 | 45 | zfsbootmenu_install_bios(TL, BD, RD) :- 46 | soft_install_soft_chroot([syslinux], RD), 47 | syslinux_install_bios(TL, BD, RD), 48 | tui_progressbox_safe(['xbps-reconfigure', o(r, RD), '-f', zfsbootmenu, '2>&1'], '', [title(' Reconfigure zfsBootMenu '), sz([18, 60])]), 49 | true. 50 | 51 | zfsbootmenu_configure(RD) :- 52 | os_mkdir_p(RD + '/etc/zfsbootmenu'), 53 | atom_concat(RD, '/etc/zfsbootmenu/config.yaml', FN), 54 | open(FN, write, S), 55 | ( \+ inst_setting(system(efi), _) 56 | ; zfsbootmenu_configure_efi(S) 57 | ), !, 58 | ( \+ inst_setting(system(bios), _) 59 | ; zfsbootmenu_configure_bios(S), 60 | zfsbootmenu_configure_syslinux(RD) 61 | ), !, 62 | close(S), 63 | true. 64 | 65 | zfsbootmenu_configure_efi(S) :- 66 | write(S, 'Global:'), nl(S), 67 | write(S, ' ManageImages: true'), nl(S), 68 | write(S, ' BootMountPoint: /boot/efi'), nl(S), 69 | write(S, 'Components:'), nl(S), 70 | write(S, ' Enabled: false'), nl(S), 71 | write(S, 'EFI:'), nl(S), 72 | write(S, ' ImageDir: /boot/efi/EFI/zbm'), nl(S), 73 | write(S, ' Versions: false'), nl(S), 74 | write(S, ' Enabled: true'), nl(S), 75 | write(S, 'Kernel:'), nl(S), 76 | write(S, ' CommandLine: quiet loglevel=0'), nl(S), 77 | true. 78 | 79 | zfsbootmenu_configure_bios(S) :- 80 | write(S, 'Global:'), nl(S), 81 | write(S, ' ManageImages: true'), nl(S), 82 | write(S, ' BootMountPoint: /boot/syslinux'), nl(S), 83 | write(S, 'Components:'), nl(S), 84 | write(S, ' Enabled: true'), nl(S), 85 | write(S, ' Versions: false'), nl(S), 86 | write(S, ' ImageDir: /boot/syslinux/zfsbootmenu'), nl(S), 87 | true. 88 | 89 | zfsbootmenu_configure_syslinux(RD) :- 90 | os_mkdir_p(RD + '/boot/syslinux'), 91 | atom_concat(RD, '/boot/syslinux/syslinux.cfg', FN), 92 | open(FN, write, S), 93 | 94 | write(S, 'UI menu.c32'), nl(S), 95 | write(S, 'PROMPT 0'), nl(S), 96 | nl(S), 97 | write(S, 'MENU TITLE ZFSBootMenu'), nl(S), 98 | write(S, 'TIMEOUT 50'), nl(S), 99 | nl(S), 100 | write(S, 'DEFAULT zfsbootmenu'), nl(S), 101 | nl(S), 102 | write(S, 'LABEL zfsbootmenu'), nl(S), 103 | write(S, ' MENU LABEL ZFSBootMenu'), nl(S), 104 | write(S, ' KERNEL /zfsbootmenu/vmlinuz-bootmenu'), nl(S), 105 | write(S, ' INITRD /zfsbootmenu/initramfs-bootmenu.img'), nl(S), 106 | write(S, ' APPEND zfsbootmenu quiet loglevel=4'), nl(S), 107 | nl(S), 108 | write(S, 'LABEL zfsbootmenu-backup'), nl(S), 109 | write(S, ' MENU LABEL ZFSBootMenu (Backup)'), nl(S), 110 | write(S, ' KERNEL /zfsbootmenu/vmlinuz-bootmenu-backup'), nl(S), 111 | write(S, ' INITRD /zfsbootmenu/initramfs-bootmenu-backup.img'), nl(S), 112 | write(S, ' APPEND zfsbootmenu quiet loglevel=4'), nl(S), 113 | write(S, 'EOF'), nl(S), 114 | 115 | close(S), 116 | true. 117 | 118 | -------------------------------------------------------------------------------- /module/void_grub.pl: -------------------------------------------------------------------------------- 1 | % vi: noexpandtab:tabstop=4:ft=gprolog 2 | % Copyright (c) 2023-2024 Sergey Sikorskiy, released under the GNU GPLv2 license. 3 | 4 | % https://github.com/AdisonCavani/distro-grub-themes 5 | 6 | arch2grub(ARCH, GRUB) :- 7 | % efi-grub depends on bios-grub. 8 | inst_setting(system(efi), _), !, 9 | arch2grub_efi(ARCH, GRUB), 10 | !. 11 | arch2grub(_, grub). 12 | 13 | arch2grub_efi('x86_64', 'grub-x86_64-efi'). 14 | arch2grub_efi('x86_64-musl', 'grub-x86_64-efi'). 15 | arch2grub_efi('i686', 'grub'). 16 | arch2grub_efi('armv6l', 'grub-arm-efi'). % ??? 17 | arch2grub_efi('armv7l', 'grub-arm-efi'). % ??? 18 | arch2grub_efi('aarch64', 'grub-arm64-efi'). 19 | % arch2grub_efi('ia32', 'grub-i386-efi'). 20 | 21 | % Install grub. 22 | % BD is the disk (not a partition) 23 | grub_install(TL, BD, RD) :- 24 | grub_install_env(TL, ENV), 25 | ( \+ inst_setting(system(efi), _) 26 | ; grub_install_efi(ENV, BD, RD) 27 | ), !, 28 | ( \+ inst_setting(system(bios), _) 29 | ; grub_install_bios(ENV, BD, RD) 30 | ), !, 31 | true. 32 | 33 | % BD is the disk (not a partition) 34 | grub_install_efi(ENV, BD, RD) :- 35 | inst_setting(system(efi), EFI_TARGET), 36 | OL = [oo(target, EFI_TARGET), '--efi-directory=/boot/efi', '--bootloader-id=void_grub_efi', '--recheck'], 37 | CL = [ENV, chroot, RD, 'grub-install', OL, BD, '2>&1'], 38 | % os_shell2(CL), 39 | tui_progressbox_safe(CL, '', [title(' Installing GRUB for EFI '), sz([6, 60])]), 40 | true. 41 | 42 | grub_install_bios(ENV, BD, RD) :- 43 | OL = ['--target=i386-pc', '--bootloader-id=void_grub_bios', '--recheck'], 44 | CL = [ENV, chroot, RD, 'grub-install', OL, BD, '2>&1'], 45 | % os_shell2(CL), 46 | tui_progressbox_safe(CL, '', [title(' Installing GRUB for BIOS '), sz([6, 60])]), 47 | true. 48 | 49 | grub_install_env(TL, ['ZPOOL_VDEV_NAME_PATH=1']) :- 50 | % grub_install_env(TL, ['ZPOOL_VDEV_NAME_GUID=1']) :- 51 | uses_zfs(TL), !, 52 | true. 53 | grub_install_env(_TL, []) :- 54 | true. 55 | 56 | grub_config(TL, L) :- 57 | grub_config_luks(TL, L). 58 | grub_config([ 59 | v('GRUB_DEFAULT', 0, '') 60 | , v('GRUB_TIMEOUT', 5, '') 61 | , v('GRUB_DISTRIBUTOR', 'Void', '') 62 | , v('GRUB_DISABLE_OS_PROBER', true, '') 63 | % , v('GRUB_DISABLE_RECOVERY', true, '') 64 | % , v('GRUB_TERMINAL_INPUT', console, '') 65 | % , v('GRUB_TERMINAL_OUTPUT', console, '') 66 | % , v('', '', '') 67 | ]). 68 | 69 | grub_config_luks(TL, [ 70 | v('GRUB_ENABLE_CRYPTODISK', y, '') 71 | , v('GRUB_CMDLINE_LINUX_DEFAULT', V, 'Generic settings') 72 | ]) :- 73 | uses_luks(TL), !, 74 | findall(M, (grub_linux_cmdline_luks(TL, L), member(M, L)), VL), 75 | os_scmdl(VL, V). 76 | grub_config_luks(TL, [ 77 | v('GRUB_CMDLINE_LINUX_DEFAULT', V, 'Generic settings') 78 | ]) :- 79 | ( root_fs(TL, bcachefs) -> 80 | % root_pd(TL, PD), 81 | % lx_get_dev_partuuid(PD, PU), 82 | % VL = [root=v('PARTUUID', PU), rootfstype=bcachefs, 'rd.luks'=0, loglevel=4] 83 | VL = [rootfstype=bcachefs, 'rd.luks'=0, loglevel=4] 84 | ; VL = ['rd.luks'=0, loglevel=4] 85 | ), 86 | os_scmdl(VL, V). 87 | 88 | grub_linux_cmdline_luks(TL, ['rd.luks.name'=v(PUUID, LUKS_PD)]) :- 89 | member(bdev(luks, luks(_, PD)), TL), 90 | lx_get_dev_uuid(PD, PUUID), 91 | lx_split_dev(PD, _P, SDN), 92 | luks_dev_name_short(SDN, LUKS_PD), 93 | true. 94 | grub_linux_cmdline_luks(_TL, ['rd.auto'=1]) :- 95 | inst_setting(hostonly, no). 96 | % grub_linux_cmdline_luks(_TL, ['rd.debug', rootfstype=zfs, loglevel=4, slub_debug='FZ', slab_nomerge=1, pti=on, mce=0, 'printk.time'=1]). 97 | % grub_linux_cmdline_luks(_TL, [root='/dev/mapper/crypt_sda2', rootfstype=zfs, loglevel=4, slub_debug='FZ', slab_nomerge=1, mce=0, 'printk.time'=1]). 98 | grub_linux_cmdline_luks(_TL, [loglevel=4, slub_debug='FZ', slab_nomerge=1, mce=0, 'printk.time'=1]). 99 | 100 | grub_configure(TL, RD) :- 101 | % Configure grub. 102 | findall(M, (grub_config(TL, L), member(M, L)), GVL), 103 | % Generate /etc/default/grub. 104 | grub_sconf(GVL, RD), 105 | true. 106 | 107 | grub_mkconfig(TL, RD) :- 108 | grub_install_env(TL, ENV), 109 | % CL2 = [chroot, RD, 'grub-mkconfig', o(o, '/boot/grub/grub.cfg'), '2>&1'], 110 | % CL2 = [stdbuf, '-oL', env, ENV, chroot, RD, 'grub-mkconfig', o(o, '/boot/grub/grub.cfg'), '2>&1'], 111 | CL2 = [ENV, chroot, RD, 'grub-mkconfig', o(o, '/boot/grub/grub.cfg'), '2>&1'], 112 | % os_shell2(CL2), 113 | tui_progressbox_safe(CL2, '', [title(' Generating grub configuration file '), sz([10, 60])]), 114 | % Watch the udev event queue, and exit if all current events are handled. 115 | os_call2([udevadm, settle]), 116 | true. 117 | 118 | -------------------------------------------------------------------------------- /lib/fs_btrfs.pl: -------------------------------------------------------------------------------- 1 | % vi: noexpandtab:tabstop=4:ft=gprolog 2 | % Copyright (c) 2024 Sergey Sikorskiy, released under the GNU GPLv2 license. 3 | 4 | % man btrfs 5 | % man 5 btrfs 6 | % man mkfs.btrfs 7 | % ls /sys/fs/btrfs/features/ 8 | % btrfs_feat(name, [compat, safe, default], descr), "default" means "enabled". 9 | % kv - kernel version. 10 | prop_info(btrfs_feat, feat4s('--features', ',', pref1('^')), [ 11 | prop_feat4('mixed-bg', off, std, attr([compat=kv(2, 6, 37), safe=kv(2, 6, 37)], 'mixed data and metadata block groups (compat=2.6.37, safe=2.6.37)')) 12 | , prop_feat4(quota, off, std, attr([compat=kv(3, 4, 0)], 'quota support (qgroups) (compat=3.4)')) 13 | , prop_feat4(extref, on, std, attr([compat=kv(3, 7, 0), safe=kv(3, 12, 0), default=kv(3, 12, 0)], 'increased hardlink limit per file to 65536 (compat=3.7, safe=3.12, default=3.12)')) 14 | , prop_feat4(raid56, off, std, attr([compat=kv(3, 9, 0)], 'raid56 extended format (compat=3.9)')) 15 | , prop_feat4('skinny-metadata', on, std, attr([compat=kv(3, 10, 0), safe=kv(3, 18, 0), default=kv(3, 18, 0)], 'reduced-size metadata extent refs (compat=3.10, safe=3.18, default=3.18)')) 16 | , prop_feat4('no-holes', on, std, attr([compat=kv(3, 14, 0), safe=kv(4, 0, 0), default=kv(5, 15, 0)], 'no explicit hole extents for files (compat=3.14, safe=4.0, default=5.15)')) 17 | , prop_feat4('free-space-tree', on, std, attr([compat=kv(4, 5, 0), safe=kv(4, 9, 0), default=kv(5, 15, 0)], 'free space tree (space_cache=v2) (compat=4.5, safe=4.9, default=5.15)')) 18 | , prop_feat4(raid1c34, off, std, attr([compat=kv(5, 5, 0)], 'RAID1 with 3 or 4 copies (compat=5.5)')) 19 | , prop_feat4(zoned, off, std, attr([compat=kv(5, 12, 0)], 'support zoned devices (compat=5.12)')) 20 | , prop_feat4('block-group-tree', off, std, attr([compat=kv(6, 1, 0)], 'block group tree to reduce mount time (compat=6.1)')) 21 | , prop_feat4('raid-stripe-tree', off, std, attr([compat=kv(6, 7, 0)], '')) 22 | , prop_feat4(squota, off, std, attr([compat=kv(6, 7, 0)], '')) 23 | ]). 24 | 25 | prop_info(btrfs_rw, opt4s(' '), [ 26 | opt4('byte-count', int, 0, '--byte-count') 27 | , opt4(checksum, enum([crc32c, xxhash, sha256, blake2]), crc32c, '--checksum') 28 | , opt4(data, enum([raid0, raid1, raid1c3, raid1c4, raid5, raid6, raid10, single, dup]), single, '--data') 29 | , opt4(metadata, enum([raid0, raid1, raid1c3, raid1c4, raid5, raid6, raid10, single, dup]), raid1, '--metadata') 30 | , opt4(mixed, enable, no, '--mixed') 31 | , opt4(nodesize, size, 16384, '--nodesize') 32 | , opt4(sectorsize, size, 16384, '--sectorsize') 33 | , opt4(label, str, '', '--label') 34 | , opt4(nodiscard, enable, no, '--nodiscard') 35 | , opt4(shrink, enable, no, '--shrink') 36 | , opt4(force, enable, no, '--force') 37 | , opt4(uuid, guid, '', '--uuid') 38 | ]). 39 | 40 | prop_info(mnt_btrfs_feat, feat4s('--options', ',', on_off), [ 41 | prop_feat4(acl, on, pref1(no), none) 42 | , prop_feat4(autodefrag, off, pref1(no), none) 43 | , prop_feat4(compress, off, std, none) 44 | , prop_feat4('compress-force', off, std, none) 45 | , prop_feat4(barrier, on, pref1(no), none) 46 | , prop_feat4(check_int, off, std, none) 47 | , prop_feat4(check_int_data, off, std, none) 48 | , prop_feat4(clear_cache, off, std, none) 49 | , prop_feat4(datacow, on, pref1(no), none) 50 | , prop_feat4(datasum, on, pref1(no), none) 51 | , prop_feat4(degraded, off, std, none) 52 | , prop_feat4(discard, off, pref1(no), none) 53 | , prop_feat4(enospc_debug, off, pref1(no), none) 54 | , prop_feat4(flushoncommit, off, pref1(no), none) 55 | , prop_feat4(nologreplay, off, pref1(no), none) 56 | , prop_feat4(norecovery, off, pref1(no), none) 57 | , prop_feat4(rescan_uuid_tree, off, pref1(no), none) 58 | , prop_feat4(skip_balance, off, pref1(no), none) 59 | , prop_feat4(space_cache, off, pref1(no), none) 60 | , prop_feat4(ssd, off, pref1(no), none) 61 | , prop_feat4(ssd_spread, off, pref1(no), none) 62 | , prop_feat4(treelog, on, pref1(no), none) 63 | , prop_feat4(usebackuproot, off, pref1(no), none) 64 | , prop_feat4(user_subvol_rm_allowed, off, pref1(no), none) 65 | ]). 66 | 67 | prop_info(mnt_btrfs_opt, opt3s('--options', ','), [ 68 | opt3(check_int_print_mask, int, 0) 69 | , opt3(commit, int, 30) 70 | , opt3(compress, enum([zlib, lzo, zstd, no]), zlib) 71 | , opt3('compress-force', enum([zlib, lzo, zstd, no]), zlib) 72 | , opt3(device, str, '') 73 | , opt3(discard, enum([sync, async]), async) 74 | , opt3(fatal_errors, enum([bug, panic]), bug) 75 | , opt3(fragment, enum([off, data, metadata, all]), off) 76 | , opt3(max_inline, int, 2048) 77 | , opt3(metadata_ratio, int, 0) 78 | , opt3(rescue, enum([usebackuproot, nologreplay, ignorebadroots, ibadroots, ignoredatacsums, idatacsums, all]), all) 79 | , opt3(space_cache, enum([v1, v2]), v2) 80 | , opt3(subvol, str, '') 81 | , opt3(subvolid, int, 0) 82 | , opt3(thread_pool, int, 0) 83 | ]). 84 | 85 | -------------------------------------------------------------------------------- /module/void_fstab.pl: -------------------------------------------------------------------------------- 1 | % vi: noexpandtab:tabstop=4:ft=gprolog 2 | % Copyright (c) 2023-2024 Sergey Sikorskiy, released under the GNU GPLv2 license. 3 | 4 | % generate fstab 5 | make_fstab(TL, RD) :- 6 | atom_concat(RD, '/etc/fstab', FN), 7 | open(FN, write, S), 8 | get_fstab_list(TL, MPL), 9 | forall(member(MP, MPL), make_fstab_(S, MP)), 10 | close(S), 11 | os_call2([chmod, '644', FN]), 12 | !. 13 | make_fstab(_TL, _) :- 14 | tui_msgbox('Making of fstab has failed.'), 15 | fail. 16 | 17 | get_fstab_attrs(COL, L) :- 18 | findall(LM, (member(CO, COL), get_fstab_attrs0(CO, L0), member(LM, L0)), L1), 19 | ( L1 = [] -> 20 | L = [defaults] 21 | ; L = L1 22 | ), !, 23 | true. 24 | 25 | get_fstab_attrs0(attr(TAG, OL), L) :- 26 | prop_info(TAG, Format, FL), 27 | get_fstab_attrs1(Format, FL, OL, L), 28 | true. 29 | 30 | get_fstab_attrs1(feat4s(_Opt, _Sep, Fmt), FL, OL, COL) :- !, % "s" stands for "separator" 31 | findall(O, (member(F=V, OL), memberchk(prop_feat4(F, _, LFmt, _), FL), make_fs_feat(LFmt, Fmt, F, V, O)), COL), 32 | true. 33 | get_fstab_attrs1(opt3s(_Opt, _Sep), FL, OL, COL) :- !, % "s" stands for "separator" 34 | findall(O, (member(P=V, OL), make_opt3_val(FL, P, V, O)), COL), 35 | true. 36 | get_fstab_attrs1(opt3p(_Opt), FL, OL, COL) :- !, % "p" stands for "prefix" 37 | findall(O, (member(P=V, OL), make_opt3_val(FL, P, V, O)), COL), 38 | true. 39 | get_fstab_attrs1(Format, _FL, _OL, _COL) :- 40 | format_to_atom(A, 'Invalid formatter: ~w', [Format]), 41 | tui_msgbox(A, [title(' ERROR ')]), 42 | fail. 43 | 44 | make_fstab_(S, fstab4(MP, FS, MOL, PD)) :- 45 | get_fstab_attrs(MOL, MOL1), 46 | write_fstab(FS, PD, MP, MOL1, S), 47 | true. 48 | 49 | % For debugging. 50 | % write_fstab(FS, D, MP, MOL, _S) :- 51 | % tui_msgbox_w([write_fstab, FS, D, MP, MOL]), 52 | % fail. 53 | write_fstab(zfs, _D, _MP, _MOL, _S) :- !, 54 | % Do nothing 55 | true. 56 | write_fstab(btrfs, D, MP, MOL, S) :- !, 57 | lx_get_dev_uuid(D, U), 58 | format(S, '# ~w\n', [D]), 59 | L = ['UUID'=U, MP, btrfs, lc(MOL), '0 0'], 60 | write_fstab_line(L, S), 61 | true. 62 | write_fstab(bcachefs, D, MP, MOL, S) :- !, 63 | lx_get_dev_partuuid(D, PU), 64 | format(S, '# ~w\n', [D]), 65 | L = ['PARTUUID'=PU, MP, bcachefs, lc(MOL), '0 0'], 66 | write_fstab_line(L, S), 67 | true. 68 | write_fstab(swap, D, _MP, MOL, S) :- !, 69 | lx_get_dev_uuid(D, U), 70 | format(S, '# ~w\n', [D]), 71 | L = ['UUID'=U, none, swap, lc(MOL), '0 0'], 72 | write_fstab_line(L, S), 73 | true. 74 | write_fstab(proc, _, _, MOL, S) :- !, 75 | write(S, '# /proc with hidepid (https://wiki.archlinux.org/index.php/Security#hidepid)'), nl(S), 76 | L = [proc, '/proc', proc, lc(MOL), '0 0'], 77 | write_fstab_line(L, S), 78 | true. 79 | write_fstab(tmpfs, _, _, MOL, S) :- !, 80 | L = [tmpfs, '/tmp', tmpfs, lc(MOL), '0 0'], 81 | write_fstab_line(L, S), 82 | true. 83 | write_fstab(efivarfs, _, _, MOL, S) :- !, 84 | L = [efivarfs, '/sys/firmware/efi/efivars', efivarfs, lc(MOL), '0 0'], 85 | write_fstab_line(L, S), 86 | true. 87 | write_fstab(FS, D, MP, MOL, S) :- !, 88 | lx_get_dev_uuid(D, U), 89 | format(S, '# ~w\n', [D]), 90 | fapassno(FS, MP, FSPASSNO), 91 | L = ['UUID'=U, MP, FS, lc(MOL), '0', FSPASSNO], 92 | write_fstab_line(L, S), 93 | true. 94 | 95 | get_mol(FS, MP, MOL) :- 96 | inst_setting(fs_attr(FS, MP, _), mount(MOL)), 97 | !. 98 | get_mol(_FS, _MP, []). 99 | 100 | get_col(FS, MP, B, COL) :- 101 | inst_setting(fs_attr(FS, MP, B), create(COL)), 102 | !. 103 | get_col(_FS, _MP, _B, []). 104 | 105 | get_col_multi(zfs, B, COL) :- !, 106 | phrase(zfs_get_col(B), COL). 107 | get_col_multi(FS, B, COL) :- 108 | get_col(FS, (/), B, COL). 109 | 110 | write_fstab_line(L, S) :- 111 | os_wcmdl(L, '\t', S), nl(S), nl(S). 112 | 113 | fapassno(FS, _MP, '0') :- 114 | member(FS, [f2fs, xfs, btrfs, bcachefs]). 115 | fapassno(_FS, '/', '1'). 116 | fapassno(_FS, _MP, '2'). 117 | 118 | % Similar to get_mp_list but including swap. 119 | % fstab4(MP, FS, MOL, PD) 120 | get_fstab_list(TL, MPL3) :- 121 | % fs6(Name, MountPoint, Dev, [CreateOptList], [MountOptList], create/keep) 122 | findall(fstab4(MP, FS, MOL, PD), member(fs6(FS, MP, PD, _COL, MOL, _CK), TL), MPL0), 123 | findall(FSTAB4, get_fstab_list_multi(FS, TL, FSTAB4), MPL1), 124 | append(MPL0, MPL1, MPL2), 125 | sort(MPL2, MPL3), 126 | true. 127 | 128 | get_fstab_list_multi(btrfs, TL, fstab4(MP, btrfs, MOL, PD)) :- 129 | member(fs5_multi(btrfs, _COL, [PD|_], PTL, _), TL), 130 | get_fstab_list_multi_btrfs(PTL, MP, MOL), 131 | true. 132 | 133 | get_fstab_list_multi_btrfs(PTL, MP, MOL1) :- 134 | member(subv(SV, MP, MOL, _), PTL), 135 | attr_list_set(MOL, mnt_btrfs_opt, subvol, SV, MOL1), 136 | true. 137 | get_fstab_list_multi_btrfs(_PTL, '/mnt/btr_pool', [attr(mnt_indpn_feat, [atime=off]), attr(mnt_btrfs_opt, [subvolid=5])]). 138 | 139 | -------------------------------------------------------------------------------- /lib/fs_bcachefs.pl: -------------------------------------------------------------------------------- 1 | % vi: noexpandtab:tabstop=4:ft=gprolog 2 | % Copyright (c) 2024 Sergey Sikorskiy, released under the GNU GPLv2 license. 3 | 4 | % man bcachefs 5 | prop_info(bcachefs_rw, opt4s('='), [ 6 | opt4(acl, enable, no, '--acl') % , attr('Enable POSIX acls') 7 | , opt4(background_compression, enum([none, lz4, gzip, zstd]), none, '--background_compression') 8 | , opt4(background_target, target, '', '--background_target') % Device or label to move data to in the background 9 | , opt4(block_size, size, 0, '--block_size') % block size, in bytes (e.g. 4k) 10 | , opt4(btree_node_size, size, '256k', '--btree_node_size') % Btree node size, default 256k 11 | , opt4(compression, enum([none, lz4, gzip, zstd]), none, '--compression') % Set compression type (default: none). 12 | , opt4(data_checksum, enum([none, crc32c, crc64, xxhash]), crc32c, '--data_checksum') % Set data checksum type (default: crc32c). 13 | , opt4(data_replicas, int, 1, '--data_replicas') % Number of data replicas 14 | , opt4(data_replicas_required, int, 1, '--data_replicas_required') 15 | , opt4(encoded_extent_max, size, 0, '--encoded_extent_max') % Maximum size of checksummed/compressed extents 16 | , opt4(encrypted, enable, no, '--encrypted') % attr('Enable whole filesystem encryption (chacha20/poly1305); passphrase will be prompted for.') 17 | , opt4(erasure_code, enable, no, '--erasure_code') % , attr('Enable erasure coding (DO NOT USE YET)') 18 | , opt4(errors, enum([continue, ro, panic]), panic, '--errors') % Action to take on filesystem error 19 | , opt4(foreground_target, target, '', '--foreground_target') % Device or label for foreground writes 20 | , opt4(gc_reserve_bytes, percentage, 10, '--gc_reserve_bytes') % Amount of disk space to reserve for copygc. This takes precedence over gc_reserve_percent if set 21 | , opt4(gc_reserve_percent, percentage, 10, '--gc_reserve_percent') % Percentage of disk space to reserve for copygc 22 | , opt4(grpquota, enable, no, '--grpquota') % , attr('Enable group quotas') 23 | , opt4(inodes_32bit, enable, no, '--inodes_32bit') % , attr('Constrain inode numbers to 32 bits') 24 | , opt4(inodes_use_key_cache, enable, no, '--inodes_use_key_cache') % , attr('Use the btree key cache for the inodes btree') 25 | , opt4(journal_transaction_names, enable, no, '--journal_transaction_names') % , attr('Log transaction function names in journal') 26 | , opt4(label, str, '', '--fs_label') 27 | , opt4(metadata_checksum, enum([none, crc32c, crc64, xxhash]), crc32c, '--metadata_checksum') % Set metadata checksum type (default: crc32c). 28 | , opt4(metadata_replicas, int, 1, '--metadata_replicas') % Number of metadata replicas 29 | , opt4(metadata_replicas_required, int, 1, '--metadata_replicas_required') 30 | , opt4(metadata_target, target, '', '--metadata_target') % Device or label for metadata writes 31 | , opt4(no_passphrase, enable, no, '--no_passphrase') % attr('Don\'t encrypt master encryption key') 32 | , opt4(nocow, enable, no, '--nocow') % , attr('Nocow mode: Writes will be done in place when possible. Snapshots and reflink will still caused writes to be COW. This flag implicitly disables data checksumming, compression and encryption.') 33 | , opt4(prjquota, enable, no, '--prjquota') % , attr('Enable project quotas') 34 | , opt4(promote_target, target, '', '--promote_target') % Device or label to promote data to on read 35 | , opt4(replicas, int, 1, '--replicas') % Sets both data and metadata replicas 36 | , opt4(root_reserve_percent, percentage, 10, '--root_reserve_percent') % Percentage of disk space to reserve for superuser 37 | , opt4(shared_inode_numbers, enable, no, '--shared_inode_numbers') % , attr('Shared new inode numbers by CPU id') 38 | , opt4(str_hash, enum([crc32c, crc64, siphash]), crc32c, '--str_hash') % Hash function for directory entries and xattrs 39 | , opt4(superblock_size, size, 0, '--superblock_size') 40 | , opt4(usrquota, enable, no, '--usrquota') % , attr('Enable user quotas') 41 | , opt4(uuid, guid, '', '--uuid') 42 | , opt4(wide_macs, enable, no, '--wide_macs') % , attr('Store full 128bits of cryptographic MACS, instead of 80') 43 | 44 | % Device-specific 45 | , opt4(bucket, size, 0, '--bucket') % Specifies the bucket size; must be greater than the btree node size 46 | , opt4(dev_label, str, '', '--label') 47 | , opt4(discard, enable, no, '--discard') % , attr('Enable discard/TRIM support') 48 | , opt4(durability, int, 1, '--durability') % Data written to this device will be considered to have already been replicated n times 49 | , opt4(force, enable, no, '--force') 50 | , opt4(fs_size, size, 0, '--fs_size') % Create the filesystem using size bytes on the subsequent device. 51 | ]). 52 | 53 | prop_info(mnt_bcachefs_feat, feat4s('--options', ',', on_off), [ 54 | prop_feat4(degraded, off, std, none) 55 | , prop_feat4(very_degraded, off, std, none) 56 | , prop_feat4(nochanges, off, std, none) 57 | ]). 58 | 59 | 60 | -------------------------------------------------------------------------------- /lib/fs_f2fs.pl: -------------------------------------------------------------------------------- 1 | % vi: noexpandtab:tabstop=4:ft=gprolog 2 | % Copyright (c) 2024 Sergey Sikorskiy, released under the GNU GPLv2 license. 3 | 4 | % man mkfs.f2fs 5 | % f2fs_feat(name, depends_list) 6 | prop_info(f2fs_feat, feat4s('-O', ',', on_off), [ 7 | prop_feat4(encrypt, off, std, attr([], '')) 8 | , prop_feat4(extra_attr, off, std, attr([], '')) 9 | , prop_feat4(project_quota, off, std, attr([extra_attr], '')) 10 | , prop_feat4(inode_checksum, off, std, attr([extra_attr], '')) 11 | , prop_feat4(flexible_inline_xattr, off, std, attr([extra_attr], '')) 12 | , prop_feat4(quota, off, std, attr([], '')) 13 | , prop_feat4(inode_crtime, off, std, attr([extra_attr], '')) 14 | , prop_feat4(lost_found, off, std, attr([], '')) 15 | , prop_feat4(verity, off, std, attr([], '')) 16 | , prop_feat4(sb_checksum, off, std, attr([], '')) 17 | , prop_feat4(casefold, off, std, attr([], '')) 18 | , prop_feat4(compression, off, std, attr([extra_attr], '')) 19 | ]). 20 | 21 | prop_info(f2fs_rw, opt4s(' '), [ 22 | opt4('heap-based-allocation', enum([0, 1]), 1, '-a') 23 | % , opt4('device-list', int, 0, '-c') 24 | , opt4('debug-level', int, 0, '-d') 25 | % , opt4('extension-list', int, 0, '-e') 26 | % , opt4('extension-list', int, 0, '-E') 27 | , opt4('force', enable, no, '-f') 28 | % , opt4('default-options', int, 0, '-g') 29 | , opt4('extended node bitmap', enable, no, '-i') 30 | , opt4('label', str, '', '-l') 31 | , opt4('block zoned', enable, no, '-m') 32 | , opt4('overprovision-ratio', percentage, 5, '-o') 33 | % , opt4('casefolding encoding:flags', int, 0, '-C') 34 | , opt4('disable checkpointing srand seed', enable, no, '-r') 35 | % , opt4('uid:gid"', int, 0, '-R') 36 | , opt4('segments per section', int, 1, '-s') 37 | , opt4('sparse mode', enable, no, '-S') 38 | , opt4('discard policy', enum([0, 1]), 1, '-t') 39 | , opt4('timestamp', int, -1, '-T') 40 | , opt4('wanted-sector-size', size, 0, '-w') 41 | , opt4('sections-per-zone', int, 1, '-z') 42 | ]). 43 | 44 | prop_info(mnt_f2fs_feat, feat4s('--options', ',', on_off), [ 45 | prop_feat4(age_extent_cache, off, std, none) 46 | , prop_feat4(atgc, off, std, none) 47 | , prop_feat4(barrier, off, pref1(no), none) 48 | , prop_feat4(checkpoint_merge, off, pref1(no), none) 49 | , prop_feat4(compress_cache, off, std, none) 50 | , prop_feat4(compress_chksum, off, std, none) 51 | , prop_feat4(data_flush, off, std, none) 52 | , prop_feat4(disable_ext_identify, off, std, none) 53 | , prop_feat4(disable_roll_forward, off, std, none) 54 | , prop_feat4(discard, off, pref1(no), none) 55 | , prop_feat4(extent_cache, off, pref1(no), none) 56 | , prop_feat4(fastboot, off, std, none) 57 | , prop_feat4(flush_merge, off, std, none) 58 | , prop_feat4(gc_merge, off, pref1(no), none) 59 | , prop_feat4(grpquota, off, std, none) 60 | , prop_feat4(inline_data, on, pref1(no), none) 61 | , prop_feat4(inline_dentry, off, pref1(no), none) 62 | , prop_feat4(inline_xattr, off, pref1(no), none) 63 | , prop_feat4(inlinecrypt, off, std, none) 64 | , prop_feat4(noacl, off, std, none) 65 | , prop_feat4(norecovery, off, std, none) 66 | , prop_feat4(nouser_xattr, off, std, none) 67 | , prop_feat4(offgrpjquota, off, std, none) 68 | , prop_feat4(offprjjquota, off, std, none) 69 | , prop_feat4(offusrjquota, off, std, none) 70 | , prop_feat4(prjquota, off, std, none) 71 | , prop_feat4(quota, off, pref1(no), none) 72 | , prop_feat4(test_dummy_encryption, off, std, none) 73 | , prop_feat4(usrquota, off, std, none) 74 | ]). 75 | 76 | prop_info(mnt_f2fs_opt, opt3s('--options', ','), [ 77 | opt3(active_logs, enum(2, 4, 6), 6) 78 | , opt3(alloc_mode, enum(default, reuse), default) 79 | , opt3(background_gc, enum(on, off, sync), on) 80 | , opt3(checkpoint, enum(disable, enable), enable) 81 | , opt3(compress_algorithm, enum(default, lzo, lz4, zstd, 'lzo-rle'), default) 82 | % , opt3(compress_algorithm, enum(default, lz4, zstd), default) 83 | , opt3(compress_extension, str, '') 84 | , opt3(compress_log_size, size, '16KB') 85 | , opt3(compress_mode, enum(fs, user), fs) 86 | , opt3(discard_unit, enum(block, segment, section), block) 87 | , opt3(errors, enum(panic, continue, 'remount-ro'), continue) 88 | , opt3(fault_injection, int, 0) 89 | , opt3(fault_type, int, 0) 90 | , opt3(fsync_mode, enum(posix, strict, nobarrier), posix) 91 | , opt3(grpjquota, file, '') 92 | , opt3(inline_xattr_size, int, 0) 93 | , opt3(io_bits, int, 0) 94 | , opt3(jqfmt, enum(vfsold, vfsv0, vfsv1), vfsv1) 95 | , opt3(memory, enum(normal, low), normal) 96 | , opt3(mode, enum(adaptive, lfs), adaptive) 97 | , opt3(nocompress_extension, str, '') 98 | , opt3(prjjquota, file, '') 99 | , opt3(reserve_root, int, 0) 100 | , opt3(resgid, int, 0) 101 | , opt3(resuid, int, 0) 102 | , opt3(test_dummy_encryption, enum(none, v1, v2), none) 103 | , opt3(usrjquota, file, '') 104 | ]). 105 | 106 | 107 | -------------------------------------------------------------------------------- /module/void_menu_fs.pl: -------------------------------------------------------------------------------- 1 | % vi: noexpandtab:tabstop=4:ft=gprolog 2 | % Copyright (c) 2023-2024 Sergey Sikorskiy, released under the GNU GPLv2 license. 3 | 4 | menu_filesystem(TL) :- 5 | MT1 = ' Select the partition to edit ', 6 | menu_list_2(edit_fs_short, TL, [], [], [title(MT1), cancel-label('Done')]), 7 | !. 8 | 9 | part2menu_tag(PIL, PD, [PD, SZ]) :- 10 | memberchk(dev_part(PD,_,_,SZ), PIL), 11 | true. 12 | 13 | menu_fs_short(PD) :- 14 | dialog_msg(menu, LABEL), 15 | format_to_atom(TA, ' Set ~w filesystem parameters ', [PD]), 16 | menu_fs_short_pre(PD, IL), 17 | menu_list_3(file_system(PD), LABEL, [], IL, OL, [title(TA), extra-label('Accept'), ok-label('Edit')]), 18 | menu_fs_short_post(PD, IL, OL), 19 | !. 20 | 21 | menu_fs_short_pre(PD, [ 22 | label = Label, 23 | type = FS, 24 | mount_point = MP, 25 | create = FV 26 | ]) :- 27 | % We HAVE to get TL here. 28 | inst_setting(template(_TT), TL), 29 | % fs6(Name, MountPoint, Dev, [CreateOptList], [MountOptList], create/keep) 30 | ( memberchk(fs6(FS, MP, PD, COL, _MOL, CK), TL) 31 | ; memberchk(fs5_multi(FS, COL, [PD], _PTL, CK), TL), 32 | MP = '' 33 | ), !, 34 | fs_get_label(FS, COL, Label), 35 | ( CK = create -> 36 | FV = yes 37 | ; FV = no 38 | ), 39 | true. 40 | 41 | menu_fs_short_post(_PD, IL, IL) :- !. 42 | menu_fs_short_post(PD, _IL, OL) :- 43 | retract(inst_setting(template(TT), TL)), 44 | get_bootloader(TL, B), 45 | findall(E2, (member(E1, TL), replace_pd(E1, E2, PD, B, OL)), TL1), 46 | assertz(inst_setting(template(TT), TL1)), 47 | true. 48 | 49 | % menu_fs_info(tag, name, format) 50 | menu_fs_info(create, 'Create FS', enable) :- !. 51 | menu_fs_info(mount_point, 'Mount point', str) :- !. 52 | menu_fs_info(type, 'Type', fmt(select_fs)) :- !. 53 | menu_fs_info(label, 'Label', str) :- !. 54 | 55 | replace_element(E1, E2, E1, E2) :- !. 56 | replace_element(_, _, E1, E1) :- !. 57 | 58 | replace_pd(fs6(FS, _MP, PD, COL, MOL, _CK), fs6(FS, MP, PD, COL1, MOL, CK), PD, _B, OL) :- 59 | memberchk(type=FS, OL), !, % fs6 -> fs6. FS type hasn't changed. 60 | OL = [ 61 | label=Label, 62 | type=FS, 63 | mount_point=MP, 64 | create=FV 65 | ], 66 | ( FV = yes -> 67 | CK = create 68 | ; CK = keep 69 | ), 70 | fs_set_label(FS, Label, COL, COL1), 71 | true. 72 | replace_pd(fs6(_FS, _MP, PD, _COL, _MOL, _CK), fs5_multi(FS, COL1, [PD], PTL, CK), PD, B, OL) :- 73 | memberchk(type=FS, OL), 74 | memberchk(FS, [zfs, btrfs]), !, % fs6 -> fs5_multi 75 | OL = [ 76 | label=Label, 77 | type=FS, 78 | mount_point=_MP, 79 | create=FV 80 | ], 81 | ( FV = yes -> 82 | CK = create 83 | ; CK = keep 84 | ), 85 | get_col_multi(FS, B, COL), 86 | fs_set_label(FS, Label, COL, COL1), 87 | part_tmpl(FS, root, PTL), 88 | true. 89 | replace_pd(fs6(_FS, _MP, PD, _COL, _MOL, _CK), fs6(FS, MP, PD, COL1, MOL, CK), PD, B, OL) :- !, 90 | % fs6 -> fs6. FS type HAS changed. 91 | OL = [ 92 | label=Label, 93 | type=FS, 94 | mount_point=MP, 95 | create=FV 96 | ], 97 | ( FV = yes -> 98 | CK = create 99 | ; CK = keep 100 | ), 101 | get_col(FS, MP, B, COL), 102 | get_mol(FS, MP, MOL), 103 | fs_set_label(FS, Label, COL, COL1), 104 | true. 105 | replace_pd(fs5_multi(FS, COL, [PD], PTL, _CK), fs5_multi(FS, COL, [PD], PTL, CK), PD, _B, OL) :- 106 | memberchk(type=FS, OL), !, % fs5_multi -> fs5_multi. FS type hasn't changed. 107 | memberchk(create=FV, OL), 108 | ( FV = yes -> 109 | CK = create 110 | ; CK = keep 111 | ), 112 | true. 113 | replace_pd(fs5_multi(_FS, _COL, [PD], _PTL, _CK), fs5_multi(FS, COL1, [PD], PTL, CK), PD, B, OL) :- 114 | memberchk(type=FS, OL), 115 | memberchk(FS, [zfs, btrfs]), !, % fs5_multi -> fs5_multi. FS type HAS changed. 116 | OL = [ 117 | label=Label, 118 | type=FS, 119 | mount_point=_MP, 120 | create=FV 121 | ], 122 | ( FV = yes -> 123 | CK = create 124 | ; CK = keep 125 | ), 126 | get_col_multi(FS, B, COL), 127 | fs_set_label(FS, Label, COL, COL1), 128 | part_tmpl(FS, root, PTL), 129 | true. 130 | replace_pd(fs5_multi(_FS, _COL, [PD], _PTL, _CK), fs6(FS, MP, PD, COL1, MOL, CK), PD, B, OL) :- 131 | % fs5_multi -> fs6 132 | OL = [ 133 | label=Label, 134 | type=FS, 135 | mount_point=MP, 136 | create=FV 137 | ], 138 | ( FV = yes -> 139 | CK = create 140 | ; CK = keep 141 | ), 142 | get_col(FS, MP, B, COL), 143 | get_mol(FS, MP, MOL), 144 | fs_set_label(FS, Label, COL, COL1), 145 | true. 146 | replace_pd(E1, E1, _PD, _B, _OL) :- !. 147 | 148 | fs_type_to_menu(FST, [FST, Descr]) :- 149 | fs_info(FST, Descr), 150 | true. 151 | 152 | % It is not supposed to assertz. 153 | % TT - template name 154 | % OFS - old file system 155 | % NFS - new file system 156 | menu_select_fs(_TT, zfsBootMenu, _OFS, zfs) :- !. 157 | menu_select_fs(TT, B, OFS, NFS) :- 158 | inst_setting(fs, available(FSL)), 159 | % template_info(name, descr, except_fs). 160 | template_info(TT, _Descr, EL1), 161 | subtract(FSL, EL1, ML1), 162 | % bootloader_info(bootloade, supported_fs, supported_template, except_fs). 163 | bootloader_info(B, _FSL, _, EL2), 164 | subtract(ML1, EL2, ML2), 165 | maplist(fs_type_to_menu, ML2, ML), 166 | dialog_msg(radiolist, LABEL), 167 | tui_radiolist_tag2(ML, OFS, LABEL, [title(' Select the filesystem type ')], NFS), 168 | true. 169 | 170 | -------------------------------------------------------------------------------- /module/void_soft.pl: -------------------------------------------------------------------------------- 1 | % vi: noexpandtab:tabstop=4:ft=gprolog 2 | % Copyright (c) 2023-2024 Sergey Sikorskiy, released under the GNU GPLv2 license. 3 | 4 | % https://github.com/Oku-Code/void-linux-guide/blob/main/void-setup-guide.md 5 | 6 | % soft_info(Name, B, FS, PkgList, Descr). 7 | soft_info(btrbk, _, btrfs, [snooze, btrbk], 'Backup tool for btrfs subvolumes'). 8 | soft_info('grub-btrfs', grub2, btrfs, ['grub-btrfs', 'grub-btrfs-runit'], 'Add a btrfs snapshots sub-menu to GRUB'). 9 | % soft_info(snapper, _, btrfs, [snapper], 'Manage filesystem snapshots and allow undo of system modifications'). 10 | 11 | % Remove list of packages 12 | soft_remove_pkg_list(L, RD) :- 13 | % find installed packages 14 | findall(P0, (member(P0, L), os_call2_rc(['xbps-query', P0], 0)), L0), 15 | % find dependencies 16 | findall(P2, (member(P1, L0), os_shell2_lines(['xbps-query', '-X', P1], P2L), member(P2, P2L)), L1), 17 | append(L1, L0, L3), 18 | tui_progressbox_safe(['xbps-remove', o(r, RD), '-Ry', L3, '2>&1'], '', [title(' xbps-remove '), sz([12, 80])]), 19 | true. 20 | 21 | soft_install(TL, RD) :- 22 | findall(S, member(soft(S), TL), SL), 23 | % Add dependencies to the list. 24 | findall(S1, (member(S0, SL), soft_info(S0, _, _, PKGL, _), member(S1, PKGL)), SL1), 25 | dedup(SL1, SL2), 26 | soft_install_soft_chroot(SL2, RD), 27 | maplist(soft_configure(RD), SL2), 28 | true. 29 | soft_install(_TL, _RD) :- 30 | tui_msgbox('Software installation has failed.'), 31 | fail. 32 | 33 | soft_update(RD) :- 34 | inst_setting(system(arch), ARCH), 35 | make_chroot_inst_pref_chroot(ARCH, Pref, RD), 36 | % This is mandatory. 37 | tui_progressbox_safe([Pref, 'xbps-install', '-uy', xbps, '2>&1'], '', [title(' Update xbps '), sz([12, 80])]), 38 | tui_progressbox_safe([Pref, 'xbps-install', '-Suy', '2>&1'], '', [title(' Update Software '), sz(max)]), 39 | true. 40 | 41 | soft_install_soft([], _RD) :- !. 42 | soft_install_soft(SL, RD) :- 43 | inst_setting(system(arch), ARCH), 44 | make_chroot_inst_pref(ARCH, Pref), 45 | % This is mandatory. 46 | tui_progressbox_safe([Pref, 'xbps-install', o(r, RD), '-uy', xbps, '2>&1'], '', [title(' Update xbps '), sz([12, 80])]), 47 | tui_progressbox_safe([Pref, 'xbps-install', o(r, RD), '-Sy', SL, '2>&1'], '', [title(' Install Software '), sz([12, 80])]), 48 | true. 49 | 50 | soft_install_soft_chroot([], _RD) :- !. 51 | soft_install_soft_chroot(SL, RD) :- 52 | inst_setting(system(arch), ARCH), 53 | make_chroot_inst_pref_chroot(ARCH, Pref, RD), 54 | % This is mandatory. 55 | tui_progressbox_safe([Pref, 'xbps-install', '-uy', xbps, '2>&1'], '', [title(' Update xbps '), sz([12, 80])]), 56 | tui_progressbox_safe([Pref, 'xbps-install', '-Sy', SL, '2>&1'], '', [title(' Install Software '), sz([12, 80])]), 57 | true. 58 | 59 | soft_install_deps(_, []) :- !. 60 | soft_install_deps(Pref, D) :- 61 | % This is mandatory. 62 | tui_progressbox_safe([Pref, 'xbps-install', '-uy', xbps, '2>&1'], '', [title(' Update xbps '), sz([12, 80])]), 63 | tui_progressbox_safe([Pref, 'xbps-install', '-SyU', D, '2>&1'], '', [title(' Install Dependencies '), sz([12, 80])]). 64 | 65 | soft_install_deps_rd(_, [], _RD) :- !. 66 | soft_install_deps_rd(Pref, D, RD) :- 67 | tui_progressbox_safe([Pref, 'xbps-install', o(r, RD), '-SyU', D, '2>&1'], '', [title(' Install Dependencies '), sz([12, 80])]). 68 | 69 | soft_configure(RD, snooze) :- !, 70 | os_call2([chroot, RD, ln, '-sf', '/etc/sv/snooze-daily', '/var/service']), 71 | os_call2([chroot, RD, ln, '-sf', '/etc/sv/snooze-hourly', '/var/service']), 72 | true. 73 | soft_configure(RD, 'grub-btrfs') :- !, 74 | os_call2([chroot, RD, ln, '-sf', '/etc/sv/grub-btrfs', '/var/service']), 75 | true. 76 | soft_configure(RD, btrbk) :- !, 77 | os_mkdir_p(RD + '/mnt/btr_pool/btrbk_snapshots'), 78 | atom_concat(RD, '/etc/btrbk/btrbk.conf', CF1), 79 | open(CF1, write, S1), 80 | soft_btrbk_write_cfg(S1), 81 | close(S1), 82 | 83 | atom_concat(RD, '/etc/cron.daily/btrbk', CF2), 84 | open(CF2, write, S2), 85 | soft_btrbk_write_cron(S2), 86 | close(S2), 87 | os_shell2([chmod, '711', CF2]), 88 | true. 89 | soft_configure(RD, snapper) :- !, 90 | soft_update(RD), 91 | os_call2([chroot, RD, ln, '-srf', '/etc/sv/dbus', '/var/service']), 92 | os_call2([chroot, RD, umount, '/.snapshots']), 93 | os_call2([chroot, RD, rm, '-rf', '/.snapshots']), 94 | os_call2([chroot, RD, snapper, '-c', root, 'create-config', '/']), 95 | os_call2([chroot, RD, mkdir, '/.snapshots']), 96 | os_call2([chroot, RD, chmod, '750', '/.snapshots']), 97 | os_call2([chroot, RD, mount, '-a']), 98 | os_call2([chroot, RD, ln, '-srf', '/etc/sv/snapperd', '/var/service']), 99 | true. 100 | soft_configure(_RD, _) :- 101 | true. 102 | 103 | soft_btrbk_write_cfg(S) :- 104 | write(S, 'transaction_log /var/log/btrbk.log'), nl(S), 105 | write(S, 'timestamp_format long'), nl(S), 106 | write(S, 'stream_buffer 256m'), nl(S), nl(S), 107 | 108 | write(S, '# Configuration:'), nl(S), 109 | write(S, 'snapshot_preserve_min 2d'), nl(S), 110 | write(S, 'snapshot_preserve 14d'), nl(S), nl(S), 111 | 112 | write(S, 'target_preserve_min no'), nl(S), 113 | write(S, 'target_preserve 20d 10w *m'), nl(S), nl(S), 114 | 115 | write(S, 'archive_preserve_min latest'), nl(S), 116 | write(S, 'archive_preserve 12m 10y'), nl(S), nl(S), 117 | 118 | write(S, 'volume /mnt/btr_pool'), nl(S), 119 | write(S, ' snapshot_dir btrbk_snapshots'), nl(S), nl(S), 120 | 121 | write(S, ' subvolume @'), nl(S), 122 | write(S, ' subvolume @opt'), nl(S), 123 | write(S, ' subvolume @var'), nl(S), 124 | write(S, ' subvolume @srv'), nl(S), 125 | write(S, ' subvolume @home'), nl(S), 126 | true. 127 | 128 | soft_btrbk_write_cron(S) :- 129 | write(S, '#!/bin/sh'), nl(S), 130 | write(S, 'exec /usr/bin/btrbk -q run'), nl(S), 131 | true. 132 | 133 | -------------------------------------------------------------------------------- /lib/os_grub.pl: -------------------------------------------------------------------------------- 1 | % vi: noexpandtab:tabstop=4:ft=gprolog 2 | % Copyright (c) 2023-2024 Sergey Sikorskiy, released under the GNU GPLv2 license. 3 | 4 | grub_sconf(VL, RD) :- 5 | % v(name, value, comment) 6 | ALL = [ 7 | v('GRUB_DEFAULT', 0, 'The default menu entry.') 8 | , v('GRUB_SAVEDEFAULT', false, 'If this option is set to "true", then, when an entry is selected, save it as a new default entry for use by future runs of GRUB. This is only useful if "GRUB_DEFAULT=saved".') 9 | , v('GRUB_TIMEOUT', 5, 'Boot the default entry this many seconds after the menu is displayed, unless a key is pressed.') 10 | , v('GRUB_TIMEOUT_STYLE', 'menu', 'Values: menu, countdown, hidden.') % countdown, hidden 11 | , v('GRUB_DEFAULT_BUTTON', '', 'Support vendor-specific power button.') 12 | , v('GRUB_TIMEOUT_BUTTON', '', 'Support vendor-specific power button.') 13 | , v('GRUB_TIMEOUT_STYLE_BUTTON', '', 'Support vendor-specific power button.') 14 | , v('GRUB_BUTTON_CMOS_ADDRESS', '', 'Support vendor-specific power button.') 15 | , v('GRUB_DISTRIBUTOR', 'Unix', 'Set by distributors of GRUB to their identifying name.') 16 | % console, serial, serial_, at_keyboard, usb_keyboard. 17 | , v('GRUB_TERMINAL_INPUT', 'console', 'Select the terminal input device. You may select multiple devices here, separated by spaces.') 18 | % console, serial, serial_, gfxterm, vga_text, mda_text, morse, spkmodem. 19 | , v('GRUB_TERMINAL_OUTPUT', 'console', 'Select the terminal output device. You may select multiple devices here, separated by spaces.') 20 | , v('GRUB_TERMINAL', 'console', 'If this option is set, it overrides both "GRUB_TERMINAL_INPUT" and "GRUB_TERMINAL_OUTPUT" to the same value.') 21 | , v('GRUB_SERIAL_COMMAND', 'serial', 'A command to configure the serial port when using the serial console.') 22 | , v('GRUB_CMDLINE_LINUX', '', 'Command-line arguments to add to menu entries for the Linux kernel.') 23 | , v('GRUB_CMDLINE_LINUX_DEFAULT', 'loglevel=4', ' This option lists command-line arguments to add only to the default menu entry, after those listed in GRUB_CMDLINE_LINUX.') 24 | % , v('GRUB_CMDLINE_NETBSD', '', '') 25 | % , v('GRUB_CMDLINE_NETBSD_DEFAULT', '', '') 26 | % , v('GRUB_CMDLINE_GNUMACH', '', '') 27 | % , v('GRUB_CMDLINE_XEN', '', '') 28 | % , v('GRUB_CMDLINE_XEN_DEFAULT', '', '') 29 | % , v('GRUB_CMDLINE_LINUX_XEN_REPLACE', '', '') 30 | % , v('GRUB_CMDLINE_LINUX_XEN_REPLACE_DEFAULT', '', '') 31 | % , v('GRUB_EARLY_INITRD_LINUX_CUSTOM', '', '') 32 | % , v('GRUB_EARLY_INITRD_LINUX_STOCK', '', '') 33 | , v('GRUB_DISABLE_LINUX_UUID', false, 'To disable the use of UUIDs, set this option to "true".') 34 | , v('GRUB_DISABLE_LINUX_PARTUUID', true, 'To enable the use of partition UUIDs, set this option to "false".') 35 | , v('GRUB_DISABLE_RECOVERY', false, 'If this option is set to "true", disable the generation of recovery mode menu entries.') 36 | , v('GRUB_DISABLE_UUID', false, ' To disable this use of UUIDs, set this option to "true".') 37 | , v('GRUB_VIDEO_BACKEND', '', 'If graphical video support is required, either because the "gfxterm" graphical terminal is in use or because "GRUB_GFXPAYLOAD_LINUX" is set, then grub-mkconfig will normally load all available GRUB video drivers and use the one most appropriate for your hardware. If you need to override this for some reason, then you can set this option.') 38 | , v('GRUB_GFXMODE', '1920x1080x32', 'Set the resolution used on the ‘gfxterm’ graphical terminal.') 39 | , v('GRUB_BACKGROUND', '/usr/share/void-artwork/splash.png', 'Set a background image for use with the ‘gfxterm’ graphical terminal.') 40 | , v('GRUB_THEME', '', 'Set a theme for use with the "gfxterm" graphical terminal.') 41 | , v('GRUB_GFXPAYLOAD_LINUX', '', 'Set to "text" to force the Linux kernel to boot in normal text mode, "keep" to preserve the graphics mode set using "GRUB_GFXMODE", "widthxheight"["xdepth"] to set a particular graphics mode, or a sequence of these separated by commas or semicolons to try several modes in sequence.') 42 | , v('GRUB_DISABLE_OS_PROBER', false, 'The grub-mkconfig has a feature to use the external os-prober program to discover other operating systems installed on the same machine and generate appropriate menu entries for them.') 43 | , v('GRUB_OS_PROBER_SKIP_LIST', '', 'List of space-separated FS UUIDs of filesystems to be ignored from os-prober output.') 44 | , v('GRUB_DISABLE_SUBMENU', false, 'If this option is set to "true", flat menu with all entries on top level will be generated.') 45 | , v('GRUB_ENABLE_CRYPTODISK', 'n', 'If set to "y", grub-mkconfig and grub-install will check for encrypted disks and generate additional commands needed to access them during boot.') 46 | , v('GRUB_INIT_TUNE', '', 'Play a tune on the speaker when GRUB starts.') 47 | , v('GRUB_BADRAM', '', 'If this option is set, GRUB will issue a badram command to filter out specified regions of RAM.') 48 | , v('GRUB_PRELOAD_MODULES', '', 'List of GRUB module names separated by spaces.') 49 | , v('GRUB_HIDDEN_TIMEOUT', '5', 'Wait this many seconds before displaying the menu.') 50 | , v('GRUB_HIDDEN_TIMEOUT_QUIET', 'false', 'In conjunction with "GRUB_HIDDEN_TIMEOUT", set this to "true" to suppress the verbose countdown while waiting for a key to be pressed before displaying the menu.') 51 | , v('GRUB_HIDDEN_TIMEOUT_BUTTON', '', 'Support vendor-specific power button') 52 | , v('GRUB_COLOR_NORMAL', 'light-blue/black', '') 53 | , v('GRUB_COLOR_HIGHLIGHT', 'light-cyan/blue', '') 54 | % Deprecated 55 | % , v('GRUB_HIDDEN_TIMEOUT', '', '') 56 | % Deprecated 57 | % , v('GRUB_HIDDEN_TIMEOUT_QUIET', '', '') 58 | % Deprecated 59 | % , v('GRUB_HIDDEN_TIMEOUT_BUTTON', '', '') 60 | ], 61 | atom_concat(RD, '/etc/default/grub', CF), 62 | open(CF, write, S), 63 | write(S, '#\n'), 64 | write(S, '# Configuration file for GRUB.\n'), 65 | write(S, '#\n'), 66 | maplist(write_grub_sconf_value(VL, S), ALL), 67 | close(S), 68 | true. 69 | 70 | write_grub_sconf_value(VL, S, v(K, V1, C1)) :- 71 | ( memberchk(v(K, V2, C2), VL) -> 72 | write_grub_sconf_value(S, K, V2, C2, enable) 73 | ; write_grub_sconf_value(S, K, V1, C1, disable) 74 | ), 75 | true. 76 | 77 | write_grub_sconf_value(S, K, V, C, ED) :- 78 | ( C = '' 79 | ; write(S, '# '), write(S, C), nl(S) 80 | ), 81 | ( ED = enable 82 | ; write(S, '#') 83 | ), 84 | write(S, K), write(S, '='), 85 | ( number(V) -> 86 | write(S, V), nl(S) 87 | ; write(S, '"'), write(S, V), write(S, '"'), nl(S) 88 | ), 89 | !. 90 | 91 | -------------------------------------------------------------------------------- /module/void_menu_dev.pl: -------------------------------------------------------------------------------- 1 | % vi: noexpandtab:tabstop=4:ft=gprolog 2 | % Copyright (c) 2023-2024 Sergey Sikorskiy, released under the GNU GPLv2 license. 3 | 4 | % Select devices to use 5 | % OL - old used/boot dev7 list. 6 | menu_dev7_use(TT, OL, DL) :- 7 | % multi-device templates. 8 | % memberchk(TT, [gpt_basic, gpt_wizard, gpt_lvm, gpt_lvm_luks, gpt_luks_lvm]), !, 9 | memberchk(TT, [gpt_basic, gpt_wizard, gpt_lvm, gpt_lvm_luks, gpt_luks, gpt_luks_lvm]), !, 10 | menu_dev71_checklist(' Select device(s) to use ', OL, DL), 11 | true. 12 | menu_dev7_use(_TT, _OL, DL) :- 13 | inst_setting(dev7, available(DL)), 14 | true. 15 | 16 | % OL - old list 17 | menu_dev7_boot_dev(DL, OL, DEV71) :- 18 | ( OL = [DI|_] 19 | ; DI = none 20 | ), !, 21 | menu_dev71_radiolist(' Select boot device ', DL, DI, DEV71), 22 | true. 23 | 24 | % It is not used at this time. 25 | menu_dev4_boot_dev(D4L, D4) :- 26 | menu_d41_menu(' Select boot device ', D4L, SD), 27 | menu_sdn_to_d4(D4L, SD, D4), 28 | true. 29 | 30 | % It is used with manual template. 31 | menu_bootloader_dev(TL) :- 32 | ( get_bootloader_dev7(TL, DEV7) -> 33 | lx_dev7_to_sdn(DEV7, OSN) 34 | ; OSN = none 35 | ), 36 | st_used_d7(TL, L), 37 | menu_select_bootloader_dev7(L, OSN, NSN), 38 | ( OSN = NSN 39 | ; replace_bootloader_dev7(OSN, NSN, L, TL, NTL), 40 | retract(inst_setting(template(TT), _)), 41 | assertz(inst_setting(template(TT), NTL)) 42 | ), 43 | !. 44 | 45 | % Select from dev7 list. 46 | menu_select_bootloader_dev7(DEV7L, OSN, NSN) :- 47 | maplist(conv_dev7_to_menu_, DEV7L, DL), 48 | dialog_msg(radiolist, LABEL), 49 | append(DL, [[none, 'Manage bootloader otherwise']], BL1), 50 | tui_radiolist_tag2(BL1, OSN, LABEL, [title(' Select the disk to install the bootloader ')], NSN), 51 | true. 52 | 53 | % L - list of devices to select from. 54 | menu_dev7_menu(Title, L, DEV7) :- 55 | maplist(conv_dev7_to_menu_, L, DL), 56 | dialog_msg(menu, LABEL), 57 | tui_menu_tag(DL, LABEL, [title(Title)], SDN), 58 | lx_sdn_to_dev7(L, SDN, DEV7), 59 | !. 60 | 61 | % L - list of devices to select from. 62 | % DI - default item. 63 | menu_dev7_radiolist(Title, L, DI, DEV7) :- 64 | maplist(conv_dev7_to_menu_, L, DL), 65 | ( lx_dev7_to_sdn(DI, SDI) 66 | ; SDI = none 67 | ), !, 68 | dialog_msg(radiolist, LABEL), 69 | tui_radiolist_tag2(DL, SDI, LABEL, [title(Title)], Tag), 70 | lx_sdn_to_dev7(L, Tag, DEV7), 71 | true. 72 | 73 | % L - list of devices to select from. 74 | menu_dev7_checklist(Title, L, DL) :- 75 | maplist(conv_dev7_to_menu_, L, DL0), 76 | dialog_msg(checklist, LABEL), 77 | tui_checklist_tag(DL0, LABEL, [title(Title)], DL1), 78 | maplist(lx_sdn_to_dev7(L), DL1, DL), 79 | true. 80 | 81 | % AL - list of all dev7. 82 | menu_dev7_checklist(Title, AL, OL, NL) :- 83 | menu_dev7_checklist_2(Title, AL, OL, NL), 84 | ( NL \= [] 85 | ; tui_msgbox('No device selected'), 86 | fail 87 | ), 88 | !. 89 | 90 | % L - list of devices to select from. 91 | % OL - old list of selected items. 92 | % NL - new list of selected items. 93 | menu_dev7_checklist_2(Title, L, OL, NL) :- 94 | maplist(conv_dev7_to_menu_, L, DL0), 95 | ( maplist(lx_dev7_to_sdn, OL, DL1) 96 | ; DL1 = [none] 97 | ), !, 98 | dialog_msg(checklist, LABEL), 99 | tui_checklist_tag2(DL0, DL1, LABEL, [title(Title)], NL1), 100 | maplist(lx_sdn_to_dev7(L), NL1, NL), 101 | true. 102 | 103 | menu_dev_combo_checklist2(Title, L, OL, NL) :- 104 | maplist(conv_cdev_to_menu_, L, DL0), 105 | maplist(conv_cdev_to_sdn_, OL, DL1), 106 | dialog_msg(checklist, LABEL), 107 | tui_checklist_tag2(DL0, DL1, LABEL, [title(Title)], NL1), 108 | maplist(conv_sdn_to_cdev_(L), NL1, NL), 109 | true. 110 | 111 | % OV - old combo value 112 | % NV - new combo value 113 | menu_dev_combo_menu(Title, L, OV, NV) :- 114 | maplist(conv_cdev_to_menu_, L, DL0), 115 | conv_cdev_to_sdn_(OV, DI), 116 | dialog_msg(menu, LABEL), 117 | tui_menu_tag(DL0, LABEL, [default-item(DI), title(Title)], NV1), 118 | conv_sdn_to_cdev_(L, NV1, NV), 119 | true. 120 | 121 | conv_dev7_to_d4(DEV7, d4(LN, SN, D, 1)) :- 122 | lx_dev7_to_ldn_sdn(DEV7, LN, SN), 123 | lx_part_name(SN, 1, D), 124 | true. 125 | 126 | % 127 | conv_dev7_to_menu_(dev7(_NAME,SNAME,disk,_RO,_RM,SIZE,SSZ), [SNAME, DIA]) :- 128 | format_to_atom(DIA, 'size:~w; sector size:~d', [SIZE, SSZ]), 129 | true. 130 | 131 | % 132 | conv_cdev_to_menu_(dev7(_NAME,SNAME,disk,_RO,_RM,SIZE,SSZ), [SNAME, DIA]) :- !, 133 | format_to_atom(DIA, 'disk size:~w; sector size:~d', [SIZE, SSZ]), 134 | true. 135 | conv_cdev_to_menu_(lvm_vg(_LNAME,SNAME), [SNAME, 'VG']) :- !, 136 | true. 137 | conv_cdev_to_menu_(luks(_LNAME,SNAME), [SNAME, 'LUKS']) :- !, 138 | true. 139 | 140 | % 141 | conv_cdev_to_sdn_(DEV7, SDN) :- 142 | lx_dev7_to_sdn(DEV7, SDN), 143 | !. 144 | conv_cdev_to_sdn_(lvm_vg(_LNAME,SNAME), SNAME) :- !, 145 | true. 146 | conv_cdev_to_sdn_(luks(_LNAME,SNAME), SNAME) :- !, 147 | true. 148 | conv_cdev_to_sdn_(V, V) :- !. 149 | 150 | % 151 | conv_sdn_to_cdev_(L, SDN, DEV7) :- 152 | lx_sdn_to_dev7(L, SDN, DEV7), 153 | !. 154 | conv_sdn_to_cdev_(L, SDN, VG) :- 155 | member(VG, L), 156 | VG = lvm_vg(_LNAME,SDN), 157 | !. 158 | conv_sdn_to_cdev_(L, SDN, LUKS) :- 159 | member(LUKS, L), 160 | LUKS = luks(_LNAME,SDN), 161 | !. 162 | 163 | menu_dev7_menu(Title, D) :- 164 | lx_list_dev7_disk(L), 165 | menu_dev7_menu(Title, L, D). 166 | 167 | menu_dev7_radiolist(Title, OD, ND) :- 168 | lx_list_dev7_disk(L), 169 | menu_dev7_radiolist(Title, L, OD, ND). 170 | 171 | menu_dev71_menu(Title, D) :- 172 | lx_list_dev7_disk(L), 173 | menu_dev71_menu(Title, L, D). 174 | 175 | % L - list of devices to select from. 176 | menu_dev71_menu(_Title, [D], D) :- !. 177 | menu_dev71_menu(Title, L, D) :- 178 | menu_dev7_menu(Title, L, D). 179 | 180 | % L - list of devices to select from. 181 | menu_dev71_radiolist(_Title, [ND], _OD, ND) :- !. 182 | menu_dev71_radiolist(Title, L, OD, ND) :- 183 | menu_dev7_radiolist(Title, L, OD, ND). 184 | 185 | menu_dev71_checklist(Title, OL, NL) :- 186 | inst_setting(dev7, available(AL)), 187 | ( AL = [_] -> 188 | NL = AL 189 | ; menu_dev7_checklist(Title, AL, OL, NL) 190 | ), 191 | !. 192 | 193 | % L - list of devices to select from. 194 | menu_d4_checklist(Title, L, DL) :- 195 | maplist(menu_d4_to_sdn, L, DL0), 196 | dialog_msg(checklist, LABEL), 197 | tui_checklist_tag(DL0, LABEL, [title(Title)], DL1), 198 | maplist(menu_sdn_to_d4(L), DL1, DL), 199 | true. 200 | 201 | menu_d41_menu(_Title, [D4], D4) :- !. 202 | menu_d41_menu(Title, L, D4) :- 203 | menu_d4_menu(Title, L, D4). 204 | 205 | menu_d4_menu(Title, L, D4) :- 206 | maplist(menu_d4_to_sdn, L, DL), 207 | dialog_msg(menu, LABEL), 208 | tui_menu_tag(DL, LABEL, [title(Title)], SN), 209 | menu_sdn_to_d4(L, SN, D4), 210 | true. 211 | 212 | menu_d4_to_sdn(d4(LDN,_SN,SDN,N), [SDN, A]) :- 213 | format_to_atom(A, '~w~d', [LDN, N]). 214 | 215 | menu_sdn_to_d4(L, SDN, D4) :- 216 | member(D4, L), 217 | D4 = d4(_LN,_SN,SDN,_N), 218 | !. 219 | 220 | menu_d4_checklist_light(_Title, [D], [D]) :- !. 221 | menu_d4_checklist_light(Title, L, DL) :- 222 | menu_d4_checklist(Title, L, DL). 223 | 224 | -------------------------------------------------------------------------------- /module/void_zfs.pl: -------------------------------------------------------------------------------- 1 | % vi: noexpandtab:tabstop=4:ft=gprolog 2 | % Copyright (c) 2023-2024 Sergey Sikorskiy, released under the GNU GPLv2 license. 3 | 4 | % Installing Void on a ZFS Root - https://docs.voidlinux.org/installation/guides/zfs.html 5 | % https://openzfs.github.io/openzfs-docs/man/master/8/zpool.8.html#ENVIRONMENT_VARIABLES 6 | 7 | zfs_col(TL, COL) :- 8 | memberchk(fs5_multi(zfs, COL, _DL, _PTL, _CK), TL). 9 | 10 | uses_zfs(TL) :- 11 | zfs_col(TL, _COL), 12 | true. 13 | 14 | zfs_uses_encr(TL) :- 15 | zfs_col(TL, COL), 16 | zfs_pool_encryption_col(COL), 17 | !. 18 | 19 | zfs_pool_keylocation_file(TL, FN) :- 20 | zfs_col(TL, COL), 21 | zfs_pool_keylocation_file_col(COL, FN). 22 | 23 | % Do not mount the newly created file system. 24 | zfs_mkfs_cmd(MP, _DS, _AL, _PN, [zfs, create, '-u', o(o, mountpoint=MP)]). 25 | zfs_mkfs_cmd(_MP, _DS, AL, _PN, OL) :- 26 | findall(o(o, A), member(A, AL), OL). 27 | zfs_mkfs_cmd(_MP, DS, _AL, PN, [concat(PN, DS)]). 28 | 29 | zfs_mkfs_multi(PN, PTL) :- 30 | forall(member(dataset(DS, MP, AL), PTL), (findall(C, (zfs_mkfs_cmd(MP, DS, AL, PN, CL), member(C, CL)), CMD), os_shell2(CMD))), 31 | true. 32 | 33 | zfs_install(TL, _RD) :- 34 | uses_zfs(TL), 35 | % os_call2([zgenhostid, '-f']), % Void won't boot with hostid generated by zgenhostid. 36 | lx_gen_hostid(''), 37 | !. 38 | zfs_install(_TL, _). 39 | 40 | zfs_passphrase(PASS, FN, RD) :- 41 | atom_concat(RD, FN, PFN), 42 | open(PFN, write, S), 43 | write(S, PASS), 44 | close(S), 45 | os_call2([chmod, '000', PFN]), 46 | true. 47 | 48 | zfs_zpool_destroy_all :- 49 | zpool_list(L), 50 | member(zp(PN,_A2,_A3,_A4,_A5,_A6,_A7,_A8,_A9,_A10,_A11), L), 51 | % tui_progressbox_safe([zpool, export, '-f', PN, '2>&1'], '', [title(' exporting zpool '), sz([6, 40])]), 52 | tui_progressbox_safe([zpool, destroy, '-f', PN, '2>&1'], '', [title(' destroying zpool '), sz([6, 40])]), 53 | fail. 54 | zfs_zpool_destroy_all. 55 | 56 | zfs_pool_encryption_col(COL) :- 57 | memberchk(attr(zfs_rw, AL), COL), 58 | memberchk(encryption=V, AL), 59 | V \= off. 60 | 61 | zfs_pool_keylocation_file_col(COL, FN) :- 62 | memberchk(attr(zfs_rw, AL), COL), 63 | memberchk(keylocation= =(file, FN), AL), 64 | true. 65 | 66 | zfs_pool_compatibility_col(COL, V) :- 67 | memberchk(attr(zpool_props_rw, AL), COL), 68 | ( memberchk(compatibility=V, AL) 69 | ; V = off 70 | ), !. 71 | 72 | % "has_boot_part" replacement. We do not check for templates. 73 | zfs_has_boot_part(B) :- 74 | % bootloader_info(bootloade, supported_fs, supported_template, except_fs). 75 | bootloader_info(B, FSL, _, _), 76 | \+ memberchk(zfs, FSL). 77 | 78 | zfs_pool_base --> [ 79 | force=yes 80 | , mountpoint=none 81 | ]. 82 | 83 | zfs_pool_props --> [ 84 | ashift=12 85 | , autotrim=on 86 | ]. 87 | 88 | zfs_pool_props(grub2) --> [ 89 | compatibility=grub2 90 | ]. 91 | zfs_pool_props(_) --> []. 92 | 93 | zfs_pool_feats --> [ 94 | compression=lz4 95 | , acltype=posixacl 96 | , xattr=sa % vastly improves the performance of extended attributes 97 | , relatime=on % is a middle ground between classic POSIX atime behavior (with its significant performance impact) and atime=off (which provides the best performance by completely disabling atime updates). 98 | , normalization=formD % eliminates some corner cases relating to UTF-8 filename normalization 99 | ]. 100 | 101 | zfs_pool_feats(grub2) --> []. 102 | zfs_pool_feats(_) --> [ 103 | dnodesize=auto 104 | ]. 105 | 106 | zfs_get_col1(B) --> 107 | zfs_pool_props, 108 | zfs_pool_props(B). 109 | 110 | zfs_get_col2(B) --> 111 | zfs_pool_feats, 112 | zfs_pool_feats(B). 113 | 114 | zfs_get_col(B) --> 115 | ({ phrase(zfs_get_col1(B), PL), PL \= [], ! } -> [attr(zpool_props_rw, PL)]; []), 116 | ({ phrase(zfs_get_col2(B), FL), FL \= [], ! } -> [attr(zfs_rw, FL)]; []), 117 | ({ phrase(zfs_pool_base, BL), BL \= [], ! } -> [attr(zpool_rw, BL)]; []). 118 | 119 | % PN - pool name. 120 | zfs_make_zpool_create_cmd(PN, DL, COL, OL) :- 121 | findall(PID, (member(PD, DL), (atom_concat('/dev/mapper', _, PD) -> PID = PD; lx_get_dev_disk_id(PD, PID))), PIDL), 122 | append([zpool, create| COL], [PN, PIDL, '2>&1'], OL), 123 | true. 124 | 125 | zfs_zpool_export_import(PN, RD) :- 126 | % export and re-import the pool with a temporary, alternate root path 127 | % os_shell2([zpool, export, PN]), 128 | format_to_atom(ETitle, ' exporting pool ~w ', [PN]), 129 | tui_progressbox_safe([zpool, export, PN, '2>&1'], '', [title(ETitle), sz([6, 40])]), 130 | % os_shell2([zpool, import, '-N', o('R', RD), PN]), 131 | format_to_atom(ITitle, ' importing pool ~w ', [PN]), 132 | tui_progressbox_safe([zpool, import, '-N', o('R', RD), PN, '2>&1'], '', [title(ITitle), sz([8, 60])]), 133 | true. 134 | 135 | zfs_mkfs(PN, B, PTL) :- 136 | zfs_mkfs_multi(PN, PTL), 137 | zfs_set_bootfs(PN, B), 138 | true. 139 | 140 | zfs_set_bootfs(PN, B) :- 141 | zfs_has_boot_part(B), !, 142 | os_call2([zpool, set, bootfs='zroot/ROOT/void', PN]). 143 | zfs_set_bootfs(_PN, _B). 144 | 145 | % PN - pool name. 146 | % No native encryption. 147 | zfs_zpool_create(false, Title, B, PN, DL, PTL, COL, RD) :- !, 148 | get_mkfs_attrs(COL, OL), 149 | zfs_make_zpool_create_cmd(PN, DL, OL, CL), 150 | 151 | tui_progressbox_safe(CL, '', [title(Title), sz([12, 80])]), 152 | 153 | zfs_zpool_export_import(PN, RD), 154 | zfs_mkfs(PN, B, PTL), 155 | !. 156 | zfs_zpool_create(true, Title, B, PN, DL, PTL, COL, RD) :- !, 157 | inst_setting_tmp(passwd('$_zfs_$'), PSWD), 158 | get_mkfs_attrs(COL, OL), 159 | ( zfs_has_boot_part(B) 160 | ; zfs_pool_keylocation_file_col(COL, FN), 161 | zfs_passphrase(PSWD, FN, '') 162 | ), !, 163 | 164 | zfs_make_zpool_create_cmd(PN, DL, OL, CL), 165 | 166 | tui_infobox(Title, [sz([4, 40])]), 167 | os_scmdl(CL, CA), 168 | popen(CA, write, WS), 169 | write(WS, PSWD), % no nl(WS) should be here. 170 | close(WS), 171 | 172 | zfs_zpool_export_import(PN, RD), 173 | zfs_load_key(PN, PSWD), 174 | zfs_mkfs(PN, B, PTL), 175 | !. 176 | 177 | zfs_export_pool_rd(RD) :- 178 | zpool_list(L), 179 | memberchk(zp(PN,_A2,_A3,_A4,_A5,_A6,_A7,_A8,_A9,_A10,RD), L), 180 | % os_call2_rc([zfs, unmount, '-f', '-a'], _), 181 | format_to_atom(Title, ' exporting pool ~w ', [PN]), 182 | tui_progressbox_safe([zpool, export, '-f', PN, '2>&1'], '', [title(Title), sz([6, 40])]), 183 | true. 184 | 185 | zfs_destroy_pool_rd(RD) :- 186 | zpool_list(L), 187 | memberchk(zp(PN,_A2,_A3,_A4,_A5,_A6,_A7,_A8,_A9,_A10,RD), L), 188 | % tui_progressbox_safe([zpool, export, '-f', PN, '2>&1'], '', [title(' exporting zpool '), sz([6, 40])]), 189 | format_to_atom(Title, ' destroying pool ~w ', [PN]), 190 | tui_progressbox_safe([zpool, destroy, '-f', PN, '2>&1'], '', [title(Title), sz([6, 40])]), 191 | true. 192 | 193 | zfs_mount_multi(PN, RD) :- 194 | % Mount the ZFS hierarchy 195 | os_call2([zfs, mount, 'zroot/ROOT/void']), 196 | os_call2([zfs, mount, '-a']), 197 | % Update device symlinks 198 | os_call2([udevadm, trigger]), 199 | 200 | % record the current pool configuration in a cache file that Void will use to avoid walking the entire device hierarchy to identify importable pools. 201 | os_mkdir_p(RD + '/etc/zfs'), 202 | os_call2([zpool, set, cachefile=concat(RD, '/etc/zfs/zpool.cache'), PN]), 203 | true. 204 | 205 | zfs_setup_encr(FN, RD) :- 206 | os_call2([mv, FN, RD + FN]), 207 | true. 208 | 209 | -------------------------------------------------------------------------------- /module/void_net.pl: -------------------------------------------------------------------------------- 1 | % vi: noexpandtab:tabstop=4:ft=gprolog 2 | % Copyright (c) 2023-2024 Sergey Sikorskiy, released under the GNU GPLv2 license. 3 | 4 | set_network(RD) :- 5 | inst_setting(network, NC), !, 6 | ( set_network_(NC, RD) 7 | ; tui_msgbox('Setting up of network has failed.'), 8 | fail 9 | ), 10 | !. 11 | set_network(_RD). 12 | 13 | set_network_(none, _RD) :- !. 14 | set_network_(dhcp(D), RD) :- !, 15 | ( atom_concat('wl', _, D) -> 16 | os_call2([cp, '/etc/wpa_supplicant/wpa_supplicant.conf', RD + '/etc/wpa_supplicant']), 17 | os_call2([ln, '-sf', '/etc/sv/wpa_supplicant', RD + '/etc/runit/runsvdir/default/wpa_supplicant']) 18 | ; true 19 | ), 20 | enable_dhcpd(RD), 21 | true. 22 | set_network_(static(D, IP, GW, DNS1, DNS2), RD) :- !, 23 | % static IP through dhcpcd. 24 | atom_concat(RD, '/etc/dhcpcd.conf', CF), 25 | os_call2([mv, CF, CF + '.orig']), 26 | open(CF, write, S), 27 | format(S, '# Static IP configuration set by the void-installer for ~w.\n', [D]), 28 | format(S, 'interface ~w\n', [D]), 29 | format(S, 'static ip_address=~w\n', [IP]), 30 | format(S, 'static routers=~w\n', [GW]), 31 | format(S, 'static domain_name_servers=~w ~w\n', [DNS1, DNS2]), 32 | close(S), 33 | enable_dhcpd(RD), 34 | true. 35 | set_network_(_, _RD) :- !, 36 | tui_msgbox('Invalid network connection type.'), 37 | fail. 38 | 39 | enable_dhcpd(RD) :- 40 | os_call2([ln, '-sf', '/etc/sv/dhcpcd', RD + '/etc/runit/runsvdir/default/dhcpcd']), 41 | true. 42 | 43 | test_network(_) :- 44 | tui_infobox('Testing network connection.', [sz([4, 40])]), 45 | between(1, 20, _), 46 | sleep(0.25), 47 | os_rm_f(otime), 48 | os_shell('xbps-uhelper fetch https://repo-default.voidlinux.org/current/otime 1>/dev/null 2>&1'), 49 | % os_shell('xbps-uhelper fetch https://repo-default.voidlinux.org/current/otime 2>&1'), 50 | % tui_progressbox_safe(['xbps-uhelper', fetch, 'https://repo-default.voidlinux.org/current/otime', '2>&1'], '', [title(' Test Network Connection '), sz([6, 80])]), 51 | tui_msgbox('Network is working properly!', [sz([6, 40])]), 52 | !. 53 | test_network(nm) :- 54 | tui_msgbox('Network Manager is enabled but network is inaccessible, please set it up externally with nmcli, nmtui, or the Network Manager tray applet.', [sz([6, 40])]), !, 55 | fail. 56 | test_network(_) :- 57 | tui_msgbox('Network is inaccessible, please set it up properly.', [sz([6, 40])]), !, 58 | fail. 59 | 60 | select_net_conf(D, dhcp) :- 61 | format_to_atom(A, 'Do you want to use DHCP or STATIC for ~w?', [D]), 62 | tui_yesno(A, [yes-label(dhcp), no-label(static), sz([6, 40])]), !. 63 | select_net_conf(_, static). 64 | 65 | configure_net(D, dhcp) :- 66 | lx_iface_setup(D, RC1), 67 | ( RC1 = 1 -> 68 | ( os_shell2([sv, restart, dhcpcd, '1>/dev/null']) 69 | ; tui_msgbox('ERROR: failed to run dhcpcd'), 70 | fail 71 | ), !, 72 | ( tui_infobox('Retrieving IP address.', [sz([6, 40])]), 73 | between(1, 40, _), 74 | sleep(0.25), 75 | lx_iface_setup(D, 0) 76 | ; tui_msgbox2(['ERROR: DHCP request failed for', D]), 77 | fail 78 | ) 79 | ; true 80 | ), 81 | test_network(any), 82 | retractall(inst_setting(network, _)), 83 | assertz(inst_setting(network, dhcp(D))), 84 | !. 85 | configure_net(D, static) :- 86 | format_to_atom(MA, 'Static IP configuration for ~w:', [D]), 87 | dialog_msg(form, FORMLABEL), 88 | tui_form_v(20, 0, [ 89 | item('IP address:'), 90 | item('Gateway:'), 91 | item('DNS Primary', '8.8.8.8'), 92 | item('DNS Secondary', '8.8.4.4') 93 | ], FORMLABEL, [title(MA)], L), 94 | L = [IP, GW, DNS1, DNS2| _], 95 | ( IP = '' -> tui_msgbox('IP adress is missing'), fail ; true), 96 | ( GW = '' -> tui_msgbox('Gateway adress is missing'), fail ; true), 97 | ( DNS1 = '' -> tui_msgbox('Primary DNS is missing'), fail ; true), 98 | ( DNS2 = '' -> tui_msgbox('Secondary DNS is missing'), fail ; true), 99 | ( os_shell2([ip, link, set, dev, D, up]) 100 | ; format_to_atom(EA1, 'ERROR: Failed to bring ~w interface.', [D]), 101 | tui_msgbox(EA1), 102 | fail 103 | ), !, 104 | % format_to_atom(IPA, 'ip addr add "~w" dev ~w', [IP, D]), 105 | ( os_call2([ip, addr, add, IP, dev, D]) 106 | ; format_to_atom(EA2, 'ERROR: Failed to set ip to the ~w interface.', [D]), 107 | tui_msgbox(EA2), 108 | fail 109 | ), !, 110 | ( os_call2([ip, route, add, default, via, GW]) 111 | ; format_to_atom(EA3, 'ERROR: Failed to setup gateway.', []), 112 | tui_msgbox(EA3), 113 | fail 114 | ), !, 115 | open('/etc/resolv.conf', write, S), 116 | write(S, 'nameserver '), write(S, DNS1), nl(S), 117 | write(S, 'nameserver '), write(S, DNS2), nl(S), 118 | close(S), 119 | test_network(all), 120 | retractall(inst_setting(network, _)), 121 | assertz(inst_setting(network, static(D, IP, GW, DNS1, DNS2))), 122 | !. 123 | 124 | configure_wifi(D) :- 125 | % format_to_atom(MA, 'Wireless configuration for ~w\n(encryption type: wep or wpa)', [D]), 126 | format_to_atom(MA, ' Wireless configuration for ~w (wep or wpa) ', [D]), 127 | dialog_msg(form, FORMLABEL), 128 | tui_mixedform_v(30, 100, [ 129 | item('SSID:', ''), 130 | item('Password:', '', 1), 131 | item('Encryption:', 'wpa') 132 | ], FORMLABEL, [title(MA)], L), 133 | L = [SSID, PSWD, ENCR| _], 134 | ( SSID = '' -> 135 | tui_msgbox('Invalid SSID.'), fail 136 | ; true 137 | ), 138 | ( \+ member(ENCR, [wep, wpa]) -> 139 | tui_msgbox('Invalid encryption type (possible values: wep or wpa'), fail 140 | ; true 141 | ), 142 | ( PSWD = '' -> 143 | tui_msgbox('Invalid AP password.'), fail 144 | ; true 145 | ), 146 | WPASUPCONF = '/etc/wpa_supplicant/wpa_supplicant.conf', 147 | % reset the configuration to the default, if necessary otherwise backup the configuration 148 | atom_concat(WPASUPCONF, '.orig', OF), 149 | ( file_exists(OF) -> 150 | os_call2([cp, '-f', OF, WPASUPCONF]) 151 | ; os_call2([cp, '-f', WPASUPCONF, OF]) 152 | ), 153 | ( ENCR = 'wep' -> 154 | format_to_atom(WA, 'network={\n\tssid="~w"\n\twep_key0="~w"\n\twep_tx_keyidx=0\n\tauth_alg=SHARED\n}', [SSID, PSWD]), 155 | open(WPASUPCONF, write, S), 156 | write(S, WA), 157 | close(S) 158 | ; format_to_atom(WPAA, 'wpa_passphrase "~w" "~w" >> ~w', [SSID, PSWD, WPASUPCONF]), 159 | os_shell(WPAA) 160 | % os_shell2([wpa_supplicant, '-B', '-i', D, '-c', WPASUPCONF]) 161 | ), 162 | os_call2([sv, restart, wpa_supplicant]), 163 | configure_net(D, dhcp), 164 | true. 165 | 166 | configure_net(none) :- !, 167 | retractall(inst_setting(network, _)), 168 | assertz(inst_setting(network, none)). 169 | configure_net(D) :- 170 | select_net_conf(D, NT), 171 | configure_net(D, NT), 172 | true. 173 | 174 | get_mac_addr(N, [N, M]) :- 175 | lx_get_mac_addr(N, M), 176 | true. 177 | 178 | net_dev_name(dhcp(D), D) :- !. 179 | net_dev_name(static(D, _IP, _GW, _DNS1, _DNS2), D) :- !. 180 | 181 | get_net_devs :- 182 | lx_get_net_devs(AL1), 183 | maplist(get_mac_addr, AL1, AL2), 184 | dialog_msg(radiolist, RADIOLABEL), 185 | ( inst_setting(network, D), net_dev_name(D, ON) 186 | ; ON = none 187 | ), 188 | append(AL2, [[none, 'Disable network']], AL3), 189 | tui_radiolist_tag2(AL3, ON, RADIOLABEL, [title(' Select the network interface to configure ')], Tag), !, 190 | ( Tag = ON % value hasn't change. 191 | ; ( atom_concat('wl', _, Tag) -> 192 | configure_wifi(Tag) 193 | ; configure_net(Tag) 194 | ) 195 | ), 196 | true. 197 | 198 | 199 | -------------------------------------------------------------------------------- /lib/unix_common.pl: -------------------------------------------------------------------------------- 1 | % vi: noexpandtab:tabstop=4:ft=gprolog 2 | % Copyright (c) 2023-2024 Sergey Sikorskiy, released under the GNU GPLv2 license. 3 | 4 | ux_user_root :- 5 | os_shell_number('id -u', 0). 6 | 7 | % sgdisk -L 8 | % sfdisk --list-types 9 | % names should correspond to os_gpt_part_type. 10 | part_typecode(sys_efi, 'EF00'). 11 | part_typecode(sys_mbr, 'EF01'). 12 | part_typecode(sys_bios_boot, 'EF02'). 13 | part_typecode(solaris_root, 'BF00'). 14 | part_typecode(solaris_boot, 'BF01'). 15 | part_typecode(linux_swap, '8200'). 16 | part_typecode(linux_data, '8300'). 17 | part_typecode(linux_luks, '8309'). 18 | part_typecode(linux_lvm, '8E00'). 19 | part_typecode(linux_raid, 'FD00'). 20 | 21 | % Directory to store installation key files and detached headers 22 | config_dir(instkey, '/boot'). 23 | % Directory to store boot key files and detached headers 24 | config_dir(bootkey, '/root'). 25 | % Intended mounting point of EFI System Partition 26 | config_dir(efi, '/boot/efi'). 27 | 28 | config_file('CRYPTTAB', '/etc/crypttab'). 29 | config_file('DHCPCD', '/etc/dhcpcd.conf'). 30 | config_file('DNSCRYPT-PROXY', '/etc/dnscrypt-proxy.toml'). 31 | config_file('DRACUT', '/etc/dracut.conf.d'). 32 | config_file('EFI-STARTUP', F) :- 33 | config_dir(efi,D), 34 | format_to_atom(F, '%s/startup.nsh', D). 35 | config_file(fstab, '/etc/fstab'). 36 | config_file(grub_default, '/etc/default/grub'). 37 | config_file(grub_linux, '/etc/grub.d/10_linux'). 38 | config_file(hosts, '/etc/hosts'). 39 | config_file(locales, '/etc/default/libc-locales'). 40 | config_file(openresolv, '/etc/resolvconf.conf'). 41 | config_file(openssh_daemon, '/etc/ssh/sshd_config'). 42 | config_file(openssh_moduli, '/etc/ssh/moduli'). 43 | config_file(pam, '/etc/pam.d/passwd'). 44 | config_file(rc, '/etc/rc.conf'). 45 | config_file(securetty, '/etc/securetty'). 46 | config_file(sudoers, '/etc/sudoers'). 47 | config_file(sysctl, '/etc/sysctl.d/99-sysctl.conf'). 48 | 49 | % ISO-639 language names for locales 50 | get_lng_name(LC, N) :- 51 | iso639(LC, N), !. 52 | get_lng_name(LC, LC). 53 | 54 | iso639(aa, 'Afar'). 55 | iso639(af, 'Afrikaans'). 56 | iso639(an, 'Aragonese'). 57 | iso639(ar, 'Arabic'). 58 | iso639(ast, 'Asturian'). 59 | iso639(be, 'Belgian'). 60 | iso639(bg, 'Bulgarian'). 61 | iso639(bhb, 'Bhili'). 62 | iso639(br, 'Breton'). 63 | iso639(bs, 'Bosnian'). 64 | iso639(ca, 'Catalan'). 65 | iso639(cs, 'Czech'). 66 | iso639(cy, 'Welsh'). 67 | iso639(da, 'Danish'). 68 | iso639(de, 'German'). 69 | iso639(el, 'Greek'). 70 | iso639(en, 'English'). 71 | iso639(es, 'Spanish'). 72 | iso639(et, 'Estonian'). 73 | iso639(eu, 'Basque'). 74 | iso639(fi, 'Finnish'). 75 | iso639(fo, 'Faroese'). 76 | iso639(fr, 'French'). 77 | iso639(ga, 'Irish'). 78 | iso639(gd, 'Scottish Gaelic'). 79 | iso639(gl, 'Galician'). 80 | iso639(gv, 'Manx'). 81 | iso639(he, 'Hebrew'). 82 | iso639(hr, 'Croatian'). 83 | iso639(hsb, 'Upper Sorbian'). 84 | iso639(hu, 'Hungarian'). 85 | iso639(id, 'Indonesian'). 86 | iso639(is, 'Icelandic'). 87 | iso639(it, 'Italian'). 88 | iso639(iw, 'Hebrew'). 89 | iso639(ja, 'Japanese'). 90 | iso639(ka, 'Georgian'). 91 | iso639(kk, 'Kazakh'). 92 | iso639(kl, 'Kalaallisut'). 93 | iso639(ko, 'Korean'). 94 | iso639(ku, 'Kurdish'). 95 | iso639(kw, 'Cornish'). 96 | iso639(lg, 'Ganda'). 97 | iso639(lt, 'Lithuanian'). 98 | iso639(lv, 'Latvian'). 99 | iso639(mg, 'Malagasy'). 100 | iso639(mi, 'Maori'). 101 | iso639(mk, 'Macedonian'). 102 | iso639(ms, 'Malay'). 103 | iso639(mt, 'Maltese'). 104 | iso639(nb, 'Norwegian Bokmål'). 105 | iso639(nl, 'Dutch'). 106 | iso639(nn, 'Norwegian Nynorsk'). 107 | iso639(oc, 'Occitan'). 108 | iso639(om, 'Oromo'). 109 | iso639(pl, 'Polish'). 110 | iso639(pt, 'Portugese'). 111 | iso639(ro, 'Romanian'). 112 | iso639(ru, 'Russian'). 113 | iso639(sk, 'Slovak'). 114 | iso639(sl, 'Slovenian'). 115 | iso639(so, 'Somali'). 116 | iso639(sq, 'Albanian'). 117 | iso639(st, 'Southern Sotho'). 118 | iso639(sv, 'Swedish'). 119 | iso639(tcy, 'Tulu'). 120 | iso639(tg, 'Tajik'). 121 | iso639(th, 'Thai'). 122 | iso639(tl, 'Tagalog'). 123 | iso639(tr, 'Turkish'). 124 | iso639(uk, 'Ukrainian'). 125 | iso639(uz, 'Uzbek'). 126 | iso639(wa, 'Walloon'). 127 | iso639(xh, 'Xhosa'). 128 | iso639(yi, 'Yiddish'). 129 | iso639(zh, 'Chinese'). 130 | iso639(zu, 'Zulu'). 131 | 132 | % ISO-3166 country codes for locales 133 | get_country_name(LC, C) :- 134 | iso3166(LC, C), !. 135 | get_country_name(LC, LC). 136 | 137 | iso3166('AD', 'Andorra'). 138 | iso3166('AE', 'United Arab Emirates'). 139 | iso3166('AL', 'Albania'). 140 | iso3166('AR', 'Argentina'). 141 | iso3166('AT', 'Austria'). 142 | iso3166('AU', 'Australia'). 143 | iso3166('BA', 'Bonsia and Herzegovina'). 144 | iso3166('BE', 'Belgium'). 145 | iso3166('BG', 'Bulgaria'). 146 | iso3166('BH', 'Bahrain'). 147 | iso3166('BO', 'Bolivia'). 148 | iso3166('BR', 'Brazil'). 149 | iso3166('BW', 'Botswana'). 150 | iso3166('BY', 'Belarus'). 151 | iso3166('CA', 'Canada'). 152 | iso3166('CH', 'Switzerland'). 153 | iso3166('CL', 'Chile'). 154 | iso3166('CN', 'China'). 155 | iso3166('CO', 'Colombia'). 156 | iso3166('CR', 'Costa Rica'). 157 | iso3166('CY', 'Cyprus'). 158 | iso3166('CZ', 'Czech Republic'). 159 | iso3166('DE', 'Germany'). 160 | iso3166('DJ', 'Djibouti'). 161 | iso3166('DK', 'Denmark'). 162 | iso3166('DO', 'Dominican Republic'). 163 | iso3166('DZ', 'Algeria'). 164 | iso3166('EC', 'Ecuador'). 165 | iso3166('EE', 'Estonia'). 166 | iso3166('EG', 'Egypt'). 167 | iso3166('ES', 'Spain'). 168 | iso3166('FI', 'Finland'). 169 | iso3166('FO', 'Faroe Islands'). 170 | iso3166('FR', 'France'). 171 | iso3166('GB', 'Great Britain'). 172 | iso3166('GE', 'Georgia'). 173 | iso3166('GL', 'Greenland'). 174 | iso3166('GR', 'Greece'). 175 | iso3166('GT', 'Guatemala'). 176 | iso3166('HK', 'Hong Kong'). 177 | iso3166('HN', 'Honduras'). 178 | iso3166('HR', 'Croatia'). 179 | iso3166('HU', 'Hungary'). 180 | iso3166('ID', 'Indonesia'). 181 | iso3166('IE', 'Ireland'). 182 | iso3166('IL', 'Israel'). 183 | iso3166('IN', 'India'). 184 | iso3166('IQ', 'Iraq'). 185 | iso3166('IS', 'Iceland'). 186 | iso3166('IT', 'Italy'). 187 | iso3166('JO', 'Jordan'). 188 | iso3166('JP', 'Japan'). 189 | iso3166('KE', 'Kenya'). 190 | iso3166('KR', 'Korea, Republic of'). 191 | iso3166('KW', 'Kuwait'). 192 | iso3166('KZ', 'Kazakhstan'). 193 | iso3166('LB', 'Lebanon'). 194 | iso3166('LT', 'Lithuania'). 195 | iso3166('LI', 'Liechtenstein'). 196 | iso3166('LU', 'Luxembourg'). 197 | iso3166('LV', 'Latvia'). 198 | iso3166('LY', 'Libya'). 199 | iso3166('MA', 'Morocco'). 200 | iso3166('MG', 'Madagascar'). 201 | iso3166('MK', 'Macedonia'). 202 | iso3166('MT', 'Malta'). 203 | iso3166('MX', 'Mexico'). 204 | iso3166('MY', 'Malaysia'). 205 | iso3166('NI', 'Nicaragua'). 206 | iso3166('NL', 'Netherlands'). 207 | iso3166('NO', 'Norway'). 208 | iso3166('NZ', 'New Zealand'). 209 | iso3166('OM', 'Oman'). 210 | iso3166('PA', 'Panama'). 211 | iso3166('PE', 'Peru'). 212 | iso3166('PH', 'Philippines'). 213 | iso3166('PL', 'Poland'). 214 | iso3166('PR', 'Puerto Rico'). 215 | iso3166('PT', 'Portugal'). 216 | iso3166('PY', 'Paraguay'). 217 | iso3166('QA', 'Qatar'). 218 | iso3166('RO', 'Romania'). 219 | iso3166('RU', 'Russian Federation'). 220 | iso3166('SA', 'Saudi Arabia'). 221 | iso3166('SC', 'Seychelles'). 222 | iso3166('SD', 'Sudan'). 223 | iso3166('SE', 'Sweden'). 224 | iso3166('SG', 'Singapore'). 225 | iso3166('SI', 'Slovenia'). 226 | iso3166('SK', 'Slovakia'). 227 | iso3166('SO', 'Somalia'). 228 | iso3166('SV', 'El Salvador'). 229 | iso3166('SY', 'Syria'). 230 | iso3166('TH', 'Thailand'). 231 | iso3166('TJ', 'Tajikistan'). 232 | iso3166('TN', 'Tunisia'). 233 | iso3166('TR', 'Turkey'). 234 | iso3166('TW', 'Taiwan'). 235 | iso3166('UA', 'Ukraine'). 236 | iso3166('UG', 'Uganda'). 237 | iso3166('US', 'United States of America'). 238 | iso3166('UY', 'Uruguay'). 239 | iso3166('UZ', 'Uzbekistan'). 240 | iso3166('VE', 'Venezuela'). 241 | iso3166('YE', 'Yemen'). 242 | iso3166('ZA', 'South Africa'). 243 | iso3166('ZW', 'Zimbabwe'). 244 | 245 | -------------------------------------------------------------------------------- /lib/fs_extfs.pl: -------------------------------------------------------------------------------- 1 | % vi: noexpandtab:tabstop=4:ft=gprolog 2 | % Copyright (c) 2024 Sergey Sikorskiy, released under the GNU GPLv2 license. 3 | 4 | % man ext4 5 | % man mkfs.ext4 6 | % extfs_feat(name, fs_ver, kernel) 7 | prop_info_extfs(extfs_feat, FS, L) :- 8 | prop_extfs_info(FS, N), !, 9 | prop_info(extfs_feat, _, IL), 10 | findall(prop_feat4(F, OF, Fmt, attr(N1, KV)), (member(prop_feat4(F, OF, Fmt, attr(N1, KV)), IL), prop_extfs_filter(N, N1)), L), 11 | true. 12 | prop_info_extfs(mnt_extfs_feat, FS, L) :- 13 | prop_extfs_info(FS, N), !, 14 | prop_info(mnt_extfs_feat, _, IL), 15 | findall(prop_feat4(F, OF, Fmt, attr(N1)), (member(prop_feat4(F, OF, Fmt, attr(N1)), IL), prop_extfs_filter(N, N1)), L), 16 | true. 17 | prop_info_extfs(mnt_extfs_opt, FS, L) :- 18 | prop_extfs_info(FS, N), !, 19 | prop_info(mnt_extfs_opt, _, IL), 20 | findall(opt3(F, Fmt, DV), (member(opt3a(F, Fmt, DV, attr(N1)), IL), prop_extfs_filter(N, N1)), L), 21 | true. 22 | prop_info_extfs(TAG, _FS, L) :- 23 | prop_info(TAG, _, L). 24 | 25 | prop_extfs_info(ext2, 2). 26 | prop_extfs_info(ext3, 3). 27 | prop_extfs_info(ext4, 4). 28 | 29 | % There is a bug with findall. 30 | prop_extfs_filter(N1, N2) :- 31 | N1 >= N2. 32 | 33 | prop_info(extfs_feat, feat4s('-O', ',', pref1('^')), [ 34 | prop_feat4('64bit', on, std, attr(4, kv(2, 6, 28))) 35 | , prop_feat4(bigalloc, off, std, attr(4, kv(3, 2, 0))) 36 | , prop_feat4(casefold, off, std, attr(4, kv(5, 2, 0))) 37 | , prop_feat4(dir_index, on, std, attr(2, kv(2, 6, 0))) 38 | , prop_feat4(dir_nlink, on, std, attr(4, kv(2, 6, 28))) 39 | , prop_feat4(ea_inode, off, std, attr(4, kv(4, 13, 0))) 40 | , prop_feat4(encrypt, off, std, attr(4, kv(4, 1, 0))) 41 | , prop_feat4(ext_attr, on, std, attr(2, kv(2, 6, 0))) 42 | , prop_feat4(extent, on, std, attr(4, kv(2, 6, 28))) 43 | , prop_feat4(extra_isize, on, std, attr(4, kv(2, 6, 28))) 44 | , prop_feat4(filetype, on, std, attr(2, kv(2, 2, 0))) 45 | , prop_feat4(flex_bg, on, std, attr(4, kv(2, 6, 28))) 46 | , prop_feat4(has_journal, on, std, attr(3, kv(2, 4, 15))) 47 | , prop_feat4(huge_file, on, std, attr(4, kv(2, 6, 28))) 48 | , prop_feat4(inline_data, off, std, attr(4, kv(3, 8, 0))) 49 | % , prop_feat4(journal_dev, off, std, attr(3, kv(2, 6, 10))) % !!! no info in man !!! 50 | , prop_feat4(large_dir, off, std, attr(4, kv(4, 13, 0))) 51 | , prop_feat4(large_file, on, std, attr(2, kv(2, 2, 0))) 52 | , prop_feat4(metadata_csum, on, std, attr(4, kv(3, 18, 0))) 53 | , prop_feat4(metadata_csum_seed, on, std, attr(4, kv(4, 4, 0))) 54 | , prop_feat4(meta_bg, off, std, attr(4, kv(2, 6, 28))) 55 | , prop_feat4(mmp, off, std, attr(4, kv(3, 0, 0))) 56 | , prop_feat4(project, off, std, attr(4, kv(4, 5, 0))) 57 | , prop_feat4(quota, off, std, attr(4, kv(3, 6, 0))) 58 | % , prop_feat4(orphan_file, on, std, attr(4, '???')) 59 | , prop_feat4(resize_inode, on, std, attr(2, kv(2, 6, 10))) 60 | , prop_feat4(sparse_super, on, std, attr(2, kv(2, 2, 0))) 61 | , prop_feat4(sparse_super2, off, std, attr(4, kv(3, 16, 0))) 62 | , prop_feat4(stable_inodes, off, std, attr(4, kv(5, 5, 0))) 63 | , prop_feat4(uninit_bg, off, std, attr(4, kv(2, 6, 28))) 64 | , prop_feat4(verity, off, std, attr(4, kv(5, 4, 0))) 65 | ]). 66 | 67 | prop_info(extfs_rw, opt4s(' '), [ 68 | opt4('block-size', int, 4096, '-b') 69 | , opt4('cluster-size', int, 2048, '-C') 70 | , opt4('root-directory', path, '', '-d') 71 | , opt4('error-behavior', enum([continue, 'remount-ro', panic]), continue, '-e') 72 | , opt4('force', enable, no, '-F') 73 | , opt4('blocks-per-group', int, 256, '-g') 74 | , opt4('number-of-groups', int, 2048, '-G') 75 | , opt4('bytes-per-inode', int, 16384, '-i') 76 | , opt4('inode-size', int, 256, '-I') 77 | , opt4('ext3 journal', enable, no, '-j') 78 | , opt4('bad blocks list', file, '', '-l') 79 | , opt4('label', str, '', '-L') 80 | , opt4('reserved-blocks-percentage', percentage, 5, '-m') 81 | , opt4('last-mounted-directory', path, '', '-M') 82 | , opt4('number-of-inodes', int, 2048, '-N') 83 | , opt4('creator-os', str, '', '-o') 84 | , opt4('revision', int, 1, '-r') 85 | , opt4('write descriptors only', enable, no, '-S') 86 | , opt4('fs-type', enum([ext2, ext3, ext4]), ext2, '-t') 87 | , opt4('usage-type', enum([default, floppy, small, big, huge]), default, '-T') 88 | % , opt4('UUID', combo_val(extfs_uuid, [opt3(default, none, random), opt3(clear, none, clear), opt3(time, none, time), opt3(uuid, guid, '')]), random, '-U') 89 | , opt4('undo_file', file, '', '-z') 90 | ]). 91 | 92 | prop_info(extfs_cs_ext_opts, opt3s('-E', ','), [ 93 | opt3(encoding, str, '') 94 | % , opt3(encoding_flags, int, 2048) 95 | % , opt3(mmp_update_interval, int, 2048) 96 | , opt3(stride, int, 2048) 97 | , opt3(stripe_width, int, 2048) 98 | , opt3(offset, int, 2048) 99 | , opt3(resize, int, 2048) 100 | , opt3(lazy_itable_init, enum([0, 1]), 1) 101 | , opt3(assume_storage_prezeroed, enum([0, 1]), 0) 102 | , opt3(no_copy_xattrs, enable, no) 103 | , opt3(num_backup_sb, enum([0, 1, 2]), 0) 104 | , opt3(packed_meta_blocks, enum([0, 1]), 0) 105 | % , opt3(root_owner, int, 2048) 106 | , opt3(test_fs, enable, no) 107 | , opt3(orphan_file_size, int, 0) 108 | % , opt3(discard, enable, yes) % This is a default value. 109 | , opt3(nodiscard, enable, no) 110 | % , opt3(quotatype, int, 2048) 111 | ]). 112 | 113 | prop_info(extfs_cs_jrn_opts, opt3s('-J', ','), [ 114 | opt3(size, size, 0) 115 | , opt3(fast_commit_size, size, 0) 116 | % , opt3(location, size, 0) 117 | % , opt3(device, size, 0) 118 | ]). 119 | 120 | prop_info(mnt_extfs_feat, feat4s('--options', ',', pref1('^')), [ 121 | prop_feat4(acl, on, pref1(no), attr(2)) 122 | , prop_feat4(abort, off, std, attr(4)) 123 | , prop_feat4(auto_da_alloc, off, pref1(no), attr(4)) 124 | , prop_feat4(block_validity, off, pref1(no), attr(4)) 125 | , prop_feat4(bsddf, off, std, attr(2)) 126 | , prop_feat4(debug, off, std, attr(2)) 127 | , prop_feat4(delalloc, off, pref1(no), attr(4)) 128 | , prop_feat4(dioread_lock, on, v2(dioread_lock, dioread_nolock), attr(4)) 129 | , prop_feat4(discard, off, pref1(no), attr(4)) 130 | , prop_feat4(grpid, off, pref1(no), attr(2)) 131 | , prop_feat4(grpquota, off, on_off, attr(2)) 132 | , prop_feat4(i_version, off, std, attr(4)) 133 | , prop_feat4(journal_async_commit, off, std, attr(4)) 134 | , prop_feat4(journal_checksum, off, pref1(no), attr(4)) 135 | , prop_feat4(minixdf, off, std, attr(2)) 136 | , prop_feat4(nocheck, off, std, attr(2)) 137 | , prop_feat4(noinit_itable, off, std, attr(4)) 138 | , prop_feat4(nombcache, off, std, attr(4)) 139 | , prop_feat4(norecovery, off, std, attr(3)) 140 | , prop_feat4(nouid32, off, std, attr(2)) 141 | , prop_feat4(oldalloc, off, v2(oldalloc, orlov), attr(2)) 142 | , prop_feat4(prjquota, off, std, attr(4)) 143 | , prop_feat4(quota, off, pref1(no), attr(2)) 144 | , prop_feat4(user_xattr, off, pref1(no), attr(2)) 145 | , prop_feat4(user_xattr, off, std, attr(3)) 146 | , prop_feat4(usrquota, off, on_off, attr(2)) 147 | ]). 148 | 149 | prop_info(mnt_extfs_opt, opt3s('--options', ','), [ 150 | opt3a(errors, enum([continue, 'remount-ro', panic]), 'remount-ro', attr(2)) 151 | , opt3a(resgid, int, 0, attr(2)) 152 | , opt3a(resuid, int, 0, attr(2)) 153 | , opt3a(sb, int, 0, attr(2)) 154 | , opt3a(journal_dev, int, 0, attr(3)) 155 | , opt3a(journal_path, str, '', attr(3)) 156 | , opt3a(data, enum([journal, ordered, writeback]), ordered, attr(3)) 157 | , opt3a(data_err, enum([none, ignore, abort]), none, attr(3)) 158 | , opt3a(barrier, enum([0, 1]), 1, attr(3)) 159 | , opt3a(commit, int, 0, attr(3)) 160 | , opt3a(jqfmt, enum([vfsold, vfsv0, vfsv1]), vfsv1, attr(3)) 161 | , opt3a(usrjquota, int, 0, attr(3)) 162 | , opt3a(grpjquota, int, 0, attr(3)) 163 | , opt3a(inode_readahead_blks, int, 0, attr(4)) 164 | , opt3a(stripe, int, 0, attr(4)) 165 | , opt3a(max_batch_time, int, 15000, attr(4)) 166 | , opt3a(min_batch_time, int, 0, attr(4)) 167 | , opt3a(journal_ioprio, enum([0, 1, 2, 3, 4, 5, 6, 7]), 3, attr(4)) 168 | , opt3a(init_itable, int, 1, attr(4)) 169 | , opt3a(max_dir_size_kb, int, 0, attr(4)) 170 | ]). 171 | 172 | -------------------------------------------------------------------------------- /module/void_bootloader.pl: -------------------------------------------------------------------------------- 1 | % vi: noexpandtab:tabstop=4:ft=gprolog 2 | % Copyright (c) 2023-2024 Sergey Sikorskiy, released under the GNU GPLv2 license. 3 | 4 | % List of boot managers which require mounting of EFI partition to /boot. 5 | bootloader_boot_efi([syslinux, efistub, gummiboot]). 6 | 7 | % bootloader_info(bootloader, supported_fs, supported_template, except_fs). 8 | bootloader_info(grub2, [ 9 | btrfs 10 | , ext2 11 | , ext3 12 | , ext4 13 | % , f2fs % drub doesn't support extra_attr 14 | , swap 15 | , vfat 16 | % , xfs % GRUB2 Fails to boot off XFS Partition (https://bugzilla.redhat.com/show_bug.cgi?id=2254370) 17 | , zfs 18 | , nilfs2 19 | ], [ 20 | manual 21 | , gpt_basic 22 | , gpt_lvm 23 | , gpt_lvm_luks 24 | , gpt_luks 25 | , gpt_luks_lvm 26 | % , gpt_wizard 27 | % , gpt_raid 28 | ], [ 29 | exfat 30 | ]). 31 | bootloader_info(rEFInd, [ 32 | btrfs 33 | , ext2 34 | , ext3 35 | , ext4 36 | , vfat 37 | ], [ 38 | manual 39 | , gpt_basic 40 | , gpt_lvm 41 | , gpt_lvm_luks 42 | , gpt_luks 43 | , gpt_luks_lvm 44 | ], [ 45 | exfat 46 | ]). 47 | bootloader_info(limine, [ 48 | vfat 49 | % , ext2 % Limine extX support is removed in 6.20231210.0 as per the ChangeLog. 50 | % , ext3 51 | % , ext4 52 | ], [ 53 | manual 54 | , gpt_basic 55 | , gpt_lvm 56 | , gpt_lvm_luks 57 | , gpt_luks 58 | , gpt_luks_lvm 59 | ], [ 60 | btrfs % Error: dracut /sysroot has no proper rootfs layout. Can't mount root filesystem. 61 | , exfat 62 | ]). 63 | bootloader_info(efistub, [ 64 | ext2 65 | , ext3 66 | , ext4 67 | , vfat 68 | ], [ 69 | manual 70 | , gpt_basic 71 | , gpt_lvm 72 | , gpt_lvm_luks 73 | , gpt_luks 74 | , gpt_luks_lvm 75 | ], [ 76 | btrfs % Error: dracut /sysroot has no proper rootfs layout. Can't mount root filesystem. 77 | , exfat 78 | ]). 79 | bootloader_info(syslinux, [ 80 | % btrfs % Only uncompressed single-device Btrfs is supported. 81 | ext2 82 | , ext3 83 | , ext4 84 | % , f2fs % Not supported by syslinux 85 | % , swap 86 | , vfat 87 | % , xfs 88 | % , ufs 89 | ], [ 90 | manual 91 | , gpt_basic 92 | , gpt_lvm 93 | , gpt_lvm_luks 94 | , gpt_luks 95 | , gpt_luks_lvm 96 | ], [ 97 | % btrfs % It boots with the current configuration of btrfs + EFI. Doesn't boot with BIOS. 98 | btrfs % Error: dracut /sysroot has no proper rootfs layout. Can't mount root filesystem. 99 | , exfat 100 | % , f2fs % Not supported by syslinux 101 | % , xfs % Won't boot with BIOS 102 | ]). 103 | bootloader_info(gummiboot, [ 104 | btrfs 105 | , ext2 106 | , ext3 107 | , ext4 108 | , f2fs 109 | % , swap 110 | , vfat 111 | , xfs 112 | , nilfs2 113 | ], [ 114 | manual 115 | , gpt_basic 116 | , gpt_lvm 117 | , gpt_lvm_luks 118 | , gpt_luks 119 | , gpt_luks_lvm 120 | ], [ 121 | exfat 122 | , btrfs % Error: dracut /sysroot has no proper rootfs layout. Can't mount root filesystem. 123 | ]). 124 | bootloader_info(zfsBootMenu, [ 125 | zfs 126 | ], [ 127 | % manual 128 | gpt_basic 129 | ], []). 130 | 131 | % Get bootloader name/dependency 132 | target_dep_bootloader(efistub, [efibootmgr]) :- !. 133 | target_dep_bootloader(zfsBootMenu, [zfsbootmenu, efibootmgr, 'gummiboot-efistub']) :- !. 134 | target_dep_bootloader(gummiboot, [gummiboot]) :- !. 135 | target_dep_bootloader(syslinux, [syslinux, efibootmgr]) :- !. 136 | target_dep_bootloader(limine, [limine]) :- !. 137 | target_dep_bootloader(rEFInd, [refind]) :- !. 138 | target_dep_bootloader(grub2, [GRUB]) :- 139 | inst_setting(system(arch), ARCH), 140 | arch2grub(ARCH, GRUB), !. 141 | 142 | get_bootloader(TL, B) :- 143 | memberchk(bootloader(B), TL). 144 | 145 | get_bootloader_dev7(TL, DEV7) :- 146 | memberchk(bootloader_dev7(DEV7), TL). 147 | 148 | install_bootloader(TL, _RD) :- 149 | % Do not install bootloader if a bootloader dev has not been selected. 150 | \+ get_bootloader_dev7(TL, _), !. 151 | install_bootloader(TL, RD) :- 152 | get_bootloader(TL, B), 153 | get_bootloader_dev7(TL, DEV7), 154 | lx_dev7_to_ldn(DEV7, BD), 155 | install_bootloader(B, TL, BD, RD), !. 156 | install_bootloader(_TL, _RD) :- 157 | tui_msgbox('Setting up of a bootloader has failed.'), 158 | fail. 159 | 160 | % Get bootloader mount point. 161 | get_bootloader_mp(B, MP) :- 162 | bootloader_boot_efi(BL), 163 | ( memberchk(B, BL) -> 164 | MP = '/boot' 165 | ; MP = '/boot/efi' 166 | ). 167 | 168 | % install_bootloader(bootloader, template_list, bootloader_dev, root_dir) 169 | install_bootloader(_, _TL, none, _RD) :- !. 170 | install_bootloader(grub2, TL, BD, RD) :- !, 171 | grub_configure(TL, RD), 172 | grub_install(TL, BD, RD), 173 | grub_mkconfig(TL, RD), 174 | !. 175 | install_bootloader(rEFInd, TL, _BD, RD) :- !, 176 | refind_install(TL, RD), 177 | refind_configure(TL, RD), 178 | !. 179 | install_bootloader(limine, TL, BD, RD) :- !, 180 | limine_install(BD, RD), 181 | limine_configure(TL, RD), 182 | !. 183 | install_bootloader(efistub, _TL, _BD, _RD) :- !, 184 | % efistub_install(BD, RD), 185 | % efistub_configure(TL, RD), 186 | !. 187 | install_bootloader(syslinux, TL, BD, RD) :- !, 188 | syslinux_install(TL, BD, RD), 189 | syslinux_configure(TL, RD), 190 | !. 191 | install_bootloader(gummiboot, _TL, _BD, RD) :- !, 192 | gummiboot_install(RD), 193 | % gummiboot_configure(TL, RD), 194 | !. 195 | install_bootloader(zfsBootMenu, TL, BD, RD) :- !, 196 | % tui_msgbox(install_bootloader_1), 197 | zfsbootmenu_configure(RD), 198 | % tui_msgbox(install_bootloader_2), 199 | zfsbootmenu_install(TL, BD, RD), 200 | % tui_msgbox(install_bootloader_3), 201 | !. 202 | 203 | /* Old code. */ 204 | bootloader_kernel_params(TL, L) :- 205 | bootloader_kernel_params_root(TL, L), 206 | true. 207 | /* DO NOT delete. New code. It doesn't work with Limine and gpt_luks template 208 | bootloader_kernel_params(TL, L) :- 209 | % Device 210 | root_pd(TL, ROOT_PD), 211 | ( atom_concat('/dev/mapper/', _, ROOT_PD) -> 212 | L = [root=ROOT_PD] 213 | ; lx_get_dev_uuid(ROOT_PD, RPID), 214 | L = [root=v('UUID', RPID)] 215 | ), 216 | lx_get_dev_uuid(ROOT_PD, RPID), 217 | true. 218 | */ 219 | bootloader_kernel_params(_TL, [init='/sbin/init', rw]). 220 | bootloader_kernel_params(TL, L) :- 221 | % LUKS 222 | ( uses_luks(TL) -> 223 | bootloader_kernel_params_luks(TL, L) 224 | ; L = ['rd.luks'=0] 225 | ), 226 | true. 227 | bootloader_kernel_params(TL, ['rd.lvm'=0]) :- 228 | \+ memberchk(bdev(lvm, _Value), TL), 229 | true. 230 | bootloader_kernel_params(_TL, [ 231 | 'rd.md'=0 232 | , 'rd.dm'=0 233 | , loglevel=4 234 | , gpt 235 | , 'vconsole.unicode'=1 236 | , 'vconsole.keymap'=KB 237 | , 'locale.LANG'=LC 238 | % , 'rd.live.overlay.overlayfs'=1 239 | ]) :- 240 | inst_setting(keymap, KB), 241 | inst_setting(locale, LC), 242 | true. 243 | 244 | bootloader_kernel_params_luks(TL, ['rd.luks.name'=v(PUUID, LUKS_PD)]) :- 245 | member(bdev(luks, luks(_, PD)), TL), 246 | lx_get_dev_uuid(PD, PUUID), 247 | lx_split_dev(PD, _P, SDN), 248 | luks_dev_name_short(SDN, LUKS_PD), 249 | true. 250 | bootloader_kernel_params_luks(_TL, ['rd.auto'=1]) :- 251 | inst_setting(hostonly, no). 252 | 253 | bootloader_kernel_params_root(TL, [root='zfs:AUTO']) :- 254 | uses_zfs(TL), 255 | % has_boot_part(TL), 256 | !. 257 | bootloader_kernel_params_root(TL, [root=v('UUID', RPID)]) :- 258 | root_pd(TL, ROOT_PD), 259 | lx_get_dev_uuid(ROOT_PD, RPID), 260 | true. 261 | 262 | bootloader_write_cmdline(TL, S) :- 263 | findall(P0, (bootloader_kernel_params(TL, PL0), member(P0, PL0)), AL), 264 | os_wcmdl(AL, S), 265 | true. 266 | 267 | reconfig_kernel(RD) :- 268 | lx_kernel_ver(RD, LV), 269 | tui_progressbox_safe([chroot, RD, 'xbps-reconfigure', '-f', concat(linux, LV), '2>&1'], '', [title(' Reconfigure Linux '), sz([18, 60])]), 270 | true. 271 | 272 | setup_bootloader(_B, TL, _RD) :- 273 | % Do not setup bootloader if a bootloader dev has not been selected. 274 | \+ get_bootloader_dev7(TL, _), !. 275 | setup_bootloader(gummiboot, TL, RD) :- !, 276 | gummiboot_configure(TL, RD), 277 | reconfig_kernel(RD), 278 | true. 279 | setup_bootloader(efistub, _TL, RD) :- !, 280 | reconfig_kernel(RD), 281 | efistub_install(RD), 282 | true. 283 | setup_bootloader(_B, _TL, _RD) :- 284 | true. 285 | 286 | replace_bootloader_dev7(none, NSN, L, TL, NTL) :- !, 287 | % add 288 | lx_sdn_to_dev7(L, NSN, DEV7), 289 | NTL = [bootloader_dev7(DEV7)| TL]. 290 | replace_bootloader_dev7(_, none, _L, TL, NTL) :- !, 291 | % remove 292 | findall(E, (member(E, TL), E \= bootloader_dev7(_)), NTL). 293 | replace_bootloader_dev7(_, NSN, L, TL, NTL) :- 294 | % replace 295 | lx_sdn_to_dev7(L, NSN, DEV7), 296 | maplist(replace_element(bootloader_dev7(_), bootloader_dev7(DEV7)), TL, NTL). 297 | 298 | replace_bootloader(B) :- 299 | retract(inst_setting(template(TT), OTL)), 300 | replace_bootloader(B, OTL, NTL), 301 | assertz(inst_setting(template(TT), NTL)). 302 | 303 | % B - new bootloader. 304 | replace_bootloader(B, TL, NTL) :- 305 | maplist(replace_element(bootloader(_), bootloader(B)), TL, NTL). 306 | 307 | ensure_bootloader_dev(manual, _TL) :- !. 308 | ensure_bootloader_dev(TT, TL) :- 309 | ( get_bootloader_dev7(TL, _) 310 | ; cmd_menu(bootloader_dev, TT, TL) 311 | ), !. 312 | 313 | -------------------------------------------------------------------------------- /lib/os_common.pl: -------------------------------------------------------------------------------- 1 | % vi: noexpandtab:tabstop=4:ft=gprolog 2 | % Copyright (c) 2023-2024 Sergey Sikorskiy, released under the GNU GPLv2 license. 3 | 4 | :- dynamic([get_log/1]). 5 | 6 | get_log('/dev/tty8'). 7 | 8 | os_mkdir(D) :- 9 | os_call2([mkdir, D]). 10 | 11 | os_mkdir_p(D) :- 12 | os_call2([mkdir, '-p', D]). 13 | 14 | os_rm_f(F) :- 15 | os_call2([rm, '-f', F]). 16 | 17 | % C - command. 18 | os_call(C, O) :- 19 | spawn(C, O, 0). 20 | 21 | os_call_rc(C, O, RC) :- 22 | spawn(C, O, RC). 23 | 24 | os_call2(L) :- 25 | os_ccmdl(L, [H|T]), 26 | os_call(H, T). 27 | 28 | os_call2_rc(L, RC) :- 29 | os_ccmdl(L, [H|T]), 30 | os_call_rc(H, T, RC). 31 | 32 | os_shell(C) :- 33 | shell(C, 0). 34 | 35 | os_shell_rc(C, RC) :- 36 | shell(C, RC). 37 | 38 | os_shell2(L) :- 39 | os_scmdl(L, LA), 40 | os_shell(LA). 41 | 42 | os_shell2l(L) :- 43 | get_log(LOG), 44 | os_scmdl([L, ['>>', LOG]], LA), 45 | os_shell(LA). 46 | 47 | os_shell2_rc(L, RC) :- 48 | os_scmdl(L, LA), 49 | os_shell_rc(LA, RC). 50 | 51 | % Empty list if there is no output. 52 | os_shell_codes(C, L) :- 53 | popen(C, read, RS), !, 54 | ( at_end_of_stream(RS) -> 55 | L = [] 56 | ; read_file_codes(RS, L), 57 | close(RS) 58 | ), 59 | true. 60 | 61 | os_shell2_codes(IL, L) :- 62 | os_scmdl(IL, C), 63 | os_shell_codes(C, L). 64 | 65 | % VL - value list. 66 | os_shell_codes(C, VL, L) :- 67 | format_to_atom(CA, C, VL), 68 | os_shell_codes(CA, L). 69 | 70 | os_shell2_codes(IL, VL, L) :- 71 | os_scmdl(IL, C), 72 | os_shell_codes(C, VL, L). 73 | 74 | % Empty list if there is no output. 75 | os_shell_codes_rc(C, L, RC) :- 76 | exec(C, SI, SO, SE, Pid), 77 | wait(Pid, RC), 78 | ( at_end_of_stream(SO) -> 79 | L = [] 80 | ; read_file_codes(SO, L), 81 | close(SI), 82 | close(SO), 83 | close(SE) 84 | ), 85 | true. 86 | 87 | % VL - value list. 88 | os_shell_codes_rc(C, VL, L, RC) :- 89 | format_to_atom(CA, C, VL), 90 | os_shell_codes_rc(CA, L, RC). 91 | 92 | os_shell_atom(C, A) :- 93 | os_shell_codes(C, L), 94 | atom_codes(A, L). 95 | 96 | os_shell2_atom(IL, A) :- 97 | os_shell2_codes(IL, L), 98 | atom_codes(A, L). 99 | 100 | os_shell_atom(C, VL, A) :- 101 | os_shell_codes(C, VL, L), 102 | atom_codes(A, L). 103 | 104 | os_shell2_atom(IL, VL, A) :- 105 | os_shell2_codes(IL, VL, L), 106 | atom_codes(A, L). 107 | 108 | os_shell_atom_list(C, AL) :- 109 | os_shell_codes(C, L), 110 | split_atom_codes_ne(L, LL), 111 | maplist(codes_atom, LL, AL). 112 | 113 | os_shell_atom_list(C, VL, AL) :- 114 | os_shell_codes(C, VL, L), 115 | split_atom_codes_ne(L, LL), 116 | maplist(codes_atom, LL, AL). 117 | 118 | os_shell_number(C, N) :- 119 | os_shell_codes_line(C, L), 120 | number_codes(N, L). 121 | 122 | os_shell2_number(IL, N) :- 123 | os_shell2_codes_line(IL, L), 124 | number_codes(N, L). 125 | 126 | os_shell_number(C, VL, N) :- 127 | os_shell_codes_line(C, VL, L), 128 | number_codes(N, L). 129 | 130 | os_shell2_number(IL, VL, N) :- 131 | os_shell2_codes_line(IL, VL, L), 132 | number_codes(N, L). 133 | 134 | os_shell_codes_line(C, L) :- 135 | popen(C, read, RS), !, 136 | ( at_end_of_stream(RS) -> 137 | L = [] 138 | ; read_file_codes_line(RS, L), 139 | close(RS) 140 | ), 141 | true. 142 | 143 | os_shell2_codes_line(IL, L) :- 144 | os_scmdl(IL, LA), 145 | os_shell_codes_line(LA, L). 146 | 147 | os_shell_codes_line(C, VL, L) :- 148 | format_to_atom(CA, C, VL), 149 | os_shell_codes_line(CA, L). 150 | 151 | os_shell2_codes_line(IL, VL, L) :- 152 | os_scmdl(IL, LA), 153 | os_shell_codes_line(LA, VL, L). 154 | 155 | os_shell_line(C, A) :- 156 | os_shell_codes_line(C, L), 157 | atom_codes(A, L). 158 | 159 | os_shell2_line(IL, A) :- 160 | os_shell2_codes_line(IL, L), 161 | atom_codes(A, L). 162 | 163 | os_shell_line(C, VL, A) :- 164 | os_shell_codes_line(C, VL, L), 165 | atom_codes(A, L). 166 | 167 | os_shell2_line(IL, VL, A) :- 168 | os_shell2_codes_line(IL, VL, L), 169 | atom_codes(A, L). 170 | 171 | os_shell_lines_codes(C, CL) :- 172 | popen(C, read, RS), 173 | ( at_end_of_stream(RS) -> 174 | CL = [] 175 | ; read_file_codes_lines(RS, CL), 176 | close(RS) 177 | ), 178 | true. 179 | 180 | % Reads lines, skip comments. 181 | os_shell_lines_codes_nc(C, CMNT, CL) :- 182 | popen(C, read, RS), 183 | ( at_end_of_stream(RS) -> 184 | CL = [] 185 | ; read_file_codes_lines_nc(RS, CMNT, CL), 186 | close(RS) 187 | ), 188 | true. 189 | 190 | os_shell2_lines_codes(IL, CL) :- 191 | os_scmdl(IL, LA), 192 | os_shell_lines_codes(LA, CL), 193 | true. 194 | 195 | os_shell2_lines_codes_nc(IL, CMNT, CL) :- 196 | os_scmdl(IL, LA), 197 | os_shell_lines_codes_nc(LA, CMNT, CL), 198 | true. 199 | 200 | % Always succeeds. Returns [] if there are no lines. 201 | os_shell_lines(C, AL) :- 202 | os_shell_lines_codes(C, CL), 203 | maplist(codes_atom, CL, AL). 204 | 205 | os_shell_lines(C, VL, A) :- 206 | format_to_atom(CA, C, VL), 207 | os_shell_lines(CA, A). 208 | 209 | % Always succeeds. Returns [] if there are no lines. 210 | os_shell2_lines(IL, AL) :- 211 | os_shell2_lines_codes(IL, CL), 212 | maplist(codes_atom, CL, AL). 213 | 214 | os_shell2_lines_nc(IL, CMNT, AL) :- 215 | os_shell2_lines_codes_nc(IL, CMNT, CL), 216 | maplist(codes_atom, CL, AL). 217 | 218 | % Pipe output of the command C1 into the command C2. 219 | % OA - output atom 220 | os_shell_pipe_rc(C1, C2, OA, RC) :- 221 | exec(C1, SI, SO, SE, Pid), 222 | popen(C2, write, WS), 223 | open_output_atom_stream(AS), 224 | set_stream_type(SO, binary), 225 | set_stream_type(WS, binary), 226 | set_stream_buffering(SO, block), 227 | % set_stream_buffering(WS, block), 228 | set_stream_buffering(WS, none), 229 | add_stream_mirror(SO, WS), 230 | add_stream_mirror(SO, AS), 231 | repeat, 232 | get_byte(SO, -1), 233 | close(SI), 234 | close(SO), 235 | close(SE), 236 | close(WS, [force(true)]), 237 | close_output_atom_stream(AS, OA), 238 | ( wait(Pid, RC) 239 | ; RC = -12345 240 | ), 241 | !. 242 | 243 | % OA - output atom 244 | os_shell_ostream_rc(C1, OA, RC) :- 245 | exec(C1, SI, SO, SE, Pid), 246 | open_output_atom_stream(AS), 247 | set_stream_type(SO, binary), 248 | set_stream_buffering(SO, block), 249 | add_stream_mirror(SO, AS), 250 | repeat, 251 | get_byte(SO, -1), 252 | close(SI), 253 | close(SO), 254 | close(SE), 255 | close_output_atom_stream(AS, OA), 256 | ( wait(Pid, RC) 257 | ; RC = -12345 258 | ), 259 | !. 260 | 261 | os_shell2_ostream_rc(IL, OA, RC) :- 262 | os_scmdl(IL, ILA), 263 | os_shell_ostream_rc(ILA, OA, RC). 264 | 265 | % Pipe input stream into a command. 266 | os_shell_istream(RS, C2) :- 267 | popen(C2, write, WS), 268 | set_stream_type(RS, binary), 269 | set_stream_type(WS, binary), 270 | set_stream_buffering(RS, block), 271 | % set_stream_buffering(WS, block), 272 | set_stream_buffering(WS, none), 273 | add_stream_mirror(RS, WS), 274 | repeat, 275 | get_byte(RS, -1), 276 | close(WS), 277 | !. 278 | 279 | % Pipe atom to a command. 280 | os_shell_atom_input(A, C2) :- 281 | open_input_atom_stream(A, SO), 282 | os_shell_istream(SO, C2), 283 | close_input_atom_stream(SO). 284 | 285 | os_shell2_pipe_rc(IL1, IL2, OA, RC) :- 286 | os_scmdl(IL1, IL1A), 287 | os_scmdl(IL2, IL2A), 288 | os_shell_pipe_rc(IL1A, IL2A, OA, RC). 289 | 290 | os_shell2_istream(SI, IL2) :- 291 | os_scmdl(IL2, IL2A), 292 | os_shell_istream(SI, IL2A). 293 | 294 | os_shell2_atom_input(A, IL2) :- 295 | os_scmdl(IL2, IL2A), 296 | os_shell_atom_input(A, IL2A). 297 | 298 | os_ccmdl(IL, OL) :- 299 | phrase(os_cmdl(IL), OL), !. 300 | 301 | os_scmdl(IL, A) :- 302 | os_ccmdl(IL, OL), 303 | join_atoms(OL, ' ', A), !. 304 | 305 | % Space-separated. 306 | os_wcmdl(IL, S) :- 307 | os_wcmdl(IL, ' ', S). 308 | 309 | os_wcmdl(IL, SEP, S) :- 310 | os_ccmdl(IL, OL), 311 | write_atoms(OL, SEP, S), !. 312 | 313 | % !!! Do not use os_cmdl directly! 314 | os_cmdl([H|T]) --> 315 | os_cmd(H), 316 | os_cmdl(T). 317 | os_cmdl([]) --> 318 | []. 319 | 320 | os_cmd(o(O)) --> { atom_concat('-', O, O1) }, [O1]. 321 | os_cmd(o(O, V)) --> { phrase(os_cmd(V), [V1]), atom_concat('-', O, O1) }, [O1, V1]. 322 | os_cmd(oo(O)) --> { atom_concat('--', O, O1) }, [O1]. 323 | os_cmd(oo(O, V)) --> { phrase(os_cmd(V), [V1]), format_to_atom(O1, '--~w=~w', [O, V1]) }, [O1]. 324 | % double-quoted 325 | os_cmd(dq(V)) --> { phrase(os_cmd(V), [V1]), format_to_atom(QV, '"~w"', [V1]) }, [QV]. 326 | % list with separator 327 | os_cmd(l(L, S)) --> { os_ccmdl(L, OL), join_atoms(OL, S, A) }, [A]. 328 | % comma-separated list 329 | os_cmd(lc(L)) --> { os_ccmdl(L, OL), join_atoms(OL, ',', A) }, [A]. 330 | % value assignment 331 | os_cmd(v(O, V)) --> { phrase(os_cmd(V), [V1]), format_to_atom(O1, '~w=~w', [O, V1]) }, [O1]. 332 | os_cmd(O = V) --> { phrase(os_cmd(V), [V1]), format_to_atom(O1, '~w=~w', [O, V1]) }, [O1]. 333 | % value assignment with a separator 334 | os_cmd(vs(O, Sep, V)) --> { phrase(os_cmd(V), [V1]), format_to_atom(O1, '~w~w~w', [O, Sep, V1]) }, [O1]. 335 | % concat 336 | os_cmd(concat(V1, V2)) --> { phrase(os_cmd(V1), [V11]), phrase(os_cmd(V2), [V21]), atom_concat(V11, V21, V3) }, [V3]. 337 | os_cmd(V1 + V2) --> { phrase(os_cmd(V1), [V11]), phrase(os_cmd(V2), [V21]), atom_concat(V11, V21, V3) }, [V3]. 338 | 339 | os_cmd(V) --> { is_list(V) }, os_cmdl(V). 340 | os_cmd(V) --> { number(V), number_atom(V, A) }, [A]. 341 | os_cmd(V) --> [V]. 342 | 343 | -------------------------------------------------------------------------------- /lib/linux_dracut.pl: -------------------------------------------------------------------------------- 1 | % vi: noexpandtab:tabstop=4:ft=gprolog 2 | % Copyright (c) 2023-2024 Sergey Sikorskiy, released under the GNU GPLv2 license. 3 | 4 | % v(name, value, comment) 5 | lx_dracut_conf(VL, FN, RD) :- 6 | format_to_atom(DF, '~w/etc/dracut.conf.d/~w.conf', [RD, FN]), 7 | open(DF, write, S), 8 | maplist(lx_write_dracut_value(S), VL), 9 | close(S), 10 | true. 11 | 12 | lx_write_dracut_value(S, VV) :- 13 | ( VV = v(K, V, C1) 14 | ; VV = v(K, V), C1 = '' 15 | ), 16 | lx_dracut_info(K, OP, F, C2), 17 | % Write comment. 18 | write(S, '# '), 19 | ( C1 \= '' -> 20 | write(S, C1) 21 | ; write(S, C2) 22 | ), 23 | nl(S), 24 | % Write value. 25 | write(S, K), write(S, OP), write(S, '"'), 26 | lx_write_dracut_value2(F, V, S), 27 | write(S, '"\n'), 28 | true. 29 | 30 | lx_write_dracut_value2(f(SEP, _C), VL, S) :- 31 | write(S, ' '), 32 | ( list(VL) -> 33 | write_atoms(VL, SEP, S) 34 | ; write(S, VL) 35 | ), 36 | write(S, ' '), 37 | true. 38 | 39 | lx_write_dracut_value2(f(_C), V, S) :- 40 | ( list(V) -> 41 | maplist(write(S), V) 42 | ; write(S, V) 43 | ), 44 | true. 45 | 46 | % lx_dracut_info(name, operand, f(sep, comment), comment) 47 | lx_dracut_info(add_dracutmodules, '+=', f(' ', ' '), 'Specify a space-separated list of dracut modules to call when building the initramfs.'). 48 | lx_dracut_info(force_add_dracutmodules, '+=', f(' ', ' '), 'Force to add a space-separated list of dracut modules to the default set of modules, when host-only mode is specified.'). 49 | lx_dracut_info(omit_dracutmodules, '+=', f(' ', ' '), 'Omit a space-separated list of dracut modules to call when building the initramfs.'). 50 | lx_dracut_info(dracutmodules, '+=', f(' ', ' '), 'Specify a space-separated list of dracut modules to call when building the initramfs.'). 51 | lx_dracut_info(add_drivers, '+=', f(' ', ' '), 'Specify a space-separated list of kernel modules to add to the initramfs.'). 52 | lx_dracut_info(force_drivers, '+=', f(' ', ' '), 'See add_drivers. But in this case it is ensured that the drivers are tried to be loaded early via modprobe.'). 53 | lx_dracut_info(omit_drivers, '+=', f(' ', ' '), 'Specify a space-separated list of kernel modules not to add to the initramfs.'). 54 | lx_dracut_info(drivers, '+=', f(' ', ' '), 'Specify a space-separated list of kernel modules to exclusively include in the initramfs.'). 55 | lx_dracut_info(filesystems, '+=', f(' ', ' '), 'Specify a space-separated list of kernel filesystem modules to exclusively include in the generic initramfs.'). 56 | lx_dracut_info(drivers_dir, '=', f(''), 'Specify the directory where to look for kernel modules.'). 57 | lx_dracut_info(fw_dir, '+=', f(':', ' :[: ...] '), 'Specify additional colon-separated list of directories where to look for firmware files.'). 58 | lx_dracut_info(libdirs, '+=', f(' ', ' [ ...] '), 'Specify a space-separated list of directories where to look for libraries.'). 59 | lx_dracut_info(install_items, '+=', f(' ', ' [ ...] '), 'Specify additional files to include in the initramfs, separated by spaces.'). 60 | lx_dracut_info(install_optional_items, '+=', f(' ', ' [ ...] '), 'Specify additional files to include in the initramfs, separated by spaces, if they exist.'). 61 | lx_dracut_info(compress, '=', f('{cat|bzip2|lzma|xz|gzip|lzop|lz4|zstd|}'), 'Compress the generated initramfs using the passed compression program.'). 62 | lx_dracut_info(squash_compress, '=', f('{}'), 'Compress the squashfs image using the passed compressor and compressor specific options for mksquashfs.'). 63 | lx_dracut_info(do_strip, '=', f('{yes|no}'), 'Strip binaries in the initramfs (default=yes).'). 64 | lx_dracut_info(aggressive_strip, '=', f('{yes|no}'), 'Strip more than just debug symbol and sections, for a smaller initramfs build.'). 65 | lx_dracut_info(do_hardlink, '=', f('{yes|no}'), 'Hardlink files in the initramfs (default=yes).'). 66 | lx_dracut_info(prefix, '=', f('', ' '), 'Prefix initramfs files with .'). 67 | lx_dracut_info(hostonly, '=', f('{yes|no}'), 'Host-only mode: Install only what is needed for booting the local host instead of a generic host and generate host-specific configuration (default=no).'). 68 | lx_dracut_info(hostonly_mode, '=', f('{sloppy|strict}'), 'Specify the host-only mode to use (default=sloppy).'). 69 | lx_dracut_info(hostonly_cmdline, '=', f('{yes|no}'), 'If set to "yes", store the kernel command line arguments needed in the initramfs. If hostonly="yes" and this option is not configured, it’s automatically set to "yes".'). 70 | lx_dracut_info(hostonly_nics, '+=', f(' ', ' [[ ...]] '), 'Only enable listed NICs in the initramfs.'). 71 | lx_dracut_info(persistent_policy, '=', f(''), 'Use to address disks and partitions.'). 72 | lx_dracut_info(tmpdir, '=', f(''), 'Specify temporary directory to use.'). 73 | lx_dracut_info(use_fstab, '=', f('{yes|no}'), 'Use /etc/fstab instead of /proc/self/mountinfo (default=no).'). 74 | lx_dracut_info(add_fstab, '+=', f('', ' '), 'Add entries of to the initramfs /etc/fstab.'). 75 | lx_dracut_info(add_device, '+=', f('', ' '), 'Bring up in initramfs, should be the device name.'). 76 | lx_dracut_info(mdadmconf, '=', f('{yes|no}'), 'Include local /etc/mdadm.conf (default=no).'). 77 | lx_dracut_info(lvmconf, '=', f(''), '"{yes|no}" Include local /etc/lvm/lvm.conf (default=no).'). 78 | lx_dracut_info(fscks, '=', f(' ', ' '), 'Add a space-separated list of fsck tools.'). 79 | lx_dracut_info(nofscks, '=', f('{yes|no}'), 'If specified, inhibit installation of any fsck tools (default=no).'). 80 | lx_dracut_info(ro_mnt, '=', f('{yes|no}'), 'Mount / and /usr read-only by default (default=no).'). 81 | lx_dracut_info(kernel_cmdline, '=', f('parameters'), 'Specify default kernel command line parameters.'). 82 | lx_dracut_info(kernel_only, '=', f('{yes|no}'), 'Only install kernel drivers and firmware files (default=no).'). 83 | lx_dracut_info(no_kernel, '=', f('{yes|no}'), 'Do not install kernel drivers and firmware files (default=no).'). 84 | lx_dracut_info(acpi_override, '=', f('{yes|no}'), 'Override BIOS provided ACPI tables.'). 85 | lx_dracut_info(acpi_table_dir, '=', f(''), 'Directory to search for ACPI tables if acpi_override= is set to yes.'). 86 | lx_dracut_info(early_microcode, '=', f('{yes|no}'), 'Combine early microcode with ramdisk (default=yes).'). 87 | lx_dracut_info(stdloglvl, '=', f('{0-6}'), 'Specify logging level for standard error (default=4).'). 88 | lx_dracut_info(sysloglvl, '=', f('{0-6}'), 'Specify logging level for syslog (default=0).'). 89 | lx_dracut_info(fileloglvl, '=', f('{0-6}'), 'Specify logging level for logfile (default=4).'). 90 | lx_dracut_info(logfile, '=', f(''), 'Path to logfile.'). 91 | lx_dracut_info(sshkey, '=', f(''), 'SSH key file used with ssh-client module.'). 92 | lx_dracut_info(show_modules, '=', f('{yes|no}'), 'Print the name of the included modules to standard output during build (default=no).'). 93 | lx_dracut_info(i18n_vars, '=', f(''), 'Distribution specific variable mapping.'). 94 | lx_dracut_info(i18n_default_font, '=', f(''), 'The font to install, if not specified otherwise. Default is "eurlatgr".'). 95 | lx_dracut_info(i18n_install_all, '=', f('{yes|no}'), 'Install everything regardless of generic or host-only mode (default=no).'). 96 | lx_dracut_info(reproducible, '=', f('{yes|no}'), 'Create reproducible images (default=no).'). 97 | lx_dracut_info(noimageifnotneeded, '=', f('{yes|no}'), 'Do not create an image in host-only mode, if no kernel driver is needed and no /etc/cmdline/*.conf will be generated into the initramfs (default=no).'). 98 | lx_dracut_info(loginstall, '=', f(''), 'Log all files installed from the host to .'). 99 | lx_dracut_info(uefi, '=', f('{yes|no}'), 'Instead of creating an initramfs image, dracut will create an UEFI executable, which can be executed by an UEFI BIOS (default=no).'). 100 | lx_dracut_info(machine_id, '=', f('{yes|no}'), 'Affects the default output filename of the UEFI executable, including the part (default=yes).'). 101 | lx_dracut_info(uefi_stub, '=', f(''), 'Specifies the UEFI stub loader, which will load the attached kernel, initramfs and kernel command line and boots the kernel.'). 102 | lx_dracut_info(uefi_splash_image, '=', f(''), 'Specifies the UEFI stub loader’s splash image. Requires bitmap (.bmp) image format.'). 103 | lx_dracut_info(uefi_secureboot_cert, '=', f(''), 'Specifies a certificate, which are used to sign the created UEFI executable.'). 104 | lx_dracut_info(uefi_secureboot_key, '=', f(''), 'Specifies a key, which are used to sign the created UEFI executable.'). 105 | lx_dracut_info(uefi_secureboot_engine, '=', f('parameter'), 'Specifies an engine to use when signing the created UEFI executable. E.g. "pkcs11"'). 106 | lx_dracut_info(kernel_image, '=', f(''), 'Specifies the kernel image, which to include in the UEFI executable.'). 107 | lx_dracut_info(enhanced_cpio, '=', f('{yes|no}'), 'Attempt to use the dracut-cpio binary, which optimizes archive creation for copy-on-write filesystems (default=no).'). 108 | lx_dracut_info(parallel, '=', f('{yes|no}'), 'If set to yes, try to execute tasks in parallel (currently only supported for --regenerate-all).'). 109 | 110 | -------------------------------------------------------------------------------- /lib/fs_zfs.pl: -------------------------------------------------------------------------------- 1 | % vi: noexpandtab:tabstop=4:ft=gprolog 2 | % Copyright (c) 2023-2024 Sergey Sikorskiy, released under the GNU GPLv2 license. 3 | 4 | zpool_list(L) :- 5 | os_shell_lines_codes('zpool list -Hp 2>/dev/null', CL), 6 | CL \= [], 7 | maplist(zpool_list_convert, CL, L), 8 | true. 9 | 10 | zpool_list_convert(L, zp(A1,A2,A3,A4,A5,A6,A7,A8,A9,A10,A11)) :- 11 | split_list_ne(L, "\t", SL), 12 | maplist(codes_atom, SL, [A1,A2,A3,A4,A5,A6,A7,A8,A9,A10,A11]), 13 | true. 14 | 15 | % PN - pool name. 16 | zfs_load_key(PN, PSWD) :- 17 | atom_concat('zfs load-key -L prompt ', PN, CA), 18 | popen(CA, write, WS), 19 | write(WS, PSWD), % no nl(WS) should be here. 20 | close(WS), 21 | true. 22 | 23 | prop_info_zfs_feat(off, L) :- !, 24 | prop_info(zpool_feat, _, L). 25 | prop_info_zfs_feat(legacy, L) :- !, 26 | prop_info(zpool_feat, _, L1), 27 | findall(prop_feat4(Tag, off, Fmt, Attr), member(prop_feat4(Tag, _, Fmt, Attr), L1), L). 28 | prop_info_zfs_feat(C, L) :- !, 29 | os_shell2_lines_nc([cat, '/usr/share/zfs/compatibility.d/' + C], "#", CL), 30 | prop_info(zpool_feat, _, L1), 31 | findall(prop_feat4(Tag, E, Fmt, Attr), (member(prop_feat4(Tag, _, Fmt, Attr), L1), (memberchk(Tag, CL) -> E = on; E = off)), L). 32 | 33 | % prop_info_zfs(TAG, _FS, L) :- 34 | % prop_info(TAG, _, L). 35 | 36 | % prop_ro(name, format) 37 | % man zpoolprops 38 | prop_info(zpool_ro, prop2, [ 39 | prop_ro(allocated, int) 40 | , prop_ro(bcloneratio, real) 41 | , prop_ro(bclonesaved, int) 42 | , prop_ro(bcloneused, int) 43 | , prop_ro(capacity, real) 44 | , prop_ro(expandsize, int) 45 | , prop_ro(fragmentation, int) 46 | , prop_ro(free, int) 47 | , prop_ro(freeing, int) 48 | , prop_ro(guid, guid) 49 | , prop_ro(health, enum(['ONLINE', 'DEGRADED', 'FAULTED', 'OFFLINE', 'REMOVED', 'UNAVAIL'])) 50 | , prop_ro(leaked, int) 51 | , prop_ro(load_guid, guid) 52 | , prop_ro(size, int) 53 | ]). 54 | 55 | % opt3(name, format, def_value) 56 | % "-o" 57 | prop_info(zpool_props_rw, opt3p('-o'), [ 58 | opt3(altroot, path, '') 59 | , opt3(readonly, enum([on, off]), off) 60 | , opt3(ashift, enum([0, 9, 10, 11, 12, 13, 14, 15, 16]), 0) 61 | , opt3(autoexpand, enum([on, off]), off) 62 | , opt3(autoreplace, enum([on, off]), off) 63 | , opt3(autotrim, enum([on, off]), off) 64 | , opt3(bootfs, combo_val(zpool_bootfs), default=unset) 65 | , opt3(cachefile, combo_val(zpool_cachefile), default=unset) 66 | , opt3(comment, str, '') 67 | , opt3(compatibility, fmt(zpool_compatibility), off) 68 | , opt3(dedupditto, int, 0) 69 | , opt3(delegation, enum([on, off]), off) 70 | , opt3(failmode, enum([wait, continue, panic]), wait) 71 | , opt3(listsnapshots, enum([on, off]), off) 72 | , opt3(multihost, enum([on, off]), off) 73 | , opt3(version, int, 5000) 74 | ]). 75 | 76 | % zpool_feat(name, depends_list) 77 | % man zpool-features 78 | % Feature flags implementation per OS: https://openzfs.github.io/openzfs-docs/Basic%20Concepts/Feature%20Flags.html 79 | prop_info(zpool_feat, feat4p('-o', on_off), [ 80 | prop_feat4(allocation_classes, on, std, attr([], '')) 81 | , prop_feat4(async_destroy, on, std, attr([], '')) 82 | , prop_feat4(blake3, on, std, attr([extensible_dataset], '')) 83 | , prop_feat4(block_cloning, on, std, attr([], '')) 84 | , prop_feat4(bookmarks, on, std, attr([extensible_dataset], '')) 85 | , prop_feat4(bookmark_v2, on, std, attr([bookmark, extensible_dataset], '')) 86 | , prop_feat4(bookmark_written, on, std, attr([bookmark, extensible_dataset, bookmark_v2], '')) 87 | , prop_feat4(device_rebuild, on, std, attr([], '')) 88 | , prop_feat4(device_removal, on, std, attr([], '')) 89 | , prop_feat4(draid, on, std, attr([], '')) 90 | , prop_feat4(edonr, on, std, attr([extensible_dataset], '')) 91 | , prop_feat4(embedded_data, on, std, attr([], '')) 92 | , prop_feat4(empty_bpobj, on, std, attr([], '')) 93 | , prop_feat4(enabled_txg, on, std, attr([], '')) 94 | , prop_feat4(encryption, on, std, attr([bookmark_v2, extensible_dataset], '')) 95 | , prop_feat4(extensible_dataset, on, std, attr([], '')) 96 | , prop_feat4(filesystem_limits, on, std, attr([extensible_dataset], '')) 97 | , prop_feat4(head_errlog, on, std, attr([], '')) 98 | , prop_feat4(hole_birth, on, std, attr([enabled_txg], '')) 99 | , prop_feat4(large_blocks, on, std, attr([extensible_dataset], '')) 100 | , prop_feat4(large_dnode, on, std, attr([extensible_dataset], '')) 101 | , prop_feat4(livelist, on, std, attr([], '')) 102 | , prop_feat4(log_spacemap, on, std, attr([spacemap_v2], '')) 103 | , prop_feat4(lz4_compress, on, std, attr([], '')) 104 | , prop_feat4(multi_vdev_crash_dump, on, std, attr([], '')) 105 | , prop_feat4(obsolete_counts, on, std, attr([device_removal], '')) 106 | , prop_feat4(project_quota, on, std, attr([extensible_dataset], '')) 107 | , prop_feat4(redaction_bookmarks, on, std, attr([bookmarks, extensible_dataset], '')) 108 | , prop_feat4(redacted_datasets, on, std, attr([extensible_dataset], '')) 109 | , prop_feat4(resilver_defer, on, std, attr([], '')) 110 | , prop_feat4(sha512, on, std, attr([extensible_dataset], '')) 111 | , prop_feat4(skein, on, std, attr([extensible_dataset], '')) 112 | , prop_feat4(spacemap_histogram, on, std, attr([], '')) 113 | , prop_feat4(spacemap_v2, on, std, attr([], '')) 114 | , prop_feat4(userobj_accounting, on, std, attr([extensible_dataset], '')) 115 | , prop_feat4(vdev_zaps_v2, on, std, attr([], '')) 116 | , prop_feat4(zilsaxstd, attr, on, std, attr([extensible_dataset], '')) 117 | , prop_feat4(zpool_checkpoint, on, std, attr([], '')) 118 | , prop_feat4(zstd_compress, on, std, attr([extensible_dataset], '')) 119 | ]). 120 | 121 | % zfs_prop(name, format) 122 | % man zfsprops 123 | prop_info(zfs_ro, prop2, [ 124 | prop_ro(available, none) 125 | , prop_ro(compressratio, none) 126 | , prop_ro(createtxg, none) 127 | , prop_ro(creation, none) 128 | , prop_ro(clones, none) 129 | , prop_ro(defer_destroy, none) 130 | , prop_ro(encryptionroot, none) 131 | , prop_ro(filesystem_count, none) 132 | , prop_ro(keystatus, none) 133 | , prop_ro(guid, guid) 134 | , prop_ro(logicalreferenced, none) 135 | , prop_ro(logicalused, none) 136 | , prop_ro(mounted, none) 137 | , prop_ro(objsetid, none) 138 | , prop_ro(origin, none) 139 | , prop_ro(receive_resume_token, none) 140 | , prop_ro(redact_snaps, none) 141 | , prop_ro(referenced, none) 142 | , prop_ro(refcompressratio, none) 143 | , prop_ro(snapshot_count, none) 144 | , prop_ro(type, none) 145 | , prop_ro(used, none) 146 | , prop_ro(usedbychildren, none) 147 | , prop_ro(usedbydataset, none) 148 | , prop_ro(usedbyrefreservation, none) 149 | , prop_ro(usedbysnapshots, none) 150 | , prop_ro(userrefs, none) 151 | , prop_ro(snapshots_changed, none) 152 | , prop_ro(volblocksize, none) 153 | , prop_ro(written, none) 154 | 155 | , prop_ro(fmt(usedby), none) 156 | , prop_ro(fmt('userused@user'), none) 157 | , prop_ro(fmt('userobjused@user'), none) 158 | , prop_ro(fmt('groupused@group'), none) 159 | , prop_ro(fmt('groupobjused@group'), none) 160 | , prop_ro(fmt('projectused@project'), none) 161 | , prop_ro(fmt('projectobjused@project'), none) 162 | , prop_ro(fmt('written@snapshot'), none) 163 | ]). 164 | 165 | % opt3(name, format, def_value) 166 | % man zfsprops 167 | % "-O" 168 | % -O file-system-property=value 169 | prop_info(zfs_rw, opt3p('-O'), [ 170 | opt3(aclinherit, enum([discard, noallow, restricted, passthrough, 'passthrough-x']), restricted) 171 | , opt3(aclmode, enum([discard, groupmask, passthrough, restricted]), discard) 172 | , opt3(acltype, enum([off, nfsv4, posix]), off) 173 | , opt3(atime, enum([on, off]), on) 174 | , opt3(canmount, enum([on, off, noauto]), on) 175 | , opt3(checksum, enum([on, off, fletcher2, fletcher4, sha256, noparity, sha512, skein, edonr, blake3]), on) 176 | , opt3(compression, enum([on, off, gzip, 'gzip-1', lz4, lzjb, zle, zstd, 'zstd-1', 'zstd-fast', 'zstd-fast-1']), on) 177 | % , opt3(context, enum([none, 'SELinux-User:SELinux-Role:SELinux-Type:Sensitivity-Level']), none) 178 | % , opt3(fscontext, enum([none, 'SELinux-User:SELinux-Role:SELinux-Type:Sensitivity-Level']), none) 179 | % , opt3(defcontext, enum([none, 'SELinux-User:SELinux-Role:SELinux-Type:Sensitivity-Level']), none) 180 | % , opt3(rootcontext, enum([none, 'SELinux-User:SELinux-Role:SELinux-Type:Sensitivity-Level']), none) 181 | , opt3(copies, enum([1, 2, 3]), 1) 182 | , opt3(devices, enum([on, off]), on) 183 | , opt3(dedup, enum([off, on, verify, sha256, sha512, skein, edonr, blake3]), off) 184 | , opt3(dnodesize, enum([legacy, auto, '1k', '2k', '4k', '8k', '16k']), legacy) 185 | , opt3(encryption, enum([off, on, 'aes-128-ccm', 'aes-192-ccm', 'aes-256-ccm', 'aes-128-gcm', 'aes-192-gcm', 'aes-256-gcm']), off) 186 | , opt3(keyformat, enum([none, raw, hex, passphrase]), none) 187 | , opt3(keylocation, combo_val(zfs_keylocation), default=prompt) 188 | , opt3(pbkdf2iters, int, 350000) 189 | , opt3(exec, enum([on, off]), on) 190 | , opt3(filesystem_limit, combo_val(zfs_filesystem_limit, [opt3(default, none, none), opt3(limit, int, 1)]), default=none) 191 | , opt3(special_small_blocks, int, 0) 192 | , opt3(mountpoint, combo_val(zfs_mountpoint, [opt3(default, none, none), opt3(legacy, none, legacy), opt3(path, path, '')]), default=none) 193 | , opt3(nbmand, enum([on, off]), off) 194 | , opt3(overlay, enum([on, off]), on) 195 | , opt3(prefetch, enum([all, none, metadata]), all) 196 | , opt3(primarycache, enum([all, none, metadata]), all) 197 | , opt3(quota, combo_val(zfs_quota, [opt3(default, none, none), opt3(quota, int, 0)]), default=none) 198 | , opt3(snapshot_limit, combo_val(zfs_snapshot_limit, [opt3(default, none, none), opt3(limit, int, 0)]), default=none) 199 | , opt3(readonly, enum([on, off]), off) 200 | , opt3(recordsize, int, 512) 201 | , opt3(redundant_metadata, enum([all, most, some, none]), all) 202 | , opt3(refquota, combo_val(zfs_refquota, [opt3(default, none, none), opt3(quota, int, 0)]), default=none) 203 | , opt3(refreservation, combo_val(zfs_refreservation, [opt3(default, none, none), opt3(auto, none, auto), opt3(size, int, 0)]), default=none) 204 | , opt3(relatime, enum([on, off]), on) 205 | , opt3(reservation, combo_val(zfs_reservation, [opt3(default, none, none), opt3(size, int, 0)]), default=none) 206 | , opt3(secondarycache, enum([all, none, metadata]), all) 207 | , opt3(setuid, enum([on, off]), on) 208 | , opt3(sharesmb, combo_val(zfs_sharesmb, [opt3(default, none, on), opt3(legacy, none, off), opt3(opts, str, '')]), legacy=off) 209 | , opt3(sharenfs, combo_val(zfs_sharenfs, [opt3(default, none, on), opt3(legacy, none, off), opt3(opts, str, '')]), legacy=off) 210 | , opt3(logbias, enum([latency, throughput]), latency) 211 | , opt3(snapdev, enum([hidden, visible]), hidden) 212 | , opt3(snapdir, enum([hidden, visible]), hidden) 213 | , opt3(sync, enum([standard, always, disabled]), standard) 214 | , opt3(version, combo_val(zfs_version, [opt3(default, none, current), opt3(version, int, 0)]), default=current) 215 | , opt3(volsize, int, 0) 216 | , opt3(volmode, enum([default, full, geom, dev, none]), full) 217 | , opt3(volthreading, enum([on, off]), on) 218 | , opt3(vscan, enum([on, off]), off) 219 | , opt3(xattr, enum([on, off, sa]), on) 220 | , opt3(jailed, enum([on, off]), off) 221 | , opt3(zoned, enum([on, off]), off) 222 | 223 | % , opt3('userquota@user', enum([int, none]), none) 224 | % , opt3('userobjquota@user', enum([int, none]), none) 225 | % , opt3('groupquota@group', enum([int, none]), none) 226 | % , opt3('groupobjquota@group', enum([int, none]), none) 227 | % , opt3('projectquota@project', enum([int, none]), none) 228 | % , opt3('projectobjquota@project', enum([int, none]), none) 229 | 230 | % The following three properties cannot be changed after the file system is 231 | % created, and therefore, should be set when the file system is created. 232 | , opt3(casesensitivity, enum([sensitive, insensitive, mixed]), sensitive) 233 | , opt3(normalization, enum([none, formC, formD, formKC, formKD]), none) 234 | , opt3(utf8only, enum([on, off]), off) 235 | ]). 236 | 237 | prop_info(zpool_rw, opt4s(' '), [ 238 | opt4(force, enable, no, '-f') 239 | , opt4(mountpoint, str, '', '-m') 240 | , opt4(tname, str, '', '-t') 241 | ]). 242 | 243 | -------------------------------------------------------------------------------- /module/void_fs.pl: -------------------------------------------------------------------------------- 1 | % vi: noexpandtab:tabstop=4:ft=gprolog 2 | % Copyright (c) 2023-2024 Sergey Sikorskiy, released under the GNU GPLv2 license. 3 | 4 | % https://wiki.archlinux.org/title/Dm-crypt 5 | % https://wiki.archlinux.org/title/Dm-crypt/Encrypting_an_entire_system 6 | % https://wiki.archlinux.org/title/Dm-crypt/System_configuration 7 | % Stacking LVM volumes: https://access.redhat.com/articles/2106521 8 | % https://wiki.syslinux.org/wiki/index.php?title=Filesystem#ext 9 | % Free Software EFI/UEFI file system drivers: https://efi.akeo.ie/ 10 | % EfiFs - EFI File System Drivers: https://github.com/pbatard/efifs 11 | 12 | has_boot_part(TL) :- 13 | % fs6(Name, MountPoint, Dev, [CreateOptList], [MountOptList], create/keep) 14 | memberchk(fs6(_FS, '/boot', _D, _COL, _MOL, _CK), TL), !, 15 | true. 16 | 17 | has_root_part(TL) :- 18 | % fs6(Name, MountPoint, Dev, [CreateOptList], [MountOptList], create/keep) 19 | memberchk(fs6(_FS, '/', _D, _COL, _MOL, _CK), TL), !, 20 | true. 21 | has_root_part(TL) :- 22 | member(fs5_multi(btrfs, _COL, _DL, PTL, _CK), TL), 23 | memberchk(subv(_Name, '/', _MOL, _), PTL), !, 24 | true. 25 | has_root_part(TL) :- 26 | member(fs5_multi(zfs, _COL, _DL, PTL, _CK), TL), 27 | memberchk(dataset(_, '/', _), PTL), !, 28 | true. 29 | 30 | has_usr_part(TL) :- 31 | % fs6(Name, MountPoint, Dev, [CreateOptList], [MountOptList], create/keep) 32 | memberchk(fs6(_FS, '/usr', _D, _COL, _MOL, _CK), TL), !, 33 | true. 34 | 35 | root_pd(TL, PD) :- 36 | % fs6(Name, MountPoint, Dev, [CreateOptList], [MountOptList], create/keep) 37 | memberchk(fs6(_FS, '/', PD, _COL, _MOL, _CK), TL), 38 | !. 39 | root_pd(TL, PD) :- 40 | member(fs5_multi(btrfs, _COL, [PD|_], PTL, _CK), TL), 41 | memberchk(subv(_Name, '/', _MOL, _), PTL), 42 | !. 43 | root_pd(TL, PD) :- 44 | member(fs5_multi(zfs, _COL, [PD|_], PTL, _CK), TL), 45 | memberchk(dataset(_, '/', _), PTL), !, 46 | true. 47 | root_pd(_TL, _PD) :- 48 | tui_msgbox2(['root partition was not found']), 49 | fail. 50 | 51 | root_fs(TL, FS) :- 52 | % fs6(Name, MountPoint, Dev, [CreateOptList], [MountOptList], create/keep) 53 | memberchk(fs6(FS, '/', _D, _COL, _MOL, _CK), TL), !, 54 | true. 55 | root_fs(TL, btrfs) :- 56 | member(fs5_multi(btrfs, _COL, _DL, PTL, _CK), TL), 57 | memberchk(subv(_Name, '/', _MOL, _), PTL), !, 58 | true. 59 | root_fs(TL, zfs) :- 60 | member(fs5_multi(zfs, _COL, _DL, PTL, _CK), TL), 61 | memberchk(dataset(_, '/', _), PTL), !, 62 | true. 63 | 64 | get_boot_part(TL, PD) :- 65 | % fs6(Name, MountPoint, Dev, [CreateOptList], [MountOptList], create/keep) 66 | member(fs6(_FS, MP, PD, _, _, _CK1), TL), 67 | ( MP = '/boot'; MP = (/) ), !, 68 | % memberchk(p4(_, bd1([PD| _]), _CK2, _SZ), TL), 69 | !. 70 | get_boot_part(TL, PD) :- 71 | member(fs5_multi(FS, _COL, [PD|_], PTL, _CK1), TL), 72 | get_boot_part_1(FS, PTL), 73 | !. 74 | 75 | get_boot_part_1(zfs, PTL) :- !, 76 | member(dataset(_, MP, _), PTL), 77 | ( MP = '/boot'; MP = (/) ), !, 78 | true. 79 | get_boot_part_1(btrfs, PTL) :- !, 80 | member(subv(_, MP, _, _), PTL), 81 | ( MP = '/boot'; MP = (/) ), !, 82 | true. 83 | 84 | boot_pref(TL, '') :- 85 | has_boot_part(TL), 86 | !. 87 | boot_pref(_TL, 'boot/'). 88 | 89 | get_mkfs_attrs(COL, L) :- 90 | findall(LM, (member(CO, COL), get_mkfs_attrs0(CO, L0), member(LM, L0)), L), 91 | true. 92 | 93 | get_mkfs_attrs0(attr(TAG, OL), L) :- 94 | prop_info(TAG, Format, FL), 95 | get_mkfs_attrs1(Format, TAG, FL, OL, L), 96 | true. 97 | 98 | get_mkfs_attrs1(feat4p(Opt, _Fmt), zpool_feat, _FL, OL, COL) :- !, % "p" stands for "prefix" 99 | findall(CO, (member(F=V, OL), make_zfs_feat(V, F, O), member(CO, [Opt, O])), COL), 100 | true. 101 | get_mkfs_attrs1(feat4p(Opt, Fmt), _TAG, FL, OL, COL) :- !, % "p" stands for "prefix" 102 | findall(CO, (member(F=V, OL), memberchk(prop_feat4(F, _, LFmt, _), FL), make_fs_feat(LFmt, Fmt, F, V, O), member(CO, [Opt, O])), COL), 103 | true. 104 | get_mkfs_attrs1(feat4s(Opt, Sep, Fmt), _TAG, FL, OL, [Opt, l(COL, Sep)]) :- !, % "s" stands for "separator" 105 | findall(O, (member(F=V, OL), memberchk(prop_feat4(F, _, LFmt, _), FL), make_fs_feat(LFmt, Fmt, F, V, O)), COL), 106 | true. 107 | get_mkfs_attrs1(opt3s(Opt, Sep), _TAG, FL, OL, [Opt, l(COL, Sep)]) :- !, % "s" stands for "separator" 108 | findall(O, (member(P=V, OL), make_opt3_val(FL, P, V, O)), COL), 109 | true. 110 | get_mkfs_attrs1(opt3p(Opt), _TAG, FL, OL, COL) :- !, % "p" stands for "prefix" 111 | findall(CO, (member(P=V, OL), make_opt3_val(FL, P, V, OV), member(CO, [Opt, OV])), COL), 112 | true. 113 | get_mkfs_attrs1(opt4s(Sep), _TAG, FL, OL, COL) :- !, 114 | findall(O, (member(P=V, OL), make_opt4_val(FL, P, V, Sep, O)), COL), 115 | true. 116 | get_mkfs_attrs1(Format, _TAG, _FL, _OL, _COL) :- 117 | format_to_atom(A, 'Invalid formatter: ~w', [Format]), 118 | tui_msgbox(A, [title(' ERROR ')]), 119 | fail. 120 | 121 | % make_fs_feat(LocalFormat, Format, V, F, FV). 122 | make_fs_feat(std, Fmt, F, V, FV) :- !, 123 | make_fs_feat0(V, Fmt, F, FV). 124 | make_fs_feat(LFmt, _Fmt, F, V, FV) :- 125 | make_fs_feat0(V, LFmt, F, FV). 126 | 127 | make_fs_feat0(off, on_off, _F, '') :- !, 128 | fail. 129 | make_fs_feat0(off, pref1(P), F, FV) :- !, 130 | atom_concat(P, F, FV). 131 | make_fs_feat0(off, v2(_ON, OFF), _F, OFF) :- !. 132 | make_fs_feat0(off, Fmt, _F, '') :- !, 133 | format_to_atom(A, 'Invalid formatter: ~w', [Fmt]), 134 | tui_msgbox(A, [title(' ERROR ')]), 135 | fail. 136 | make_fs_feat0(on, v2(ON, _OFF), _F, ON) :- !. 137 | make_fs_feat0(_, _Fmt, F, F) :- !. 138 | 139 | make_zfs_feat(V, F, FV) :- !, 140 | ( V = on -> 141 | VA = enabled 142 | ; VA = disabled 143 | ), 144 | format_to_atom(FV, 'feature@~w=~w', [F, VA]). 145 | 146 | make_opt4_val(PL, P, V, Sep, PVL) :- 147 | memberchk(opt4(P, Fmt, _, AN), PL), 148 | make_opt4_val0(Fmt, AN, V, Sep, PVL), 149 | true. 150 | 151 | make_opt4_val0(enable, AN, _V, _Sep, AN) :- !. 152 | make_opt4_val0(Fmt, AN, V, Sep, vs(AN, Sep, OV)) :- 153 | make_opt_dq(Fmt, V, OV). 154 | 155 | make_opt3_val(PL, P, V, OV) :- 156 | ( memberchk(opt3(P, Fmt, _), PL) 157 | ; memberchk(opt3a(P, Fmt, _, _), PL) 158 | ), !, 159 | make_opt3_val0(P, V, Fmt, OV), 160 | true. 161 | 162 | make_opt3_val0(keylocation, =(file, V), _Fmt, keylocation = dq(AV)) :- !, 163 | atom_concat('file://', V, AV), 164 | true. 165 | make_opt3_val0(P, =(_, V), Fmt, P = DQV) :- !, 166 | make_opt_dq(Fmt, V, DQV), 167 | true. 168 | make_opt3_val0(P, V, Fmt, P = DQV) :- 169 | make_opt_dq(Fmt, V, DQV), 170 | true. 171 | 172 | make_opt_dq(Fmt, V, dq(V)) :- 173 | opt_require_dq(Fmt, V), !. 174 | make_opt_dq(_Fmt, V, V). 175 | 176 | opt_require_dq(Fmt, V) :- 177 | memberchk(Fmt, [str, file, path]), 178 | sub_atom(V, _Before, _Length, _After, ' '), !, 179 | true. 180 | 181 | mkfs(swap, PD, _COL) :- !, 182 | os_shell2_rc([swapoff, PD, '>/dev/null', '2>&1'], _), 183 | ( os_shell2l([mkswap, PD, '2>&1']) 184 | ; tui_msgbox('ERROR: failed to create swap'), 185 | fail 186 | ), !, 187 | ( os_shell2l([swapon, PD, '2>&1']) 188 | ; tui_msgbox('ERROR: failed to activate swap'), 189 | fail 190 | ), !, 191 | true. 192 | mkfs(FS, D, COL) :- 193 | get_mkfs_attrs(COL, OL), 194 | mkfs_cl(FS, D, OL, CL), 195 | format_to_atom(Title, ' Creating filesystem ~w ', [FS]), 196 | tui_progressbox_safe(CL, '', [title(Title), sz([12, 80])]), 197 | !. 198 | mkfs(FS, _, _) :- !, 199 | tui_msgbox2(['Couldn\'t create filesystem', FS]), 200 | fail. 201 | 202 | mkfs_cl(ext2, D, OL, ['mke2fs', OL, D, '2>&1']) :- !. 203 | mkfs_cl(ext3, D, OL, ['mke2fs', OL, D, '2>&1']) :- !. 204 | mkfs_cl(ext4, D, OL, ['mke2fs', OL, D, '2>&1']) :- !. 205 | mkfs_cl(f2fs, D, OL, CL) :- !, 206 | CL = ['mkfs.f2fs', OL, D, '2>&1'], 207 | true. 208 | mkfs_cl(vfat, D, OL, CL) :- !, 209 | CL = ['mkfs.vfat', OL, D, '2>&1'], 210 | true. 211 | mkfs_cl(xfs, D, OL, CL) :- !, 212 | CL = ['mkfs.xfs', OL, D, '2>&1'], 213 | true. 214 | mkfs_cl(bcachefs, D, OL, CL) :- !, 215 | CL = ['mkfs.bcachefs', OL, D, '2>&1'], 216 | true. 217 | mkfs_cl(nilfs2, D, OL, CL) :- !, 218 | CL = ['mkfs.nilfs2', OL, D, '2>&1'], 219 | true. 220 | 221 | mkfs_multi(zfs, Title, TL, DL, PTL, COL, RD) :- !, 222 | get_bootloader(TL, B), 223 | % tui_msgbox_w(OL, [title(mkfs_multi)]), 224 | ( zfs_pool_encryption_col(COL) -> 225 | E = true 226 | ; E = false 227 | ), 228 | PN = zroot, 229 | zfs_zpool_create(E, Title, B, PN, DL, PTL, COL, RD), 230 | true. 231 | mkfs_multi(btrfs, Title, _TL, DL, PTL, COL, RD) :- !, 232 | get_mkfs_attrs(COL, OL), 233 | btrfs_mkfs(Title, DL, PTL, OL, RD), 234 | true. 235 | mkfs_multi(FS, _Title, _TL, _DL, _PTL, _COL, _RD) :- !, 236 | tui_msgbox2(['Unknown filesystem', FS]), 237 | fail. 238 | 239 | % bdev4(Name, Label, TargetDev, SourceDevList) 240 | % vg(Name, [PhysicalVolumeList], [LogicalVolumeList]) 241 | mkbd(lvm, vg(VG, SDL, CL)) :- !, 242 | maplist(mk_lvm_pvcreate, SDL), 243 | ( lvm_vgcreate(VG, SDL) 244 | ; tui_msgbox('vgcreate has failed'), 245 | fail 246 | ), !, 247 | maplist(mk_lvm_lvcreate(VG), CL), 248 | true. 249 | mkbd(luks, luks(Type, PD)) :- !, 250 | inst_setting_tmp(passwd('$_luks_$'), RPWD), 251 | atom_concat('Creating crypto-device ', PD, M1), 252 | tui_infobox(M1, [sz([4, 40])]), 253 | ( lx_luks_format(Type, PD, RPWD) 254 | ; tui_msgbox('luks_format has failed'), 255 | fail 256 | ), !, 257 | % inst_setting(luks, luks(Name)), 258 | lx_split_dev(PD, _P, SDN), 259 | luks_dev_name_short(SDN, LUKS_PD), 260 | atom_concat('Opening crypto-device ', PD, M2), 261 | tui_infobox(M2, [sz([4, 40])]), 262 | ( lx_luks_open(Type, LUKS_PD, PD, RPWD) 263 | ; tui_msgbox('luks_open has failed'), 264 | fail 265 | ), !, 266 | true. 267 | mkbd(BD, _) :- !, 268 | tui_msgbox2(['Unknown block device', BD]), 269 | fail. 270 | 271 | % PD - a single device or a list 272 | mk_lvm_pvcreate(PD) :- 273 | % A PV can be a disk partition, whole disk, meta device, or loopback file. 274 | lvm_pvcreate_unsafe(PD), !. 275 | mk_lvm_pvcreate(PD) :- 276 | tui_msgbox2(['lvm_pvcreate', PD, 'has failed']), 277 | fail. 278 | 279 | mk_lvm_lvcreate(VG, lv(LV, SZ)) :- 280 | lvm_lvcreate_unsafe(VG, LV, SZ), !. 281 | mk_lvm_lvcreate(VG, lv(LV, _SZ)) :- 282 | tui_msgbox2([lvcreate, VG, LV, 'has failed']), 283 | fail. 284 | 285 | % Get list of mounting points in order in which they should be mounted (except of swap). 286 | get_mp_list(TL, MPL1) :- 287 | % Ignore swap partition 288 | % fs6(Name, MountPoint, Dev, [CreateOptList], [MountOptList], create/keep) 289 | findall(MP, (member(fs6(FS, MP, _D, _COL, _MOL, _CK), TL), FS \= swap), MPL0), 290 | sort(MPL0, MPL1), 291 | true. 292 | 293 | % Ignore swap partition. 294 | mount_fs(FS, _D, _MP, _RD) :- 295 | memberchk(FS, [proc, tmpfs, swap]), !. 296 | % Ignore empty mount point. 297 | mount_fs(_FS, _D, '', _RD) :- 298 | !. 299 | mount_fs(FS, D, MP, RD) :- 300 | memberchk(FS, [vfat, ext2, ext3, ext4, f2fs, xfs, bcachefs, nilfs2]), 301 | atom_concat(RD, MP, MP1), 302 | os_mkdir_p(MP1), 303 | CL = [mount, o(t, FS), D, MP1, '2>&1'], 304 | % os_shell2(CL), 305 | tui_shell2_safe(CL), 306 | !. 307 | mount_fs(FS, D, MP, _RD) :- 308 | tui_msgbox2(['mount_fs has failed.', [FS, D, MP]], [sz([6, 40])]), 309 | fail. 310 | 311 | mount_fs_multi(zfs, _D, _PTL, RD) :- 312 | PN = zroot, 313 | zfs_mount_multi(PN, RD), 314 | !. 315 | mount_fs_multi(btrfs, D, PTL, RD) :- 316 | btrfs_mount_multi(D, PTL, RD), 317 | !. 318 | mount_fs_multi(FS, D, _PTL, _RD) :- 319 | tui_msgbox2(['mount_fs_multi has failed.', FS, D], [sz([6, 40])]), 320 | fail. 321 | 322 | umount_mnt(RD) :- 323 | os_shell2([umount, '--recursive', RD, '2>/dev/nul']), 324 | fail. 325 | umount_mnt(RD) :- 326 | zfs_destroy_pool_rd(RD), 327 | fail. 328 | umount_mnt(_RD). 329 | 330 | % vg(Name, [PhysicalVolumeList], [LogicalVolumeList]) 331 | ensure_lvm(TL) :- 332 | % Check for already taken VG-LV pairs. 333 | lx_list_dev_part(PL), 334 | member(bdev(lvm, vg(VG, _, LVL)), TL), 335 | member(lv(LV, _SZ), LVL), 336 | format_to_atom(LVM_PD, '/dev/mapper/~w-~w', [VG, LV]), 337 | member(dev_part(LVM_PD,_,_,_), PL), !, 338 | PL = [dev_part(D2,_,_,_)| _], 339 | format_to_atom(M, 'LVM device ~w (VG: ~w, LV: ~w) already exists on the device ~w', [LVM_PD, VG, LV, D2]), 340 | tui_msgbox(M, [title(' Ensure LVM ERROR ')]), !, 341 | fail. 342 | ensure_lvm(TL) :- 343 | % Check for already taken VG. 344 | findall(VG1, member(bdev(lvm, vg(VG1, _, _LVL)), TL), VGL), 345 | sort(VGL, SVGL), 346 | lvm_pvs(L), 347 | member(VG, SVGL), 348 | memberchk(pv(PV, VG), L), 349 | format_to_atom(M, 'Volume group called "~w" already exists on the device ~w', [VG, PV]), 350 | tui_msgbox(M, [title(' Ensure LVM ERROR ')]), !, 351 | fail. 352 | ensure_lvm(_TL) :- 353 | % halt, 354 | true. 355 | 356 | -------------------------------------------------------------------------------- /lib/linux_common.pl: -------------------------------------------------------------------------------- 1 | % vi: noexpandtab:tabstop=4:ft=gprolog 2 | % Copyright (c) 2023-2024 Sergey Sikorskiy, released under the GNU GPLv2 license. 3 | 4 | lx_distro_props(L1) :- 5 | os_shell_lines('cat /etc/*-release', L), 6 | maplist(parse_prop, L, L1), 7 | true. 8 | 9 | lx_is_ssd(D) :- 10 | format_to_atom(C, 'cat /sys/block/~d/queue/rotational', [D]), 11 | os_shell_number(C, 0). 12 | 13 | % Similar to lx_sys_kernel. 14 | lx_kernel_ver(RD, LV) :- 15 | os_shell2_codes_line([ls, RD + '/lib/modules/'], KL), 16 | split_list_ne(KL, ".", [V1, V2|_]), 17 | maplist(codes_atom, [V1, V2], VL), 18 | format_to_atom(LV, '~w.~w', VL), 19 | true. 20 | 21 | % V1N - number 22 | % V2N - number 23 | % V3A - atom 24 | lx_sys_kernel(V1N, V2N, V3A) :- 25 | os_shell_codes_line('uname -r', CL), 26 | split_list_ne(CL, ".", [V1, V2, V3| _]), 27 | number_codes(V1N, V1), 28 | number_codes(V2N, V2), 29 | atom_codes(V3A, V3), 30 | true. 31 | 32 | lx_sys_arch(ARCH) :- 33 | % x86_64 | x86_64-musl ... 34 | % os_shell_line('xbps-uhelper arch', ARCH), 35 | os_shell_line('uname -m', A0), 36 | ( A0 = x86_64, file_exists('/lib/ld-musl-x86_64.so.1') -> 37 | ARCH = 'x86_64-musl' 38 | ; ARCH = A0 39 | ), 40 | true. 41 | 42 | % Check for EFI. 43 | lx_sys_efi(EFI_TARGET) :- 44 | file_exists('/sys/firmware/efi/systab'), 45 | os_shell2_number([cat, '/sys/firmware/efi/fw_platform_size'], N), 46 | ( N = 64 -> 47 | EFI_TARGET='x86_64-efi' 48 | ; EFI_TARGET='i386-efi' 49 | ), 50 | true. 51 | 52 | % Generate EFI value. 53 | lx_gen_efi(EFI_TARGET) :- 54 | os_shell_line('uname -m', A0), 55 | ( A0 ='x86_64' -> 56 | EFI_TARGET = 'x86_64-efi' 57 | ; EFI_TARGET = 'i386-efi' 58 | ), 59 | !. 60 | 61 | % N is atom. 62 | lx_parent_dev_name(D, N, PD) :- 63 | % sub_atom(Atom, Before, Length, After, SubAtom), 64 | sub_atom(D, _, 1, 0, N), 65 | ( (sub_atom(D, _, _, _, 'nvme'); sub_atom(D, _, _, _, 'mmcblk')) -> 66 | sub_atom(D, 0, _, 2, PD) % p + N 67 | ; sub_atom(D, 0, _, 1, PD) % N 68 | ). 69 | 70 | % N is digit. 71 | lx_part_name(D, N, PD) :- 72 | % sub_atom(Atom, Before, Length, After, SubAtom), 73 | (sub_atom(D, _, _, _, 'nvme'); sub_atom(D, _, _, _, 'mmcblk')), !, 74 | format_to_atom(PD, '~wp~d', [D, N]). 75 | lx_part_name(D, N, PD) :- 76 | format_to_atom(PD, '~w~d', [D, N]). 77 | 78 | lx_set_keymap(RD, KM) :- 79 | file_exists('/etc/vconsole.conf'), !, 80 | format_to_atom(KMA, 's|KEYMAP=.*|KEYMAP=~w|g', [KM]), 81 | os_call2([sed, '-i', '-e', KMA, RD + '/etc/vconsole.conf']). 82 | lx_set_keymap(RD, KM) :- 83 | format_to_atom(KMA, 's|#\\?KEYMAP=.*|KEYMAP=~w|g', [KM]), 84 | os_call2([sed, '-i', '-e', KMA, RD + '/etc/rc.conf']). 85 | 86 | lx_set_locale(RD, LC) :- 87 | atom_concat(RD, '/etc/default/libc-locales', FN), 88 | file_exists(FN), 89 | format_to_atom(LCA1, 's|LANG=.*|LANG=~w|g', [LC]), 90 | atom_concat(RD, '/etc/locale.conf', LCN), 91 | os_call2([sed, '-i', '-e', LCA1, LCN]), 92 | % Uncomment locale from /etc/default/libc-locales and regenerate it. 93 | format_to_atom(LCA2, '/~w/s/^\\#//', [LC]), 94 | os_call2([sed, '-e', LCA2, '-i', FN]) , 95 | CL = [chroot, RD, 'xbps-reconfigure', '-f', 'glibc-locales', '2>&1'], 96 | % os_shell2l(CL) 97 | tui_progressbox_safe(CL, '', [title(' Set locale '), sz([6, 40])]), 98 | !. 99 | 100 | lx_set_timezone(RD, TZ) :- 101 | C = [ln, '-sf', '/usr/share/zoneinfo/' + TZ, RD + '/etc/localtime'], 102 | os_call2(C), 103 | !. 104 | 105 | lx_set_hostname(RD, HN) :- 106 | format_to_atom(HNA, 'echo "~w" > ~w/etc/hostname', [HN, RD]), 107 | os_shell(HNA), 108 | !. 109 | 110 | lx_set_password(RD, UL, UP) :- 111 | format_to_atom(UPA, 'echo "~w:~w" | chpasswd -R ~w -c SHA512', [UL, UP, RD]), 112 | os_shell(UPA), 113 | !. 114 | 115 | lx_useradd_rc(0, success). 116 | lx_useradd_rc(1, 'can\'t update password file'). 117 | lx_useradd_rc(2, 'invalid command syntax'). 118 | lx_useradd_rc(3, 'invalid argument to option'). 119 | lx_useradd_rc(4, 'UID already in use (and no -o)'). 120 | lx_useradd_rc(6, 'specified group doesn\'t exist'). 121 | lx_useradd_rc(9, 'username already in use'). 122 | lx_useradd_rc(10, 'can\'t update group file'). 123 | lx_useradd_rc(12, 'can\'t create home directory'). 124 | lx_useradd_rc(14, 'can\'t update SELinux user mapping'). 125 | 126 | % UL - user login 127 | % UN - user name 128 | % UGL - user group list 129 | lx_chroot_useradd_rc(RD, UL, UN, UGL, RC) :- 130 | % There is no need to add quotes here because this is an os_call2_rc. 131 | CL = [chroot, RD, useradd, '-m', '-G', lc(UGL), '-c', UN, UL], 132 | os_call2_rc(CL, RC). 133 | 134 | lx_chroot_useradd(RD, UL, UN, UGL) :- 135 | lx_chroot_useradd_rc(RD, UL, UN, UGL, 0). 136 | 137 | % UL - user login 138 | % UGL - user group list 139 | lx_chroot_set_sudoers(UL, UGL) :- 140 | file_exists('/mnt/etc/sudoers.d'), 141 | ( memberchk(wheel, UGL) -> 142 | % enable the sudoers entry for members of group wheel 143 | os_shell('echo "%wheel ALL=(ALL:ALL) ALL" > "/mnt/etc/sudoers.d/wheel"') 144 | ; % enable sudo for primary user USERLOGIN who is not member of wheel 145 | format_to_atom(C1, 'echo "# Enable sudo for login \'~w\'" > "/mnt/etc/sudoers.d/$USERLOGIN"', [UL]), 146 | os_shell(C1), 147 | format_to_atom(C2, 'echo "~w ALL=(ALL:ALL) ALL" >> "/mnt/etc/sudoers.d/~w"', [UL, UL]), 148 | os_shell(C2) 149 | ), 150 | !. 151 | 152 | lx_iface_setup(D, RC) :- 153 | format_to_atom(A, 'ip addr show dev ~w | grep -q -e \'inet \' -e \'inet6 \'', [D]), 154 | os_shell_rc(A, RC), 155 | true. 156 | 157 | % UUID is a mechanism to give each filesystem a unique identifier. 158 | lx_get_dev_uuid(D, A) :- 159 | atom_concat('blkid -s UUID -o value ', D, C), 160 | os_shell_line(C, A). 161 | 162 | lx_get_dev_disk_uuid(D, PID) :- 163 | lx_get_dev_uuid(D, A), 164 | atom_concat('/dev/disk/by-uuid/', A, PID). 165 | 166 | % GPT partition UUIDs are defined in the partition entry on GPT disks. 167 | % /dev/disk/by-partuuid/XXXXX 168 | lx_get_dev_partuuid(D, PID) :- 169 | atom_concat('blkid -s PARTUUID -o value ', D, C), 170 | os_shell_line(C, PID). 171 | 172 | lx_get_dev_disk_partuuid(D, PID) :- 173 | lx_get_dev_partuuid(D, A), 174 | atom_concat('/dev/disk/by-partuuid/', A, PID). 175 | 176 | % D - long device name 177 | % /dev/disk/by-id/XXXXX 178 | lx_get_dev_disk_id(D, DID) :- 179 | atom_concat('udevadm info -q symlink --name=', D, C), 180 | os_shell_atom_list(C, AL), 181 | findall(Suf, (member(A, AL), atom_concat('disk/by-id/', Suf, A)), IDL), 182 | lx_fiter_disk_wwn(IDL, WWN), 183 | atom_concat('/dev/disk/by-id/', WWN, DID), 184 | true. 185 | 186 | lx_fiter_disk_wwn([H|T], ID) :- 187 | ( atom_concat('wwn-', _, H) -> 188 | ID = H 189 | ; lx_fiter_disk_wwn(T, ID) 190 | ), !. 191 | lx_fiter_disk_wwn([H], H). 192 | 193 | % D - device short name 194 | % DA - device full name 195 | % GB - device size in GB 196 | % DSSZ - device block size 197 | lx_dev_info_ide_sata(D, dev4(D, DA, GB, DSSZ)) :- 198 | os_shell_number('cat /sys/block/~w/size', [D], DSZ), 199 | os_shell_number('cat /sys/block/~w/queue/hw_sector_size', [D], DSSZ), 200 | GB is DSZ * DSSZ / 1024 / 1024 /1024, 201 | atom_concat('/dev/', D, DA), 202 | true. 203 | 204 | lx_list_dev7(DL) :- 205 | os_shell_lines_codes('lsblk -nrdp -o NAME,TYPE,RO,RM,SIZE,PHY-SEC', CLL), 206 | maplist(lx_list_dev_, CLL, DL), 207 | true. 208 | 209 | lx_list_dev7_disk(DL) :- 210 | lx_list_dev7(L), 211 | findall(D, (member(D, L), D=dev7(_NAME,_SNAME,disk,_RO,_RM,_SIZE,_SSZ)), DL), 212 | true. 213 | 214 | % List all available partitions. 215 | lx_list_part(PL) :- 216 | findall(P, (lx_list_dev_part([_| PL0]), member(P, PL0)), PL), 217 | true. 218 | 219 | % It supposed to be used with backtracking. 220 | lx_list_dev_part(PL) :- 221 | lx_list_dev7_disk(DL), 222 | member(DEV7, DL), 223 | lx_dev7_to_ldn(DEV7, D), 224 | lx_list_dev_part(D, PL), 225 | true. 226 | 227 | % PL - linear list of a device and all sub-devices. 228 | lx_list_dev_part(D, PL) :- 229 | % lsblk -prn -o NAME,KNAME,TYPE,SIZE 230 | os_shell2_lines_codes([lsblk, '-prn', '-o', 'NAME,KNAME,TYPE,SIZE', D], CLL), 231 | maplist(lx_list_dev_part_, CLL, PL), 232 | !. 233 | 234 | % SSZ - number. 235 | lx_list_dev_(IL, dev7(NAME,SNAME,TYPE,RO,RM,SIZE,SSZ)) :- 236 | split_list_ne(IL, " ", LL), 237 | LL = [H|_], 238 | split_list_ne(H, "/", NL), 239 | append(_, [SN], NL), 240 | maplist(codes_atom, [SN|LL], [SNAME,NAME,TYPE,RO,RM,SIZE,SSIZE]), 241 | number_atom(SSZ, SSIZE), 242 | true. 243 | 244 | % SIZE - atom 245 | % name(sort_name, kernel_name, dev_dep_list) 246 | % dev_part(name, ext_name, type, size) 247 | lx_list_dev_part_(IL, dev_part(NAME,name(SNAME,KNAME,DL),ET,SIZE)) :- 248 | split_list(IL, " ", LL), 249 | LL = [H|_], 250 | split_list_ne(H, "/", NL), 251 | append(_, [SN], NL), 252 | maplist(codes_atom, [SN|LL], [SNAME,NAME,KNAME,TYPE,SIZE]), 253 | lx_dev_part_type(TYPE, NAME, ET), 254 | lx_dev_part_parents(NAME, DL), 255 | true. 256 | 257 | % PTTYPE: gpt, dos, sun, sgi 258 | lx_dev_part_type(part, NAME, part5(PTTYPE,TYPE,PARTUUID,UUID,FSTYPE_1)) :- !, 259 | os_shell2_codes_line(['lsblk -dnr -o PTTYPE,PARTUUID,UUID,PARTTYPE,FSTYPE', NAME], CL), 260 | split_atom(" ", CL, [PTTYPE,PARTUUID,UUID,PARTTYPE,FSTYPE]), 261 | lx_part_type(PTTYPE, PARTTYPE, TYPE), 262 | ( FSTYPE = zfs_member -> 263 | FSTYPE_1 = zfs 264 | ; FSTYPE_1 = FSTYPE 265 | ), 266 | true. 267 | lx_dev_part_type(crypt, NAME, crypt(UUID)) :- !, 268 | % !!! UUID doesn't seem to exist. 269 | os_shell2_atom(['lsblk -dnr -o UUID', NAME], UUID), 270 | true. 271 | lx_dev_part_type(lvm, NAME, lv(VG,LV)) :- 272 | lvm_lvs(L), 273 | memberchk(lv(VG,LV,NAME), L), 274 | true. 275 | lx_dev_part_type(Type, _NAME, Type). 276 | 277 | lx_part_type(gpt, PARTTYPE, TYPE) :- !, 278 | os_gpt_part_type(PARTTYPE, TYPE, _), 279 | !. 280 | lx_part_type(dos, PARTTYPE, TYPE) :- !, 281 | os_mbr_part_type(PARTTYPE, [t4(TYPE, _, _, _)|_]), 282 | !. 283 | lx_part_type(_, _PARTTYPE, unknown). 284 | 285 | % dependency 286 | lx_dev_part_parents(NAME, OLL) :- 287 | os_shell2_lines_codes(['lsblk -pns -o NAME', NAME], L), 288 | lx_dev_part_parents_0(L, OLL). 289 | 290 | lx_dev_part_parents_0([], []) :- !. 291 | lx_dev_part_parents_0([_|T], LL) :- 292 | lx_dev_part_parents_1(T, LL). 293 | 294 | lx_dev_part_parents_1([], []) :- !. 295 | lx_dev_part_parents_1(IL, [RL|OLL]) :- 296 | lx_dev_part_parents_2(IL, 0, RL, OL1), 297 | lx_dev_part_parents_1(OL1, OLL). 298 | 299 | % RL - result list 300 | % lx_dev_part_parents_2(IL, N, RL, OL) :- !. 301 | lx_dev_part_parents_2([], _N, [], []) :- !. 302 | lx_dev_part_parents_2([H|T], N, [S|RL], OL) :- 303 | suffix(0'/, H, N1, SL), 304 | N1 > N, !, 305 | atom_codes(S, SL), 306 | lx_dev_part_parents_2(T, N1, RL, OL), 307 | true. 308 | lx_dev_part_parents_2(IL, _N, [], IL). 309 | 310 | % PL - partition list 311 | % TL - tree list of long device names. 312 | lx_make_dev3(D, dev3(D, PL, TL)) :- 313 | lx_list_dev_part(D, PL), 314 | lx_dev_part_tree(D, PL, TL), 315 | true. 316 | 317 | lx_dev7_to_dev3(dev7(LN,_SNAME,_TYPE,_RO,_RM,_SIZE,_SSZ), DEV3) :- 318 | lx_make_dev3(LN, DEV3), 319 | true. 320 | 321 | lx_dev3_to_sdn(dev3(_, [dev_part(_, name(SN, _, _), _, _)| _], _TL), SN). 322 | 323 | % convert dev7 to long device name. 324 | lx_dev7_to_ldn(dev7(LN,_SN,_TYPE,_RO,_RM,_SIZE,_SSZ), LN). 325 | 326 | % convert dev7 to short device name. 327 | lx_dev7_to_sdn(dev7(_LN,SN,_TYPE,_RO,_RM,_SIZE,_SSZ), SN). 328 | 329 | % convert dev7 to long and short device names. 330 | lx_dev7_to_ldn_sdn(dev7(LN,SN,_TYPE,_RO,_RM,_SIZE,_SSZ), LN, SN). 331 | 332 | % Short device name to dev7 333 | lx_sdn_to_dev7(L, SDN, DEV7) :- 334 | member(DEV7, L), 335 | DEV7 = dev7(_LN,SDN,_TYPE,_RO,_RM,_SIZE,_SSZ), 336 | !. 337 | 338 | % Long device name to dev7 339 | lx_ldn_to_dev7(L, LDN, DEV7) :- 340 | member(DEV7, L), 341 | DEV7 = dev7(LDN,_SD,_TYPE,_RO,_RM,_SIZE,_SSZ), 342 | !. 343 | 344 | lx_dev_part_tree(D, PL, OL) :- 345 | % dev_part(NAME,name(SNAME,KNAME,DL),ET,SIZE) 346 | lx_dev_part_tree_1(D, PL, [tree(D, [])], OL), 347 | true. 348 | 349 | % lx_dev_part_tree_1(D, PL, IL, OL) 350 | lx_dev_part_tree_1(_D, [], L, L) :- !. 351 | lx_dev_part_tree_1(D, [dev_part(NAME,name(_SNAME,_KNAME,DL),_ET,_SIZE)|T], IL, OL) :- 352 | ( DL = [] -> 353 | DLM = DL 354 | ; member(DLM, DL), 355 | memberchk(D, DLM) 356 | ), !, 357 | reverse([NAME|DLM], RL), 358 | lx_dev_part_tree_2(RL, IL, IL1), 359 | lx_dev_part_tree_1(D, T, IL1, OL), 360 | true. 361 | 362 | % lx_dev_part_tree_2(RL, IL, OL) 363 | lx_dev_part_tree_2([], L, L) :- !. 364 | lx_dev_part_tree_2([H|T], IL, OL) :- 365 | memberchk2(tree(H, L), IL, PL, SL), !, 366 | lx_dev_part_tree_2(T, L, L1), 367 | append(PL, [tree(H, L1)|SL], OL), 368 | true. 369 | lx_dev_part_tree_2([H|T], IL, [tree(H, L)|IL]) :- 370 | lx_dev_part_tree_3(T, L). 371 | 372 | lx_dev_part_tree_3([], []) :- !. 373 | lx_dev_part_tree_3([H|T], [tree(H, L)]) :- 374 | lx_dev_part_tree_3(T, L). 375 | 376 | % In addition to checking for membership retrieve prefix and suffix lists 377 | % surrounding a member of a list. 378 | % memberchk2(member, list, pref, suf) 379 | memberchk2(V, [V|T], [], T) :- !. 380 | memberchk2(V, [H|T], [H|PL], SL) :- 381 | memberchk2(V, T, PL, SL), !. 382 | 383 | % D - device 384 | % P - prefix 385 | % S - suffix 386 | lx_split_dev(D, P, S) :- 387 | atom_codes(D, L), 388 | split_list_ne(L, "/", LL), 389 | ( LL = [_, SL] 390 | ; LL = [_, _, SL] 391 | ), 392 | atom_codes(S, SL), 393 | atom_concat(P, S, D), 394 | !. 395 | 396 | lx_split_dev_codes(D, PL, SL) :- 397 | atom_codes(D, L), 398 | split_list_ne(L, "/", LL), 399 | ( LL = [_, SL] 400 | ; LL = [_, _, SL] 401 | ), 402 | append(PL, SL, L), 403 | !. 404 | 405 | lx_get_mac_addr_codes(N, MC) :- 406 | format_to_atom(C, '/sys/class/net/~w/address', [N]), 407 | open(C, read, S), 408 | read_stream_codes_line(S, MC), 409 | close(S), 410 | !. 411 | 412 | lx_get_mac_addr(N, M) :- 413 | lx_get_mac_addr_codes(N, MC), 414 | atom_codes(M, MC). 415 | 416 | lx_get_net_devs(AL) :- 417 | % os_shell_lines('ls /sys/class/net', CL), 418 | % delete(CL, lo, AL), 419 | directory_files('/sys/class/net', CL), 420 | subtract(CL, [lo, '.', '..'], AL), 421 | !. 422 | 423 | lx_gen_hostid(RD) :- 424 | lx_get_net_devs([ND|_]), 425 | lx_get_mac_addr_codes(ND, MC), 426 | split_list_ne(MC, ":", ML), 427 | flatten(ML, FML), 428 | atom_codes(HID, FML), 429 | atom_concat(RD, '/etc/hostid', FN), 430 | open(FN, write, S), 431 | write(S, HID), 432 | close(S), 433 | true. 434 | 435 | -------------------------------------------------------------------------------- /module/void_menu_linux.pl: -------------------------------------------------------------------------------- 1 | % vi: noexpandtab:tabstop=4:ft=gprolog 2 | % Copyright (c) 2023-2024 Sergey Sikorskiy, released under the GNU GPLv2 license. 3 | 4 | menu_password_user(UL) :- 5 | format_to_atom(Title, ' Enter password for user ~w ', [UL]), 6 | menu_password0(UL, Title). 7 | 8 | menu_password_for(For, Tag) :- 9 | format_to_atom(Title, ' Enter password for ~w ', [For]), 10 | menu_password0(Tag, Title). 11 | 12 | menu_password0(UL, Title) :- 13 | dialog_msg(form, FORMLABEL), 14 | ( inst_setting_tmp(passwd(UL), UP) 15 | ; UP = '' 16 | ), 17 | tui_passwordform_v(25, 1024, [item('Choose a password:', UP), item('Confirm your password:', UP)], FORMLABEL, [title(Title)], [P1, P2|_]), 18 | check_password(UL, Title, P1, P2), !, 19 | retractall(inst_setting_tmp(passwd(UL), _)), 20 | assertz(inst_setting_tmp(passwd(UL), P1)). 21 | 22 | menu_password_luks(UL) :- 23 | inst_setting(useraccount, user(UD, _, _)), 24 | format_to_atom(Title, ' Enter LUKS password for user ~w ', [UD]), 25 | menu_password0(UL, Title). 26 | 27 | check_password(UL, Title, P1, P2) :- 28 | P1 \= P2, !, 29 | tui_yesno('Passwords don\'t match. Would you like to reenter?', [sz([6, 40])]), 30 | menu_password0(UL, Title). 31 | check_password(UL, Title, '', _) :- !, 32 | tui_yesno('Password is empty. Would you like to reenter?', [sz([6, 40])]), 33 | menu_password0(UL, Title). 34 | check_password(_, _, _, _). 35 | 36 | menu_part_soft(S) :- 37 | SL = [[cfdisk, 'Easy to use'], [fdisk, 'More advanced']], 38 | dialog_msg(menu, MENULABEL), 39 | tui_menu_tag(SL, MENULABEL, [title(' Select the software for partitioning ')], S). 40 | 41 | menu_part_manually :- 42 | menu_dev7_menu(' Select the disk to partition ', DEV7), 43 | lx_dev7_to_ldn(DEV7, LN), 44 | menu_part_soft(S), 45 | os_call2([S, LN]), 46 | retract(inst_setting(template(TT), TL)), 47 | % Remove all selected partitions and file systems. 48 | findall(E, (member(E, TL), E \= p4(_, _, _, _), E \= fs6(_, _, _, _, _, _)), NTL), 49 | assertz(inst_setting(template(TT), NTL)), 50 | true. 51 | 52 | % OPTN - old partition template name 53 | % NPTN - new partition template name 54 | menu_part_tmpl(FS, OPTN, NPTN) :- 55 | findall([TN, TD], (part_tmpl(FS, TN, _), part_tmpl_info(FS, TN, TD)), TL), 56 | dialog_msg(radiolist, LABEL), 57 | tui_radiolist_tag2(TL, OPTN, LABEL, [title(' Select partition template ')], NPTN), 58 | true. 59 | 60 | menu_part_select(TL) :- 61 | % OPL - list of already configured partitions. 62 | findall(PD, member(p4(_PT, bd1([PD| _]), _CK, _SZ), TL), OPL), 63 | lx_list_part(PIL), 64 | PIL \= [], !, 65 | maplist(part2taglist, PIL, ML1), 66 | MT1 = ' Select partition(s) to use ', 67 | dialog_msg(checklist, LABEL), 68 | tui_checklist_tag2(ML1, OPL, LABEL, [title(MT1)], NPL), 69 | update_part_info(PIL, OPL, NPL, TL, NTL), 70 | retract(inst_setting(template(TN), _)), 71 | assertz(inst_setting(template(TN), NTL)), 72 | !. 73 | menu_part_select(_TL) :- 74 | tui_msgbox('There are no partitions available.', [sz([6, 40])]), 75 | true. 76 | 77 | part2taglist(dev_part(PD,_,_,SZ), [PD, SZ]). 78 | 79 | update_part_info(_PIL, OPL, OPL, IL, IL) :- !. 80 | update_part_info(PIL, OPL, NPL, IL, OL) :- 81 | findall(M, (member(M, IL), keep_part_(NPL, M)), IL1), 82 | get_bootloader(IL, B), 83 | add_part_(B, PIL, OPL, NPL, IL1, OL), 84 | true. 85 | 86 | % KL - list of partitions to keep. 87 | keep_part_(KL, p4(_PT, bd1([PD| _]), _CK, _SZ)) :- !, 88 | memberchk(PD, KL). 89 | keep_part_(KL, fs6(_, _, PD, _, _, _)) :- !, 90 | memberchk(PD, KL). 91 | keep_part_(KL, fs5_multi(_FS, _COL, [PD| _], _PTL, _CK)) :- !, 92 | memberchk(PD, KL). 93 | keep_part_(_KL, _). 94 | 95 | % SL - list of partitions to skip. 96 | add_part_(B, PIL, SL, [PD| T], IL, OL) :- 97 | memberchk(PD, SL), !, 98 | add_part_(B, PIL, SL, T, IL, OL). 99 | add_part_(B, PIL, SL, [PD| T], IL, [p4(PT, BD1, keep, SZ), FSX| OL]) :- 100 | % fs6(Name, MountPoint, Dev, [CreateOptList], [MountOptList], create/keep) 101 | % dev_part(NAME,name(SNAME,KNAME,DL),ET,SIZE) 102 | member(dev_part(PD, name(_SNAME, _KNAME, [DL|_]), part5(_PTTYPE, PT, _PARTUUID, _UUID, FS), SZ), PIL), 103 | BD1 = bd1([PD|DL]), !, 104 | add_part_fs(FS, B, PT, PD, FSX), 105 | add_part_(B, PIL, SL, T, IL, OL). 106 | add_part_(_B, _PIL, _SL, [], L, L) :- 107 | true. 108 | 109 | add_part_fs(FS, B, PT, PD, fs5_multi(FS, COL1, [PD], PTL, keep)) :- 110 | memberchk(FS, [zfs, btrfs]), !, 111 | guess_part_info(PT, Label, _MP), 112 | get_col_multi(FS, B, COL), 113 | fs_set_label(FS, Label, COL, COL1), 114 | part_tmpl(FS, root, PTL), 115 | true. 116 | add_part_fs(FS, B, PT, PD, fs6(FS, MP, PD, COL1, MOL, keep)) :- 117 | guess_part_info(PT, Label, MP), 118 | % menu_fs_settings0(FS, MP, B, COL), 119 | get_col(FS, MP, B, COL), 120 | get_mol(FS, MP, MOL), 121 | fs_set_label(FS, Label, COL, COL1), 122 | true. 123 | 124 | guess_part_info(sys_efi, 'EFI', '/boot/efi') :- !. 125 | guess_part_info(linux_data, 'ROOT', '/') :- !. 126 | guess_part_info(_PT, '', ''). 127 | 128 | menu_bios_efi(TT, TL) :- 129 | findall([M, MT], (member(M, [bios, efi]), boot_info(M, MT)), ML), 130 | findall(M, (member(M, [bios, efi]), inst_setting(system(M), _)), OL), 131 | dialog_msg(checklist, LABEL), 132 | tui_checklist_tag2(ML, OL, LABEL, [title(' Boot via BIOS/EFI ')], NL), 133 | ( NL \= [] 134 | ; tui_msgbox('No boot method was selected', [title(' ERROR ')]), 135 | fail 136 | ), !, 137 | ( OL = NL 138 | ; retractall(inst_setting(system(bios), _)), 139 | retractall(inst_setting(system(efi), _)), 140 | maplist(menu_bios_efi_, NL), 141 | 142 | findall(B, (menu_bootloader_(L0), member(B, L0)), BL), 143 | get_bootloader(TL, OB), 144 | 145 | ( memberchk(OB, BL) -> 146 | NB = OB 147 | ; BL = [NB| _], 148 | tui_msgbox2(['Bootloader will be reset to', NB]), 149 | replace_bootloader(NB) 150 | ), !, 151 | 152 | ( TT = manual 153 | ; tui_msgbox('Template will be reset to "Manual"'), 154 | set_template(manual, NB) 155 | ) 156 | ), 157 | !. 158 | 159 | menu_bios_efi_(bios) :- !, 160 | setup_sys_bios. 161 | menu_bios_efi_(efi) :- !, 162 | force_sys_efi. 163 | 164 | menu_hostonly :- 165 | inst_setting(hostonly, OHO), 166 | dialog_msg(radiolist, LABEL), 167 | tui_radiolist_tag2([[yes, 'Only for experts. Booting only the local host'], [no, 'Include all drivers']], OHO, LABEL, [title(' Host-only ')], NHO), 168 | ( OHO = NHO 169 | ; retractall(inst_setting(hostonly, _)), 170 | assertz(inst_setting(hostonly, NHO)) 171 | ), 172 | !. 173 | 174 | menu_keymap :- 175 | os_shell_lines('find /usr/share/kbd/keymaps/ -type f -iname "*.map.gz" -printf "%f\n" | sed \'s|.map.gz||g\' | sort', KML), 176 | dialog_msg(radiolist, LABEL), 177 | ( inst_setting(keymap, OKM) 178 | ; OKM = us 179 | ), 180 | tui_radiolist_tag2(KML, OKM, LABEL, [no-tags, title(' Select your keymap ')], KM), !, 181 | retractall(inst_setting(keymap, _)), 182 | assertz(inst_setting(keymap, KM)). 183 | 184 | make_lng_cntr(A1, [A1, R]) :- 185 | atom_concat(A2, '.UTF-8', A1), 186 | atom_chars(A2, LC), 187 | split_list_ne(LC, ['_'], LCL), 188 | chars_lc(LCL, LNG, CNTR), 189 | get_lng_name(LNG, LN), 190 | get_country_name(CNTR, CN), 191 | format_to_atom(R, '~w (~w)', [LN, CN]). 192 | 193 | chars_lc([LC1, LC2], LNG, CNTR) :- !, 194 | atom_chars(LNG, LC1), 195 | atom_chars(CNTR, LC2). 196 | chars_lc([LC1], LNG, '') :- !, 197 | atom_chars(LNG, LC1). 198 | 199 | menu_locale :- 200 | os_shell_lines('grep -E \'\\.UTF-8\' /etc/default/libc-locales|awk \'{print $1}\'|sed -e \'s/^#//\'', LCL), 201 | maplist(make_lng_cntr, LCL, LCL1), 202 | dialog_msg(radiolist, RADIOLABEL), 203 | ( inst_setting(locale, OLC) 204 | ; OLC = 'en_US.UTF-8' 205 | ), 206 | tui_radiolist_tag2(LCL1, OLC, RADIOLABEL, [title(' Select your locale ')], LC), !, 207 | retractall(inst_setting(locale, _)), 208 | assertz(inst_setting(locale, LC)). 209 | 210 | menu_timezone :- 211 | AREAS = ['Africa', 'America', 'Antarctica', 'Arctic', 'Asia', 'Atlantic', 'Australia', 'Europe', 'Indian', 'Pacific'], 212 | 213 | dialog_msg(radiolist, RADIOLABEL), 214 | ( inst_setting(timezone, OTZ) 215 | ; OTZ = 'America/New_York' 216 | ), 217 | split_tz(OTZ, A1, A2), 218 | tui_radiolist_tag2(AREAS, A1, RADIOLABEL, [no-tags, title(' Select area ')], A), !, 219 | 220 | os_shell2_lines([ls, '/usr/share/zoneinfo/' + A], TZL), 221 | 222 | ( A1 = A -> 223 | TZ1 = A2 224 | ; TZ1 = none 225 | ), 226 | tui_radiolist_tag2(TZL, TZ1, RADIOLABEL, [no-tags, title(' Select location ')], TZ), !, 227 | 228 | format_to_atom(ATZ, '~w/~w', [A, TZ]), 229 | retractall(inst_setting(timezone, _)), 230 | assertz(inst_setting(timezone, ATZ)). 231 | 232 | split_tz(TZ, A1, A2) :- 233 | atom_codes(TZ, TZL), 234 | split_list_ne(TZL, "/", LL), 235 | maplist(codes_atom, LL, [A1, A2]), 236 | true. 237 | 238 | menu_bootloader_([grub2, syslinux]) :- 239 | % grub2 and syslinux cannot be installed for EFI if booted in BIOS mode. 240 | ( lx_sys_efi(_) -> 241 | true 242 | ; \+ inst_setting(system(efi), _) 243 | ), 244 | true. 245 | menu_bootloader_([limine]). 246 | menu_bootloader_([rEFInd, gummiboot]) :- 247 | inst_setting(system(efi), _), 248 | \+ inst_setting(system(bios), _), 249 | true. 250 | menu_bootloader_([zfsBootMenu]) :- 251 | inst_setting(system(efi), _), 252 | \+ inst_setting(system(bios), _), 253 | inst_setting(fs, available(FSL)), 254 | memberchk(zfs, FSL), 255 | true. 256 | menu_bootloader_([efistub]) :- 257 | % efistub cannot be installed if booted in BIOS mode. 258 | lx_sys_efi(_), 259 | \+ inst_setting(system(bios), _), 260 | true. 261 | 262 | split_grp(G, GL) :- 263 | atom_chars(G, GC), 264 | split_list_ne(GC, [':'], GCL), 265 | maplist(chars_atom, GCL, GL), 266 | true. 267 | 268 | grp_on_off(ON, [G, _, N|_], [A, I]) :- 269 | ( member(G, ON) -> 270 | I = on 271 | ; I = off 272 | ), 273 | format_to_atom(A, '~w:~w', [G, N]). 274 | 275 | grp_ind2name(L, N, G) :- 276 | nth(N, L, [G|_]). 277 | 278 | menu_usergroups(GL3) :- 279 | G = [wheel, audio, video, floppy, cdrom, optical, kvm, xbuilder], 280 | os_shell_lines('cat /etc/group', GL), 281 | maplist(split_grp, GL, GL1), 282 | maplist(grp_on_off(G), GL1, GL2), 283 | dialog_msg(menu, LISTLABEL), 284 | tui_checklist_ind(GL2, LISTLABEL, [title(' Select group ')], SGL1), 285 | maplist(grp_ind2name(GL1), SGL1, GL3), 286 | true. 287 | 288 | menu_useraccount_info :- 289 | inst_setting(useraccount, user(LN, UN, _GL)), 290 | dialog_msg(form, FORMLABEL), 291 | tui_form_v(20, 100, [ 292 | item('Login name:', LN), 293 | item('User name:', UN) 294 | ], FORMLABEL, [title(' User account settings ')], [LN1, UN1|_]), 295 | menu_password_user(LN1), 296 | menu_usergroups(GL), 297 | retractall(inst_setting(useraccount, _)), 298 | assertz(inst_setting(useraccount, user(LN1, UN1, GL))), 299 | true. 300 | 301 | menu_lvm_info :- 302 | inst_setting(lvm, lv(VG, LV, SZ)), 303 | dialog_msg(form, FORMLABEL), 304 | tui_form_v(20, 100, [ 305 | item('Volume Group:', VG), 306 | item('Logic Volume:', LV) 307 | ], FORMLABEL, [title(' LVM settings ')], [VG1, LV1|_]), 308 | retractall(inst_setting(lvm, _)), 309 | assertz(inst_setting(lvm, lv(VG1, LV1, SZ))), 310 | true. 311 | 312 | menu_luks_info :- 313 | inst_setting(luks, luks(Name)), 314 | dialog_msg(form, FORMLABEL), 315 | tui_form_v(20, 100, [ 316 | item('Name:', Name) 317 | ], FORMLABEL, [title(' LUKS settings ')], [NName|_]), 318 | retractall(inst_setting(luks, _)), 319 | assertz(inst_setting(luks, luks(NName))), 320 | true. 321 | 322 | menu_mnt_opts(_TT, TL) :- 323 | st_bootloader(TL, B), 324 | findall(mnt_opt3(MP, FS, MOL), member(fs6(FS, MP, _PD, _COL, MOL, _CK), TL), IL1), 325 | % findall(mnt_opt3('/', FS, COL), (member(fs5_multi(FS, COL, _PDL, _PTL, _CK), TL), FS=btrfs), IL2), 326 | % append(IL1, IL2, IL), 327 | IL = IL1, 328 | menu_list_2(mnt_opts_main, ev(B, IL), IL, _OL, [title(' Mount Options '), cancel-label('Return'), ok-label('Edit')]), 329 | true. 330 | 331 | menu_mnt_opts0(FS, MP, B, OMOL, NMOL) :- 332 | format_to_atom(Title, ' \'~w\' ~w mount options ', [MP, FS]), 333 | % MP is used with mnt_opts to create an unique key. 334 | menu_list_2(mnt_opts(FS, MP), ev(B), OMOL, NMOL, [title(Title), cancel-label('Return'), ok-label('Edit'), no-tags]), 335 | ( OMOL = NMOL 336 | ; retractall(inst_setting(fs_attr(FS, MP, B), mount(_))), 337 | assertz(inst_setting(fs_attr(FS, MP, B), mount(NMOL))) 338 | ), !, 339 | true. 340 | 341 | menu_fs_settings0(FS, MP, B, OCOL, NCOL) :- 342 | format_to_atom(Title, ' \'~w\' ~w settings ', [MP, FS]), 343 | % get_col(FS, MP, B, OCOL), 344 | % MP is used with fs_settings to create an unique key. 345 | menu_list_2(fs_settings(FS, MP), ev(B), OCOL, NCOL, [title(Title), cancel-label('Return'), ok-label('Edit'), no-tags]), 346 | ( OCOL = NCOL 347 | ; retractall(inst_setting(fs_attr(FS, MP, B), create(_))), 348 | assertz(inst_setting(fs_attr(FS, MP, B), create(NCOL))) 349 | ), !, 350 | true. 351 | 352 | menu_fs_settings(TT, TL) :- 353 | st_bootloader(TL, B), 354 | findall(fs_sett3(MP, FS, COL), member(fs6(FS, MP, _PD, COL, _MOL, create), TL), IL1), 355 | findall(fs_sett3('/', FS, COL), member(fs5_multi(FS, COL, _PDL, _PTL, create), TL), IL2), 356 | append(IL1, IL2, IL), 357 | menu_list_2(fs_settings_main, ev(B, IL), IL, OL, [title(' FS Settings '), cancel-label('Return'), ok-label('Edit')]), 358 | ( IL = OL 359 | ; menu_fs_settings_(IL, OL, TL, NTL), 360 | retractall(inst_setting(template(TT), _)), 361 | assertz(inst_setting(template(TT), NTL)) 362 | ), !, 363 | true. 364 | 365 | menu_fs_settings_([fs_sett3(MP, FS, OCOL)|T], NL, OTL, NTL) :- 366 | memberchk(fs_sett3(MP, FS, NCOL), NL), 367 | OCOL \= NCOL, !, 368 | ( memberchk(FS, [btrfs, zfs]) -> 369 | replace_fs5_multi(OTL, FS, NCOL, TL) 370 | ; replace_fs6(OTL, MP, FS, NCOL, TL) 371 | ), 372 | menu_fs_settings_(T, NL, TL, NTL). 373 | menu_fs_settings_([_|T], NL, OTL, NTL) :- !, 374 | menu_fs_settings_(T, NL, OTL, NTL). 375 | menu_fs_settings_([], _OL, OTL, OTL). 376 | 377 | % MP + FS = unique key. 378 | replace_fs6([fs6(FS, MP, PD, _COL, MOL, create)|T], MP, FS, NCOL, [fs6(FS, MP, PD, NCOL, MOL, create)|T]) :- !. 379 | replace_fs6([H|T], MP, FS, NCOL, [H|T1]) :- !, 380 | replace_fs6(T, MP, FS, NCOL, T1). 381 | replace_fs6([], _MP, _FS, _NCOL, []). 382 | 383 | replace_fs5_multi([fs5_multi(FS, _COL1, PDL, PTL, create)|T], FS, NCOL, [fs5_multi(FS, NCOL, PDL, PTL, create)|T]) :- !. 384 | replace_fs5_multi([H|T], FS, NCOL, [H|T1]) :- !, 385 | replace_fs5_multi(T, FS, NCOL, T1). 386 | replace_fs5_multi([], _FS, _NCOL, []). 387 | 388 | --------------------------------------------------------------------------------