penguins-eggs
Version:
A remaster system tool, compatible with Almalinux, Alpine, Arch, Debian, Devuan, Fedora, Manjaro, Opensuse, Ubuntu and derivatives
398 lines (397 loc) • 16.9 kB
JavaScript
/**
* ./src/classes/ovary.d/produce.ts
* penguins-eggs v.25.7.x / ecmascript 2020
* author: Piero Proietti
* email: piero.proietti@gmail.com
* license: MIT
*/
import chalk from 'chalk';
import mustache from 'mustache';
// packages
import fs from 'node:fs';
import path from 'path';
import { shx } from '../../lib/utils.js';
// libraries
import { exec } from '../../lib/utils.js';
import Bleach from './../bleach.js';
import Diversions from './../diversions.js';
import Incubator from './../incubation/incubator.js';
import Pacman from './../pacman.js';
// classes
import Utils from './../utils.js';
import Repo from './../yolk.js';
// _dirname
const __dirname = path.dirname(new URL(import.meta.url).pathname);
/**
* produce
* @param clone
* @param homecrypt
* @param scriptOnly
* @param yolkRenew
* @param release
* @param myAddons
* @param nointeractive
* @param noicons
* @param includeRootHome
* @param verbose
*/
export async function produce(kernel = '', clone = false, homecrypt = false, fullcrypt = false, hidden = false, scriptOnly = false, yolkRenew = false, release = false, myAddons, myLinks, excludes, nointeractive = false, noicons = false, includeRootHome = false, verbose = false) {
this.verbose = verbose;
this.echo = Utils.setEcho(verbose);
if (this.verbose) {
this.toNull = ' > /dev/null 2>&1';
}
this.kernel = kernel;
this.liveRoot = this.settings.work_dir.merged;
this.dotOverlay = this.settings.work_dir;
this.clone = clone;
this.homecrypt = homecrypt;
this.fullcrypt = fullcrypt;
this.hidden = hidden;
// Crittografia
if (this.homecrypt || this.fullcrypt) {
if (this.homecrypt) {
this.luksMappedName = 'home.img';
}
else if (this.fullcrypt) {
this.luksMappedName = 'root.img';
}
this.luksFile = `/var/tmp/${this.luksMappedName}`;
this.luksMappedName = this.luksMappedName;
this.luksMountpoint = `/tmp/mnt/${this.luksMappedName}`;
this.luksDevice = `/dev/mapper/${this.luksMappedName}`;
this.luksPassword = '0'; // USARE UNA PASSWORD SICURA IN PRODUZIONE!
this.luksConfig = await this.interactiveCryptoConfig();
Utils.warning('You choose an encrypted eggs');
await this.luksGetPassword();
}
/**
* define kernel
*/
if (this.kernel === '') {
if (this.familyId === 'alpine') {
const moduleDirs = fs.readdirSync('/lib/modules');
this.kernel = moduleDirs[0];
}
else if (this.familyId === 'archlinux') {
// arch, manjaro
const moduleDirs = fs.readdirSync('/usr/lib/modules');
this.kernel = moduleDirs[0];
}
else {
// debian, fedora, openmamba, opensuse, voidlinux
const vmlinuz = path.basename(Utils.vmlinuz());
this.kernel = vmlinuz.slice(Math.max(0, vmlinuz.indexOf('-') + 1));
}
}
/**
* define vmlinuz and initrd fullpath
*/
this.vmlinuz = Utils.vmlinuz();
this.initrd = Utils.initrdImg(this.kernel);
/**
* yolk
*/
if (this.familyId === 'debian' && Utils.uefiArch() === 'amd64') {
const yolk = new Repo();
if (!yolk.exists()) {
Utils.warning('creating yolk');
await yolk.create(verbose);
}
else if (yolkRenew) {
Utils.warning('refreshing yolk');
await yolk.erase();
await yolk.create(verbose);
}
else {
Utils.warning('using preesixent yolk');
}
}
if (!fs.existsSync(this.settings.config.snapshot_dir)) {
shx.mkdir('-p', this.settings.config.snapshot_dir);
}
if (Utils.isLive()) {
console.log(chalk.red('>>> eggs: This is a live system! An egg cannot be produced from an egg!'));
}
else {
await this.liveCreateStructure();
// Carica calamares solo se le icone sono accettate
if (!noicons && // se VOGLIO le icone
!nointeractive &&
this.settings.distro.isCalamaresAvailable &&
Pacman.isInstalledGui() &&
this.settings.config.force_installer &&
!Pacman.calamaresExists()) {
console.log('Installing ' + chalk.bgGray('calamares') + ' due force_installer=yes.');
await Pacman.calamaresInstall(verbose);
const bleach = new Bleach();
await bleach.clean(verbose);
}
/**
* homecrypt/fullcrypt/clone/standard
*/
if (this.homecrypt) {
Utils.warning("eggs will SAVE users and users' data ENCRYPTED on the live (ISO)/live/home.img");
}
else if (this.fullcrypt) {
Utils.warning('eggs will SAVE full system ENCRYPTED on the live (ISO)/live/root.img');
}
else if (this.clone) {
this.settings.config.user_opt = 'live'; // patch for humans
this.settings.config.user_opt_passwd = 'evolution';
this.settings.config.root_passwd = 'evolution';
Utils.warning("eggs will SAVE users and users' data on CLEAR on the live (ISO)/live/filesystem.squashfs");
}
else {
Utils.warning("eggs will REMOVE users and users' data from live");
}
/**
* exclude.list
*/
if (!excludes.static && !fs.existsSync('/etc/penguins-eggs/exclude.list')) {
const excludeListTemplateDir = '/etc/penguins-eggs.d/exclude.list.d/';
const excludeListTemplate = excludeListTemplateDir + 'master.list';
if (!fs.existsSync(excludeListTemplate)) {
Utils.warning('Cannot find: ' + excludeListTemplate);
process.exit(1);
}
let excludeUsr = '';
let excludeVar = '';
let excludeHomes = '';
let excludeHome = '';
if (excludes.usr) {
excludeUsr = fs.readFileSync(`${excludeListTemplateDir}usr.list`, 'utf8');
}
if (excludes.var) {
excludeVar = fs.readFileSync(`${excludeListTemplateDir}var.list`, 'utf8');
}
if (excludes.homes) {
excludeHomes = fs.readFileSync(`${excludeListTemplateDir}homes.list`, 'utf8');
}
if (excludes.home) {
excludeHome = `home/${await Utils.getPrimaryUser()}/*`;
}
const view = {
home_list: excludeHome,
homes_list: excludeHomes,
usr_list: excludeUsr,
var_list: excludeVar
};
const template = fs.readFileSync(excludeListTemplate, 'utf8');
fs.writeFileSync(this.settings.config.snapshot_excludes, mustache.render(template, view));
}
/**
* NOTE: reCreate = false
*
* reCreate = false is just for develop
* put reCreate = true in release
*/
const reCreate = true;
let mksquashfsCmd = '';
if (reCreate) {
// start pre-clone
/**
* installer
*/
this.incubator = new Incubator(this.settings.remix, this.settings.distro, this.settings.config.user_opt, this.theme, this.clone || this.fullcrypt || this.homecrypt, verbose);
await this.incubator.config(release);
// 5 secondi di sleep
await Utils.sleep(5000);
await this.bindLiveFs();
await this.bindVfs();
/**
* kernelCopy
*/
await this.kernelCopy();
/**
* initrd creation
*/
if (fullcrypt) {
await this.luksRootInitrd();
}
else
switch (this.familyId) {
case 'alpine': {
await this.initrdAlpine();
break;
}
case 'archlinux': {
await this.initrdArch();
break;
}
case 'debian': {
await this.initrdDebian();
break;
}
case 'fedora':
case 'openmamba':
case 'opensuse':
case 'voidlinux': {
await this.initrdDracut();
break;
}
// No default
}
// We don't need more
await this.ubindVfs();
// mistero della Fede
await this.editLiveFs();
/**
* Autologin solo se per non clone
*/
if (!(this.clone || this.homecrypt || this.fullcrypt)) {
await this.usersRemove();
await this.userCreateLive();
if (Pacman.isInstalledGui()) {
// add GUI autologin
await this.createXdgAutostart(this.settings.config.theme, myAddons, myLinks, noicons);
}
else {
// add cli-autologin
this.cliAutologin.add(this.settings.distro.distroId, this.settings.distro.codenameId, this.settings.config.user_opt, this.settings.config.user_opt_passwd, this.settings.config.root_passwd, this.settings.work_dir.merged);
}
}
/**
* configurazione homecrypt
*/
if (this.homecrypt) {
// Occorre forzare il login CLI
if (Utils.isSystemd()) {
const systemdDir = `${this.settings.work_dir.merged}/etc/systemd/system`;
// remove eventuali autologin
if (fs.existsSync(`${systemdDir}/getty@tty1.service.d`)) {
await exec(`rm -rf ${systemdDir}/getty@tty1.service.d/*`);
}
else {
await exec(`mkdir -p ${systemdDir}/getty@tty1.service.d`);
}
let content = ``;
content += `[Service]\n`;
content += `ExecStart=\n`;
content += `ExecStart=-/sbin/agetty -o '-p -- \\u' --noclear %I \$TERM\n`;
fs.writeFileSync(`${systemdDir}/getty@tty1.service.d/no-autologin.conf`, content);
}
else if (Utils.isSysvinit()) {
const inittabPath = `${this.settings.work_dir.merged}/etc/inittab`;
if (fs.existsSync(inittabPath)) {
// Non funziona per Devuan
let content = fs.readFileSync(inittabPath, 'utf8');
// La riga standard per Devuan/Debian SysVinit.
// 1: ID
// 2345: Runlevels in cui attivarlo (Nota che il tuo boot è in runlevel 2)
// respawn: Riavvia getty se muore
// /sbin/getty 38400 tty1: Il comando
const tty1Line = '1:2345:respawn:/sbin/agetty --noclear tty1 linux';
// Rimuoviamo vecchie definizioni di tty1 (anche se commentate o diverse)
// Cerca righe che iniziano con "1:"
const regex = /^1:.*$/gm;
if (regex.test(content)) {
// Sostituisce la riga esistente
content = content.replaceAll(regex, tty1Line);
console.log('Fixed tty1 in inittab (replaced)');
}
else {
// Se non c'è, la aggiunge in fondo (dopo i commenti iniziali)
content += `\n${tty1Line}\n`;
console.log('Fixed tty1 in inittab (appended)');
}
// FIX CRITICO PER --cryptedhome / LIVE:
// A volte le live commentano tutte le tty per usare i propri hook.
// Assicurati che non ci siano altre righe strane che confliggono.
fs.writeFileSync(inittabPath, content);
}
}
/**
* homecrypt: installa il supporto
*/
const squashfsRoot = this.settings.work_dir.merged;
const homeImgPath = this.distroLiveMediumPath + 'live/home.img';
this.installHomecryptSupport(squashfsRoot, homeImgPath);
}
mksquashfsCmd = await this.makeSquashfs(scriptOnly, includeRootHome);
await this.uBindLiveFs(); // we don't need more
}
/**
* we now work just on ISO and filesystem.squashfs
*/
if (homecrypt) {
await this.luksHome();
}
else if (fullcrypt) {
await this.luksRoot();
}
/**
* makeEfi and syslinux was moved
* after luksRoot
* to get luks.uuid
*/
if (this.settings.config.make_efi) {
await this.makeEfi(this.theme);
}
// need syslinux?
const { arch } = process;
if (arch === 'ia32' || arch === 'x64') {
await this.syslinux(this.theme);
}
const mkIsofsCmd = (await this.xorrisoCommand(clone, homecrypt, fullcrypt)).replaceAll(/\s\s+/g, ' ');
this.makeDotDisk(this.volid, mksquashfsCmd, mkIsofsCmd);
/**
* AntiX/MX LINUX
*/
if (fs.existsSync('/etc/antix-version')) {
let uname = (await exec('uname -r', { capture: true })).data;
uname = uname.replaceAll('\n', '');
let content = '';
content = '#!/usr/bin/env bash';
content += 'mkdir /live/bin -p\n';
content += '## \n';
content += '# cp /usr/lib/penguins-eggs/scripts/non-live-cmdline /live/bin -p\n';
content += '# chmod +x /live/bin/non-live-cmdline\n';
content += '## \n';
content += 'mkdir /live/boot-dev/antiX -p\n';
content += 'ln -s /run/live/medium/live/filesystem.squashfs /live/boot-dev/antiX/linuxfs\n';
content += `ln -s /run/live/medium/live/initrd.img-${uname} /live/boot-dev/antiX/initrd.gz\n`;
content += `ln -s /run/live/medium/live/vmlinuz-${uname} /live/boot-dev/antiX/vmlinuz\n`;
content += `# md5sum /live/boot-dev/antiX/linuxfs > /live/boot-dev/antiX/linuxfs.md5\n`;
content += `# md5sum /live/boot-dev/antiX/initrd.gz > /live/boot-dev/antiX/initrd.gz.md5\n`;
content += `# md5sum /live/boot-dev/antiX/vmlinuz > /live/boot-dev/antiX/vmlinuz.md5\n`;
content += `# /live/aufs -> /run/live/rootfs/filesystem.squashfs\n`;
content += 'ln -s /run/live/rootfs/filesystem.squashfs /live/aufs\n';
content += `# use: minstall -no-media-check\n`;
content += 'minstall --no-media-check\n';
const file = `${this.settings.iso_work}antix-mx-installer`;
fs.writeFileSync(file, content);
await exec(`chmod +x ${file}`);
}
/**
* patch to emulate miso/archiso on archilinux family
*/
if (this.familyId === 'archlinux') {
let filesystemName = `arch/x86_64/airootfs.sfs`;
let hashCmd = 'sha512sum';
let hashExt = '.sha512';
if (Diversions.isManjaroBased(this.settings.distro.distroId)) {
filesystemName = `manjaro/x86_64/livefs.sfs`;
hashCmd = `md5sum`;
hashExt = '.md5';
}
await exec(`mkdir ${this.settings.iso_work}${path.dirname(filesystemName)} -p`, this.echo);
await exec(`ln ${this.settings.iso_work}live/filesystem.squashfs ${this.settings.iso_work}${filesystemName}`, this.echo);
/**
* patch 4 mksquashfs
*/
const fname = `${this.settings.work_dir.bin}mksquashfs`;
let content = fs.readFileSync(fname, 'utf8');
const patched = '# Arch and Manjaro based distro need this link';
// not need check, is always clean here... but OK
if (!content.includes(patched)) {
content += patched + '\n';
content += `if [ ! -e "${filesystemName}" ]; then\n`;
content += ` ln ${this.settings.iso_work}live/filesystem.squashfs ${this.settings.iso_work}${filesystemName}\n`;
content += `fi\n`;
fs.writeFileSync(fname, content, 'utf8');
}
}
await this.makeIso(mkIsofsCmd, scriptOnly);
}
}