Skip to content

Commit 2a4f95e

Browse files
committed
Use new RunCommandwithOutput Function instead of calling toolbox from
toolbox Signed-off-by: Hadi Chokr <[email protected]>
1 parent 4c7d7a7 commit 2a4f95e

File tree

2 files changed

+254
-20
lines changed

2 files changed

+254
-20
lines changed

src/cmd/export.go

Lines changed: 37 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -72,14 +72,20 @@ func runExport(cmd *cobra.Command, args []string) error {
7272
}
7373

7474
func exportBinary(binName, containerName string) error {
75-
// Find the binary's full path inside the container
76-
checkCmd := fmt.Sprintf("toolbox run -c %s which %s", containerName, binName)
77-
out, err := exec.Command("sh", "-c", checkCmd).Output()
78-
if err != nil || strings.TrimSpace(string(out)) == "" {
75+
// 1. Find the binary path in the container
76+
checkCmd := []string{"which", binName}
77+
out, err := runCommandWithOutput(
78+
containerName,
79+
false, "", "", 0,
80+
checkCmd,
81+
false, false, true,
82+
)
83+
if err != nil || strings.TrimSpace(out) == "" {
7984
return fmt.Errorf("binary %s not found in container %s", binName, containerName)
8085
}
81-
binPath := strings.TrimSpace(string(out))
86+
binPath := strings.TrimSpace(out)
8287

88+
// 2. Prepare export path and script
8389
homeDir, err := os.UserHomeDir()
8490
if err != nil {
8591
return err
@@ -90,7 +96,7 @@ func exportBinary(binName, containerName string) error {
9096
# toolbox_binary
9197
# name: %s
9298
exec toolbox run -c %s %s "$@"
93-
`, containerName, containerName, binPath)
99+
`, binName, containerName, binPath)
94100

95101
if err := os.WriteFile(exportedBinPath, []byte(script), 0755); err != nil {
96102
return fmt.Errorf("failed to create wrapper: %v", err)
@@ -101,21 +107,35 @@ func exportBinary(binName, containerName string) error {
101107
}
102108

103109
func exportApplication(appName, containerName string) error {
104-
// Find the desktop file inside the container
105-
findCmd := fmt.Sprintf("toolbox run -c %s sh -c 'find /usr/share/applications -name \"*%s*.desktop\" | head -1'", containerName, appName)
106-
out, err := exec.Command("sh", "-c", findCmd).Output()
107-
if err != nil || strings.TrimSpace(string(out)) == "" {
110+
// 1. Find the .desktop file inside the container
111+
findCmd := []string{
112+
"sh", "-c",
113+
fmt.Sprintf("find /usr/share/applications -name '*%s*.desktop' | head -1", appName),
114+
}
115+
out, err := runCommandWithOutput(
116+
containerName,
117+
false, "", "", 0,
118+
findCmd,
119+
false, false, true,
120+
)
121+
if err != nil || strings.TrimSpace(out) == "" {
108122
return fmt.Errorf("application %s not found in container %s", appName, containerName)
109123
}
110-
desktopFile := strings.TrimSpace(string(out))
124+
desktopFile := strings.TrimSpace(out)
111125

112-
// Read the desktop file content
113-
catCmd := fmt.Sprintf("toolbox run -c %s cat %s", containerName, desktopFile)
114-
content, err := exec.Command("sh", "-c", catCmd).Output()
126+
// 2. Read the content of the desktop file
127+
catCmd := []string{"cat", desktopFile}
128+
content, err := runCommandWithOutput(
129+
containerName,
130+
false, "", "", 0,
131+
catCmd,
132+
false, false, true,
133+
)
115134
if err != nil {
116135
return fmt.Errorf("failed to read desktop file: %v", err)
117136
}
118-
lines := strings.Split(string(content), "\n")
137+
138+
lines := strings.Split(content, "\n")
119139
var newLines []string
120140
hasNameTranslations := false
121141

@@ -145,6 +165,7 @@ func exportApplication(appName, containerName string) error {
145165
}
146166
}
147167

168+
// 3. Write the new desktop file
148169
homeDir, err := os.UserHomeDir()
149170
if err != nil {
150171
return err
@@ -160,7 +181,7 @@ func exportApplication(appName, containerName string) error {
160181
return fmt.Errorf("failed to create desktop file: %v", err)
161182
}
162183

163-
// Update desktop database
184+
// 4. Refresh desktop database
164185
exec.Command("update-desktop-database", appsPath).Run()
165186

166187
fmt.Printf("Successfully exported %s from container %s to %s\n", appName, containerName, exportedPath)

src/cmd/run.go

Lines changed: 217 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,9 @@
1717
package cmd
1818

1919
import (
20+
"slices"
2021
"bufio"
22+
"bytes"
2123
"context"
2224
"encoding/json"
2325
"errors"
@@ -517,13 +519,10 @@ func callFlatpakSessionHelper(container podman.Container) error {
517519
var needsFlatpakSessionHelper bool
518520

519521
mounts := container.Mounts()
520-
for _, mount := range mounts {
521-
if mount == "/run/host/monitor" {
522+
if slices.Contains(mounts, "/run/host/monitor") {
522523
logrus.Debug("Requires org.freedesktop.Flatpak.SessionHelper")
523524
needsFlatpakSessionHelper = true
524-
break
525525
}
526-
}
527526

528527
if !needsFlatpakSessionHelper {
529528
return nil
@@ -1110,3 +1109,217 @@ func (err *entryPointError) Is(target error) bool {
11101109
targetErrMsg := target.Error()
11111110
return err.msg == targetErrMsg
11121111
}
1112+
1113+
func runCommandWithOutput(container string,
1114+
defaultContainer bool,
1115+
image, release string,
1116+
preserveFDs uint,
1117+
command []string,
1118+
emitEscapeSequence, fallbackToBash, pedantic bool) (string, error) {
1119+
1120+
if !pedantic {
1121+
if image == "" {
1122+
panic("image not specified")
1123+
}
1124+
if release == "" {
1125+
panic("release not specified")
1126+
}
1127+
}
1128+
1129+
logrus.Debugf("Checking if container %s exists", container)
1130+
1131+
if _, err := podman.ContainerExists(container); err != nil {
1132+
logrus.Debugf("Container %s not found", container)
1133+
1134+
if pedantic {
1135+
return "", createErrorContainerNotFound(container)
1136+
}
1137+
1138+
containers, err := getContainers()
1139+
if err != nil {
1140+
return "", createErrorContainerNotFound(container)
1141+
}
1142+
1143+
if len(containers) == 0 {
1144+
shouldCreate := rootFlags.assumeYes || askForConfirmation("No Toolbx containers found. Create now? [y/N]")
1145+
if !shouldCreate {
1146+
fmt.Printf("A container can be created later with the 'create' command.\n")
1147+
fmt.Printf("Run '%s --help' for usage.\n", executableBase)
1148+
return "", nil
1149+
}
1150+
if err := createContainer(container, image, release, "", false); err != nil {
1151+
return "", err
1152+
}
1153+
} else if len(containers) == 1 && defaultContainer {
1154+
fmt.Fprintf(os.Stderr, "Error: container %s not found\n", container)
1155+
container = containers[0].Name()
1156+
fmt.Fprintf(os.Stderr, "Entering container %s instead.\n", container)
1157+
fmt.Fprintf(os.Stderr, "Use the 'create' command to create a different Toolbx.\n")
1158+
fmt.Fprintf(os.Stderr, "Run '%s --help' for usage.\n", executableBase)
1159+
} else {
1160+
return "", fmt.Errorf("container %s not found\nUse the '--container' option to select a Toolbx.\nRun '%s --help' for usage.", container, executableBase)
1161+
}
1162+
}
1163+
1164+
containerObj, err := podman.InspectContainer(container)
1165+
if err != nil {
1166+
return "", fmt.Errorf("failed to inspect container %s", container)
1167+
}
1168+
1169+
if containerObj.EntryPoint() != "toolbox" {
1170+
return "", fmt.Errorf("container %s is too old and no longer supported\nRecreate it with Toolbx version 0.0.17 or newer.", container)
1171+
}
1172+
1173+
if err := callFlatpakSessionHelper(containerObj); err != nil {
1174+
return "", err
1175+
}
1176+
1177+
var cdiEnviron []string
1178+
cdiSpec, err := nvidia.GenerateCDISpec()
1179+
if err != nil {
1180+
if errors.Is(err, nvidia.ErrNVMLDriverLibraryVersionMismatch) {
1181+
return "", fmt.Errorf("the proprietary NVIDIA driver's kernel and user space don't match\nCheck the host operating system and systemd journal.")
1182+
} else if !errors.Is(err, nvidia.ErrPlatformUnsupported) {
1183+
return "", err
1184+
}
1185+
} else {
1186+
cdiEnviron = append(cdiEnviron, cdiSpec.ContainerEdits.Env...)
1187+
}
1188+
1189+
p11Environ, err := startP11KitServer()
1190+
if err != nil {
1191+
return "", err
1192+
}
1193+
1194+
entryPointPID := containerObj.EntryPointPID()
1195+
startTime := time.Unix(-1, 0)
1196+
1197+
if entryPointPID <= 0 {
1198+
if cdiSpec != nil {
1199+
cdiFile, err := getCDIFileForNvidia(currentUser)
1200+
if err != nil {
1201+
return "", err
1202+
}
1203+
if err := saveCDISpecTo(cdiSpec, cdiFile); err != nil {
1204+
return "", err
1205+
}
1206+
}
1207+
1208+
startTime = time.Now()
1209+
if err := startContainer(container); err != nil {
1210+
return "", err
1211+
}
1212+
1213+
containerObj, err = podman.InspectContainer(container)
1214+
if err != nil {
1215+
return "", fmt.Errorf("failed to inspect container %s", container)
1216+
}
1217+
1218+
entryPointPID = containerObj.EntryPointPID()
1219+
if entryPointPID <= 0 {
1220+
if err := showEntryPointLogs(container, startTime); err != nil {
1221+
logrus.Debugf("Reading logs from container %s failed: %s", container, err)
1222+
}
1223+
return "", fmt.Errorf("invalid entry point PID of container %s", container)
1224+
}
1225+
}
1226+
1227+
if err := ensureContainerIsInitialized(container, entryPointPID, startTime); err != nil {
1228+
return "", err
1229+
}
1230+
1231+
environ := append(cdiEnviron, p11Environ...)
1232+
return runCommandWithFallbacksWithOutput(container, preserveFDs, command, environ, emitEscapeSequence, fallbackToBash)
1233+
}
1234+
1235+
func runCommandWithFallbacksWithOutput(container string,
1236+
preserveFDs uint,
1237+
command, environ []string,
1238+
emitEscapeSequence, fallbackToBash bool) (string, error) {
1239+
1240+
detachKeysSupported := podman.CheckVersion("1.8.1")
1241+
1242+
envOptions := utils.GetEnvOptionsForPreservedVariables()
1243+
for _, env := range environ {
1244+
envOptions = append(envOptions, "--env="+env)
1245+
}
1246+
1247+
preserveFDsString := fmt.Sprint(preserveFDs)
1248+
var ttyNeeded bool
1249+
var stdout, stderr bytes.Buffer
1250+
1251+
if term.IsTerminal(os.Stdin) && term.IsTerminal(os.Stdout) {
1252+
ttyNeeded = true
1253+
}
1254+
1255+
workDir := workingDirectory
1256+
cmdIdx, dirIdx := 0, 0
1257+
1258+
for {
1259+
execArgs := constructExecArgs(container,
1260+
preserveFDsString,
1261+
command,
1262+
detachKeysSupported,
1263+
envOptions,
1264+
fallbackToBash,
1265+
ttyNeeded,
1266+
workDir)
1267+
1268+
if emitEscapeSequence {
1269+
fmt.Printf("\033]777;container;push;%s;toolbox;%s\033\\", container, currentUser.Uid)
1270+
}
1271+
1272+
stdout.Reset()
1273+
stderr.Reset()
1274+
1275+
exitCode, err := shell.RunWithExitCode("podman", os.Stdin, &stdout, &stderr, execArgs...)
1276+
1277+
if emitEscapeSequence {
1278+
fmt.Printf("\033]777;container;pop;;;%s\033\\", currentUser.Uid)
1279+
}
1280+
1281+
switch exitCode {
1282+
case 0:
1283+
if err != nil {
1284+
panic("unexpected error: 'podman exec' succeeded but returned error")
1285+
}
1286+
return stdout.String(), nil
1287+
case 125:
1288+
return "", &exitError{exitCode, fmt.Errorf("failed to invoke 'podman exec' in container %s", container)}
1289+
case 126:
1290+
return "", &exitError{exitCode, fmt.Errorf("failed to invoke command %s in container %s", command[0], container)}
1291+
case 127:
1292+
if ok, _ := isPathPresent(container, workDir); !ok {
1293+
if dirIdx < len(runFallbackWorkDirs) {
1294+
fmt.Fprintf(os.Stderr, "Error: directory %s not found in container %s\n", workDir, container)
1295+
workDir = runFallbackWorkDirs[dirIdx]
1296+
if workDir == "" {
1297+
workDir = getCurrentUserHomeDir()
1298+
}
1299+
fmt.Fprintf(os.Stderr, "Using %s instead.\n", workDir)
1300+
dirIdx++
1301+
continue
1302+
}
1303+
return "", &exitError{exitCode, fmt.Errorf("directory %s not found in container %s", workDir, container)}
1304+
}
1305+
1306+
if _, err := isCommandPresent(container, command[0]); err != nil {
1307+
if fallbackToBash && cmdIdx < len(runFallbackCommands) {
1308+
fmt.Fprintf(os.Stderr, "Error: command %s not found in container %s\n", command[0], container)
1309+
command = runFallbackCommands[cmdIdx]
1310+
fmt.Fprintf(os.Stderr, "Using %s instead.\n", command[0])
1311+
cmdIdx++
1312+
continue
1313+
}
1314+
return "", &exitError{exitCode, fmt.Errorf("command %s not found in container %s", command[0], container)}
1315+
}
1316+
1317+
if command[0] == "toolbox" {
1318+
return "", &exitError{exitCode, nil}
1319+
}
1320+
return stdout.String(), nil
1321+
default:
1322+
return "", &exitError{exitCode, nil}
1323+
}
1324+
}
1325+
}

0 commit comments

Comments
 (0)