Porteus-v5.01 bug reports

Please reproduce your error on a second machine before posting, and check the error by running without saved changes or extra modules (See FAQ No. 13, "How to report a bug"). For unstable Porteus versions (alpha, beta, rc) please use the relevant thread in our "Development" section.
swissluxtom
White ninja
White ninja
Posts: 23
Joined: 11 Mar 2026, 11:12
Distribution: Porteus 5.01

Porteus-v5.01 bug reports

Post#331 by swissluxtom » 12 Aug 2026, 20:29

Blaze the culprit is, non-root users by default do NOT have access to /dev/vboxuser (! the 'communication channel' device)
I had a long conversation again with Google AI, conclusion

Code: Select all

Your doubt is completely understandable, as the naming convention VirtualBox
uses for its various users and groups is notoriously confusing. However, there
is a distinct difference between how permissions are managed on the Host versus
the Guest.The difference in groups and how they apply to your Porteus VM (Guest) is
detailed below:

1. vboxusers is a Host-side group .. (thus irrelevant for 32-bit)
2. vboxadd is a Guest-side system user

Inside the Guest (your Porteus VM), the VirtualBox Guest Additions installer
creates a system user called vboxadd.

  - The Device Owner: If you inspect the Guest Additions source code
(  https://github.com/VirtualBox/virtualbox/blob/main/src/VBox/Additions/linux/installer/vboxadd.sh )

    (specifically the vboxadd.sh init script), /dev/vboxuser is explicitly
    assigned to the owner vboxadd (the user, not a group):
    dev=/dev/vboxguest
    userdev=/dev/vboxuser
    owner=vboxadd
    group=1  # (Group ID 1 is typically 'daemon' or 'bin' depending on the distro)
  - Because of this, the Guest Additions driver assigns the ownership of
    /dev/vboxuser to the vboxadd user and the group to daemon (or sometimes
    root) with 0660 permissions.

3. What about vboxsf?

Inside the Guest, the only group Guest Additions relies on for user permissions
is vboxsf (VirtualBox Shared Folders). Users must be a member of vboxsf to
access mounted shared folders, but this group does not govern /dev/vboxuser.

Summary

Because /dev/vboxuser is owned by the vboxadd user (with group daemon or root)
and is set to 0660 or 0600, your unprivileged guest user is locked out.

Adding the guest user to vboxusers inside the VM has no effect on /dev/vboxuser
because the Guest Additions driver does not associate that device node with the
vboxusers group. This is why either changing the permissions of /dev/vboxuser to
666 (via udev or rc.local) or adding the guest user to the daemon group (if
/dev/vboxuser is owned by group daemon) is required to make the clipboard
function without root privileges.
Thus either GLOBALLY open per "chmod 666 /dev/vboxuser",
or some hacks needed for a clean group (often based on the 'daemon' group) permission concept (see former GIT src of GA 'vboxadd.sh').
I don't know how (if at all) this has to be added to Porteus VBox-Builder?

At least there should be a Dialog-Box at the very end to inform the (novice) user
about the permission issue? as well hint users have to be added to 'vboxsf' group for 'Shared-Folders' access.

User avatar
Blaze
DEV Team
DEV Team
Posts: 4108
Joined: 28 Dec 2010, 11:31
Distribution: ⟰ Porteus current ☯ all DEs ☯
Location: ☭ Russian Federation, Lipetsk region, Dankov
Contact:

Porteus-v5.01 bug reports

Post#332 by Blaze » 12 Aug 2026, 21:21

Script from 2026-08-23

Code: Select all

#!/usr/bin/env python3
# Porteus VirtualBox builder
# Authors: Hamza & Brokenman
# Python port: Blaze

import os, sys, subprocess, re, tempfile, shutil, signal, time, fnmatch, tarfile, stat
import urllib.request, threading
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk, GLib, GdkPixbuf

VBOX = 'https://download.virtualbox.org/virtualbox'
IMGS = '/mnt/live/memory/images'
PKGDIR = '/var/lib/pkgtools/packages'
ICON = '/usr/share/pixmaps/porteus/vbox.png'

T = {
    'en': {
        'title': 'VirtualBox builder for Porteus', 'host': 'VirtualBox Host (for running VMs)',
        'guest': 'Guest Additions (inside VM)',
        'desc': 'Select a file or download a version.\nThe file will be converted to a Porteus module.',
        'file:': 'Select file:', 'dl': 'Download latest version', 'browse': 'Browse',
        'cancel': 'Cancel', 'ok': 'OK', 'close': 'Close', '_cancel': '_Cancel', '_open': '_Open',
        'no_opt': 'Select file or download!', 'no_file': 'File not found',
        'all': 'All files', 'f_iso': 'ISO images (*.iso)', 'f_vbox': 'VirtualBox files (*.run, *.iso)',
        'no_ver': 'Could not determine version from filename.\nRename file to include version, e.g.:\n  VBoxGuestAdditions_7.2.14.iso\n  VirtualBox-7.2.14-Linux_amd64.run',
        'no_link': 'Could not get link',
        'downloading': 'Downloading', 'building': 'Building', 'preparing': 'Preparing environment...',
        'installing': 'Installing VirtualBox...', 'copying': 'Copying files...',
        'finalizing': 'Finalizing...', 'mkmod': 'Creating XZM module...',
        'mnt_fail': 'Failed to mount ISO', 'no_additions': 'VBoxLinuxAdditions.run not found in ISO',
        'ready': 'Module ready:', 'fail': 'Build failed',
        'mv_act': 'Move and activate', 'open_dir': 'Open folder',
        'moved': 'moved to', 'act_ok': 'successfully activated', 'act_ng': 'activation failed',
        'mv_fail': 'Move error', 'act_fail': 'Activation failed',
        'action': 'Choose action:', 'more': 'You can build more or close.',
        'no_moddir': 'Modules folder not found', 'low_space': 'Low space in',
        'need': 'Need', 'free': 'Free', 'root': 'Root required!', 'x11': 'X session required!',
        'sel_ver': 'Select version', 'version': 'Version',
        'installed': 'Installed VirtualBox version', 'na': 'n/a',
        'dl_fail': 'Download failed', 'show_all': 'Show all versions',
        'no_src': 'Kernel sources not found.\nPlease get and activate module:\n  06-crippled_sources-*.xzm\n(available on Porteus server or USB)',
        'no_tools': 'Build tools not found (gcc/make).\nPlease get and activate module:\n  05-devel-*.xzm\n(available on Porteus server or USB)',
        'stage_fail': 'Staging build failed. The live system was not modified.',
        'extracting': 'Extracting installer...', 'compiling': 'Compiling kernel modules...',
        'staging': 'Staging files...',
        'reboot_guest': 'Recommended: reboot or log out/in.',
        'reboot_host': 'Recommended: reboot or run init script.',
    },
    'ru': {
        'title': 'Сборка VirtualBox для Porteus', 'host': 'Хост VirtualBox (для запуска ВМ)',
        'guest': 'Гостевые дополнения (внутри ВМ)',
        'desc': 'Выберите файл или загрузите версию.\nФайл будет преобразован в модуль Porteus.',
        'file:': 'Выберите файл:', 'dl': 'Загрузить последнюю версию', 'browse': 'Указать файл',
        'cancel': 'Отмена', 'ok': 'ОК', 'close': 'Закрыть', '_cancel': '_Отмена', '_open': '_Открыть',
        'no_opt': 'Выберите файл или загрузку!', 'no_file': 'Файл не найден',
        'all': 'Все файлы', 'f_iso': 'ISO-образы (*.iso)', 'f_vbox': 'Файлы VirtualBox (*.run, *.iso)',
        'no_ver': 'Не удалось определить версию из имени файла.\nПереименуйте файл, включив версию, например:\n  VBoxGuestAdditions_7.2.14.iso\n  VirtualBox-7.2.14-Linux_amd64.run',
        'no_link': 'Не удалось получить ссылку',
        'downloading': 'Загрузка', 'building': 'Сборка', 'preparing': 'Подготовка окружения...',
        'installing': 'Установка VirtualBox...', 'copying': 'Копирование файлов...',
        'finalizing': 'Финализация...', 'mkmod': 'Создание модуля XZM...',
        'mnt_fail': 'Не удалось смонтировать ISO', 'no_additions': 'VBoxLinuxAdditions.run не найден в ISO',
        'ready': 'Модуль готов:', 'fail': 'Сборка не удалась',
        'mv_act': 'Переместить и активировать', 'open_dir': 'Открыть папку',
        'moved': 'перемещён в', 'act_ok': 'успешно активирован', 'act_ng': 'активация не удалась',
        'mv_fail': 'Ошибка перемещения', 'act_fail': 'Ошибка активации',
        'action': 'Выберите действие:', 'more': 'Можно собрать ещё или закрыть.',
        'no_moddir': 'Папка модулей не найдена', 'low_space': 'Мало места в',
        'need': 'Нужно', 'free': 'Свободно', 'root': 'Нужен root!', 'x11': 'Нужна X-сессия!',
        'sel_ver': 'Выбор версии', 'version': 'Версия',
        'installed': 'Установленная версия VirtualBox', 'na': 'н/д',
        'dl_fail': 'Ошибка загрузки', 'show_all': 'Показывать все версии',
        'no_src': 'Исходники ядра не найдены.\nПолучите и активируйте модуль:\n  06-crippled_sources-*.xzm\n(доступен на сервере Porteus или USB)',
        'no_tools': 'Инструменты сборки не найдены (gcc/make).\nПолучите и активируйте модуль:\n  05-devel-*.xzm\n(доступен на сервере Porteus или USB)',
        'stage_fail': 'Изолированная сборка не удалась. Живая система не была изменена.',
        'extracting': 'Извлечение установщика...', 'compiling': 'Компиляция модулей ядра...',
        'staging': 'Подготовка файлов...',
        'reboot_guest': 'Рекомендуется перезагрузка или повторный вход.',
        'reboot_host': 'Рекомендуется перезагрузка или запуск init-скрипта.',
    }
}

def _(k): return T.get(os.environ.get('LANG', 'en')[:2], T['en']).get(k, k)

def safe_rm(p):
    try: os.remove(p)
    except: pass

def cmd(c, sh=False, t=60, env=None, input_data=None):
    try:
        kw = dict(shell=sh, capture_output=True, text=True, errors='replace', timeout=t, env=env)
        if input_data is None:
            kw['stdin'] = subprocess.DEVNULL
        else:
            kw['input'] = input_data
        return subprocess.run(c, **kw)
    except subprocess.TimeoutExpired:
        return subprocess.CompletedProcess(c, 124, '', f'Timeout after {t}s')
    except Exception as e:
        return subprocess.CompletedProcess(c, 1, '', str(e))

def load_icon(size=48):
    try: return GdkPixbuf.Pixbuf.new_from_file_at_size(ICON, size, size)
    except: return None

def pver():
    try:
        m = re.search(r'(\d+\.\d+(?:\.\d+)?)', open('/etc/porteus-version').read().strip())
        return f'v{m.group(1)}' if m else 'unknown'
    except: return 'unknown'

def is_block(p):
    try: return stat.S_ISBLK(os.stat(p).st_mode)
    except: return False

def is_vbox_file(f, kind=None):
    f = os.path.basename(f)
    if not f.endswith('.xzm'): return False
    if kind == 'guest': return f.startswith('vbox-guest-')
    if kind == 'host': return f.startswith('VirtualBox-')
    return f.startswith(('VirtualBox-', 'vbox-guest-'))

def find_vbox_mods(kind=None):
    r = []
    for d in (IMGS, os.environ.get('MODDIR', '')):
        if d and os.path.isdir(d):
            try: r.extend(os.path.join(d, f) for f in os.listdir(d) if is_vbox_file(f, kind))
            except: pass
    return r

def vbox_in_images(kind=None):
    try: return os.path.isdir(IMGS) and any(is_vbox_file(f, kind) for f in os.listdir(IMGS))
    except: return False

def deactivate_silent(m):
    if os.path.isfile(m): cmd(['deactivate', m, '-q'], t=30)

def deactivate_all_wait(kind=None):
    for m in find_vbox_mods(kind): deactivate_silent(m)
    for _ in range(15):
        if not vbox_in_images(kind): break
        time.sleep(1)

def pkg_ver():
    if not os.path.isdir(PKGDIR): return None, None
    try:
        for f in os.listdir(PKGDIR):
            m = re.match(r'(?:VirtualBox|vbox-guest)-(\d+\.\d+\.\d+)', f)
            if m: return m.group(1), f
    except: pass
    return None, None

def space_ok(p, mb=800):
    try:
        s = os.statvfs(p); free = s.f_bavail * s.f_frsize // (1024 * 1024)
        return free >= mb, free
    except: return True, 0

def is_vm():
    try:
        for p in ('/sys/class/dmi/id/product_name', '/sys/class/dmi/id/sys_vendor'):
            if os.path.exists(p) and any(x in open(p).read() for x in ('VirtualBox', 'Oracle', 'innotek')):
                return True
    except: pass
    return 'VirtualBox' in cmd(['/usr/sbin/dmidecode', '-s', 'system-product-name'], t=5).stdout

def fm_open(path):
    try:
        subprocess.Popen(['xdg-open', path], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL,
                         stdin=subprocess.DEVNULL, start_new_session=True)
        return True
    except: return False

def mod_dir():
    d = os.environ.get('MODDIR', '')
    if d and os.path.isdir(d): return d
    for p in (IMGS, '/porteus/modules', '/mnt/sda1/porteus/modules'):
        if os.path.isdir(p): return p
    return None

def _unescape(s):
    try: return s.encode('utf-8').decode('unicode_escape').encode('latin-1').decode('utf-8')
    except: return s

def _mount_point_of(dev):
    r = cmd(['findmnt', '-rn', '-o', 'TARGET', '-S', dev], t=5)
    if r.returncode == 0 and r.stdout.strip():
        return _unescape(r.stdout.strip().split('\n')[0])
    try:
        for line in open('/proc/mounts'):
            parts = line.split()
            if len(parts) >= 2 and os.path.realpath(parts[0]) == dev:
                return _unescape(parts[1])
    except: pass
    return None

def find_ga_cd():
    try:
        by_label = '/dev/disk/by-label'
        if os.path.isdir(by_label):
            for name in sorted(os.listdir(by_label)):
                if not name.startswith('VBox_GAs_'): continue
                dev = os.path.realpath(os.path.join(by_label, name))
                label = _unescape(name)
                mp = _mount_point_of(dev)
                if mp:
                    if os.path.exists(os.path.join(mp, 'VBoxLinuxAdditions.run')):
                        return mp, label
                    continue
                if os.path.exists(dev): return dev, label
    except: pass
    try:
        for d in sorted(os.listdir('/mnt')):
            if d.startswith('VBox_GAs_'):
                base = os.path.join('/mnt', d)
                if os.path.exists(os.path.join(base, 'VBoxLinuxAdditions.run')):
                    return base, d
    except: pass
    try:
        r = cmd(['blkid'], t=10)
        if r.returncode == 0:
            for line in r.stdout.splitlines():
                if 'LABEL="VBox_GAs_' not in line: continue
                dev = line.split(':')[0].strip()
                m = re.search(r'LABEL="([^"]+)"', line)
                label = m.group(1) if m else 'VBox_GAs'
                mp = _mount_point_of(dev)
                if mp:
                    if os.path.exists(os.path.join(mp, 'VBoxLinuxAdditions.run')):
                        return mp, label
                    continue
                if os.path.exists(dev): return dev, label
    except: pass
    return None, None

def vbox_version_lists(mode='host', arch='x86_64'):
    try:
        vs = sorted(re.findall(r'href="(\d+\.\d+\.\d+)/"',
                    urllib.request.urlopen(f'{VBOX}/', timeout=15).read().decode()),
                    key=lambda v: [int(x) for x in v.split('.')], reverse=True)
        # 32-bit hosts: max 5.2.44
        max32 = (5, 2, 44) if mode == 'host' and arch not in ('x86_64', 'amd64') else None
        all_out, latest, seen = [], [], set()
        for v in vs:
            try: parts = tuple(int(x) for x in v.split('.'))
            except: continue
            if max32 and parts > max32: continue
            all_out.append(v)
            key = '.'.join(v.split('.')[:2])
            if key not in seen: seen.add(key); latest.append(v)
        return latest, all_out
    except: return [], []

def vbox_url(v, a, dt):
    tg = 'amd64' if a == 'x86_64' else 'x86'
    if dt == 'vbox':
        try:
            for l in re.findall(r'href="([^"]+\.run)"',
                                urllib.request.urlopen(f'{VBOX}/{v}/', timeout=10).read().decode()):
                if tg in l: return f'{VBOX}/{v}/{l}'
        except: pass
        return f'{VBOX}/{v}/VirtualBox-{v}-Linux_{tg}.run'
    return f'{VBOX}/{v}/VBoxGuestAdditions_{v}.iso'

def vbox_extpack_url(v):
    try:
        for l in re.findall(r'href="([^"]+\.vbox-extpack)"',
                            urllib.request.urlopen(f'{VBOX}/{v}/', timeout=10).read().decode()):
            return f'{VBOX}/{v}/{l}'
    except: pass
    return None

def mount_iso(p, mnt):
    args = ['mount', '-o', 'loop', p, mnt] if not is_block(p) else ['mount', p, mnt]
    r = cmd(args, t=30)
    return r.returncode == 0 and os.path.ismount(mnt)

def inst_ver():
    try:
        r = cmd(['VBoxManage', '--version'], t=5)
        if r.returncode == 0:
            m = re.match(r'(\d+\.\d+\.\d+)', r.stdout.strip())
            if m: return m.group(0)
    except: pass
    v, _ = pkg_ver(); return v

def inst_short():
    f = inst_ver(); m = re.match(r'(\d+\.\d+)', f) if f else None
    return m.group(1) if m else None

def clean_tmp_files():
    for f in ('/tmp/.message', '/tmp/.cdload', '/tmp/vboxguest-Module.symvers'): safe_rm(f)

def find_file(root, name):
    for dp, dn, fn in os.walk(root):
        if name in fn: return os.path.join(dp, name)
    return None

def find_files_pattern(root, pattern):
    return [os.path.join(dp, f) for dp, dn, fn in os.walk(root)
            for f in fn if fnmatch.fnmatch(f.lower(), pattern.lower())]

def extract_tar_compat(tar, path):
    try: tar.extractall(path, filter='data')
    except TypeError: tar.extractall(path)

def normalize_perms(path):
    # chmod the root itself too (mkdtemp leaves 0700)
    try: os.chmod(path, 0o755)
    except: pass
    for root, dirs, files in os.walk(path):
        for d in dirs:
            try: os.chmod(os.path.join(root, d), 0o755)
            except: pass
        for f in files:
            p = os.path.join(root, f)
            try: os.chmod(p, 0o755 if os.stat(p).st_mode & 0o111 else 0o644)
            except: pass

def strip_unwanted(path):
    bad_dirs = {'sdk', 'doc', 'docs', 'manual', 'src', 'source', 'sources', 'dtrace',
                'additions', 'unattendedtemplates', 'init'}
    bad_files = ('*.cmd', '*.o', '*.mod', '*.mod.c', '*.order', '*.symvers', '*.c', '*.h',
                 '*.cpp', '*.cc', '*.s', '*.S', 'Makefile', 'Makefile.*', 'Kbuild', '*.debug',
                 '*.pdb', '*.sym', '*.map', '*.a', '*.la', '*.spec', 'UserManual.pdf',
                 'UserManual.qch', 'UserManual.qhc', 'postinst-common.sh', 'prerm-common.sh',
                 'routines.sh', 'check_module_dependencies.sh', 'VBox.sh', 'uninstall.sh',
                 '.autoreg', 'vboxshell.py', 'VBoxPython2.so', 'VBoxPython2_7.so', 'VBoxDTrace')
    old_drv = ('vboxmouse_drv_13.so', 'vboxmouse_drv_14.so', 'vboxmouse_drv_15.so',
               'vboxmouse_drv_16.so', 'vboxmouse_drv_70.so', 'vboxmouse_drv_71.so',
               'vboxvideo_drv_13.so', 'vboxvideo_drv_14.so', 'vboxvideo_drv_15.so',
               'vboxvideo_drv_16.so', 'vboxvideo_drv_17.so', 'vboxvideo_drv_18.so',
               'vboxvideo_drv_19.so', 'vboxvideo_drv_70.so', 'vboxvideo_drv_71.so',
               'vboxvideo_drv_110.so', 'vboxvideo_drv_111.so', 'vboxvideo_drv_112.so',
               'vboxvideo_drv_113.so', 'vboxvideo_drv_114.so', 'vboxvideo_drv_115.so',
               'vboxvideo_drv_116.so', 'vboxvideo_drv_117.so', 'vboxvideo_drv_118.so',
               'x11config.sh', '98vboxadd-xclient', 'pam_vbox.so', 'vbox_accel.pp',
               'vbox_x11.pp', 'VBoxAudioTest')
    for root, dirs, files in os.walk(path, topdown=False):
        for d in list(dirs):
            if d.lower() in bad_dirs:
                shutil.rmtree(os.path.join(root, d), ignore_errors=True); dirs.remove(d)
        for f in files:
            lf = f.lower()
            if any(fnmatch.fnmatch(lf, p.lower()) for p in bad_files) or f in old_drv:
                safe_rm(os.path.join(root, f))
    for root, dirs, files in os.walk(path, topdown=False):
        try:
            if not os.listdir(root): os.rmdir(root)
        except: pass

class Prog(Gtk.Dialog):
    def __init__(self, par, t, m):
        super().__init__(title=t, transient_for=par, modal=True)
        self.set_deletable(False)
        self.set_default_size(340, 110)
        self.set_position(Gtk.WindowPosition.CENTER_ON_PARENT)
        b = self.get_content_area(); b.set_spacing(12)
        b.set_margin_top(20); b.set_margin_bottom(20)
        try: b.set_margin_start(24); b.set_margin_end(24)
        except: b.set_margin_left(24); b.set_margin_right(24)
        self.lbl = Gtk.Label(label=m); self.lbl.set_line_wrap(True)
        self.bar = Gtk.ProgressBar(); self.bar.set_pulse_step(0.08)
        b.pack_start(self.lbl, False, False, 0)
        b.pack_start(self.bar, False, False, 0)
        self.show_all()
        self._tid = GLib.timeout_add(80, self._tk)
        self._d = False
    def _tk(self):
        if not self._d: self.bar.pulse(); return True
        return False
    def msg(self, m):
        if not self._d: self.lbl.set_text(m)
    def close(self):
        if not self._d:
            self._d = True
            if self._tid: GLib.source_remove(self._tid)
            self.destroy()

class VerDlg(Gtk.Dialog):
    def __init__(self, par, latest, all_vs, iv):
        super().__init__(title=_('sel_ver'), transient_for=par, modal=True)
        self.set_default_size(360, 430)
        self.set_position(Gtk.WindowPosition.CENTER_ON_PARENT)
        b = self.get_content_area(); b.set_spacing(8)
        b.set_margin_top(12); b.set_margin_bottom(12)
        try: b.set_margin_start(16); b.set_margin_end(16)
        except: b.set_margin_left(16); b.set_margin_right(16)
        il = Gtk.Label(); il.set_markup(f'<b>{_("installed")}: {iv or _("na")}</b>')
        il.set_halign(Gtk.Align.START); b.pack_start(il, False, False, 0)
        self.latest, self.all = latest, all_vs
        self.chk_all = Gtk.CheckButton(label=_('show_all'))
        self.chk_all.connect('toggled', self._fill)
        b.pack_start(self.chk_all, False, False, 0)
        sw = Gtk.ScrolledWindow(); sw.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC)
        self.st = Gtk.ListStore(str)
        self.tv = Gtk.TreeView(model=self.st)
        self.tv.append_column(Gtk.TreeViewColumn(_('version'), Gtk.CellRendererText(), text=0))
        self.tv.set_headers_visible(True)
        self.tv.get_selection().set_mode(Gtk.SelectionMode.SINGLE)
        sw.add(self.tv); b.pack_start(sw, True, True, 0)
        self._fill()
        self.add_button(_('_cancel'), Gtk.ResponseType.CANCEL)
        self.add_button(_('ok'), Gtk.ResponseType.OK)
        self.show_all()
    def _fill(self, *a):
        self.st.clear()
        for v in (self.all if self.chk_all.get_active() else self.latest):
            self.st.append([v])
        if len(self.st): self.tv.get_selection().select_path(Gtk.TreePath.new_first())
    def chosen(self):
        _, it = self.tv.get_selection().get_selected()
        return self.st[it][0] if it else None

