#!/usr/bin/python3 

#$Id: debclone,v 1.16 2025/06/07 11:43:55 daddio Exp $
# patched syslinux.cfg to use target boot partition 
## Id: debclone,v 1.13 2025/06/01 11:53:30 Exp $
# fixed bug in -m
## id: debclone,v 1.11 2025/06/01 11:52:29
# include efivars,essential for some uefi bioses

# 

# This script is free to copy and use. Absolutely no guarantees provided
# or responsibility taken , G. Eckersley

# It is a result of "handsoff" booting various pc's rather than any planned approach.
# It gets hacked whenever a PC won't boot, and includes scraps of code from all over
# the place. Happy to include your contribution if it helps dodge the rocks in the
# booting sea.

# default is to generate UEFI/Grub booting, the -m option generates mbr/syslinux boot partition.

# Run without parameters to print help file

# Run with "-d" options and understand what the shell commands do before
# executing without the -d (dummy) option

import sys
sys.path.insert(0, "./")


from gdb1 import *

#from bandb_text import * 

lf=chr(0xa)
sep=os.sep


def pos(g,d):
#  if g.v:
#    print(pe_s(d,c=1))
  if g.overwrite:
    pass
    e=d
    while (len(e)>0) and e[-1]==' ':
      e=e[:-1]
    if (e !='') and (e[0]!='#'):
        s='sudo '+d
        pd(s)
        os.system(s)
  else:
    pd('dummy run '+d)

def spl(txt):
  return st_split(txt,lf)
  


def xeclines(g,sp):
  tgt=g.dev
  lr=[]
  for line in sp:
    spl=st_split(line,'tgt')
    sjn=st_join(spl,g.dev)
    pos(g,sjn)
 


ptit="""

echo partitioning $tgt

#udevadm info -n $tgt -q property
umount tgt* || true
parted tgt  --script mktable gpt
parted tgt --script mkpart EFI fat16 1MiB 210MiB
parted tgt --script set 1 msftdata on
parted tgt --script mkpart LINUX ext4 210MiB 100%

"""

def dopart(g):
  if g.mbr:
    fl_writebya(g.tmpf,ftxt.encode('latin1'))
    s='cat '+g.tmpf+'   | sudo fdisk '+g.dev
    pd(s)
    if g.overwrite:
      xeccmd(s)
  else:  
    xeclines(g,spl(ptit))
  dofmt(g)

def dofmt(g):  
  syl=[]
  a=syl.append
  a('mkfs.vfat -n EFI '+g.ptn(1))
  a('mkfs.ext4  -F -L LINUX '+g.ptn(2))
  xeclines(g,syl)

def chkmount(g):
  if hasattr(g,'mounted'):
     return
  g.mounted=1
  xeclines(g,['mount '+g.ptn(2)+' '+g.rootfs])

def chkumount(g):
  if hasattr(g,'mounted'):
    del(g.mounted)
    xeclines(g,['umount '+g.ptn(2)])


def dosync(g):
  syl=[]
  a=syl.append
  chkmount(g)
#  a('mount '+g.ptn(2)+' '+g.rootfs)
  a('rsync -avx --delete --specials --devices --exclude /home/taint/ --exclude /swapfile /  '+g.rootfs)
#  pd(syl)
#  pd(g.v)
  xeclines(g,syl)

def xc(g,line):
  xeclines(g,[line])

def syslinuxinst(g):
  s=xc(g,'mkdir '+g.tmpmnt)
  s=xc(g,'mount '+devil(g,1)+' '+g.tmpmnt)
  s=xeccmd('uname -a')[0][0]
  ls=st_split(s)[2]
#  pd(ls)
  fl=glob.glob('/boot/*'+ls)
#  pd(fl)
  for bf in fl:
    stem=chopr(bf,sep)[-1]
#    if stem[0:6] in ['vmlinu','initrd']:
##      pd(stem)
#      xc(g,'cp -v /boot/'+stem+' '+g.tmpmnt)
    if stem[0:6] == 'vmlinu':
      xc(g,'cp -v /boot/'+stem+' '+g.tmpmnt+'/vmlinuz')
    if stem[0:6] == 'initrd':
      xc(g,'cp -v /boot/'+stem+' '+g.tmpmnt+'/initrd.img')
  pd('writing '+g.tmpmnt+'/syslinux.cfg')
  sysld=g.tmpdir+'/syslinux.cfg'
