Add missing changes

This commit is contained in:
Rudra Saraswat 2023-05-03 00:02:57 +05:30
parent eea89f26cc
commit 451ffc2667
3 changed files with 270 additions and 301 deletions

196
blend
View file

@ -1,32 +1,38 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
# Copyright (C) 2023 Rudra Saraswat # Copyright (C) 2023 Rudra Saraswat
# #
# This file is part of blend. # This file is part of blend.
# #
# blend is free software: you can redistribute it and/or modify # blend is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or # the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version. # (at your option) any later version.
# #
# blend is distributed in the hope that it will be useful, # blend is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of # but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details. # GNU General Public License for more details.
# #
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with blend. If not, see <http://www.gnu.org/licenses/>. # along with blend. If not, see <http://www.gnu.org/licenses/>.
import os, sys, getpass, time import os
import sys
import glob
import time
import shutil import shutil
import socket import socket
import getpass
import pexpect import pexpect
import argparse import argparse
import subprocess import subprocess
__version = '2.0.0' __version = '2.0.0'
### Colors # Colors
class colors: class colors:
reset = '\033[0m' reset = '\033[0m'
bold = '\033[01m' bold = '\033[01m'
@ -63,17 +69,22 @@ class colors:
cyan = '\033[46m' cyan = '\033[46m'
lightgrey = '\033[47m' lightgrey = '\033[47m'
### END # END
# Helper functions
### Helper functions
def info(msg): def info(msg):
print (colors.bold + colors.fg.cyan + '>> i: ' + colors.reset + colors.bold + msg + colors.reset) print(colors.bold + colors.fg.cyan + '>> i: ' +
colors.reset + colors.bold + msg + colors.reset)
def error(err): def error(err):
print (colors.bold + colors.fg.red + '>> e: ' + colors.reset + colors.bold + err + colors.reset) print(colors.bold + colors.fg.red + '>> e: ' +
colors.reset + colors.bold + err + colors.reset)
# END
### END
distro_map = { distro_map = {
'arch': 'docker.io/library/archlinux', 'arch': 'docker.io/library/archlinux',
@ -84,6 +95,7 @@ distro_map = {
default_distro = 'arch' default_distro = 'arch'
def get_distro(): def get_distro():
try: try:
return distro_map[args.distro] return distro_map[args.distro]
@ -91,9 +103,10 @@ def get_distro():
error(f"{args.distro} isn't supported by blend.") error(f"{args.distro} isn't supported by blend.")
exit() exit()
def list_containers(): def list_containers():
_list = subprocess.run(['podman', 'ps', '-a', '--no-trunc', '--size', '--format', _list = subprocess.run(['podman', 'ps', '-a', '--no-trunc', '--size', '--format',
'{{.Names}}:{{.Mounts}}'], stdout=subprocess.PIPE).stdout.decode('utf-8').strip() '{{.Names}}:{{.Mounts}}'], stdout=subprocess.PIPE).stdout.decode('utf-8').strip()
if len(_list) == 0: if len(_list) == 0:
info('No containers. Create one by installing a package (`blend install hello`), or manually create one (`blend create-container arch`).') info('No containers. Create one by installing a package (`blend install hello`), or manually create one (`blend create-container arch`).')
else: else:
@ -103,21 +116,25 @@ def list_containers():
print(f"{colors.bold}{i}.{colors.reset} {container.split(':')[0]}") print(f"{colors.bold}{i}.{colors.reset} {container.split(':')[0]}")
return False return False
def check_container(name): def check_container(name):
_list = subprocess.run(['podman', 'ps', '-a', '--no-trunc', '--size', '--format', _list = subprocess.run(['podman', 'ps', '-a', '--no-trunc', '--size', '--format',
'{{.Names}}:{{.Mounts}}'], stdout=subprocess.PIPE).stdout.decode('utf-8').strip() '{{.Names}}:{{.Mounts}}'], stdout=subprocess.PIPE).stdout.decode('utf-8').strip()
for container in _list.splitlines(keepends=False): for container in _list.splitlines(keepends=False):
if ('blend' in container.split(':')[1] or 'distrobox' in container.split(':')[1]) and name.strip() == container.split(':')[0]: if ('blend' in container.split(':')[1] or 'distrobox' in container.split(':')[1]) and name.strip() == container.split(':')[0]:
return True return True
return False return False
def check_container_status(name):
def check_container_status(name):
return host_get_output("podman inspect --type container " + name + " --format \"{{.State.Status}}\"") return host_get_output("podman inspect --type container " + name + " --format \"{{.State.Status}}\"")
def core_start_container(name):
subprocess.call(['podman', 'start', name], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
start_time = time.time() - 1000 # workaround def core_start_container(name):
subprocess.call(['podman', 'start', name],
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
start_time = time.time() - 1000 # workaround
if check_container_status(name) != 'running': if check_container_status(name) != 'running':
print('') print('')
error('the entry point failed to run; try again later') error('the entry point failed to run; try again later')
@ -125,12 +142,14 @@ def core_start_container(name):
subprocess.call(['podman', 'logs', '--since', str(start_time), name]) subprocess.call(['podman', 'logs', '--since', str(start_time), name])
exit(1) exit(1)
logproc = pexpect.spawn('podman', args=['logs', '-f', '--since', str(start_time), name], timeout=300) logproc = pexpect.spawn(
'podman', args=['logs', '-f', '--since', str(start_time), name], timeout=300)
logproc.logfile_read = sys.stdout.buffer logproc.logfile_read = sys.stdout.buffer
logproc.expect('Completed container setup.') logproc.expect('Completed container setup.')
logproc.terminate() logproc.terminate()
def core_create_container(): def core_create_container():
name = args.container_name name = args.container_name
distro = args.distro distro = args.distro
@ -145,32 +164,39 @@ def core_create_container():
podman_command.extend(['--network', 'host']) podman_command.extend(['--network', 'host'])
podman_command.extend(['--security-opt', 'label=disable']) podman_command.extend(['--security-opt', 'label=disable'])
podman_command.extend(['--user', 'root:root', '--pid', 'host']) podman_command.extend(['--user', 'root:root', '--pid', 'host'])
podman_command.extend(['--label', 'manager=blend']) # identify as blend container # identify as blend container
podman_command.extend(['--label', 'manager=blend'])
# Env variables # Env variables
podman_command.extend(['--env', 'HOME=' + os.path.expanduser('~')]) podman_command.extend(['--env', 'HOME=' + os.path.expanduser('~')])
podman_command.extend(['--env', 'CONTAINER_NAME=' + name])
# Volumes # Volumes
podman_command.extend(['--volume', '/:/run/host:rslave']) podman_command.extend(['--volume', '/:/run/host:rslave'])
podman_command.extend(['--volume', '/tmp:/tmp:rslave']) podman_command.extend(['--volume', '/tmp:/tmp:rslave'])
podman_command.extend(['--volume', f"{os.path.expanduser('~')}:{os.path.expanduser('~')}:rslave"]) podman_command.extend(
podman_command.extend(['--volume', f"/run/user/{os.geteuid()}:/run/user/{os.geteuid()}:rslave"]) ['--volume', f"{os.path.expanduser('~')}:{os.path.expanduser('~')}:rslave"])
podman_command.extend(
['--volume', f"/run/user/{os.geteuid()}:/run/user/{os.geteuid()}:rslave"])
# Volumes (config files) # Volumes (config files)
podman_command.extend(['--volume', f"/etc/hosts:/etc/hosts:ro"]) podman_command.extend(['--volume', f"/etc/hosts:/etc/hosts:ro"])
podman_command.extend(['--volume', f"/etc/localtime:/etc/localtime:ro"]) podman_command.extend(['--volume', f"/etc/localtime:/etc/localtime:ro"])
podman_command.extend(['--volume', f"/etc/resolv.conf:/etc/resolv.conf:ro"]) podman_command.extend(
['--volume', f"/etc/resolv.conf:/etc/resolv.conf:ro"])
# Volumes (files and tools) # Volumes (files and tools)
podman_command.extend(['--volume', '/usr/bin/init-blend:/usr/bin/init-blend:ro', podman_command.extend(['--volume', '/usr/bin/init-blend:/usr/bin/init-blend:ro',
'--entrypoint', '/usr/bin/init-blend']) # our entrypoint '--entrypoint', '/usr/bin/init-blend']) # our entrypoint
podman_command.extend(['--volume', '/usr/bin/host-blend:/usr/bin/host-blend:ro']) # and the tool to run commands on the host # and the tool to run commands on the host
podman_command.extend(
['--volume', '/usr/bin/host-blend:/usr/bin/host-blend:ro'])
podman_command.extend(['--volume', '/var/log/journal']) podman_command.extend(['--volume', '/var/log/journal'])
podman_command.extend(['--mount', 'type=devpts,destination=/dev/pts', podman_command.extend(['--mount', 'type=devpts,destination=/dev/pts',
'--userns', 'keep-id', '--userns', 'keep-id',
'--annotation', 'run.oci.keep_original_groups=1']) '--annotation', 'run.oci.keep_original_groups=1'])
podman_command.extend([get_distro()]) podman_command.extend([get_distro()])
# User (for init-blend) # User (for init-blend)
@ -189,17 +215,24 @@ def core_create_container():
core_start_container(name) core_start_container(name)
core_get_output = lambda cmd: subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.DEVNULL).stdout.decode('UTF-8').strip()
host_get_output = lambda cmd: subprocess.run(['bash', '-c', cmd], def core_get_output(cmd): return subprocess.run(
stdout=subprocess.PIPE, stderr=subprocess.DEVNULL).stdout.decode('UTF-8').strip() cmd, stdout=subprocess.PIPE, stderr=subprocess.DEVNULL).stdout.decode('UTF-8').strip()
def host_get_output(cmd): return subprocess.run(['bash', '-c', cmd],
stdout=subprocess.PIPE, stderr=subprocess.DEVNULL).stdout.decode('UTF-8').strip()
def core_get_retcode(cmd): return subprocess.run(['podman', 'exec', '--user', getpass.getuser(), '-it', args.container_name, 'bash', '-c', cmd],
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL).returncode
core_get_retcode = lambda cmd: subprocess.run(['podman', 'exec', '--user', getpass.getuser(), '-it', args.container_name, 'bash', '-c', cmd],
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL).returncode
def core_run_container(cmd): def core_run_container(cmd):
if os.getcwd() == os.path.expanduser('~') or os.getcwd().startswith(os.path.expanduser('~') + '/'): if os.getcwd() == os.path.expanduser('~') or os.getcwd().startswith(os.path.expanduser('~') + '/'):
subprocess.call(['podman', 'exec', '--user', getpass.getuser(), '-w', os.getcwd(), '-it', args.container_name, 'bash', '-c', cmd]) subprocess.call(['podman', 'exec', '--user', getpass.getuser(),
'-w', os.getcwd(), '-it', args.container_name, 'bash', '-c', cmd])
def core_install_pkg(pkg): def core_install_pkg(pkg):
if args.distro == 'fedora-rawhide': if args.distro == 'fedora-rawhide':
@ -210,8 +243,10 @@ def core_install_pkg(pkg):
elif args.distro == 'arch': elif args.distro == 'arch':
if core_get_retcode('[ -f /usr/bin/yay ]') != 0: if core_get_retcode('[ -f /usr/bin/yay ]') != 0:
core_run_container('sudo pacman -Sy') core_run_container('sudo pacman -Sy')
core_run_container('sudo pacman --noconfirm -Syu --needed git base-devel') core_run_container(
core_run_container('TEMP_DIR="$(mktemp -d)"; cd "${TEMP_DIR}"; git clone https://aur.archlinux.org/yay.git; cd yay; makepkg --noconfirm -si; rm -rf "${TEMP_DIR}"') 'sudo pacman --noconfirm -Syu --needed git base-devel')
core_run_container(
'TEMP_DIR="$(mktemp -d)"; cd "${TEMP_DIR}"; git clone https://aur.archlinux.org/yay.git; cd yay; makepkg --noconfirm -si; rm -rf "${TEMP_DIR}"')
core_run_container(f'yay -Sy') core_run_container(f'yay -Sy')
if args.noconfirm == True: if args.noconfirm == True:
core_run_container(f'yay --noconfirm -Syu {pkg}') core_run_container(f'yay --noconfirm -Syu {pkg}')
@ -224,6 +259,7 @@ def core_install_pkg(pkg):
else: else:
core_run_container(f'sudo apt-get install {pkg}') core_run_container(f'sudo apt-get install {pkg}')
def core_remove_pkg(pkg): def core_remove_pkg(pkg):
if args.distro == 'fedora-rawhide': if args.distro == 'fedora-rawhide':
if args.noconfirm == True: if args.noconfirm == True:
@ -242,6 +278,7 @@ def core_remove_pkg(pkg):
core_run_container(f'sudo apt-get purge {pkg}') core_run_container(f'sudo apt-get purge {pkg}')
core_run_container(f'sudo apt-get autoremove --purge -y {pkg}') core_run_container(f'sudo apt-get autoremove --purge -y {pkg}')
def core_search_pkg(pkg): def core_search_pkg(pkg):
if args.distro == 'fedora-rawhide': if args.distro == 'fedora-rawhide':
core_run_container(f'dnf search {pkg}') core_run_container(f'dnf search {pkg}')
@ -252,6 +289,7 @@ def core_search_pkg(pkg):
core_run_container(f'sudo apt-get update') core_run_container(f'sudo apt-get update')
core_run_container(f'apt-cache search {pkg}') core_run_container(f'apt-cache search {pkg}')
def core_show_pkg(pkg): def core_show_pkg(pkg):
if args.distro == 'fedora-rawhide': if args.distro == 'fedora-rawhide':
core_run_container(f'dnf info {pkg}') core_run_container(f'dnf info {pkg}')
@ -262,6 +300,7 @@ def core_show_pkg(pkg):
core_run_container(f'sudo apt-get update') core_run_container(f'sudo apt-get update')
core_run_container(f'apt-cache show {pkg}') core_run_container(f'apt-cache show {pkg}')
def install_blend(): def install_blend():
if len(args.pkg) == 0: if len(args.pkg) == 0:
error('no packages to install') error('no packages to install')
@ -272,6 +311,7 @@ def install_blend():
core_create_container() core_create_container()
core_install_pkg(pkg) core_install_pkg(pkg)
def remove_blend(): def remove_blend():
if len(args.pkg) == 0: if len(args.pkg) == 0:
error('no packages to remove') error('no packages to remove')
@ -282,6 +322,7 @@ def remove_blend():
error(f"container {args.container_name} doesn't exist") error(f"container {args.container_name} doesn't exist")
core_remove_pkg(pkg) core_remove_pkg(pkg)
def search_blend(): def search_blend():
if len(args.pkg) == 0: if len(args.pkg) == 0:
error('no packages to search for') error('no packages to search for')
@ -291,6 +332,7 @@ def search_blend():
error(f"container {args.container_name} doesn't exist") error(f"container {args.container_name} doesn't exist")
core_search_pkg(pkg) core_search_pkg(pkg)
def show_blend(): def show_blend():
if len(args.pkg) == 0: if len(args.pkg) == 0:
error('no packages to show') error('no packages to show')
@ -301,6 +343,7 @@ def show_blend():
error(f"container {args.container_name} doesn't exist") error(f"container {args.container_name} doesn't exist")
core_show_pkg(pkg) core_show_pkg(pkg)
def sync_blends(): def sync_blends():
if args.distro == 'fedora-rawhide': if args.distro == 'fedora-rawhide':
core_run_container(f'dnf makecache') core_run_container(f'dnf makecache')
@ -309,6 +352,7 @@ def sync_blends():
elif args.distro.startswith('ubuntu-'): elif args.distro.startswith('ubuntu-'):
core_run_container(f'sudo apt-get update') core_run_container(f'sudo apt-get update')
def update_blends(): def update_blends():
if args.distro == 'fedora-rawhide': if args.distro == 'fedora-rawhide':
if args.noconfirm == True: if args.noconfirm == True:
@ -329,38 +373,42 @@ def update_blends():
else: else:
error(f'distribution {args.distro} is not supported') error(f'distribution {args.distro} is not supported')
def enter_container(): def enter_container():
if os.environ.get('BLEND_NO_CHECK') == None: podman_args = ['--env', 'LC_ALL=C.UTF-8']
if not check_container(args.container_name):
core_create_container()
if check_container_status(args.container_name) != 'running':
core_start_container(args.container_name)
podman_args = []
sudo = [] sudo = []
if os.environ.get('SUDO_USER') == None: if os.environ.get('SUDO_USER') == None:
podman_args = ['--user', getpass.getuser()] podman_args = ['--user', getpass.getuser()]
else: else:
sudo = ['sudo', '-u', os.environ.get('SUDO_USER'), f'PATH={os.path.expanduser("~/.local/share/bin/blend_bin")}:/usr/bin'] sudo = ['sudo', '-u', os.environ.get(
'SUDO_USER'), f'PATH={os.path.expanduser("~/.local/share/bin/blend_bin")}:/usr/bin']
for name, val in os.environ.items(): for name, val in os.environ.items():
if name not in ['LANG', 'LC_CTYPE', 'PATH', 'HOST', 'HOSTNAME', 'SHELL'] and not name.startswith('_'): if name not in ['LANG', 'LC_CTYPE', 'LC_ALL', 'PATH', 'HOST', 'HOSTNAME', 'SHELL'] and not name.startswith('_'):
podman_args.append('--env') podman_args.append('--env')
podman_args.append(name + '=' + val) podman_args.append(name + '=' + val)
if os.environ.get('BLEND_COMMAND') == None or os.environ.get('BLEND_COMMAND') == '': if os.environ.get('BLEND_COMMAND') == None or os.environ.get('BLEND_COMMAND') == '':
if args.pkg == []: if args.pkg == []:
if os.getcwd() == os.path.expanduser('~') or os.getcwd().startswith(os.path.expanduser('~') + '/'): if os.getcwd() == os.path.expanduser('~') or os.getcwd().startswith(os.path.expanduser('~') + '/'):
exit(subprocess.call([*sudo, 'podman', 'exec', *podman_args, '-w', os.getcwd(), '-it', args.container_name, 'bash'])) exit(subprocess.call([*sudo, 'podman', 'exec', *podman_args,
'-w', os.getcwd(), '-it', args.container_name, 'bash']))
else: else:
exit(subprocess.call([*sudo, 'podman', 'exec', *podman_args, '-w', '/run/host' + os.getcwd(), '-it', args.container_name, 'bash'])) exit(subprocess.call([*sudo, 'podman', 'exec', *podman_args, '-w',
'/run/host' + os.getcwd(), '-it', args.container_name, 'bash']))
else: else:
if os.getcwd() == os.path.expanduser('~') or os.getcwd().startswith(os.path.expanduser('~') + '/'): if os.getcwd() == os.path.expanduser('~') or os.getcwd().startswith(os.path.expanduser('~') + '/'):
exit(subprocess.call([*sudo, 'podman', 'exec', *podman_args, '-w', os.getcwd(), '-it', args.container_name, *args.pkg])) exit(subprocess.call([*sudo, 'podman', 'exec', *podman_args,
'-w', os.getcwd(), '-it', args.container_name, *args.pkg]))
else: else:
exit(subprocess.call([*sudo, 'podman', 'exec', *podman_args, '-w', '/run/host' + os.getcwd(), '-it', args.container_name, *args.pkg])) exit(subprocess.call([*sudo, 'podman', 'exec', *podman_args, '-w',
'/run/host' + os.getcwd(), '-it', args.container_name, *args.pkg]))
else: else:
if os.getcwd() == os.path.expanduser('~') or os.getcwd().startswith(os.path.expanduser('~') + '/'): if os.getcwd() == os.path.expanduser('~') or os.getcwd().startswith(os.path.expanduser('~') + '/'):
exit(subprocess.call([*sudo, 'podman', 'exec', *podman_args, '-w', os.getcwd(), '-it', args.container_name, 'bash', '-c', os.environ.get('BLEND_COMMAND')])) exit(subprocess.call([*sudo, 'podman', 'exec', *podman_args, '-w', os.getcwd(
), '-it', args.container_name, 'bash', '-c', os.environ.get('BLEND_COMMAND')]))
else: else:
exit(subprocess.call([*sudo, 'podman', 'exec', *podman_args, '-w', '/run/host' + os.getcwd(), '-it', args.container_name, 'bash'])) exit(subprocess.call([*sudo, 'podman', 'exec', *podman_args, '-w',
'/run/host' + os.getcwd(), '-it', args.container_name, 'bash']))
def create_container(): def create_container():
for container in args.pkg: for container in args.pkg:
@ -369,15 +417,22 @@ def create_container():
args.distro = container args.distro = container
core_create_container() core_create_container()
def remove_container(): def remove_container():
for container in args.pkg: for container in args.pkg:
info(f'removing container {container}') info(f'removing container {container}')
subprocess.run(['podman', 'stop', container], stderr=subprocess.DEVNULL, stdout=subprocess.DEVNULL) subprocess.run(['podman', 'stop', container],
subprocess.run(['podman', 'rm', '-f', container], stdout=subprocess.DEVNULL) stderr=subprocess.DEVNULL, stdout=subprocess.DEVNULL)
subprocess.run(['podman', 'rm', '-f', container],
stdout=subprocess.DEVNULL)
for bin in os.listdir(os.path.expanduser('~/.local/bin/blend_bin')):
if bin.endswith(f'.{container}'):
os.remove(os.path.join(os.path.expanduser('~/.local/bin/blend_bin'), bin))
def start_containers(): def start_containers():
_list = subprocess.run(['podman', 'ps', '-a', '--no-trunc', '--size', '--format', _list = subprocess.run(['podman', 'ps', '-a', '--no-trunc', '--size', '--format',
'{{.Names}}:{{.Mounts}}'], stdout=subprocess.PIPE).stdout.decode('utf-8').strip() '{{.Names}}:{{.Mounts}}'], stdout=subprocess.PIPE).stdout.decode('utf-8').strip()
if len(_list) == 0: if len(_list) == 0:
info('No containers. Create one by installing a package (`blend install hello`), or manually create one (`blend create-container -d arch`).') info('No containers. Create one by installing a package (`blend install hello`), or manually create one (`blend create-container -d arch`).')
for container in _list.splitlines(keepends=False): for container in _list.splitlines(keepends=False):
@ -385,6 +440,7 @@ def start_containers():
info(f'starting container {container}') info(f'starting container {container}')
subprocess.call(['podman', 'start', container]) subprocess.call(['podman', 'start', container])
if shutil.which('podman') is None: if shutil.which('podman') is None:
error("podman isn't installed, which is a hard dep") error("podman isn't installed, which is a hard dep")
exit(1) exit(1)
@ -407,21 +463,27 @@ epilog = f'''
parser = argparse.ArgumentParser(description=description, usage=argparse.SUPPRESS, parser = argparse.ArgumentParser(description=description, usage=argparse.SUPPRESS,
epilog=epilog, formatter_class=argparse.RawTextHelpFormatter) epilog=epilog, formatter_class=argparse.RawTextHelpFormatter)
command_map = { 'enter': enter_container, command_map = {'enter': enter_container,
'exec': enter_container, 'exec': enter_container,
'create-container': core_create_container, 'create-container': core_create_container,
'remove-container': remove_container, 'remove-container': remove_container,
'list-containers': list_containers, 'list-containers': list_containers,
'start-containers': start_containers, 'start-containers': start_containers,
'sync': sync_blends, 'sync': sync_blends,
'help': 'help', 'help': 'help',
'version': 'version' } 'version': 'version'}
parser.add_argument('command', choices=command_map.keys(), help=argparse.SUPPRESS) parser.add_argument('command', choices=command_map.keys(),
parser.add_argument('pkg', action='store', type=str, nargs='*', help=argparse.SUPPRESS) help=argparse.SUPPRESS)
parser.add_argument('-cn', '--container-name', action='store', nargs=1, metavar='CONTAINER NAME', help=argparse.SUPPRESS) parser.add_argument('pkg', action='store', type=str,
parser.add_argument('-y', '--noconfirm', action='store_true', help=argparse.SUPPRESS) nargs='*', help=argparse.SUPPRESS)
parser.add_argument('-d', '--distro', action='store', nargs=1, metavar='DISTRO', help=argparse.SUPPRESS) parser.add_argument('-cn', '--container-name', action='store',
parser.add_argument('-v', '--version', action='version', version=f'%(prog)s {__version}', help=argparse.SUPPRESS) nargs=1, metavar='CONTAINER NAME', help=argparse.SUPPRESS)
parser.add_argument('-y', '--noconfirm',
action='store_true', help=argparse.SUPPRESS)
parser.add_argument('-d', '--distro', action='store', nargs=1,
metavar='DISTRO', help=argparse.SUPPRESS)
parser.add_argument('-v', '--version', action='version',
version=f'%(prog)s {__version}', help=argparse.SUPPRESS)
if len(sys.argv) == 1: if len(sys.argv) == 1:
parser.print_help() parser.print_help()

View file

@ -1,242 +1,19 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import os import os
import sys
import yaml
import time
import glob
import getpass
import fileinput
import subprocess import subprocess
def get_containers(): def get_containers():
container_list = subprocess.run(['sudo', '-u', user, 'podman', 'ps', '-a', '--no-trunc', '--sort=created', '--format', return subprocess.run(['podman', 'ps', '-a', '--no-trunc', '--sort=created', '--format',
'{{.Names}}'], stdout=subprocess.PIPE).stdout.decode('utf-8').strip().split('\n') '{{.Names}}'], stdout=subprocess.PIPE).stdout.decode('utf-8').strip().split('\n')
try: if os.path.isdir(os.path.expanduser('~/.local/bin/blend_bin')) and not os.path.isfile(os.path.expanduser('~/.local/bin/blend_bin/.associations')):
with open(os.path.expanduser('~/.config/blend/config.yaml')) as config_file: subprocess.call(['rm', '-rf', os.path.expanduser('~/.local/bin/blend_bin')], shell=False)
data = yaml.safe_load(config_file) subprocess.call(['bash', '-c', 'rm -f "${HOME}/.local/share/applications/blend;"*'], shell=False)
order = data['container_order'].copy()
order.reverse()
container_list.reverse()
for i in container_list:
if i.strip() not in order:
order.insert(0, i)
for i, o in enumerate(order):
if o not in container_list:
del order[i]
return order
except:
return container_list
subprocess.call(['mkdir', '-p', os.path.expanduser('~/.local/bin/blend_bin/')])
def list_use_container_bin(): subprocess.call(['touch', os.path.expanduser('~/.local/bin/blend_bin/.associations')], shell=False)
try:
with open(os.path.expanduser('~/.config/blend/config.yaml')) as config_file:
data = yaml.safe_load(config_file)
return data['use_container_bins']
except:
return []
def check_if_present(attr, desktop_str):
for l in desktop_str:
if l.startswith(attr + '='):
return True
return False
def which(bin):
results = []
for dir in os.environ.get('PATH').split(':'):
if os.path.isdir(dir):
if os.path.basename(bin) in os.listdir(dir):
results.append(os.path.join(dir, os.path.basename(bin)))
if results == []:
return None
return results
def create_container_binaries():
_binaries = []
remove_binaries = []
for c in _list:
c = c.strip()
for i in con_get_output(c, '''find /usr/bin -type f -printf "%P\n" 2>/dev/null;
find -L /usr/bin -xtype l -type f -printf "%P\n" 2>/dev/null;
find /usr/local/bin -type f -printf "%P\n" 2>/dev/null;
find -L /usr/local/bin -xtype l -type f -printf "%P\n" 2>/dev/null;''').split('\n'):
i = i.strip()
os.makedirs(os.path.expanduser(
f'~/.local/bin/blend_{c}'), exist_ok=True)
i_present = False
orig_which_out = which(os.path.basename(i))
which_out = None
if orig_which_out != None:
which_out = orig_which_out.copy()
try:
which_out.remove(os.path.expanduser(
f'~/.local/bin/blend_bin/{os.path.basename(i)}'))
except ValueError:
pass
if which_out == []:
which_out = None
if which_out != None and os.path.basename(i) not in _exceptions:
i_present = True
if os.path.basename(i) != 'host-spawn' and i != '' and not i_present:
with open(os.path.expanduser(f'~/.local/bin/blend_{c}/{os.path.basename(i)}.tmp'), 'w') as f:
f.write('#!/bin/bash\n')
f.write(f'# blend container: {c};{i}\n')
if os.path.basename(i) in _exceptions:
f.write(f'# EXCEPTION\n')
f.write('[ -f /run/.containerenv ] && { if [[ -e "/usr/bin/' + os.path.basename(i) + '" ]] || [[ -e "/usr/local/bin/' + os.path.basename(i) + '" ]]; then if [[ -e "/usr/bin/' + os.path.basename(i) + '" ]]; then /usr/bin/' + os.path.basename(
i) + ' "$@"; elif [[ -e "/usr/local/bin/' + os.path.basename(i) + '" ]]; then /usr/local/bin/' + os.path.basename(i) + ' "$@"; fi; exit $?; else echo "This command can be accessed from the host, or from the container \'' + c + '\'."; exit 127; fi } || :\n')
f.write(
f'BLEND_ALLOW_ROOT= BLEND_NO_CHECK= blend enter -cn {c} -- {os.path.basename(i)} "$@"\n')
# XXX: make this bit fully atomic
os.chmod(os.path.expanduser(
f'~/.local/bin/blend_{c}/{os.path.basename(i)}.tmp'), 0o775)
subprocess.call(['mv', os.path.expanduser(f'~/.local/bin/blend_{c}/{os.path.basename(i)}.tmp'),
os.path.expanduser(f'~/.local/bin/blend_{c}/{os.path.basename(i)}')])
_binaries.append((c, os.path.basename(os.path.basename(i))))
os.makedirs(os.path.expanduser(f'~/.local/bin/blend_bin'), exist_ok=True)
for c, i in _binaries:
try:
os.symlink(os.path.expanduser(
f'~/.local/bin/blend_{c}/{i}'), os.path.expanduser(f'~/.local/bin/blend_bin/{i}'))
except FileExistsError:
if subprocess.call(['grep', '-q', f'^# container: {c};{i}$', os.path.expanduser(f'~/.local/bin/blend_bin/{i}')], shell=False):
os.remove(os.path.expanduser(f'~/.local/bin/blend_bin/{i}'))
os.symlink(os.path.expanduser(
f'~/.local/bin/blend_{c}/{i}'), os.path.expanduser(f'~/.local/bin/blend_bin/{i}'))
for i in remove_binaries:
try:
os.remove(i)
except:
pass
for b in os.listdir(os.path.expanduser(f'~/.local/bin/blend_bin')):
if [_b for _b in _binaries if _b[1] == b] == []:
os.remove(os.path.join(os.path.expanduser(
f'~/.local/bin/blend_bin'), b))
for b_dir in glob.glob(os.path.expanduser(f'~/.local/bin/blend_*')):
if os.path.basename(b_dir) != 'blend_bin' and os.path.basename(b_dir)[6:] not in _list:
subprocess.call(['rm', '-rf', b_dir], shell=False)
def create_container_applications():
_apps = []
os.makedirs(os.path.expanduser(
f'~/.local/share/applications'), exist_ok=True)
for c in _list:
c = c.strip()
for i in con_get_output(c, 'find /usr/share/applications -type f 2>/dev/null; find /usr/local/share/applications -type f 2>/dev/null').split('\n'):
orig_path = i.strip()
i = os.path.basename(orig_path)
i_present = (os.path.isfile(f'/usr/share/applications/{i}') or os.path.isfile(f'/usr/local/share/applications/{i}')
or os.path.isfile(os.path.expanduser(f'~/.local/share/applications/{i}')))
if i != '' and not i_present:
with open(os.path.expanduser(f'~/.local/share/applications/blend;{i}'), 'w') as f:
_ = con_get_output(
c, f"sudo sed -i '/^DBusActivatable=/d' {orig_path}")
_ = con_get_output(
c, f"sudo sed -i '/^TryExec=/d' {orig_path}")
contents = con_get_output(c, f'cat {orig_path}')
f.write(contents)
for line in fileinput.input(os.path.expanduser(f'~/.local/share/applications/blend;{i}'), inplace=True):
if line.strip().startswith('Exec='):
line = f'Exec=env BLEND_NO_CHECK= blend enter -cn {c} -- {line[5:]}\n'
elif line.strip().startswith('Icon='):
if '/' in line:
line = line.strip()
_ = con_get_output(
c, f"mkdir -p ~/.local/share/blend/icons/file/\"{c}_{i}\"; cp {line[5:]} ~/.local/share/blend/icons/file/\"{c}_{i}\"")
line = f'Icon={os.path.expanduser("~/.local/share/blend/icons/file/" + c + "_" + i + "/" + os.path.basename(line[5:]))}\n'
else:
line = line.strip()
icons = con_get_output(c, f'''find /usr/share/icons /usr/share/pixmaps /var/lib/flatpak/exports/share/icons \\
-type f -iname "*{line[5:]}*" 2> /dev/null | sort''').split('\r\n')
_ = con_get_output(
c, f"mkdir -p ~/.local/share/blend/icons/\"{c}_{i}\"; cp {icons[0]} ~/.local/share/blend/icons/\"{c}_{i}\"")
line = f'Icon={os.path.expanduser("~/.local/share/blend/icons/" + c + "_" + i + "/" + os.path.basename(icons[0]))}\n'
sys.stdout.write(line)
os.chmod(os.path.expanduser(
f'~/.local/share/applications/blend;{i}'), 0o775)
_apps.append((c, i))
del _
for a in os.listdir(os.path.expanduser(f'~/.local/share/applications')):
if a.startswith('blend;'):
a = a.removeprefix('blend;')
if [_a for _a in _apps if _a[1] == a] == []:
os.remove(os.path.expanduser(
f'~/.local/share/applications/blend;{a}'))
def create_container_sessions(type='xsessions'):
session_dir = f'/usr/share/{type}'
os.makedirs('/usr/share/xsessions', exist_ok=True)
for session in os.listdir(session_dir):
if session.startswith(os.path.join(session_dir, 'blend-')):
os.remove(os.path.join(session_dir, session))
for c in _list:
c = c.strip()
for i in con_get_output(c, f'find {session_dir} -type f 2>/dev/null').split('\n'):
orig_path = i.strip()
i = os.path.basename(orig_path)
if i != '':
with open(os.path.expanduser(f'{session_dir}/blend-{c};{i}'), 'w') as f:
contents = con_get_output(c, f'cat {orig_path}')
f.write(contents)
for line in fileinput.input(os.path.expanduser(f'/{session_dir}/blend-{c};{i}'), inplace=True):
if line.strip().startswith('Name'):
name = line.split('=')[1]
line = f'Name=Container {c}: {name}'
elif line.strip().startswith('Exec='):
line = f'Exec=blend enter -cn {c} -- {line[5:]}'
elif line.strip().startswith('TryExec='):
continue
sys.stdout.write(line)
os.chmod(os.path.expanduser(
f'{session_dir}/blend-{c};{i}'), 0o775)
def con_get_output(name, cmd):
try:
return subprocess.run(['sudo', '-u', user, 'podman', 'exec', '--user', getpass.getuser(), '-it', name, 'bash', '-c', cmd],
stdout=subprocess.PIPE, stderr=subprocess.DEVNULL, timeout=5).stdout.decode('UTF-8').strip()
except subprocess.TimeoutExpired:
return ''
user = getpass.getuser()
try:
user = sys.argv[2]
except:
pass
for c in get_containers(): for c in get_containers():
c = c.strip() c = c.strip()
subprocess.call(['podman', 'start', c]) subprocess.call(['podman', 'start', c])
while True:
_list = get_containers()
_exceptions = list_use_container_bin()
create_container_binaries()
create_container_applications()
time.sleep(1)

View file

@ -109,18 +109,18 @@ if command -v apt-get &>/dev/null; then
apt-get update &>/dev/null apt-get update &>/dev/null
DEBIAN_FRONTEND=noninteractive apt-get -y install bash bc curl less wget apt-utils apt-transport-https dialog \ DEBIAN_FRONTEND=noninteractive apt-get -y install bash bc curl less wget apt-utils apt-transport-https dialog \
diffutils findutils gnupg2 sudo time util-linux libnss-myhostname \ diffutils findutils gnupg2 sudo time util-linux libnss-myhostname \
libvte-2.9[0-9]-common libvte-common lsof ncurses-base passwd \ libvte-2.9[0-9]-common libvte-common lsof ncurses-base passwd inotify-tools \
pinentry-curses libegl1-mesa libgl1-mesa-glx libvulkan1 mesa-vulkan-drivers &>/dev/null pinentry-curses libegl1-mesa libgl1-mesa-glx libvulkan1 mesa-vulkan-drivers &>/dev/null
elif command -v pacman &>/dev/null; then elif command -v pacman &>/dev/null; then
pacman --noconfirm -Syyu &>/dev/null pacman --noconfirm -Syyu &>/dev/null
pacman --noconfirm -Sy bash bc curl wget diffutils findutils gnupg sudo time util-linux vte-common lsof ncurses pinentry \ pacman --noconfirm -Sy bash bc curl wget diffutils findutils gnupg sudo time util-linux vte-common lsof ncurses pinentry \
mesa opengl-driver vulkan-intel vulkan-radeon base-devel git &>/dev/null mesa opengl-driver vulkan-intel vulkan-radeon base-devel git inotify-tools &>/dev/null
elif command -v dnf &>/dev/null; then elif command -v dnf &>/dev/null; then
dnf install -y --allowerasing bash bc curl wget diffutils findutils dnf-plugins-core gnupg2 less lsof passwd pinentry \ dnf install -y --allowerasing bash bc curl wget diffutils findutils dnf-plugins-core gnupg2 less lsof passwd pinentry \
procps-ng vte-profile ncurses util-linux sudo time shadow-utils vulkan mesa-vulkan-drivers \ procps-ng vte-profile ncurses util-linux sudo time shadow-utils vulkan mesa-vulkan-drivers \
mesa-dri-drivers &>/dev/null mesa-dri-drivers inotify-tools &>/dev/null
fi fi
@ -324,6 +324,23 @@ chmod 4755 /usr/bin/sudo
fi fi
if ! command -v inotify-tools &>/dev/null; then
if command -v apt-get &>/dev/null; then
apt-get update &>/dev/null
DEBIAN_FRONTEND=noninteractive apt-get -y install inotify-tools &>/dev/null
elif command -v pacman &>/dev/null; then
pacman --noconfirm -Syyu &>/dev/null
pacman --noconfirm -Sy inotify-tools &>/dev/null
elif command -v dnf &>/dev/null; then
dnf install -y --allowerasing inotify-tools &>/dev/null
fi
fi
source /run/.containerenv
CONTAINER_NAME="$name"
if [[ ! -f '/.init_blend.lock' ]] && command -v pacman &>/dev/null; then if [[ ! -f '/.init_blend.lock' ]] && command -v pacman &>/dev/null; then
cd /; git clone https://aur.archlinux.org/yay.git &>/dev/null; cd yay cd /; git clone https://aur.archlinux.org/yay.git &>/dev/null; cd yay
chown -R "$_uname" . &>/log chown -R "$_uname" . &>/log
@ -333,12 +350,125 @@ if [[ ! -f '/.init_blend.lock' ]] && command -v pacman &>/dev/null; then
touch /.init_blend.lock touch /.init_blend.lock
fi fi
echo for full_file in /usr/bin/*; do
if [[ -x "$full_file" ]]; then
file="$(basename "${full_file}")"
if [[ ! -f "${HOME}/.local/bin/blend_bin/${file}.${CONTAINER_NAME}" ]]; then
mkdir -p "${HOME}/.local/bin/blend_bin"
echo "#!/bin/bash" > "${HOME}/.local/bin/blend_bin/${file}.${CONTAINER_NAME}"
echo '[ -f /run/.containerenv ] && { if [[ -e "/usr/bin/'"${file}"'" ]]; then /usr/bin/'"${file}"' "$@"; exit $?; else echo "This command can be accessed from the host, or from the container '"'${CONTAINER_NAME}'"'."; exit 127; fi } || :' >> "${HOME}/.local/bin/blend_bin/${file}.${CONTAINER_NAME}"
echo 'BLEND_ALLOW_ROOT= BLEND_NO_CHECK= blend enter -cn '"${CONTAINER_NAME}"' -- '"${file}"' "$@"' >> "${HOME}/.local/bin/blend_bin/${file}.${CONTAINER_NAME}"
chmod 755 "${HOME}/.local/bin/blend_bin/${file}.${CONTAINER_NAME}"
fi
fi
done
for full_file in /usr/share/applications/*.desktop; do
file="$(basename "${full_file}")"
mkdir -p "${HOME}/.local/share/applications"
echo -n > "${HOME}/.local/share/applications/blend;${CONTAINER_NAME};${file}"
while read -r line; do
if [[ $line == Name* ]]; then
echo "${line} (container ${CONTAINER_NAME})" >> "${HOME}/.local/share/applications/blend;${CONTAINER_NAME};${file}"
elif [[ $line == Exec* ]]; then
echo "Exec=blend enter -cn ${CONTAINER_NAME} -- ${line:5}" >> "${HOME}/.local/share/applications/blend;${CONTAINER_NAME};${file}"
elif [[ $line == Icon* ]]; then
if [[ -e "${line:5}" ]]; then
mkdir -p "${HOME}/.local/share/blend/icons/${CONTAINER_NAME}_${file}"; cp "${line:5}" "${HOME}/.local/share/blend/icons/${CONTAINER_NAME}_${file}"
echo "Icon=${HOME}/.local/share/blend/icons/${CONTAINER_NAME}_${file}/$(basename "${line:5}")" >> "${HOME}/.local/share/applications/blend;${CONTAINER_NAME};${file}"
else
ICON_PATH="$(find /usr/share/icons/hicolor -type f -iname "${line:5}.*" -print -quit 2>/dev/null)"
mkdir -p "$(dirname "${ICON_PATH}" | sed 's/\/usr\/share/'"\/home\/${_uname}"'\/.local\/share/g')"
FINAL_ICON_PATH="$(dirname "${ICON_PATH}" | sed 's/\/usr\/share/'"\/home\/${_uname}"'\/.local\/share/g')/$(echo "${file%.*}").$(basename "${ICON_PATH}" | sed 's/^.*\.//')"
cp "${ICON_PATH}" "${FINAL_ICON_PATH}" &>/dev/null
echo "Icon=${FINAL_ICON_PATH}" >> "${HOME}/.local/share/applications/blend;${CONTAINER_NAME};${file}"
fi
else
echo "$line" >> "${HOME}/.local/share/applications/blend;${CONTAINER_NAME};${file}"
fi
done < "/usr/share/applications/${file}"
sed -i 's/DBusActivatable=true/DBusActivatable=false/g' "${HOME}/.local/share/applications/blend;${CONTAINER_NAME};${file}"
sed -i '/^TryExec/d' "${HOME}/.local/share/applications/blend;${CONTAINER_NAME};${file}"
chmod 755 "${HOME}/.local/share/applications/blend;${CONTAINER_NAME};${file}"
done
echo "Completed container setup." echo "Completed container setup."
mkdir -p /usr/share/applications /usr/bin
inotifywait -m /usr/share/applications /usr/bin -e create,delete,move 2>/dev/null |
while read dir action file; do
( if [[ "$dir" == "/usr/bin/" ]]; then
if [[ "$action" == *"CREATE"* ]]; then
if [[ ! -f "${HOME}/.local/bin/blend_bin/${file}.${CONTAINER_NAME}" ]] && [[ -x "/usr/bin/${file}" ]]; then
mkdir -p "${HOME}/.local/bin/blend_bin"
echo "#!/bin/bash" > "${HOME}/.local/bin/blend_bin/${file}.${CONTAINER_NAME}"
echo '[ -f /run/.containerenv ] && { if [[ -e "/usr/bin/'"${file}"'" ]]; then /usr/bin/'"${file}"' "$@"; exit $?; else echo "This command can be accessed from the host, or from the container '"'${CONTAINER_NAME}'"'."; exit 127; fi } || :' >> "${HOME}/.local/bin/blend_bin/${file}.${CONTAINER_NAME}"
echo 'BLEND_ALLOW_ROOT= BLEND_NO_CHECK= blend enter -cn '"${CONTAINER_NAME}"' -- '"${file}"' "$@"' >> "${HOME}/.local/bin/blend_bin/${file}.${CONTAINER_NAME}"
chmod 755 "${HOME}/.local/bin/blend_bin/${file}.${CONTAINER_NAME}"
fi
elif [[ "$action" == *"DELETE"* ]]; then
rm -f "${HOME}/.local/bin/blend_bin/${file}.${CONTAINER_NAME}"
elif [[ "$action" == *"MOVED_FROM"* ]]; then
rm -f "${HOME}/.local/bin/blend_bin/${file}.${CONTAINER_NAME}"
elif [[ "$action" == *"MOVED_TO"* ]]; then
if [[ ! -f "${HOME}/.local/bin/blend_bin/${file}.${CONTAINER_NAME}" ]] && [[ -x "/usr/bin/${file}" ]]; then
mkdir -p "${HOME}/.local/bin/blend_bin"
echo "#!/bin/bash" > "${HOME}/.local/bin/blend_bin/${file}.${CONTAINER_NAME}"
echo '[ -f /run/.containerenv ] && { if [[ -e "/usr/bin/'"${file}"'" ]]; then /usr/bin/'"${file}"' "$@"; exit $?; else echo "This command can be accessed from the host, or from the container '"'${CONTAINER_NAME}'"'."; exit 127; fi } || :' >> "${HOME}/.local/bin/blend_bin/${file}.${CONTAINER_NAME}"
echo 'BLEND_ALLOW_ROOT= BLEND_NO_CHECK= blend enter -cn '"${CONTAINER_NAME}"' -- '"${file}"' "$@"' >> "${HOME}/.local/bin/blend_bin/${file}.${CONTAINER_NAME}"
chmod 755 "${HOME}/.local/bin/blend_bin/${file}.${CONTAINER_NAME}"
fi
fi
fi ) &
( if [[ "$dir" == "/usr/share/applications/" ]]; then
if [[ "$action" == *"CREATE"* ]] || [[ "$action" == *"MOVED_TO"* ]]; then
if [[ "$file" == *'.desktop' ]]; then
mkdir -p "${HOME}/.local/share/applications"
echo -n > "${HOME}/.local/share/applications/blend;${CONTAINER_NAME};${file}"
while read -r line; do
if [[ $line == Name* ]]; then
echo "${line} (${CONTAINER_NAME})" >> "${HOME}/.local/share/applications/blend;${CONTAINER_NAME};${file}"
elif [[ $line == Exec* ]]; then
echo "Exec=blend enter -cn ${CONTAINER_NAME} -- ${line:5}" >> "${HOME}/.local/share/applications/blend;${CONTAINER_NAME};${file}"
elif [[ $line == Icon* ]]; then
if [[ -e "${line:5}" ]]; then
mkdir -p "${HOME}/.local/share/blend/icons/${CONTAINER_NAME}_${file}"; cp "${line:5}" "${HOME}/.local/share/blend/icons/${CONTAINER_NAME}_${file}"
echo "Icon=${HOME}/.local/share/blend/icons/${CONTAINER_NAME}_${file}/$(basename "${line:5}")" >> "${HOME}/.local/share/applications/blend;${CONTAINER_NAME};${file}"
else
ICON_PATH="$(find /usr/share/icons/hicolor -type f -iname "${line:5}.*" -print -quit 2>/dev/null)"
mkdir -p "$(dirname "${ICON_PATH}" | sed 's/\/usr\/share/'"\/home\/${_uname}"'\/.local\/share/g')"
FINAL_ICON_PATH="$(dirname "${ICON_PATH}" | sed 's/\/usr\/share/'"\/home\/${_uname}"'\/.local\/share/g')/$(echo "${file%.*}").$(basename "${ICON_PATH}" | sed 's/^.*\.//')"
cp "${ICON_PATH}" "${FINAL_ICON_PATH}"
echo "Icon=${FINAL_ICON_PATH}" >> "${HOME}/.local/share/applications/blend;${CONTAINER_NAME};${file}"
fi
else
echo "$line" >> "${HOME}/.local/share/applications/blend;${CONTAINER_NAME};${file}"
fi
done < "/usr/share/applications/${file}"
sed -i 's/DBusActivatable=true/DBusActivatable=false/g' "${HOME}/.local/share/applications/blend;${CONTAINER_NAME};${file}"
sed -i '/^TryExec/d' "${HOME}/.local/share/applications/blend;${CONTAINER_NAME};${file}"
chmod 755 "${HOME}/.local/share/applications/blend;${CONTAINER_NAME};${file}"
fi
elif [[ "$action" == *"DELETE"* ]]; then
rm -f "${HOME}/.local/share/applications/blend;${CONTAINER_NAME};${file}"
elif [[ "$action" == *"MOVED_FROM"* ]]; then
rm -f "${HOME}/.local/share/applications/blend;${CONTAINER_NAME};${file}"
fi
fi ) &
done &
while true; do while true; do
for i in /etc/hosts /etc/localtime /etc/resolv.conf; do for i in /etc/hosts /etc/localtime /etc/resolv.conf; do
cp "/run/host/${i}" / &>/dev/null || : cp "/run/host/${i}" / &>/dev/null || :
done done
sleep 5 sleep 5
done done