class App(Gtk.Window):
    def __init__(self, cli_mode=None, cli_file=None):
        super().__init__(title=_('title'))
        self._icon = load_icon(48)
        if self._icon: self.set_icon(self._icon)
        self.set_border_width(12); self.set_resizable(True)
        self.set_default_size(640, 360)
        self.set_position(Gtk.WindowPosition.CENTER)
        self.connect('destroy', Gtk.main_quit)
        raw = os.uname().machine
        self.arch = 'i586' if raw in ('i386', 'i486', 'i586', 'i686') else raw
        self.lib = 'lib64' if self.arch == 'x86_64' else 'lib'
        self.mode, self._prog = None, None
        self.cd_src, self.cd_label = None, None
        self.lang_codes = self._get_lang_codes()
        self._ui()
        if cli_mode == 'guest': self.rg.set_active(True); self.mode = 'guest'
        elif cli_mode == 'host': self.rh.set_active(True); self.mode = 'host'
        else:
            self.mode = 'guest' if is_vm() else 'host'
            if self.mode == 'guest': self.rg.set_active(True)
            else: self.rh.set_active(True)
        if self.mode == 'guest':
            self.cd_src, self.cd_label = find_ga_cd()
            if self.cd_src: self._set_path(self.cd_src)
        if cli_file and os.path.isfile(cli_file): self._set_path(cli_file)

    def _get_lang_codes(self):
        codes = []
        for var in ('LANGUAGE', 'LC_ALL', 'LC_MESSAGES', 'LANG'):
            for item in os.environ.get(var, '').split(':'):
                item = item.split('.')[0]
                if item and item not in ('C', 'POSIX'):
                    codes.append(item)
                    if '_' in item: codes.append(item.split('_')[0])
        return list(dict.fromkeys(codes))

    def _ui(self):
        vb = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=12); self.add(vb)
        hb = Gtk.Box(spacing=12)
        if self._icon: hb.pack_start(Gtk.Image.new_from_pixbuf(self._icon), False, False, 0)
        t = Gtk.Label(); t.set_markup(f'<span size="x-large" weight="bold">{_("title")}</span>')
        hb.pack_start(t, True, True, 0)
        vb.pack_start(hb, False, False, 0)
        vb.pack_start(Gtk.Separator(), False, False, 0)
        d = Gtk.Label(label=_('desc')); d.set_line_wrap(True); d.set_max_width_chars(80)
        d.set_halign(Gtk.Align.START); vb.pack_start(d, False, False, 0)
        rb = Gtk.Box(spacing=20)
        self.rh = Gtk.RadioButton.new_with_label_from_widget(None, _('host'))
        self.rh.connect('toggled', self._sm, 'host')
        self.rg = Gtk.RadioButton.new_with_label_from_widget(self.rh, _('guest'))
        self.rg.connect('toggled', self._sm, 'guest')
        rb.pack_start(self.rh, False, False, 0)
        rb.pack_start(self.rg, False, False, 0)
        vb.pack_start(rb, False, False, 0)
        fh = Gtk.Box(spacing=6)
        fh.pack_start(Gtk.Label(label=_('file:')), False, False, 0)
        self.ent = Gtk.Entry(); self.ent.set_editable(False); self.ent.set_width_chars(52)
        fh.pack_start(self.ent, True, True, 0)
        bb = Gtk.Button(label=_('browse')); bb.connect('clicked', self._br)
        fh.pack_start(bb, False, False, 0)
        vb.pack_start(fh, False, False, 0)
        self.chk = Gtk.CheckButton(label=_('dl'))
        self.chk.connect('toggled', lambda w: self.ent.set_sensitive(not w.get_active()))
        vb.pack_start(self.chk, False, False, 0)
        vb.pack_start(Gtk.Separator(), False, False, 4)
        bh = Gtk.Box(spacing=8, halign=Gtk.Align.END)
        bc = Gtk.Button(label=_('cancel')); bc.connect('clicked', lambda w: Gtk.main_quit())
        bo = Gtk.Button(label=_('ok')); bo.connect('clicked', self._ok)
        bh.pack_end(bo, False, False, 0)
        bh.pack_end(bc, False, False, 0)
        vb.pack_end(bh, False, False, 0)
        self.show_all()

    def _set_path(self, p):
        p = p or ''
        self.ent.set_text(p); self.ent.set_tooltip_text(p)

    def _sm(self, b, m):
        if b.get_active():
            self.mode = m
            self.ent.set_text(''); self.ent.set_tooltip_text('')

    def _br(self, w):
        d = Gtk.FileChooserDialog(title=_('browse'), parent=self, action=Gtk.FileChooserAction.OPEN)
        d.add_buttons(_('_cancel'), Gtk.ResponseType.CANCEL, _('_open'), Gtk.ResponseType.OK)
        fa = Gtk.FileFilter(); fa.set_name(_('all')); fa.add_pattern('*'); d.add_filter(fa)
        pt, nm = ('*.iso', _('f_iso')) if self.mode == 'guest' else ('*.run;*.iso', _('f_vbox'))
        ff = Gtk.FileFilter(); ff.set_name(nm)
        for p in pt.split(';'): ff.add_pattern(p)
        d.add_filter(ff); d.set_filter(ff); d.set_current_folder('/mnt')
        if d.run() == Gtk.ResponseType.OK:
            self._set_path(d.get_filename()); self.chk.set_active(False)
        d.destroy()

    def _ok(self, w):
        if self.chk.get_active(): return self._pick()
        p = self.ent.get_text().strip()
        if not p: self._err(_('no_opt'))
        elif not os.path.exists(p): self._err(_('no_file'))
        else: self._set_path(p); self._build(p, False)

    def _msg(self, mt, tx):
        def f():
            bt = Gtk.ButtonsType.OK if mt == Gtk.MessageType.INFO else Gtk.ButtonsType.CLOSE
            d = Gtk.MessageDialog(transient_for=self, modal=True, message_type=mt, buttons=bt, text=tx)
            d.run(); d.destroy(); return False
        GLib.idle_add(f)

    def _err(self, m): self._msg(Gtk.MessageType.ERROR, m)
    def _info(self, m): self._msg(Gtk.MessageType.INFO, m)

    def _po(self, t, m):
        def f():
            if self._prog: self._prog.close()
            self._prog = Prog(self, t, m); return False
        GLib.idle_add(f)

    def _pm(self, m): GLib.idle_add(lambda: (self._prog and self._prog.msg(m)) or False)

    def _pc(self):
        def f():
            if self._prog: self._prog.close(); self._prog = None
            return False
        GLib.idle_add(f)

    def _thread(self, fn, *args):
        threading.Thread(target=fn, args=args, daemon=True).start()

    def _pick(self):
        self._po(_('downloading'), _('downloading'))
        def w():
            latest, all_vs = vbox_version_lists(self.mode, self.arch)
            iv = inst_short()
            GLib.idle_add(self._after_pick, latest, all_vs, iv)
        self._thread(w)

    def _after_pick(self, latest, all_vs, iv):
        self._pc()
        if latest: self._verdlg(latest, all_vs, iv)
        else: self._err(_('no_link'))

    def _verdlg(self, latest, all_vs, iv):
        d = VerDlg(self, latest, all_vs, iv)
        r = d.run(); v = d.chosen(); d.destroy()
        if r == Gtk.ResponseType.OK and v: self._dlver(v)

    def _dlver(self, ver):
        self._po(_('downloading'), _('downloading'))
        def w():
            url = vbox_url(ver, self.arch, 'guest' if self.mode == 'guest' else 'vbox')
            if not url:
                GLib.idle_add(self._err, _('no_link')); GLib.idle_add(self._pc); return
            fn = os.path.basename(url)
            dest = os.path.join('/tmp', fn)
            dl = False
            if not (os.path.exists(dest) and os.path.getsize(dest) > 1048576):
                self._pm(f'{_("downloading")} {fn}...')
                try:
                    with urllib.request.urlopen(url, timeout=600) as r, open(dest, 'wb') as f:
                        shutil.copyfileobj(r, f)
                    dl = True
                except Exception as e:
                    safe_rm(dest)
                    GLib.idle_add(self._err, f'{_("dl_fail")}: {e}')
                    GLib.idle_add(self._pc); return
            GLib.idle_add(self._pc)
            GLib.idle_add(self._set_path, dest)
            GLib.idle_add(self._build, dest, dl)
        self._thread(w)

    def _build(self, fp, dl=False):
        self._po(_('building'), _('preparing'))
        def w():
            try: self._do(fp, dl)
            except Exception as e:
                GLib.idle_add(self._pc)
                GLib.idle_add(self._err, f'{_("fail")}: {e}')
        self._thread(w)

    def _file_version(self, fn):
        m = re.search(r'(?:VBoxGuestAdditions[_-]|VirtualBox-|VBox_GAs_)(\d+\.\d+\.\d+(?:[-_.][^\s/\\]+)?)', fn)
        if m:
            clean = re.search(r'(\d+\.\d+\.\d+)', m.group(1))
            if clean: return clean.group(1)
        m = re.search(r'(\d+\.\d+\.\d+)', fn)
        return m.group(1) if m else None

    def _mki(self, bd):
        if self.mode == 'guest':
            ip, ln = os.path.join(bd, 'etc/rc.d/init.d/rc.vbox-add'), 'rc.vbox-add'
            body = '#!/bin/sh\n/sbin/modprobe vboxguest 2>/dev/null\n/sbin/modprobe vboxsf 2>/dev/null\n/sbin/modprobe vboxvideo 2>/dev/null\n/usr/sbin/VBoxService\n'
        else:
            ip, ln = os.path.join(bd, 'etc/rc.d/init.d/rc.virtualbox'), 'rc.virtualbox'
            body = (
                '#!/bin/sh\n'
                'grep -q "^vboxusers:" /etc/group || groupadd -g 1001 vboxusers\n'
                'id guest >/dev/null 2>&1 && usermod -aG vboxusers guest 2>/dev/null\n'
                '/sbin/depmod -a\n'
                '/sbin/modprobe vboxdrv\n'
                '/sbin/modprobe vboxnetadp\n'
                '/sbin/modprobe vboxnetflt\n'
                'mkdir -p /dev/vboxusb\n'
                'chmod 0750 /dev/vboxusb\n'
                'chown root:vboxusers /dev/vboxusb\n'
                'chmod 0755 /opt/VirtualBox/VBoxCreateUSBNode.sh 2>/dev/null\n'
                'chmod -R a+rX /opt/VirtualBox/ExtensionPacks 2>/dev/null\n'
                'udevadm control --reload-rules 2>/dev/null\n'
                'udevadm trigger 2>/dev/null\n'
            )
        # _write_text creates parent dirs
        self._write_text(ip, body, 0o755)
        os.makedirs(os.path.join(bd, 'etc/rc.d/rc4.d'), exist_ok=True)
        cmd(['ln', '-sf', f'/etc/rc.d/init.d/{ln}',
             os.path.join(bd, 'etc/rc.d/rc4.d', 'S99' + ln.replace('rc.', ''))])

    def _link(self, bd, target, link_path):
        if not target or not os.path.exists(target): return
        link_abs = os.path.join(bd, link_path.lstrip('/'))
        os.makedirs(os.path.dirname(link_abs), exist_ok=True)
        try:
            if os.path.lexists(link_abs): os.remove(link_abs)
            os.symlink('/' + os.path.relpath(target, bd), link_abs)
        except: pass

    def _write_text(self, path, text, mode=0o755):
        os.makedirs(os.path.dirname(path), exist_ok=True)
        open(path, 'w').write(text); os.chmod(path, mode)

    def _copy_to(self, src, dst):
        if src and os.path.exists(src):
            os.makedirs(dst, exist_ok=True)
            try: shutil.copy2(src, dst)
            except: pass

    def _payload_root(self, src, markers):
        for m in markers:
            marker = find_file(src, m)
            if marker:
                d = os.path.dirname(marker)
                return os.path.dirname(d) if d != src and os.path.basename(d).lower() in ('bin', 'sbin') else d
        return src

    def _strip_nls(self, opt):
        nls = os.path.join(opt, 'nls')
        if not os.path.isdir(nls): return
        for f in os.listdir(nls):
            if not f.endswith('.qm'): continue
            base = f[:-3]
            if not (base.startswith(('VirtualBox_en', 'qt_en')) or
                    any(base == f'VirtualBox_{l}' or base == f'qt_{l}' for l in self.lang_codes)):
                safe_rm(os.path.join(nls, f))

    def _extract_tar(self, tp, dest):
        try:
            with tarfile.open(tp, 'r:*') as tar: extract_tar_compat(tar, dest)
            return True
        except Exception as e:
            print(f'[vbox-builder] tarfile failed for {os.path.basename(tp)}: {e}')
        r = cmd(['tar', '-xf', tp, '-C', dest], t=600)
        if r.returncode == 0: return True
        print(f'[vbox-builder] system tar failed rc={r.returncode}: {(r.stderr or "")[:300]}')
        return False

    def _extract_tarballs(self, src):
        tballs = find_files_pattern(src, '*.tar.bz2') + find_files_pattern(src, '*.tar.gz')
        print(f'[vbox-builder] tarballs found: {[os.path.basename(t) for t in tballs]}')
        want = 'amd64' if self.arch == 'x86_64' else 'x86'
        tballs.sort(key=lambda t: 0 if want in os.path.basename(t).lower() else 1)
        for tf in tballs:
            td = os.path.join(src, f'ex_{os.path.basename(tf)}')
            os.makedirs(td, exist_ok=True)
            if self._extract_tar(tf, td): return td
        return src

    def _find_mod_src(self, root, mod):
        ml = mod.lower()
        for dp, dn, fn in os.walk(root):
            if 'Makefile' in fn and os.path.basename(dp).lower().startswith(ml): return dp
        for dp, dn, fn in os.walk(root):
            if 'Makefile' in fn and any(x.lower().startswith(ml) and x.lower().endswith('.c') for x in fn):
                return dp
        return None

    def _make_dir(self, d, src, chk):
        kver = os.uname().release
        env = os.environ.copy()
        env.update({'PATH': '/usr/sbin:/usr/bin:/sbin:/bin',
                    'KERN_DIR': '/usr/src/linux', 'KERN_VER': kver, 'VBOX_GUEST_SRC': d})
        parent = os.path.dirname(d)
        attempts = [
            ['make', '-C', d, f'KERN_DIR=/usr/src/linux', f'KERN_VER={kver}'],
            ['make', '-C', d],
            ['make', '-C', parent, f'KERN_DIR=/usr/src/linux', f'KERN_VER={kver}'],
            ['make', '-C', parent],
            ['make', '-C', '/usr/src/linux', f'M={d}', 'modules'],
        ]
        last = None
        for a in attempts:
            r = cmd(a, t=1800, env=env); last = r
            if chk and (find_file(src, chk) or find_files_pattern(src, chk + '*')): return True
        if last is not None:
            print(f'[vbox-builder] make failed for {d}\n  last rc={last.returncode}\n'
                  f'  stdout tail: {(last.stdout or "")[-600:]}\n'
                  f'  stderr tail: {(last.stderr or "")[-600:]}')
        return False

    def _compile_guest(self, src, bd):
        kver = os.uname().release
        dest = os.path.join(bd, 'lib/modules', kver, 'misc')
        os.makedirs(dest, exist_ok=True)
        for mod in ('vboxguest', 'vboxsf', 'vboxvideo'):
            d = self._find_mod_src(src, mod)
            if not d:
                if mod != 'vboxvideo': return False
                continue
            if self._make_dir(d, src, f'{mod}.ko'):
                for ko in find_files_pattern(src, f'{mod}.ko*'):
                    self._copy_to(ko, dest)
            elif mod != 'vboxvideo':
                return False
            else:
                print(f'[vbox-builder] NOTE: {mod} not built (kernel {kver} newer than VBox DRM '
                      f'support); skipped - graphics via VMSVGA/VBoxDRMClient')
        return all(find_files_pattern(dest, f'{m}.ko*') for m in ('vboxguest', 'vboxsf'))

    def _compile_host(self, src, bd):
        kver = os.uname().release
        dest = os.path.join(bd, 'lib/modules', kver, 'misc')
        os.makedirs(dest, exist_ok=True)
        d = None
        for dp, dn, fn in os.walk(src):
            if 'Makefile' in fn and 'vboxhost' in os.path.basename(dp).lower():
                d = dp; break
        if not d:
            for dp, dn, fn in os.walk(src):
                if 'Makefile' in fn and any('vboxdrv' in x.lower() for x in fn):
                    d = dp; break
        print(f'[vbox-builder] vboxhost src dir: {d}')
        if not d: return False
        self._make_dir(d, src, 'vboxdrv.ko')
        for ko in find_files_pattern(src, '*.ko*'):
            self._copy_to(ko, dest)
        return bool(find_files_pattern(dest, 'vboxdrv.ko*'))

    def _install_extpack(self, bd, fv):
        url = vbox_extpack_url(fv)
        if not url:
            print('[vbox-builder] Extension Pack URL not found, skipping')
            return
        self._pm(_('downloading'))
        fn = os.path.basename(url)
        print(f'[vbox-builder] downloading Extension Pack: {fn}')
        dest = os.path.join(tempfile.gettempdir(), fn)
        tmp_ep = tempfile.mkdtemp(prefix='vbox-extpack-')
        try:
            try:
                with urllib.request.urlopen(url, timeout=600) as r, open(dest, 'wb') as f:
                    shutil.copyfileobj(r, f)
            except Exception as e:
                print(f'[vbox-builder] Extension Pack download failed: {e}')
                return
            if not self._extract_tar(dest, tmp_ep):
                print('[vbox-builder] Extension Pack extraction failed')
                return
            # handle archives wrapped in a top-level dir
            root_ep = tmp_ep
            if not os.path.exists(os.path.join(root_ep, 'ExtPack.xml')):
                for sub in sorted(os.listdir(root_ep)):
                    p = os.path.join(root_ep, sub)
                    if os.path.isdir(p) and os.path.exists(os.path.join(p, 'ExtPack.xml')):
                        root_ep = p; break
                else:
                    print('[vbox-builder] ExtPack.xml not found, invalid package')
                    return
            # dir name from ExtPack.xml (spaces -> underscores)
            name = 'Oracle VirtualBox Extension Pack'
            try:
                m = re.search(r'<Name>\s*([^<]+?)\s*</Name>',
                              open(os.path.join(root_ep, 'ExtPack.xml')).read())
                if m: name = m.group(1)
            except Exception as e:
                print(f'[vbox-builder] cannot read ExtPack.xml name: {e}')
            ep_dir = os.path.join(bd, 'opt', 'VirtualBox', 'ExtensionPacks', name.replace(' ', '_'))
            shutil.rmtree(ep_dir, ignore_errors=True)
            os.makedirs(os.path.dirname(ep_dir), exist_ok=True)
            shutil.move(root_ep, ep_dir)
            # drop foreign platforms/arches
            ep_arch = 'amd64' if self.arch == 'x86_64' else 'x86'
            for sub in os.listdir(ep_dir):
                sp = os.path.join(ep_dir, sub)
                if not os.path.isdir(sp): continue
                if sub.startswith(('win.', 'darwin.', 'solaris.', 'os2.')) or \
                   (sub.startswith('linux.') and sub != f'linux.{ep_arch}'):
                    shutil.rmtree(sp, ignore_errors=True)
            # fixes VERR_ACCESS_DENIED
            normalize_perms(ep_dir)
            print(f'[vbox-builder] Extension Pack installed to {ep_dir}')
        finally:
            safe_rm(dest)
            shutil.rmtree(tmp_ep, ignore_errors=True)

    def _stage_guest(self, src, bd, fv):
        self._pm(_('compiling'))
        src = self._extract_tarballs(src)
        if not self._compile_guest(src, bd): return False
        self._pm(_('staging'))
        opt = os.path.join(bd, 'opt', f'VBoxGuestAdditions-{fv}')
        try: shutil.copytree(self._payload_root(src, ['VBoxService']), opt, symlinks=True, dirs_exist_ok=True)
        except: return False
        strip_unwanted(opt); strip_unwanted(bd)
        normalize_perms(opt)
        self._link(bd, find_file(opt, 'VBoxService'), '/usr/sbin/VBoxService')
        self._link(bd, find_file(opt, 'VBoxClient'), '/usr/bin/VBoxClient')
        self._link(bd, find_file(opt, 'VBoxControl'), '/usr/bin/VBoxControl')
        self._link(bd, find_file(opt, 'mount.vboxsf'), '/sbin/mount.vboxsf')

        # VBoxClient --all removed in 7.x
        vboxclient_all = (
            '#!/bin/sh\n'
            'VBoxClient --clipboard\n'
            'VBoxClient --seamless\n'
            'VBoxClient --draganddrop\n'
            'VBoxClient --display\n'
            'VBoxClient --vmsvga-session\n'
        )
        self._write_text(os.path.join(bd, 'usr/bin/VBoxClient-all'), vboxclient_all, 0o755)

        drv = find_file(opt, 'vboxvideo_drv.so')
        if drv: self._copy_to(drv, os.path.join(bd, 'usr', self.lib, 'xorg/modules/drivers'))
        dri = find_file(opt, 'vboxvideo_dri.so')
        if dri:
            self._copy_to(dri, os.path.join(bd, 'usr', self.lib, 'dri'))
            self._copy_to(dri, os.path.join(bd, 'usr', self.lib, 'xorg/modules/dri'))
        for ogl in find_files_pattern(opt, 'VBoxOGL*.so*'):
            self._copy_to(ogl, os.path.join(bd, 'usr', self.lib))

        dtxt = '[Desktop Entry]\nType=Application\nName=VirtualBox Guest Additions\nComment=VirtualBox Guest Additions\nExec=/usr/bin/VBoxClient-all\nTerminal=false\nNoDisplay=true\nX-GNOME-Autostart-enabled=true\n'
        self._write_text(os.path.join(bd, 'etc/xdg/autostart/vboxclient.desktop'), dtxt, 0o644)
        self._write_text(os.path.join(bd, 'usr/share/autostart/vboxclient.desktop'), dtxt, 0o644)

        lxqt_dir = os.path.join(bd, 'etc/xdg/lxqt')
        os.makedirs(lxqt_dir, exist_ok=True)
        self._write_text(os.path.join(lxqt_dir, 'autostart'),
                         '[Autostart]\nVBoxClient-all=/usr/bin/VBoxClient-all\n', 0o644)

        self._write_text(os.path.join(bd, 'etc/profile.d/vboxclient.sh'),
                         '#!/bin/sh\n[ -n "$DISPLAY" ] && /usr/bin/VBoxClient-all 2>/dev/null &\n', 0o755)

        udev_rules_dir = os.path.join(bd, 'etc/udev/rules.d')
        os.makedirs(udev_rules_dir, exist_ok=True)
        udev_rule = (
            'KERNEL=="vboxuser", MODE="0666"\n'
            'KERNEL=="vboxguest", MODE="0666"\n'
        )
        self._write_text(os.path.join(udev_rules_dir, '60-vboxguest.rules'), udev_rule, 0o644)

        self._mki(bd)
        return True

    def _stage_host(self, src, bd, fv):
        self._pm(_('compiling'))
        src = self._extract_tarballs(src)
        if not self._compile_host(src, bd): return False
        self._pm(_('staging'))
        opt = os.path.join(bd, 'opt', 'VirtualBox')
        try: shutil.copytree(self._payload_root(src, ['VirtualBox', 'VBoxManage']), opt,
                             symlinks=True, dirs_exist_ok=True)
        except: return False
        strip_unwanted(opt); self._strip_nls(opt); strip_unwanted(bd)
        # before setuid chmods below, so they survive
        normalize_perms(opt)

        vbox_create_usb = find_file(src, 'VBoxCreateUSBNode.sh')
        if vbox_create_usb:
            self._copy_to(vbox_create_usb, opt)
            print(f'[vbox-builder] VBoxCreateUSBNode.sh copied to {opt}')
        else:
            print('[vbox-builder] WARNING: VBoxCreateUSBNode.sh NOT found - USB passthrough may not work')

        for b in ('VirtualBox', 'VirtualBoxVM', 'VBoxManage', 'VBoxHeadless', 'VBoxSDL',
                  'VBoxNetAdpCtl', 'VBoxNetDHCP', 'VBoxNetNAT', 'VBoxVolInfo', 'rdesktop-vrdp'):
            self._link(bd, find_file(opt, b), f'/usr/bin/{b}')
        vm, vb = os.path.join(opt, 'VirtualBoxVM'), os.path.join(opt, 'VirtualBox')
        if os.path.exists(vm): os.chmod(vm, 0o4755)
        if os.path.exists(vb): os.chmod(vb, 0o755)

        self._write_text(os.path.join(bd, 'usr/share/applications/virtualbox.desktop'),
            '[Desktop Entry]\n'
            'Name=Oracle VM VirtualBox\n'
            'GenericName=Virtual Machine\n'
            'GenericName[ru]=Виртуальная машина\n'
            'GenericName[de]=Virtuelle Maschine\n'
            'GenericName[fr]=Machine virtuelle\n'
            'GenericName[es]=Máquina virtual\n'
            'GenericName[it]=Macchina virtuale\n'
            'GenericName[pt_BR]=Máquina Virtual\n'
            'GenericName[pl]=Maszyna wirtualna\n'
            'GenericName[uk]=Віртуальна машина\n'
            'GenericName[zh_CN]=虚拟机\n'
            'GenericName[ja]=仮想マシン\n'
            'GenericName[tr]=Sanal Makine\n'
            'Type=Application\n'
            'Exec=/usr/bin/VirtualBox %U\n'
            'TryExec=/usr/bin/VirtualBox\n'
            'Icon=virtualbox\n'
            'Categories=Virtualization;Emulator;System;Utility;\n'
            'Comment=Run several virtual systems on a single host computer\n'
            'Comment[ru]=Запуск виртуальных систем на одном компьютере\n'
            'Comment[de]=Virtuelle Systeme auf einem Host-Rechner ausführen\n'
            'Comment[fr]=Exécuter des systèmes virtuels sur un ordinateur hôte\n'
            'Comment[es]=Ejecutar sistemas virtuales en un equipo anfitrión\n'
            'Comment[it]=Esegui sistemi virtuali su un computer host\n'
            'Comment[pt_BR]=Executar sistemas virtuais em um computador host\n'
            'Comment[pl]=Uruchamianie systemów wirtualnych na jednym komputerze\n'
            "Comment[uk]=Запуск віртуальних систем на одному комп'ютері\n"
            'Comment[zh_CN]=在一台主机上运行多个虚拟系统\n'
            'Comment[ja]=ホストコンピュータで仮想システムを実行\n'
            'Comment[tr]=Ana bilgisayar üzerinde sanal sistemler çalıştırın\n'
            'StartupNotify=true\n'
            'MimeType=application/x-virtualbox-vbox;application/x-virtualbox-vbox-extpack;application/x-virtualbox-ova;application/x-virtualbox-ovf;\n',
            0o644)

        icon_src = find_file(opt, 'virtualbox.png') or find_file(opt, 'VBox.png')
        if icon_src:
            icons_dir = os.path.join(bd, 'usr/share/pixmaps')
            os.makedirs(icons_dir, exist_ok=True)
            try: shutil.copy2(icon_src, os.path.join(icons_dir, 'virtualbox.png'))
            except: pass

        udev_rules_dir = os.path.join(bd, 'etc/udev/rules.d')
        os.makedirs(udev_rules_dir, exist_ok=True)
        udev_rule = (
            'KERNEL=="vboxdrv", OWNER="root", GROUP="vboxusers", MODE="0660"\n'
            'KERNEL=="vboxdrvu", OWNER="root", GROUP="root", MODE="0666"\n'
            'KERNEL=="vboxnetctl", OWNER="root", GROUP="vboxusers", MODE="0660"\n'
            'SUBSYSTEM=="usb_device", ACTION=="add", RUN+="/opt/VirtualBox/VBoxCreateUSBNode.sh $major $minor $attr{bDeviceClass} vboxusers"\n'
            'SUBSYSTEM=="usb", ACTION=="add", ENV{DEVTYPE}=="usb_device", RUN+="/opt/VirtualBox/VBoxCreateUSBNode.sh $major $minor $attr{bDeviceClass} vboxusers"\n'
            'SUBSYSTEM=="usb_device", ACTION=="remove", RUN+="/opt/VirtualBox/VBoxCreateUSBNode.sh --remove $major $minor"\n'
            'SUBSYSTEM=="usb", ACTION=="remove", ENV{DEVTYPE}=="usb_device", RUN+="/opt/VirtualBox/VBoxCreateUSBNode.sh --remove $major $minor"\n'
        )
        self._write_text(os.path.join(udev_rules_dir, '10-vboxdrv.rules'), udev_rule, 0o644)

        self._install_extpack(bd, fv)

        self._mki(bd)
        return True

    def _do(self, fp, dl=False):
        ok, free = space_ok('/tmp', 800)
        if not ok:
            self._pc(); self._err(f'{_("low_space")} /tmp\n{_("need")}: 800 MB, {_("free")}: {free} MB'); return
        if not os.path.exists('/usr/src/linux'):
            self._pc(); self._err(_('no_src')); return
        if not shutil.which('gcc') or not shutil.which('make'):
            self._pc(); self._err(_('no_tools')); return
        fv = self._file_version(os.path.basename(fp))
        if not fv and fp == self.cd_src and self.cd_label:
            fv = self._file_version(self.cd_label)
        if not fv:
            self._pc(); self._err(_('no_ver')); return
        self._do_staging(fp, dl, fv)

    def _do_staging(self, fp, dl, fv):
        with tempfile.TemporaryDirectory(prefix='vbox-stage-') as td:
            src, bd, iso = os.path.join(td, 'src'), os.path.join(td, 'build'), os.path.join(td, 'iso')
            for d in (src, bd, iso): os.makedirs(d, exist_ok=True)
            mounted, inst = False, fp
            try:
                self._pm(_('extracting'))
                if self.mode == 'host' and (fp.lower().endswith('.iso') or is_block(fp)):
                    self._pc(); self._err(_('no_file')); return
                run_src = None
                if self.mode == 'guest':
                    if fp.lower().endswith('.iso') or is_block(fp):
                        if not mount_iso(fp, iso):
                            self._pc(); self._err(_('mnt_fail')); return
                        mounted = True
                        run_src = os.path.join(iso, 'VBoxLinuxAdditions.run')
                    elif os.path.isdir(fp):
                        run_src = os.path.join(fp, 'VBoxLinuxAdditions.run')
                if run_src:
                    if not os.path.exists(run_src):
                        self._pc(); self._err(_('no_additions')); return
                    inst = os.path.join(td, 'VBoxLinuxAdditions.run')
                    shutil.copy2(run_src, inst); os.chmod(inst, 0o755)
                env = os.environ.copy()
                env.update({'TERM': 'dumb', 'PATH': '/usr/sbin:/usr/bin:/sbin:/bin', 'SETUP_NOCHECK': '1'})
                ok_ex = False
                for args in ([['sh', inst, '--quiet', '--accept', '--noexec', '--keep',
                               '--noprogress', '--nox11', '--nochown', '--nodiskspace', '--target', src],
                              ['sh', inst, '--accept', '--noexec', '--keep', '--target', src],
                              ['sh', inst, '--noexec', '--keep', '--target', src],
                              ['sh', inst, '--noexec', '--target', src]]):
                    r = cmd(args, t=900, env=env, input_data='yes\n')
                    print(f'[vbox-builder] extract rc={r.returncode}: {" ".join(args)}')
                    if r.returncode != 0:
                        print(f'  stdout: {(r.stdout or "")[:300]}\n  stderr: {(r.stderr or "")[:300]}')
                    if os.path.isdir(src) and os.listdir(src):
                        ok_ex = True; break
                if not ok_ex:
                    self._pc(); self._err(_('stage_fail')); return
                ok_st = self._stage_guest(src, bd, fv) if self.mode == 'guest' else self._stage_host(src, bd, fv)
                if not ok_st:
                    self._pc(); self._err(_('stage_fail')); return
                self._package(bd, fv, dl, fp)
            finally:
                if mounted and os.path.ismount(iso): cmd(['umount', iso], t=20)
                clean_tmp_files()

    def _build_ok(self, bd):
        if self.mode == 'guest':
            return (os.path.exists(os.path.join(bd, 'etc/rc.d/init.d/rc.vbox-add')) and
                    bool(find_file(bd, 'VBoxService')) and
                    bool(find_files_pattern(bd, 'vboxguest.ko*')))
        return (os.path.exists(os.path.join(bd, 'etc/rc.d/init.d/rc.virtualbox')) and
                bool(find_file(bd, 'VirtualBox')) and
                bool(find_files_pattern(bd, 'vboxdrv.ko*')))

    def _package(self, bd, fv, dl=False, fp=None):
        if not self._build_ok(bd):
            self._pc(); self._err(_('fail')); return False
        self._pm(_('mkmod'))
        pn = 'vbox-guest' if self.mode == 'guest' else 'VirtualBox'
        out = f'/tmp/{pn}-{fv}-porteus-{pver()}-{self.arch}-1prt.xzm'
        safe_rm(out)
        self._mkpkg(bd, pn, fv)
        r = cmd(['dir2xzm', bd, out], t=600)
        if r.returncode != 0:
            err_msg = (r.stderr or '').strip() or (r.stdout or '').strip() or _('fail')
            self._pc(); self._err(f'{_("fail")}: {err_msg}'); return False
        self._pc()
        if dl and fp: safe_rm(fp)
        GLib.idle_add(self._result, out)
        return True

    def _mkpkg(self, bd, pn, ver):
        pd = os.path.join(bd, 'var/lib/pkgtools/packages')
        os.makedirs(pd, exist_ok=True)
        nm = f'{pn}-{ver}-porteus-{pver()}-{self.arch}-1prt'
        with open(os.path.join(pd, nm), 'w') as f:
            f.write(f'PACKAGE NAME: {nm}\nPACKAGE DESCRIPTION:\n')
            for l in [f'{pn}: {pn} (virtualization software)', f'{pn}:',
                      f'{pn}: Oracle VM VirtualBox is a cross-platform virtualization',
                      f'{pn}: application that allows you to run multiple operating systems',
                      f'{pn}: simultaneously on a single physical computer.', f'{pn}:',
                      f'{pn}: https://www.virtualbox.org/', f'{pn}:', 'FILE LIST:']:
                f.write(l + '\n')
            for root, _, files in os.walk(bd):
                for fn in files:
                    rel = os.path.relpath(os.path.join(root, fn), bd)
                    if not rel.startswith('var/lib/pkgtools/packages'):
                        f.write(rel + '\n')

    def _result(self, out):
        d = Gtk.MessageDialog(transient_for=self, modal=True, message_type=Gtk.MessageType.INFO,
                              buttons=Gtk.ButtonsType.NONE, text=f'{_("ready")}\n{out}')
        d.set_property('secondary-text', f'{_("action")}\n{_("more")}')
        d.add_button(_('mv_act'), 1)
        d.add_button(_('open_dir'), 2)
        d.add_button(_('close'), 3)
        r = d.run(); d.destroy()
        if r == 1: self._thread(self._move_act, out)
        elif r == 2: fm_open('/tmp')
        else: Gtk.main_quit()
        return False

    def _move_act(self, out):
        md = mod_dir()
        if not md:
            GLib.idle_add(self._err, _('no_moddir')); return
        try:
            mn, dest = os.path.basename(out), os.path.join(md, os.path.basename(out))
            deactivate_all_wait(self.mode)
            if not os.path.isfile(out):
                GLib.idle_add(self._err, f'{_("mv_fail")}: {out}'); return
            GLib.idle_add(self._po, _('building'), _('mv_act'))
            shutil.move(out, dest); os.sync()
            r = cmd(['activate', dest], t=60)
            GLib.idle_add(self._pc)
            extra = _('reboot_guest') if self.mode == 'guest' else _('reboot_host')
            if r.returncode == 0:
                GLib.idle_add(self._info, f'{mn}\n{_("moved")} {md}\n{_("act_ok")}\n\n{extra}')
            else:
                GLib.idle_add(self._err, f'{_("act_fail")}\n{(r.stderr or "").strip() or (r.stdout or "").strip() or _("act_ng")}')
        except Exception as e:
            GLib.idle_add(self._pc)
            GLib.idle_add(self._err, f'{_("mv_fail")}: {e}')

