/**
 * penguins-eggs-v7
 * author: Piero Proietti
 * email: piero.proietti@gmail.com
 * license: MIT
 */ import shx from 'shelljs' ; import fs from 'fs' ; import dns from 'dns' ; import path from 'path' ; import os from 'os' ; import pjson from 'pjson' ; import inquirer from 'inquirer' ; import chalk from 'chalk' ; import Pacman from './pacman' ; import { green , whiteBright } from 'chalk' ; remarks export default class Utils { param distroId param versionId static snapshotPrefix ( distroId , versionId ) { let result = 'egg-of-' + distroId . toLowerCase ( ) + '-' + versionId . toLowerCase ( ) + '-' ; result = result . replace ( `/` , '-' ) ; return result ; } /**
    * Controlla se il sistema è avviato con systemd
    * funziona anche in MX che utilizza systemd
    * ma viene avviato con init
    */ static isSystemd ( ) { return ( shx . exec ( `ps -p 1 -o comm=` ) . stdout . trim ( ) === 'systemd' ) ; } static isSysvinit ( ) { return ( shx . exec ( `ps -p 1 -o comm=` ) . stdout . trim ( ) === 'init' ) ; } /**
     * ricava path per vmlinuz
     * BOOT_IMAGE=(hd0,msdos1)/vmlinuz-5.15.6-200.fc35.x86_64 root=UUID=91cf614e-62a1-464c-904f-38d0a1ceb7a7 ro rootflags=subvol=root rhgb quiet
     */ static vmlinuz ( ) { const results = fs . readFileSync ( '/proc/cmdline' , 'utf8' ) . split ( ' ' ) ; let result = results [ 0 ] ; result = result . substring ( result . indexOf ( '/' ) ) ; if ( result . indexOf ( '@' ) > 0 ) { result = result . substring ( result . indexOf ( '@' ) + 1 ) ; } if ( ! fs . existsSync ( result ) ) { if ( fs . existsSync ( '/boot' + result ) ) { result = '/boot' + result ; } } return result ; } /**
     * ricava path per initrdImg
     */ static initrdImg ( ) { const vmlinuz = Utils . vmlinuz ( ) ; const path = vmlinuz . substring ( 0 , vmlinuz . lastIndexOf ( '/' ) ) + '/' ; let initrd = '' ; let version = '' ; if ( Pacman . distro ( ) . familyId === 'debian' ) { initrd = 'initrd.img' ; version = vmlinuz . substring ( vmlinuz . indexOf ( '-' ) ) ; } else if ( Pacman . distro ( ) . familyId === 'fedora' ) { initrd = 'initramfs' ; version = vmlinuz . substring ( vmlinuz . indexOf ( '-' ) ) + '.img' ; } else if ( Pacman . distro ( ) . familyId === 'archlinux' ) { initrd = 'initramfs' ; version = vmlinuz . substring ( vmlinuz . indexOf ( '-' ) ) + '.img' ; } else if ( Pacman . distro ( ) . familyId === 'suse' ) { initrd = 'initrd' ; version = vmlinuz . substring ( vmlinuz . indexOf ( '-' ) ) ; } return path + initrd + version ; } /**
     * Occore vedere un modo per creare machine-id dove non esiste
     */ static machineId ( ) { let result = '' ; if ( fs . existsSync ( '/etc/machine-id' ) ) { result = fs . readFileSync ( '/etc/machine-id' , 'utf-8' ) . trim ( ) ; } else if ( fs . existsSync ( '/var/lib/dbus/machine-id' ) ) { result = fs . readFileSync ( '/var/lib/dbus/machine-id' , 'utf-8' ) . trim ( ) ; } return result ; } param msg static warning ( msg = '' ) { console . log ( pjson . shortName + ' >>> ' + chalk . cyanBright ( msg ) + '.' ) ; } static error ( msg = '' ) { console . error ( pjson . shortName + ' >>> ' + chalk . bgMagentaBright ( chalk . whiteBright ( msg ) ) + '.' ) ; } /**
     * Return the primary user's name
     */ static getPrimaryUser ( ) { const primaryUser = shx . exec ( 'echo $SUDO_USER' , { silent : true } ) . stdout . trim ( ) ; if ( primaryUser === '' ) { console . log ( 'Cannot find your user name. Log as normal user and run: $sudo eggs [COMMAND]' ) ; process . exit ( 1 ) ; } return primaryUser ; } param device static uuid ( device ) { const uuid = shx . exec ( `blkid -s UUID -o value ${ device }` ) . stdout . trim ( ) ; return uuid ; } param date static formatDate ( date ) { const d = new Date ( date ) ; let month = String ( d . getMonth ( ) + 1 ) ; let day = String ( d . getDate ( ) ) ; const year = d . getFullYear ( ) ; let hh = String ( d . getHours ( ) ) ; let mm = String ( d . getMinutes ( ) ) ; if ( month . length < 2 ) { month = '0' + month ; } if ( day . length < 2 ) { day = '0' + day ; } if ( hh . length < 2 ) { hh = '0' + hh ; } if ( mm . length < 2 ) { mm = '0' + mm ; } return [ year , month , day ] . join ( '-' ) + '_' + hh + mm ; } returns static getPackageName ( ) { return pjson . shortName ; } returns { number } static getSnapshotCount ( snapshot_dir = '/' ) { if ( fs . existsSync ( snapshot_dir ) ) { const list = fs . readdirSync ( snapshot_dir ) ; if ( list . length > 0 ) { return list . length - 1 ; } } return 0 ; } returns { string } static getSnapshotSize ( snapshot_dir = '/' ) { let fileSizeInBytes = 0 ; const size = shx . exec ( `/usr/bin/find /home/eggs -maxdepth 1 -type f -name '*.iso' -exec du -sc {} + | tail -1 | awk '{print $1}'` , { silent : true } ) . stdout . trim ( ) ; if ( size === '' ) { fileSizeInBytes = 0 ; } else { fileSizeInBytes = Number ( size ) ; } return fileSizeInBytes ; } returns static machineArch ( ) { let arch = '' ; if ( process . arch === 'x64' ) { arch = 'amd64' ; } else if ( process . arch === 'ia32' ) { arch = 'i386' ; if ( shx . exec ( 'uname -m' , { silent : true } ) . stdout . trim ( ) === 'x86_64' ) { arch = 'amd64' ; } } else if ( process . arch === 'arm64' ) { arch = 'arm64' ; } else if ( process . arch === 'arm' ) { arch = 'armel' ; } return arch ; } returns static machineUEFI ( ) { let arch = this . machineArch ( ) ; let machineUEFI = '' ; if ( arch === 'amd64' ) { machineUEFI = 'x86_64-efi' ; } else if ( arch === 'armel' ) { machineUEFI = 'arm-efi' ; } if ( Pacman . distro ( ) . familyId === 'fedora' ) { machineUEFI = 'i386-efi' ; } else if ( Pacman . distro ( ) . familyId === 'suse' ) { machineUEFI = 'i386-efi' ; } return machineUEFI ; } returns static eggsArch ( ) { let arch = '' ; if ( process . arch === 'x64' ) { arch = 'amd64' ; } else if ( process . arch === 'ia32' ) { arch = 'i386' ; } else if ( process . arch === 'arm64' ) { arch = 'arm64' ; } else if ( process . arch === 'arm' ) { arch = 'armel' ; } return arch ; } param prefix param backup returns static getPrefix ( prefix , backup = false ) { if ( backup ) { if ( prefix . substring ( 0 , 7 ) === 'egg-of-' ) { prefix = 'egg-EB-' + prefix . substring ( 7 ) ; } else { prefix = 'egg-EB-' + prefix ; } } return prefix ; } param volid static getVolid ( volid = 'unknown' ) { if ( volid . length >= 32 ) { volid = volid . substr ( 0 , 32 ) ; } return volid ; } param basename returns static getPostfix ( ) { let postfix = '-' + this . machineArch ( ) + '_' + Utils . formatDate ( new Date ( ) ) + '.iso' ; return postfix ; } return { void } static getUsedSpace ( ) { let fileSizeInBytes = 0 ; if ( this . isLive ( ) ) { fileSizeInBytes = 0 ; } else { fileSizeInBytes = Number ( shx . exec ( `df /home | /usr/bin/awk 'NR==2 {print $3}'` , { silent : true } ) . stdout ) ; } return fileSizeInBytes ; } returns { number } static getLiveRootSpace ( type = 'debian-live' ) { let squashFs = '/run/live/medium/live/filesystem.squashfs' ; if ( type === 'mx' ) { squashFs = '/live/boot-dev/antiX/linuxfs' ; } const compressedFs = fs . statSync ( squashFs ) . size ; const compressedFs_compression_type = shx . exec ( `dd if=${ compressedFs } bs=1 skip=20 count=2 status=none 2>/dev/null| /usr/bin/od -An -tdI` ) ; let compression_factor = 0 ; if ( compressedFs_compression_type === '1' ) { compression_factor = 37 ; } else if ( compressedFs_compression_type === '2' ) { compression_factor = 52 ; } else if ( compressedFs_compression_type === '3' ) { compression_factor = 52 ; } else if ( compressedFs_compression_type === '4' ) { compression_factor = 31 ; } else if ( compressedFs_compression_type === '5' ) { compression_factor = 52 ; } else { compression_factor = 30 ; } let rootfs_file_size = 0 ; const linuxfs_file_size = ( Number ( shx . exec ( 'df /live/linux --output=used --total | /usr/bin/tail -n1' ) . stdout . trim ( ) ) * 1024 * 100 ) / compression_factor ; if ( fs . existsSync ( '/live/persist-root' ) ) { rootfs_file_size = Number ( shx . exec ( 'df /live/persist-root --output=used --total | /usr/bin/tail -n1' ) . stdout . trim ( ) ) * 1024 ; } let rootSpaceNeeded ; if ( type === 'mx' ) { /**
             * add rootfs file size to the calculated linuxfs file size. Probaby conservative, as rootfs will likely have some overlap with linuxfs
             */ rootSpaceNeeded = linuxfs_file_size + rootfs_file_size ; } else { rootSpaceNeeded = linuxfs_file_size ; } return rootSpaceNeeded / 1073741824.0 ; } remarks returns { boolean } static isi686 ( ) { return process . arch === 'ia32' ; } /**
     * Controlla se è un pacchetto deb
     * /usr/lib/penguins-eggs/bin/node
     */ static isDebPackage ( ) { let ret = false ; if ( process . execPath === '/usr/lib/penguins-eggs/bin/node' ) { ret = true ; } return ret ; } /**
     * Controlla se è un pacchetto sorgente
     */ static isSources ( ) { let ret = false ; if ( __dirname . substring ( 0 , 6 ) === '/home/' ) { ret = true ; } return ret ; } /**
     * Controlla se è un pacchetto npm
     */ static isNpmPackage ( ) { return ! ( this . isDebPackage ( ) || this . isSources ( ) ) ; } /**
     *
     */ static rootPenguin ( ) { return path . resolve ( __dirname , '../../' ) ; } returns static getFriendName ( ) { return pjson . shortName ; } returns static getPackageVersion ( ) { return pjson . version ; } /**
     * Get author name
     */ static getAuthorName ( ) { return 'Piero Proietti piero.proietti@gmail.com' ; } returns { number } static getDebianVersion ( ) { const cmd = "cat /etc/debian_version | /usr/bin/cut -f1 -d'.'" ; const version = Number ( shx . exec ( cmd , { silent : true } ) . stdout ) ; return version ; } returns { boolean } static isLive ( ) { let retVal = false ; const paths = [ '/lib/live/mount' , '/lib/live/mount/rootfs/filesystem.squashfs' , '/live/aufs' ] ; for ( let i = 0 ; i < paths . length ; i ++ ) { if ( Utils . isMountpoint ( paths [ i ] ) ) { retVal = true ; } } return retVal ; } param path static isMountpoint ( path = '' ) { const cmd = `mountpoint -q ${ path }` ; const result = shx . exec ( cmd , { silent : true } ) . code ; return result === 0 ; } returns static isRoot ( command = '' ) { if ( process . getuid && process . getuid ( ) === 0 ) { return true ; } else { Utils . titles ( pjson . shortName + ' ' + command + ` need to run with root privileges. Please, prefix it with sudo` ) ; } return false ; } /**
     * get the kernel version
     */ static kernerlVersion ( ) { return os . release ( ) ; } /**
     * return the name of network device
     */ static iface ( ) { const interfaces = Object . keys ( os . networkInterfaces ( ) ) ; let netDeviceName = '' ; for ( const k in interfaces ) { if ( interfaces [ k ] != 'lo' ) { netDeviceName = interfaces [ k ] ; } } return netDeviceName ; } /**
     * address
     */ static address ( ) { /**
         * ip a | grep -w inet |grep -v 127.0.0.1| awk '{print $2}' | cut -d "/" -f 1
         * ifconfig | grep -w inet |grep -v 127.0.0.1| awk '{print $2}' | cut -d ":" -f 2`
         *
        */ return shx . exec ( `ip a | grep -w inet |grep -v 127.0.0.1| awk '{print $2}' | cut -d "/" -f 1` , { silent : true } ) . stdout . trim ( ) ; } /**
     * netmask
     */ static netmask ( ) { /**
         * ip a | grep -w inet |grep -v 127.0.0.1| awk '{print $2}' | cut -d "/" -f 2
         * ifconfig | grep -w inet |grep -v 127.0.0.1| awk '{print $4}' | cut -d ":" -f 2
         */ return shx . exec ( `ip a | grep -w inet |grep -v 127.0.0.1| awk '{print $2}' | cut -d "/" -f 2` , { silent : true } ) . stdout . trim ( ) ; } returns static broadcast ( ) { /**
         * ip a | grep -w inet |grep -v 127.0.0.1| awk '{print $4}'
         * ifconfig | grep -w inet |grep -v 127.0.0.1| awk '{print $6}' | cut -d ":" -f 2
         */ return shx . exec ( `ip a | grep -w inet |grep -v 127.0.0.1| awk '{print $4}'` , { silent : true } ) . stdout . trim ( ) ; } returns static getDns ( ) { return dns . getServers ( ) ; } static getDomain ( ) { return shx . exec ( 'dnsdomainname' , { silent : true } ) . stdout . trim ( ) ; } returns static gateway ( ) { return shx . exec ( `route -n | grep 'UG[ \t]' | awk '{print $2}'` , { silent : true } ) . stdout . trim ( ) ; } param target param username param password param fullName static userAdd ( target = '/TARGET' , username = 'live' , password = 'evolution' , fullName = '' ) { const cmd = `sudo chroot ${ target } adduser ${ username }\
                    --home /home/${ username } \
                    --shell /bin/bash \
                    --disabled-password \
                    --gecos ${ fullName },\
                            '',\
                            '',\
                            ''` ; console . log ( `addUser: ${ cmd }` ) ; shx . exec ( cmd ) ; const cmdPass = `echo ${ username }:${ password } | chroot ${ target } chpasswd ` ; console . log ( `addUser cmdPass: ${ cmdPass }` ) ; shx . exec ( cmdPass ) ; const cmdSudo = `chroot ${ target } addgroup ${ username } sudo` ; console . log ( `addUser cmdSudo: ${ cmdSudo }` ) ; shx . exec ( cmdSudo , { silent : true } ) ; } remarks returns { string [ ] } static usersList ( ) { const out = shx . exec ( '/usr/bin/lslogins --noheadings -u -o user | grep -vw root' , { silent : true } ) . stdout ; const users = out . split ( '\n' ) ; return users ; } param dir param varius { recursive static shxMkDir ( dir = '' , varius ) { if ( varius . recursive ) { shx . mkdir ( '-p' , dir ) ; } else { shx . mkdir ( dir ) ; } } param file param text static write ( file , text ) { text = text . trim ( ) + '\n' ; text = text . trim ( ) + '\n' ; file = file . trim ( ) ; fs . writeFileSync ( file , text ) ; } param file param cmd static writeX ( file , cmd ) { let text = `#!/bin/sh\n\n` ; text += `# Created at: ${ Utils . formatDate ( new Date ( ) ) }\n` ; text += `# By: penguins_eggs v. ${ Utils . getPackageVersion ( ) }\n` ; text += `# ==> Perri\'s Brewery edition <== \n\n` ; text += cmd ; Utils . write ( file , text ) ; shx . chmod ( '+x' , file ) ; } param file param cmd static writeXs ( file , cmds ) { let cmd = '' ; for ( const elem of cmds ) { cmd += elem + '\n' ; } Utils . writeX ( file , cmd ) ; } param msg static async customConfirm ( msg = 'Select yes to continue... ' ) { const varResult = await Utils . customConfirmCompanion ( msg ) ; const result = JSON . parse ( varResult ) ; if ( result . confirm === 'Yes' ) { return true ; } else { return false ; } } param msg static async customConfirmCompanion ( msg = 'Select yes to continue... ' ) { return new Promise ( function ( resolve ) { const questions = [ { type : 'list' , name : 'confirm' , message : msg , choices : [ 'No' , 'Yes' ] , default : 'No' } ] ; inquirer . prompt ( questions ) . then ( function ( options ) { resolve ( JSON . stringify ( options ) ) ; } ) ; } ) ; } param msg static async customConfirmAbort ( msg = 'Confirm' ) { return new Promise ( function ( resolve ) { const questions = [ { type : 'list' , name : 'confirm' , message : msg , choices : [ 'No' , 'Yes' , 'Abort' ] , default : 'No' } ] ; inquirer . prompt ( questions ) . then ( function ( options ) { resolve ( JSON . stringify ( options ) ) ; } ) ; } ) ; } /**
     * titles
     * Penguin's are gettings alive!
     */ static titles ( command = '' ) { console . clear ( ) ; console . log ( '' ) ; console . log ( ' E G G S: the reproductive system of penguins' ) ; console . log ( '' ) ; console . log ( chalk . bgGreen . whiteBright ( '      ' + pjson . name + '      ' ) + chalk . bgWhite . blue ( " Perri's Brewery edition " ) + chalk . bgRed . whiteBright ( '       ver. ' + pjson . version + '       ' ) ) ; console . log ( 'command: ' + chalk . bgBlack . white ( command ) + '\n' ) ; } param verbose static setEcho ( verbose = false ) { let echo = { echo : false , ignore : true } ; if ( verbose ) { echo = { echo : true , ignore : false } ; } return echo ; } param bytes param decimals returns static formatBytes ( bytes , decimals = 2 ) { if ( bytes === 0 ) return '0 Bytes' ; const k = 1024 ; const dm = decimals < 0 ? 0 : decimals ; const sizes = [ 'Bytes' , 'KB' , 'MB' , 'GB' , 'TB' , 'PB' , 'EB' , 'ZB' , 'YB' ] ; const i = Math . floor ( Math . log ( bytes ) / Math . log ( k ) ) ; return parseFloat ( ( bytes / Math . pow ( k , i ) ) . toFixed ( dm ) ) + sizes [ i ] ; } }