diff --git a/blend b/blend
index 4328181..570a068 100755
--- a/blend
+++ b/blend
@@ -87,19 +87,14 @@ def error(err):
distro_map = {
- 'arch': 'docker.io/library/archlinux',
- 'almalinux-9': 'quay.io/almalinux/almalinux:9',
- 'crystal-linux': 'registry.getcryst.al/crystal/misc/docker:latest',
- 'debian': 'docker.io/library/debian:latest',
- 'fedora-38': 'docker.io/library/fedora:38',
- 'kali-linux': 'docker.io/kalilinux/kali-rolling',
- 'neurodebian-bookworm': 'docker.io/library/neurodebian:nd120',
- 'rocky-linux': 'docker.io/rockylinux/rockylinux:9',
- 'ubuntu-22.04': 'docker.io/library/ubuntu:22.04',
- 'ubuntu-23.04': 'docker.io/library/ubuntu:23.04'
+ 'arch-linux': 'docker.io/library/archlinux',
+ 'debian': 'quay.io/toolbx-images/debian-toolbox:testing',
+ 'fedora-39': 'registry.fedoraproject.org/fedora-toolbox:39',
+ 'centos': 'quay.io/toolbx-images/centos-toolbox:latest',
+ 'ubuntu-22.04': 'quay.io/toolbx/ubuntu-toolbox:22.04',
}
-default_distro = 'arch'
+default_distro = 'arch-linux'
def get_distro():
@@ -133,11 +128,17 @@ def check_container(name):
def check_container_status(name):
- return host_get_output("podman inspect --type container " + name + " --format \"{{.State.Status}}\"")
+ if os.environ.get('SUDO_USER') == None:
+ return host_get_output("podman inspect --type container " + name + " --format \"{{.State.Status}}\"")
+ else:
+ return host_get_output(f"sudo -u {os.environ.get('SUDO_USER')} podman inspect --type container " + name + " --format \"{{.State.Status}}\"")
def core_start_container(name, new_container=False):
- subprocess.call(['podman', 'start', name],
+ sudo = []
+ if os.environ.get('SUDO_USER') != None:
+ sudo = ['sudo', '-u', os.environ.get('SUDO_USER')]
+ subprocess.call([*sudo, 'podman', 'start', name],
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
start_time = time.time() - 1000 # workaround
@@ -148,11 +149,15 @@ def core_start_container(name, new_container=False):
subprocess.call(['podman', 'logs', '--since', str(start_time), name])
exit(1)
- logproc = pexpect.spawn(
- 'podman', args=['logs', '-f', '--since', str(start_time), name], timeout=300)
+ if os.environ.get('SUDO_USER') == None:
+ logproc = pexpect.spawn(
+ 'podman', args=['logs', '-f', '--since', str(start_time), name], timeout=3600)
+ else:
+ logproc = pexpect.spawn(
+ 'sudo', args=['-u', os.environ.get('SUDO_USER'), 'podman', 'logs', '-f', '--since', str(start_time), name], timeout=3600)
logproc.logfile_read = sys.stdout.buffer
- logproc.expect('Completed container setup.')
+ logproc.expect('Started container.')
logproc.terminate()
diff --git a/blend-settings/src/internal/js/android.js b/blend-settings/src/internal/js/android.js
index f999b58..ae216b3 100644
--- a/blend-settings/src/internal/js/android.js
+++ b/blend-settings/src/internal/js/android.js
@@ -50,7 +50,7 @@ function install_aurora_store() {
let aurora_store_worker = new Worker(
`data:text/javascript,
require('child_process').spawnSync('sh', ['-c', 'mkdir -p ~/.cache/blend-settings; rm -f ~/.cache/blend-settings/aurora.apk'])
- let s1 = require('child_process').spawnSync('sh', ['-c', 'wget -O ~/.cache/blend-settings/aurora.apk https://gitlab.com/AuroraOSS/AuroraStore/uploads/bbc1bd5a77ab2b40bbf288ccbef8d1f0/AuroraStore_4.1.1.apk']).status
+ let s1 = require('child_process').spawnSync('sh', ['-c', 'wget -O ~/.cache/blend-settings/aurora.apk https://f-droid.org/repo/com.aurora.store_58.apk']).status
if (s1 != 0) {
postMessage('failed')
} else {
@@ -194,4 +194,4 @@ $('#automatic-state-toggle').on('change', () => {
}
}
}
-});
\ No newline at end of file
+});
diff --git a/blend-settings/src/internal/js/system.js b/blend-settings/src/internal/js/system.js
index ef7f3c5..25644ab 100644
--- a/blend-settings/src/internal/js/system.js
+++ b/blend-settings/src/internal/js/system.js
@@ -18,6 +18,16 @@ function update_system() {
}
function check_system_update() {
+ if (require('fs').existsSync('/.update')) {
+ document.getElementById('update-btn').onclick = () => {
+ require('child_process').spawnSync('reboot')
+ }
+
+ document.getElementById('update-btn').textContent = 'Reboot'
+ document.getElementById('update-btn').disabled = false
+
+ return;
+ }
let start_update_worker = new Worker(
`data:text/javascript,
let s = require('child_process').spawnSync('systemctl', ['is-active', '--quiet', 'akshara-system-update']).status
diff --git a/blend-settings/src/package-installer.html b/blend-settings/src/package-installer.html
index e70380f..a20640b 100644
--- a/blend-settings/src/package-installer.html
+++ b/blend-settings/src/package-installer.html
@@ -97,18 +97,14 @@
document.getElementById('source_select').innerHTML = `
-
-
-
`
} else if (package_name.endsWith('.rpm')) {
document.getElementById('packaging-format').src = '../static/RPM.svg'
package_type = 'rpm'
document.getElementById('source_select').innerHTML = `
-
-
-
+
+
`
} else if (package_name.endsWith('.apk')) {
document.getElementById('packaging-format').src = '../static/APK.svg'
@@ -117,15 +113,14 @@
require('fs').stat('/var/lib/waydroid/waydroid.prop', (err, stat) => {
if (err != null) {
- document.getElementById('install-button').outerHTML = "
You'll need to initialize Android app support from the blendOS Settings app first.
"
+ document.getElementById('install-button').outerHTML = "You'll need to initialize Android app support from the System app first.
"
}
})
} else if (package_name.includes('.pkg.tar')) {
document.getElementById('packaging-format').src = '../static/PKG.svg'
package_type = 'pkg'
document.getElementById('source_select').innerHTML = `
-
-
+
`
}
diff --git a/blend-settings/src/pages/containers.html b/blend-settings/src/pages/containers.html
index d7bf67b..54806c2 100644
--- a/blend-settings/src/pages/containers.html
+++ b/blend-settings/src/pages/containers.html
@@ -10,16 +10,11 @@
diff --git a/init-blend b/init-blend
index ed00527..ecad72e 100755
--- a/init-blend
+++ b/init-blend
@@ -62,29 +62,7 @@ while true; do
esac
done
-cat << 'EOF'
-
-
- ▄▄▄▄ ██▓ ▓█████ ███▄ █ ▓█████▄
-▓█████▄ ▓██▒ ▓█ ▀ ██ ▀█ █ ▒██▀ ██▌
-▒██▒ ▄██▒██░ ▒███ ▓██ ▀█ ██▒░██ █▌
-▒██░█▀ ▒██░ ▒▓█ ▄ ▓██▒ ▐▌██▒░▓█▄ ▌
-░▓█ ▀█▓░██████▒░▒████▒▒██░ ▓██░░▒████▓
-░▒▓███▀▒░ ▒░▓ ░░░ ▒░ ░░ ▒░ ▒ ▒ ▒▒▓ ▒
-▒░▒ ░ ░ ░ ▒ ░ ░ ░ ░░ ░░ ░ ▒░ ░ ▒ ▒
- ░ ░ ░ ░ ░ ░ ░ ░ ░ ░ ░
- ░ ░ ░ ░ ░ ░ ░
- ░ ░
-
-===================
- Credits
-===================
-
-* NVIDIA driver support - Luca Di Maio (from Distrobox)
-EOF
-
-echo
-echo 'Starting blend... (this may take a few minutes)'
+echo 'Starting container... (this may take a few minutes)'
echo
bmount() {
@@ -408,7 +386,7 @@ for full_file in /usr/share/applications/*.desktop; do
chmod 755 "${HOME}/.local/share/applications/blend;${CONTAINER_NAME};${file}"
done
-echo "Completed container setup."
+echo "Started container."
mkdir -p /usr/share/applications /usr/bin
inotifywait -m /usr/share/applications /usr/bin -e create,delete,move 2>/dev/null |
diff --git a/user b/user
new file mode 100755
index 0000000..ed8d6a4
--- /dev/null
+++ b/user
@@ -0,0 +1,253 @@
+#!/usr/bin/python3
+
+import os
+import yaml
+import click
+import subprocess
+
+from urllib.request import urlopen
+
+
+class colors:
+ reset = '\033[0m'
+ bold = '\033[01m'
+ disable = '\033[02m'
+ underline = '\033[04m'
+ reverse = '\033[07m'
+ strikethrough = '\033[09m'
+ invisible = '\033[08m'
+
+ class fg:
+ black = '\033[30m'
+ red = '\033[31m'
+ green = '\033[32m'
+ orange = '\033[33m'
+ blue = '\033[34m'
+ purple = '\033[35m'
+ cyan = '\033[36m'
+ lightgrey = '\033[37m'
+ darkgrey = '\033[90m'
+ lightred = '\033[91m'
+ lightgreen = '\033[92m'
+ yellow = '\033[93m'
+ lightblue = '\033[94m'
+ pink = '\033[95m'
+ lightcyan = '\033[96m'
+
+ rainbow = [lightred, orange, yellow,
+ lightgreen, lightcyan, blue, purple]
+ seq = 0
+
+ def random(self):
+ if self.seq == 7:
+ self.seq = 0
+ self.seq += 1
+ return self.rainbow[self.seq - 1]
+
+ def clear_seq(self):
+ self.seq = 0
+
+ class bg:
+ black = '\033[40m'
+ red = '\033[41m'
+ green = '\033[42m'
+ orange = '\033[43m'
+ blue = '\033[44m'
+ purple = '\033[45m'
+ cyan = '\033[46m'
+ lightgrey = '\033[47m'
+
+
+fg = colors.fg()
+
+
+def info(msg):
+ print(colors.bold + fg.cyan + '[INFO] ' +
+ colors.reset + msg + colors.reset)
+
+
+def print_list(msg):
+ print(colors.bold + fg.random() + '[LIST] ' +
+ colors.reset + msg + colors.reset)
+
+
+def modrun(msg):
+ print(colors.bold + fg.green + '[MODRUN] ' +
+ colors.reset + msg + colors.reset)
+
+
+def container_msg(msg):
+ print(colors.bold + fg.purple + '[CONTAINER] ' +
+ colors.reset + msg + colors.reset)
+
+
+def association_msg(msg):
+ print(colors.bold + fg.random() + '[ASSOCIATION] ' +
+ colors.reset + msg + colors.reset)
+
+
+def warn(warning):
+ print(colors.bold + fg.yellow + '[WARNING] ' +
+ colors.reset + warning + colors.reset)
+
+
+def error(err):
+ print(colors.bold + fg.red + '[ERROR] ' +
+ colors.reset + err + colors.reset)
+
+
+def proceed():
+ print(colors.bold + fg.red + '[QUESTION] ' +
+ colors.reset + 'would you like to proceed?' + colors.reset)
+ info(f'(press {colors.bold}ENTER{colors.reset} to proceed, or {colors.bold}^C{colors.reset}/{colors.bold}^D{colors.reset} to cancel)')
+ input()
+
+
+@click.group("cli")
+def cli():
+ """Manage user operations using the user utility on blendOS."""
+
+
+def main():
+ cli(prog_name="user")
+
+
+@cli.command("associate")
+@click.argument('association')
+@click.argument('container')
+def associate_binary(association, container):
+ '''
+ Create an association (for example, apt -> ubuntu)
+ '''
+
+ if not os.path.exists(os.path.expanduser(f'~/.local/bin/blend_bin/{association}.{container}')):
+ error(f'{colors.bold}{association}.{container}{colors.reset} does not exist')
+ exit()
+ if os.path.isfile(os.path.expanduser('~/.local/bin/blend_bin/.associations')):
+ subprocess.run(['sed', '-i', f's/^{association}\\x0//g',
+ os.path.expanduser('~/.local/bin/blend_bin/.associations')])
+ with open(os.path.expanduser('~/.local/bin/blend_bin/.associations'), 'a+') as f:
+ f.write(f'{association}\0{container}\n')
+ _exists = os.path.exists(os.path.expanduser(
+ f'~/.local/bin/blend_bin/{association}'))
+ subprocess.run(['ln', '-sf', f'{association}.{container}',
+ os.path.expanduser(f'~/.local/bin/blend_bin/{association}')])
+ association_msg(('modified' if _exists else 'created') +
+ f' {colors.bold}{association} -> {container}{colors.reset}')
+
+
+@cli.command("dissociate")
+@click.argument('association')
+def associate_binary(association):
+ '''
+ Remove an association
+ '''
+
+ if not os.path.exists(os.path.expanduser(f'~/.local/bin/blend_bin/{association}')):
+ error(f'{colors.bold}{association}{colors.reset} does not exist')
+ exit()
+ if os.path.isfile(os.path.expanduser('~/.local/bin/blend_bin/.associations')):
+ subprocess.run(['sed', '-i', f's/^{association}\\x0//g',
+ os.path.expanduser('~/.local/bin/blend_bin/.associations')])
+ subprocess.run(
+ ['rm', '-f', os.path.expanduser(f'~/.local/bin/blend_bin/{association}')])
+ association_msg(f'dissociated {colors.bold}{association}')
+
+
+@cli.command("create-container")
+@click.argument('container_name')
+@click.argument('distro', default='arch')
+def create_container(container_name, distro):
+ '''
+ Create a container
+ '''
+ if distro not in ('arch', 'almalinux-9', 'crystal-linux', 'debian', 'fedora-38', 'kali-linux', 'neurodebian-bookworm', 'rocky-linux', 'ubuntu-22.04', 'ubuntu-23.04'):
+ error(
+ f'distro {colors.bold}{distro}{colors.reset} not supported')
+ if subprocess.run(['podman', 'container', 'exists', container_name], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL).returncode == 0:
+ error(f'container {colors.bold}{container_name}{colors.reset} already exists')
+ exit(1)
+ subprocess.run(['blend', 'create-container', '-cn', container_name, '-d', distro])
+
+
+@cli.command("delete-container")
+@click.argument('container')
+def delete_container(container):
+ '''
+ Delete a container
+ '''
+ if subprocess.run(['podman', 'container', 'exists', container], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL).returncode != 0:
+ error(f'container {colors.bold}{container}{colors.reset} does not exist')
+ exit(1)
+ subprocess.run(['blend', 'remove-container', container])
+
+
+@cli.command("shell")
+@click.argument('container')
+def shell(container):
+ '''
+ Enter a shell inside a container
+ '''
+ if subprocess.run(['podman', 'container', 'exists', container], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL).returncode != 0:
+ error(f'container {colors.bold}{container}{colors.reset} does not exist')
+ exit(1)
+ creation_env = os.environ.copy()
+ creation_env['BLEND_NO_CHECK'] = 'true'
+ subprocess.run(['blend', 'enter', '-cn', container], env=creation_env)
+
+
+@cli.command("exec")
+@click.argument('container')
+@click.argument('cmds', nargs=-1, required=True)
+def exec_c(container, cmds):
+ '''
+ Run a command inside a container
+ '''
+ if subprocess.run(['podman', 'container', 'exists', container], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL).returncode != 0:
+ error(f'container {colors.bold}{container}{colors.reset} does not exist')
+ exit(1)
+ creation_env = os.environ.copy()
+ creation_env['BLEND_NO_CHECK'] = 'true'
+ cmds = [ cmd.replace('\\-', '-') for cmd in cmds]
+ subprocess.run(['blend', 'enter', '-cn', container, '--', *cmds], env=creation_env)
+
+
+@cli.command("install")
+@click.argument('container')
+@click.argument('pkgs', nargs=-1, required=True)
+def install_c(container, pkgs):
+ '''
+ Install a package inside a container
+ '''
+ if os.path.isfile(os.path.expanduser(f'~/.local/bin/blend_bin/apt.{container}')):
+ subprocess.run([f'sudo.{container}', 'apt', 'update'])
+ subprocess.run([f'sudo.{container}', 'apt', 'install', *pkgs])
+ elif os.path.isfile(os.path.expanduser(f'~/.local/bin/blend_bin/dnf.{container}')):
+ subprocess.run([f'sudo.{container}', 'dnf', 'install', *pkgs])
+ elif os.path.isfile(os.path.expanduser(f'~/.local/bin/blend_bin/pacman.{container}')):
+ subprocess.run([f'sudo.{container}', 'pacman', '-Syu', *pkgs])
+ else:
+ error(f'container {colors.bold}{container}{colors.reset} does not exist')
+ exit(1)
+
+
+@cli.command("remove")
+@click.argument('container')
+@click.argument('pkgs', nargs=-1, required=True)
+def remove_c(container, pkgs):
+ '''
+ Remove a package inside a container
+ '''
+ if os.path.isfile(os.path.expanduser(f'~/.local/bin/blend_bin/apt.{container}')):
+ subprocess.run([f'sudo.{container}', 'apt', 'purge', *pkgs])
+ elif os.path.isfile(os.path.expanduser(f'~/.local/bin/blend_bin/dnf.{container}')):
+ subprocess.run([f'sudo.{container}', 'dnf', 'remove', *pkgs])
+ elif os.path.isfile(os.path.expanduser(f'~/.local/bin/blend_bin/pacman.{container}')):
+ subprocess.run([f'sudo.{container}', 'pacman', '-Rcns', *pkgs])
+ else:
+ error(f'container {colors.bold}{container}{colors.reset} does not exist')
+ exit(1)
+
+
+if __name__ == '__main__':
+ main()