def parse_args():
    mode = fp = None
    if len(sys.argv) > 1:
        a = sys.argv[1].lower()
        if a in ('vbox', 'host'): mode = 'host'
        elif a in ('vboxguest', 'guest'): mode = 'guest'
    if len(sys.argv) > 2 and os.path.isfile(sys.argv[2]): fp = sys.argv[2]
    return mode, fp

if __name__ == '__main__':
    if os.getuid() != 0: print(_('root')); sys.exit(1)
    if not os.environ.get('DISPLAY'): print(_('x11')); sys.exit(1)
    pix = load_icon(48)
    if pix:
        try: Gtk.Window.set_default_icon(pix)
        except: pass
    signal.signal(signal.SIGINT, lambda *_: Gtk.main_quit())
    GLib.set_prgname('vbox-builder')
    GLib.set_application_name(_('title'))
    cm, cf = parse_args()
    App(cm, cf).show_all()
    Gtk.main()
Linux 6.6.11-porteus #1 SMP PREEMPT_DYNAMIC Sun Jan 14 12:07:37 MSK 2024 x86_64 Intel(R) Xeon(R) CPU E3-1270 v6 @ 3.80GHz GenuineIntel GNU/Linux
MS-7A12 » [AMD/ATI] Navi 23 [Radeon RX 6600] [1002:73ff] (rev c7) » Vengeance LPX 16GB DDR4 K2 3200MHz C16

