From 4134424bc11119bd8457a583a536c4b929e7b979 Mon Sep 17 00:00:00 2001
From: Martin Mares <mj@ucw.cz>
Date: Thu, 6 Jun 2024 11:21:18 +0200
Subject: [PATCH] More commands

---
 TODO              |  1 +
 shipcat.py        | 73 +++++++++++++++++++++++++++++++++++++++++------
 shipcat/config.py |  2 +-
 3 files changed, 66 insertions(+), 10 deletions(-)

diff --git a/TODO b/TODO
index 359e7ba..ba9a0b3 100644
--- a/TODO
+++ b/TODO
@@ -1,2 +1,3 @@
 - colors
 - status: do not exit on non-zero return code
+- "shc CONTAINER start" vs. "shc CONTAINER shell"
diff --git a/shipcat.py b/shipcat.py
index d9122cb..d3c965b 100755
--- a/shipcat.py
+++ b/shipcat.py
@@ -25,10 +25,12 @@ def progress(msg: str) -> None:
     print(msg, file=sys.stderr, end="", flush=True)
 
 
-def run_command(args: List[str], *rest, **kwargs) -> None:
-    res = subprocess.run(args, *rest, **kwargs)
+def run_command(cmd: List[str], *args, **kwargs) -> None:
+    if verbose:
+        print('RUN: ' + " ".join(cmd))
+    res = subprocess.run(cmd, *args, **kwargs)
     if res.returncode != 0:
-        die('Command failed: ' + " ".join(args))
+        sys.exit(res.returncode)
 
 
 def load_config() -> GlobalConfig:
@@ -131,7 +133,7 @@ def cmd_init(args: argparse.Namespace) -> None:
     except KeyError:
         progress('creating\n')
         run_command(
-            ['adduser', '--system', '--group', '--gecos', f'Container {name}', '--disabled-password', cc.user_name],
+            ['adduser', '--system', '--group', '--gecos', f'Container {name}', '--disabled-password', cc.user_name]
         )
         pwd = getpwnam(cc.user_name)
     uid = pwd.pw_uid
@@ -145,7 +147,7 @@ def cmd_init(args: argparse.Namespace) -> None:
         progress('allocating\n')
         sur = subuids.alloc_range(cc.user_name, 65536)
         run_command(
-            ['usermod', '--add-subuids', f'{sur.first}-{sur.first + sur.count - 1}', cc.user_name],
+            ['usermod', '--add-subuids', f'{sur.first}-{sur.first + sur.count - 1}', cc.user_name]
         )
 
     progress('Subgid range: ')
@@ -157,7 +159,7 @@ def cmd_init(args: argparse.Namespace) -> None:
         progress('allocating\n')
         sgr = subgids.alloc_range(cc.user_name, 65536)
         run_command(
-            ['usermod', '--add-subgids', f'{sgr.first}-{sgr.first + sgr.count - 1}', cc.user_name],
+            ['usermod', '--add-subgids', f'{sgr.first}-{sgr.first + sgr.count - 1}', cc.user_name]
         )
 
     progress(f'Using user {cc.user_name}, uid {uid}, subuids {sur.first}+{sur.count}, subgids {sgr.first}+{sgr.count}\n')
@@ -190,7 +192,7 @@ def cmd_init(args: argparse.Namespace) -> None:
 
 def service_action(cc: ContainerConfig, action: str) -> None:
     run_command(
-        ['systemctl', action, f'shc@{cc.container}.service'],
+        ['systemctl', action, f'shc@{cc.container}.service']
     )
 
 