#  fl_writebya(sysld,sysltxt.encode('latin1'))
#  xc(g,s)
  xl=[]
  a=xl.append
  a('')
  a('PROMPT 1')
  a('timeout 10')
  a('')
  a('DEFAULT linuxmb')
  a('   SAY Now booting a kernel from SYSLINUX...')
  a('')
  a('LABEL linux')
  a('   linux /vmlinuz')
  a('   initrd /initrd.img')
  a('   append root=/dev/ram0 rw')
  a('')
  a('label linuxmb')
  a('   linux /vmlinuz')
  a('   initrd /initrd.img')
  a('   append root=' +devil(g,2)+' ro')
  a('')
  a('')
  fl_write(sysld,xl)
  if g.overwrite:
    s='cp '+sysld+' '+g.tmpmnt+'/syslinux.cfg'
    xc(g,s)
    pd('hhhhhh',sysld,s)
  s='dd if=/usr/lib/SYSLINUX/mbr.bin of='+g.dev
  pd(s)
  xc(g,s)
  s=xc(g,'umount '+devil(g,1)+' ')
  s='syslinux '+devil(g,1)
  xc(g,s)
      

def togrub(g):
  pd()
  syl=[]
#  root=g.dev+'2'
  a=syl.append
  a('mkdir '+g.rootfs)
#  a("mount "+g.dev+'2'+' '+g.rootfs)
  chkmount(g)

  a("mkdir "+g.rootfs+sep+'/boot/efi')
  a("mount "+devil(g,1)+' '+g.rootfs+sep+'boot/efi')

  a('mount --bind /dev '+g.rootfs+'/dev')
  a('mount -t devpts /dev/pts '+g.rootfs+'/dev/pts')
  a('mount -t proc proc '+g.rootfs+'/proc')
  a('mount -t sysfs sysfs '+g.rootfs+'/sys')
  a('mount -t tmpfs tmpfs '+g.rootfs+'/tmp')
  a('mount --bind /sys/firmware/efi/efivars '+g.rootfs+'/sys/firmware/efi/efivars' )
# without this latter line some bioses fail on some device partitions.   
  xeclines(g,syl)

def fromgrub(g):
  pd()
  syl=[]
#  root=g.dev+'2'
  a=syl.append
  a('umount '+g.rootfs+'/sys/firmware/efi/efivars ')
  a("umount "+g.rootfs+sep+'/dev/pts')
  a("umount "+g.rootfs+sep+'dev')
  a("umount "+g.rootfs+sep+'sys')
  a("umount "+g.rootfs+sep+'proc')
  a("umount "+g.rootfs+sep+'tmp')
  a("umount "+g.rootfs+sep+'boot/efi')
  a("umount "+g.rootfs)
  xeclines(g,syl)


def dogrub(g):
  pd()
  syl=[]
  a=syl.append
  fn2=g.tmpdir+'/chrtdta'
  pd(fn2)
  fl_write(fn2,spl(chrtit))
  a('chroot '+g.rootfs+' < '+fn2)
  xeclines(g,syl)


def mkfstab(g):
  fstl=['# fstab generated by debclone ','']
  a=fstl.append
  a('UUID='+g.bootuuid+'  /boot/efi  vfat umask=0077 0       1 ')
  a('UUID='+g.rootuuid+'  /  ext4  errors=remount-ro 0       1 ')
  a('#/swapfile    none    swap    sw    0    0 ')
  a('#enable this once a swapfile has been built')
  return fstl

def getuuids(g):
  res=xeccmd('sudo blkid |grep '+g.dev)
  for line in res[0]:
    if ":" in line:
      dev,s1=st_split(line,':')[:2]
      s2=st_split(s1,' UUID=')[-1]
      uuid=st_split(s2,' ')[0][1:-1]
      if dev[-1]=='1':
        g.bootuuid=uuid
      if dev[-1]=='2':
        g.rootuuid=uuid

def doinstall(g):     # install boot
  pd()
  syl=[]
  a=syl.append
  getuuids(g)
#  fst=mkfstab1(g)
  fst=mkfstab(g)
  fn=g.rootfs+sep+'/etc/fstab'
  fnl=g.tmpdir+'/fstab'