swissluxtom
White ninja
White ninja
Posts: 23
Joined: 11 Mar 2026, 11:12
Distribution: Porteus 5.01

Porteus-v5.01 bug reports

Post#333 by swissluxtom » 13 Aug 2026, 22:28

Blaze, I can confirm the solution per '60-vboxguest.rules' with
'KERNEL=="vboxuser", MODE="0666"\n'
'KERNEL=="vboxguest", MODE="0666"\n'

now gives a working VirtualBox clipboard and 'File Manager' (via Guest Control) integration.

I'm unsure the latest PY changes of
adding 'vboxadd' to 'etc/passwd.append' & 'etc/group.append'
has any effect. Does the (Porteus) *.append mechanism actually work?
(I see inside the generated GAs XZM module, the two *.append files are there)

One thing about the VirtualBox GAs 'Shared folder' (vboxsf) feature.
I got sporadic data corruption; but after reading their forum
https://forums.virtualbox.org/viewtopic.php?t=105367
I have to assume its still a bug, specific to 32-bit Guest Linux with a 64-bit host; nobody at VirtualBox wants to fix it.

User avatar
Blaze
DEV Team
DEV Team
Posts: 4108
Joined: 28 Dec 2010, 11:31
Distribution: ⟰ Porteus current ☯ all DEs ☯
Location: ☭ Russian Federation, Lipetsk region, Dankov
Contact:

