diff --git a/scripts/cowork-vm-service.js b/scripts/cowork-vm-service.js index 74735d31..34b8343b 100644 --- a/scripts/cowork-vm-service.js +++ b/scripts/cowork-vm-service.js @@ -52,6 +52,32 @@ const LOG_FILE = path.join( process.env.HOME || '/tmp', '.config', 'Claude', 'logs', 'cowork_vm_daemon.log' ); + +/** + * Locate virtiofsd binary. Debian installs it to /usr/libexec/virtiofsd which + * is outside PATH, so we check well-known locations after checking PATH. + * @returns {string|null} Absolute path to virtiofsd, or null if not found. + */ +function findVirtiofsd() { + const candidates = [ + '/usr/libexec/virtiofsd', + '/usr/lib/qemu/virtiofsd', + ]; + try { + const result = execFileSync('which', ['virtiofsd'], { + encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'], + }).trim(); + if (result) return result; + } catch (_) { /* not in PATH */ } + for (const p of candidates) { + try { + fs.accessSync(p, fs.constants.X_OK); + return p; + } catch (_) { /* not found */ } + } + return null; +} + function formatArgs(args) { return args.map(a => typeof a === 'string' ? a : JSON.stringify(a)) .join(' '); @@ -980,22 +1006,28 @@ class KvmBackend extends BackendBase { const initrdPath = path.join(VM_BASE_DIR, 'initrd'); // Start virtiofsd for home directory share (if available) - try { - const virtiofsSock = path.join(this.sessionDir, 'virtiofs.sock'); - this.virtiofsdProcess = spawnProcess('virtiofsd', [ - `--socket-path=${virtiofsSock}`, - '-o', `source=${os.homedir()}`, - '-o', 'cache=auto', - ], { - stdio: ['ignore', 'pipe', 'pipe'], - }); - this.virtiofsdProcess.on('error', (err) => { - log('KvmBackend: virtiofsd error:', err.message); + const virtiofsdBin = findVirtiofsd(); + if (virtiofsdBin) { + try { + const virtiofsSock = path.join(this.sessionDir, 'virtiofs.sock'); + this.virtiofsdProcess = spawnProcess(virtiofsdBin, [ + `--socket-path=${virtiofsSock}`, + '-o', `source=${os.homedir()}`, + '-o', 'cache=auto', + ], { + stdio: ['ignore', 'pipe', 'pipe'], + }); + this.virtiofsdProcess.on('error', (err) => { + log('KvmBackend: virtiofsd error:', err.message); + this.virtiofsdProcess = null; + }); + log(`KvmBackend: virtiofsd started, socket=${virtiofsSock}`); + } catch (e) { + log(`KvmBackend: virtiofsd not available: ${e.message}`); this.virtiofsdProcess = null; - }); - log(`KvmBackend: virtiofsd started, socket=${virtiofsSock}`); - } catch (e) { - log(`KvmBackend: virtiofsd not available: ${e.message}`); + } + } else { + log('KvmBackend: virtiofsd not found, skipping home directory share'); this.virtiofsdProcess = null; } diff --git a/scripts/launcher-common.sh b/scripts/launcher-common.sh index 20b49948..72278c0e 100755 --- a/scripts/launcher-common.sh +++ b/scripts/launcher-common.sh @@ -470,12 +470,35 @@ print(len(servers)) _info 'Fix: sudo modprobe vhost_vsock' fi + # virtiofsd — Debian installs it outside PATH (/usr/libexec or /usr/lib/qemu) + local _virtiofsd_bin='' + if command -v virtiofsd &>/dev/null; then + _virtiofsd_bin=$(command -v virtiofsd) + else + local _vfsd_candidate + for _vfsd_candidate in \ + /usr/libexec/virtiofsd \ + /usr/lib/qemu/virtiofsd + do + if [[ -x $_vfsd_candidate ]]; then + _virtiofsd_bin="$_vfsd_candidate" + break + fi + done + fi + + if [[ -n $_virtiofsd_bin ]]; then + _pass "virtiofsd: found ($_virtiofsd_bin)" + else + _warn 'virtiofsd: not found' + _info "Fix: $(_cowork_pkg_hint "$_distro_id" virtiofsd)" + fi + # Check required tools: label, binary, pkg-hint name local _tool_label _tool_bin _tool_pkg for _tool_label in \ 'QEMU:qemu-system-x86_64:qemu' \ 'socat:socat:socat' \ - 'virtiofsd:virtiofsd:virtiofsd' \ 'bubblewrap:bwrap:bubblewrap' do _tool_bin="${_tool_label#*:}"