@@ -202,13 +204,13 @@ def cmd_create(args: argparse.Namespace) -> None:
     ip = socket.gethostbyname(name)
 
     run_command(
-        ['podman', 'pull', cc.image],
+        ['podman', 'pull', cc.image]
     )
 
     service_action(cc, 'stop')
 
     run_command(
-        ['podman', 'rm', '-if', name],
+        ['podman', 'rm', '-if', name]
     )
 
     run_command(
@@ -243,6 +245,38 @@ def cmd_status(args: argparse.Namespace) -> None:
     service_action(cc, 'status')
 
 
+def cmd_disable(args: argparse.Namespace) -> None:
+    cc = setup_container(args, False)
+    service_action(cc, 'disable')
+
+
+def cmd_enable(args: argparse.Namespace) -> None:
+    cc = setup_container(args, False)
+    service_action(cc, 'enable')
+
+
+def cmd_shell(args: argparse.Namespace) -> None:
+    cc = setup_container(args, False)
+
+    run_command(
+        ['podman', 'exec', '-it', cc.container, '/bin/bash']
+    )
+
+
+def cmd_exec(args: argparse.Namespace) -> None:
+    cc = setup_container(args, False)
+
+    cmd = ['podman', 'exec']
+    if args.tty is not None:
+        cmd.extend(['--interactive', '--tty'])
+    if args.user is not None:
+        cmd.extend(['--user', args.user])
+    cmd.append(cc.container)
+    cmd.extend(args.arg)
+
+    run_command(cmd)
+
+
 def parse_int_list(s: str) -> List[int]:
     return list(map(int, s.split(',')))
 
@@ -252,6 +286,7 @@ parser = argparse.ArgumentParser(
 )
 parser.add_argument('--as-user', type=int, metavar='UID', help='user ID of requesting user')
 parser.add_argument('--as-groups', type=parse_int_list, metavar='GID,...', help='group IDs of requesting user (primary first)')
+parser.add_argument('--verbose', '-v', default=False, action='store_true', help='be chatty and explain what is going on')
 subparsers = parser.add_subparsers(help='action to perform', dest='action', required=True, metavar='ACTION')
 
 init_parser = subparsers.add_parser('init', help='initialize a new container', description='Initialize a new container. Should be called by root.')
@@ -260,6 +295,21 @@ init_parser.add_argument('name', help='name of the container')
 create_parser = subparsers.add_parser('create', help='create a container from an image', description='Create a container from an image.')
 create_parser.add_argument('name', help='name of the container')
 
+create_parser = subparsers.add_parser('disable', help='disable automatic startup of a container', description='Disable automatic startup of a container.')
+create_parser.add_argument('name', help='name of the container')
+
+create_parser = subparsers.add_parser('enable', help='enable automatic startup of a container', description='Enable automatic startup of a container.')
+create_parser.add_argument('name', help='name of the container')
+
+create_parser = subparsers.add_parser('exec', help='execute a command within a container', description='Execute a command within a container.')
+create_parser.add_argument('name', help='name of the container')
+create_parser.add_argument('arg', nargs='+', help='command and its arguments')
+create_parser.add_argument('--tty', '-t', default=False, action='store_true', help='stdio will be attached to a pseudo-terminal')
+create_parser.add_argument('--user', '-u', metavar='USER[:GROUP]', help='user/group to run the command under (default: root)')
+
+start_parser = subparsers.add_parser('shell', help='run a shell inside a container', description='Run a shell inside a container')
+start_parser.add_argument('name', help='name of the container')
+
 start_parser = subparsers.add_parser('start', help='start a container', description='Start a container')
 start_parser.add_argument('name', help='name of the container')
 
@@ -270,10 +320,15 @@ stop_parser = subparsers.add_parser('stop', help='stop a container', description
 stop_parser.add_argument('name', help='name of the container')
 
 args = parser.parse_args()
+verbose = args.verbose
 
 actions = {
     'create': cmd_create,
+    'disable': cmd_disable,
+    'enable': cmd_enable,
+    'exec': cmd_exec,
     'init': cmd_init,
+    'shell': cmd_shell,
     'start': cmd_start,
     'status': cmd_status,
     'stop': cmd_stop,
diff --git a/shipcat/config.py b/shipcat/config.py
index 2d34571..94be7ec 100644
--- a/shipcat/config.py
+++ b/shipcat/config.py
@@ -100,5 +100,5 @@ class ContainerConfig:
                     wu.raise_error(f'Unknown group {name}')
                 self.allowed_groups.add(grp.gr_gid)
 
-        self.pid_file = f'/run/{self.container}.pid'
+        self.pid_file = f'/run/shc/{self.container}.pid'
         self.user_name = self.container
-- 
GitLab