Porteus-v5.01 bug reports

Post#334 by Blaze » 14 Aug 2026, 05:09

I updated script (look at the previous post).
Yep I removed 'etc/passwd.append' & 'etc/group.append'
Thank you very much for testing.
Linux 6.6.11-porteus #1 SMP PREEMPT_DYNAMIC Sun Jan 14 12:07:37 MSK 2024 x86_64 Intel(R) Xeon(R) CPU E3-1270 v6 @ 3.80GHz GenuineIntel GNU/Linux
MS-7A12 » [AMD/ATI] Navi 23 [Radeon RX 6600] [1002:73ff] (rev c7) » Vengeance LPX 16GB DDR4 K2 3200MHz C16

User avatar
Ed_P
Contributor
Contributor
Posts: 9308
Joined: 06 Feb 2013, 22:12
Distribution: Cinnamon 5.01 ISO
Location: Western NY, USA

Porteus-v5.01 bug reports

Post#335 by Ed_P » 18 Aug 2026, 21:51

When did this start?? :shock:
/tmp/chg_errlog wrote:The changes location is not writable. This may be because the changes path is located on a read-only filesystem.
It makes ISO booting impossible. :(

Code: Select all

~#  uname -a
Linux porteus.example.net 7.0.12-porteus #1 SMP PREEMPT_DYNAMIC Thu Jun 11 13:40:14 Local time zone must  x86_64 13th Gen Intel(R) Core(TM) i5-13420H GenuineIntel GNU/Linux

~#  cat /etc/porteus/*
001-core.xzm:20260618
002-xorg.xzm:20260618
002-xtra.xzm:20260618
003-cinnamon.xzm:20240924
initrd.xz:20260309

~#  ls -l /mnt/live/memory/images
000-kernel.xzm : Jun 11 09:43
001-core.xzm : Jun 18 08:52
002-xorg.xzm : Jun 18 08:58
002-xtra.xzm : Jun 18 09:02
003-cinnamon.xzm : Dec 29 2023

User avatar
AcnapyxoB
Contributor
Contributor
Posts: 317
Joined: 24 Dec 2014, 10:15
Distribution: Porteus
Location: Tellus

Porteus-v5.01 bug reports

Post#336 by AcnapyxoB » 20 Aug 2026, 13:49

On last update-vbox-live 2026-08-15 USB support is broken.

I build VirtualBox 6.2.16 and noticed that it's been significantly reduced (maybe the problem is somewhere there)!
I tried with adding VBoxGuestAdditions.iso and Extension pack and executed 'usermod -aG vboxusers guest' without success :cry:
Porteus v5.01 KDE x86_64

User avatar
Blaze
DEV Team
DEV Team
Posts: 4108
Joined: 28 Dec 2010, 11:31
Distribution: ⟰ Porteus current ☯ all DEs ☯
Location: ☭ Russian Federation, Lipetsk region, Dankov
Contact:

Porteus-v5.01 bug reports

Post#337 by Blaze » 20 Aug 2026, 19:26

Hi AcnapyxoB.
Well try these commands as root user:

Code: Select all

cat > /etc/udev/rules.d/60-vboxusb.rules << 'EOF'
# VirtualBox USB device permissions
SUBSYSTEM=="usb_device", GROUP="vboxusers", MODE="0664"
SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", GROUP="vboxusers", MODE="0664"
EOF

usermod -aG vboxusers guest
udevadm control --reload-rules
udevadm trigger
Linux 6.6.11-porteus #1 SMP PREEMPT_DYNAMIC Sun Jan 14 12:07:37 MSK 2024 x86_64 Intel(R) Xeon(R) CPU E3-1270 v6 @ 3.80GHz GenuineIntel GNU/Linux
MS-7A12 » [AMD/ATI] Navi 23 [Radeon RX 6600] [1002:73ff] (rev c7) » Vengeance LPX 16GB DDR4 K2 3200MHz C16

User avatar
ncmprhnsbl
DEV Team
DEV Team
Posts: 4635
Joined: 20 Mar 2012, 03:42
Distribution: v5.1-alpha*-64bit
Location: australia
Contact:

Porteus-v5.01 bug reports

Post#338 by ncmprhnsbl » 21 Aug 2026, 00:18

Ed_P wrote:
18 Aug 2026, 21:51
When did this start??
:/ there was a change to initrd that enabled changes cheat from porteus/porteus-v5.*-$ARCH.cfg to work .. .could be an unintended side effect ..not seeing how as yet..
what's the output of your

Code: Select all

cat /proc/cmdline
Forum Rules : https://forum.porteus.org/viewtopic.php?f=35&t=44

User avatar
Ed_P
Contributor
Contributor
Posts: 9308
Joined: 06 Feb 2013, 22:12
Distribution: Cinnamon 5.01 ISO
Location: Western NY, USA

Porteus-v5.01 bug reports

Post#339 by Ed_P » 21 Aug 2026, 02:58

ncmprhnsbl wrote:
21 Aug 2026, 00:18
what's the output of your

Code: Select all

cat /proc/cmdline
From my grub2 menu:

Code: Select all

set timeout=30
set eds_menu=1
# Set the default variable to the submenu entry you prefer
set default=11

     set linux_folder=/porteus5.0

#     set iso="/ISOs/Porteus/Porteus-CINNAMON-v5.01-x86_64-240923.iso"
#     set iso="/ISOs/Porteus/Porteus-CINNAMON-v5.01-x86_64-241211.iso"
#     set iso="/ISOs/Porteus/Porteus-CINNAMON-v5.01-x86_64-250130.iso"
#     set iso="/ISOs/Porteus/Porteus-CINNAMON-v5.01-x86_64-250404.iso"
#     set iso="/ISOs/Porteus/Porteus-CINNAMON-v5.01-x86_64-250622.iso"
#     set iso="/ISOs/Porteus/Porteus-CINNAMON-v5.01-x86_64-260116.iso"
#     set iso="/ISOs/Porteus/Porteus-CINNAMON-v5.01-x86_64-260421.iso"
#     set iso="/ISOs/Porteus/Porteus-CINNAMON-v5.01-x86_64-260710.iso"
     set iso="/ISOs/Porteus/Porteus-CINNAMON-v5.01-x86_64.iso"

     set SaveDat=50save.dat

if [ ! -z "${loop}" ]; then
   loopback -d loop
fi     
menuentry " Porteus 5.01 ISO - changes "  --class slackware   --class user-icon-porteus  {

     set Changes=$SaveDat   # porteussave.dat 
     set bootparms="volume=33 reboot=cold extramod=$linux_folder/Modules \
       changes=EXIT:$linux_folder/changes/$Changes            \ 
       noload=kde;jinn;lxqt;xfce;lxde;mate;open;dat.xzm;mychanges;autonet;pipe;menu1" # ;mychanges"

     search -f $iso --set=root
     loopback loop $iso
     linux (loop)/boot/syslinux/vmlinuz      from=$iso $bootparms
#     linux $linux_folder/BootModules/vmlinuz from=$iso $bootparms
     initrd (loop)/boot/syslinux/initrd.xz
#     initrd $linux_folder/initrd/initrd.xz
     }  

User avatar
AcnapyxoB
Contributor
Contributor
Posts: 317
Joined: 24 Dec 2014, 10:15
Distribution: Porteus
Location: Tellus

Porteus-v5.01 bug reports

Post#340 by AcnapyxoB » 21 Aug 2026, 12:21

Blaze wrote:
20 Aug 2026, 19:26
Well try these commands as root user:
Hi Blaze, unsuccessful...

VirtualBox error:

Code: Select all

Failed to enumerate host USB devices.
Could not load the Host USB Proxy service: VERR_NOT_FOUND.
Result Code:
NS_ERROR_FAILURE (0x00004005)
Component:
HostWrap
Interface:
IHost {d5dcece0-b202-4416-a138-03502784cc07}
As I mentioned above, the new vbox module is half the size (~55MB vs ~116MB)
And as for the file contents: 888 files vs 193 files!
Porteus v5.01 KDE x86_64

User avatar
Blaze
DEV Team
DEV Team
Posts: 4108
Joined: 28 Dec 2010, 11:31
Distribution: ⟰ Porteus current ☯ all DEs ☯
Location: ☭ Russian Federation, Lipetsk region, Dankov
Contact:

Porteus-v5.01 bug reports

Post#341 by Blaze » 21 Aug 2026, 15:54

AcnapyxoB,
I removed old video drivers and other unnecessary junk :hammer:
Ok, try to build and test Virtualbox via this script Porteus-v5.01 bug reports (Пост Blaze #105697)
After build Virtualbox copy a new Vbox xzm module to the modules folder and reboot Porteus. Try to test your USB devices.
Linux 6.6.11-porteus #1 SMP PREEMPT_DYNAMIC Sun Jan 14 12:07:37 MSK 2024 x86_64 Intel(R) Xeon(R) CPU E3-1270 v6 @ 3.80GHz GenuineIntel GNU/Linux
MS-7A12 » [AMD/ATI] Navi 23 [Radeon RX 6600] [1002:73ff] (rev c7) » Vengeance LPX 16GB DDR4 K2 3200MHz C16

User avatar
AcnapyxoB
Contributor
Contributor
Posts: 317
Joined: 24 Dec 2014, 10:15
Distribution: Porteus
Location: Tellus

Porteus-v5.01 bug reports

Post#342 by AcnapyxoB » 21 Aug 2026, 16:31

Blaze, as always, thank you, but nothing has changed.

Anyway, it's no big deal; I can use the old VirtualBox for now.
Porteus v5.01 KDE x86_64

User avatar
Blaze
DEV Team
DEV Team
Posts: 4108
Joined: 28 Dec 2010, 11:31
Distribution: ⟰ Porteus current ☯ all DEs ☯
Location: ☭ Russian Federation, Lipetsk region, Dankov
Contact:

Porteus-v5.01 bug reports

Post#343 by Blaze » 21 Aug 2026, 17:29

AcnapyxoB, can you share log of these commands (run as root user)

Code: Select all

echo "=== LOADED MODULES ===" && lsmod | grep vbox
echo "=== MODULE FILES ===" && ls /lib/modules/$(uname -r)/misc/vbox*.ko* 2>/dev/null || echo "NO MODULE FILES FOUND"
echo "=== GROUP ===" && grep vboxusers /etc/group
echo "=== UDEV RULES ===" && cat /etc/udev/rules.d/60-vboxusb.rules 2>/dev/null || echo "NO UDEV RULES"
echo "=== VBOX VERSION ===" && VBoxManage --version
echo "=== USB HOST ===" && VBoxManage list usbhost 2>&1
echo "=== DMESG ===" && dmesg | grep -i vbox | tail -10
Thanks
Linux 6.6.11-porteus #1 SMP PREEMPT_DYNAMIC Sun Jan 14 12:07:37 MSK 2024 x86_64 Intel(R) Xeon(R) CPU E3-1270 v6 @ 3.80GHz GenuineIntel GNU/Linux
MS-7A12 » [AMD/ATI] Navi 23 [Radeon RX 6600] [1002:73ff] (rev c7) » Vengeance LPX 16GB DDR4 K2 3200MHz C16

User avatar
AcnapyxoB
Contributor
Contributor
Posts: 317
Joined: 24 Dec 2014, 10:15
Distribution: Porteus
Location: Tellus

Porteus-v5.01 bug reports

Post#344 by AcnapyxoB » 21 Aug 2026, 17:42

Blaze wrote:
21 Aug 2026, 17:29
can you share log of these commands (run as root user)

Code: Select all

=== LOADED MODULES ===
vboxnetflt             28672  0
vboxnetadp             24576  0
vboxdrv               397312  2 vboxnetadp,vboxnetflt
=== MODULE FILES ===
/lib/modules/7.0.12-porteus/misc/vboxdrv.ko     /lib/modules/7.0.12-porteus/misc/vboxnetflt.ko
/lib/modules/7.0.12-porteus/misc/vboxnetadp.ko
=== GROUP ===
vboxusers:x:998:guest
=== UDEV RULES ===
# VirtualBox USB device permissions
SUBSYSTEM=="usb_device", GROUP="vboxusers", MODE="0664"
SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", GROUP="vboxusers", MODE="0664"
=== VBOX VERSION ===
7.2.16r174877
=== USB HOST ===
VBoxManage: warning: Could not load the Host USB Proxy service: VERR_NOT_FOUND
VBoxManage: warning: Details: code Unknown Status 0x4005 (0x4005), component HostWrap, interface IHost, callee nsISupports
VBoxManage: warning: Context: "COMGETTER(USBDevices)(ComSafeArrayAsOutParam(CollPtr))" at line 526 of file VBoxManageList.cpp
Host USB Devices:

<none>

=== DMESG ===
[   22.960452] vboxdrv: loading out-of-tree module taints kernel.
[   22.960457] vboxdrv: module verification failed: signature and/or required key missing - tainting kernel
[   22.963230] vboxdrv: Found 8 processor cores/threads
[   22.979840] vboxdrv: TSC mode is Invariant, tentative frequency 1800001181 Hz
[   22.979844] vboxdrv: Successfully loaded version 7.2.16 r174877 (interface 0x00390002)
[   22.996535] VBoxNetAdp: Successfully started.
[   23.018581] VBoxNetFlt: Successfully started.
Porteus v5.01 KDE x86_64

User avatar
ncmprhnsbl
DEV Team
DEV Team
Posts: 4635
Joined: 20 Mar 2012, 03:42
Distribution: v5.1-alpha*-64bit
Location: australia
Contact:

Porteus-v5.01 bug reports

Post#345 by ncmprhnsbl » 22 Aug 2026, 08:38

Ed_P wrote:
21 Aug 2026, 02:58
From my grub2 menu:
:) ..i'd prefer cat /proc/cmdline since that's what initrd is dealing with ..
i've having trouble understanding how changes=EXIT:/porteus5.0/changes/50save.dat (translating from your grub.cfg) would be found from the booting iso ... presuming that / is the root where grub is and for the iso / is the iso ...

@Blaze & AcnapyxoB,
in my arch setup, this is my 60-vboxdrv-rules :

Code: Select all

KERNEL=="vboxdrv", OWNER="root", GROUP="root", MODE="0600"
KERNEL=="vboxdrvu", OWNER="root", GROUP="root", MODE="0666"
KERNEL=="vboxnetctl", OWNER="root", GROUP="root", MODE="0600"

SUBSYSTEM=="usb_device", ACTION=="add", RUN+="/usr/share/virtualbox/VBoxCreateUSBNode.sh $major $minor $attr{bDeviceClass} vboxusers"
SUBSYSTEM=="usb", ACTION=="add", ENV{DEVTYPE}=="usb_device", RUN+="/usr/share/virtualbox/VBoxCreateUSBNode.sh $major $minor $attr{bDeviceClass} vboxusers"
SUBSYSTEM=="usb_device", ACTION=="remove", RUN+="/usr/share/virtualbox/VBoxCreateUSBNode.sh --remove $major $minor"
SUBSYSTEM=="usb", ACTION=="remove", ENV{DEVTYPE}=="usb_device", RUN+="/usr/share/virtualbox/VBoxCreateUSBNode.sh --remove $major $minor"
and that VBoxCreateUSBNode.sh looks like this:

Code: Select all

#! /bin/sh
# $Id: VBoxCreateUSBNode.sh $ */
## @file
# VirtualBox USB Proxy Service, Linux Specialization.
# udev helper for creating and removing device nodes for VirtualBox USB devices
#

#
# Copyright (C) 2010-2025 Oracle and/or its affiliates.
#
# This file is part of VirtualBox base platform packages, as
# available from https://www.virtualbox.org.
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation, in version 3 of the
# License.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, see <https://www.gnu.org/licenses>.
#
# SPDX-License-Identifier: GPL-3.0-only
#

# Constant, from the USB specifications
usb_class_hub=09

do_remove=0
case "$1" in "--remove")
  do_remove=1; shift;;
esac
bus=`expr "$2" '/' 128 + 1`
device=`expr "$2" '%' 128 + 1`
class="$3"
group="$4"
devdir="`printf "/dev/vboxusb/%.3d" $bus`"
devpath="`printf "/dev/vboxusb/%.3d/%.3d" $bus $device`"
case "$do_remove" in
  0)
  if test -n "$class" -a "$class" = "$usb_class_hub"
  then
      exit 0
  fi
  case "$group" in "") group="vboxusers";; esac
  mkdir /dev/vboxusb -m 0750 2>/dev/null
  chown root:$group /dev/vboxusb 2>/dev/null
  mkdir "$devdir" -m 0750 2>/dev/null
  chown root:$group "$devdir" 2>/dev/null
  mknod "$devpath" c $1 $2 -m 0660 2>/dev/null
  chown root:$group "$devpath" 2>/dev/null
  ;;
  1)
  rm -f "$devpath"
  ;;
esac
creating or removing /dev directories owned root:vboxusers
Forum Rules : https://forum.porteus.org/viewtopic.php?f=35&t=44

Post Reply