#  pd(fn,fnl)
  fl_write(fnl,fst)
  a('mv -vf '+fnl+' '+fn)
  xeclines(g,syl)
  dt=devtype(g.dev)
  pd(dt)
  if dt == devgpt:
    togrub(g)
    dogrub(g)
    fromgrub(g)
  elif dt ==devmbr:
    pd(g.dev)
    syslinuxinst(g)


def mkbuild(g):
  dopart(g)
  dosync(g)
  doinstall(g)
  pd(g.dev)

def mkdummy(g):
   pd(g.dev)


class dvv():
  def __init__(self,dv):
    self.dev=dv
    res=xeccmd('sudo ls '+dv)
    if 'cannot access' in res[1][0]:
      pd('device '+dv+' not found -exiting')
      sys.exit()
    pd(dv)
    t1='sd';t2='nvme';t3='mmcblk'
    if t1 in dv:
      self.dvtyp=t1 
      s=st_split(dv,t1)[1]
      return
    if t2 in dv:
      s=st_split(dv,t2)[1]
      self.dvtyp=t2 
      return
    if t3 in dv:
      s=st_split(dv,t3)[1]
      self.dvtyp=t3 
#    def ptn(ptno):
  def ptn(self,ptno):
      if self.dvtyp=='sd':
        return self.dev+str(ptno)
      elif self.dvtyp=='nvme':
        return self.dev+'p'+str(ptno)
      elif self.dvtyp=='mmcblk':
        return self.dev+'p'+str(ptno)

       
def devil(g,nr): # device 0:n
#  dvl=[g.dev]+glob.glob(g.dev+'*')
  dvl=glob.glob(g.dev+'*')
#  pd(dvl)
  dvl.sort()
#  pd(dvl)
#  sys.exit()
  return dvl[nr]



def main(p):
  pd(p)
  pd(p[0])
  pl={}
  for itm in p[2]:
    pd(itm)
    pl[itm[0]] = itm[1]
#  pd(pl)
  if len(p[0])==0:  # if no /dev param print help
    print(hlptxt)
    sys.exit()
  missing=needpkg(['sudo','gparted','util-linux','fdisk','gdisk','grub-common','grub2-common','python3','rsync','dosfstools','e2fsprogs'])
  if (len(missing)!=0):
    pd('missing packages ='+str(missing)  )
    sys.exit()
#  sys.exit()    
  pr=p[0][0]
  g=dvv(pr)
  g.tmpdir=tempfile.mktemp()+'bandb'
  g.tmpdir='/tmp/data-for-debclone'
  g.tmpf=g.tmpdir+sep+'tempdata'
  xeccmd('mkdir '+g.tmpdir)
  g.rootfs=g.tmpdir+sep+'rootfs'
  xeccmd('mkdir -p '+g.rootfs)
  xeccmd('ln -sfvn '+g.tmpdir+' /tmp/files+root-file-system')
  g.tmpmnt=g.tmpdir+sep+'syslinux-data'
  ol=p[1]
  pd(ol)
  ol2=''
  for ff in ol:
    ol2=ol2+ff
  pd(ol2)
  g.overwrite=not (('d' in ol2) or ('n' in ol2))
  g.mbr='m' in ol2
  g.v='v' in ol2
  pd(ol2,g.v)
#  sys.exit()
  g.dev=pr
  pd(pl,g.dev)
#  for i in range(3):
#    pd(devil(g,i))
#  sys.exit()
  if 'op' in pl:
    ops=st_split(pl['op'],',')
    ctc=[]
    ctc.append(['partition','ptn',dopart])
    ctc.append(['copy','sync',dosync])
    ctc.append(['install',doinstall])
#    ctc.append(['grub',dogrub])
    ctc.append(['tog','togrub',togrub])
    ctc.append(['fog','frog','fromgrub',fromgrub])
#    ctc.append(['fstab',mkfstab1])
    ctc.append(['build','clone',mkbuild])
#    ctc.append(['dummy',mkdummy])
    for dd in ops:
#    pd(dd)
      for cc in ctc:
        if dd in cc[:-1]:
            pd('--',dd,cc)
            cc[-1](g)
            pd('=================')
#        break
#  chkumount(g)       

      
if __name__ == '__main__':
  p=parsplit(sys.argv[1:])
  main(p)
          


