{
err := a.CheckContainerTools()
if err != nil {
fmt.Println(`One or more core components are not available.
Please refer to our documentation at https://documentation.vanillaos.org/`)
return err
}
err = a.CheckAndCreateUserStacksDirectory()
if err != nil {
fmt.Println(`Failed to create stacks directory.`)
return err
}
err = a.CheckAndCreateApxStorageDirectory()
if err != nil {
fmt.Println(`Failed to create apx storage directory.`)
return err
}
err = a.CheckAndCreateApxUserPkgManagersDirectory()
if err != nil {
fmt.Println(`Failed to create apx user pkg managers directory.`)
return err
}
return nil
}
{
_, err := os.Stat(a.Cnf.DistroboxPath)
if err != nil {
if os.IsNotExist(err) {
return errors.New("distrobox is not installed")
}
return err
}
if _, err := exec.LookPath("docker"); err != nil {
if _, err := exec.LookPath("podman"); err != nil {
return errors.New("container engine (docker or podman) not found")
}
}
return nil
}
{
_, err := os.Stat(a.Cnf.UserStacksPath)
if err != nil {
if os.IsNotExist(err) {
err = os.MkdirAll(a.Cnf.UserStacksPath, 0755)
if err != nil {
return fmt.Errorf("failed to create stacks directory: %w", err)
}
} else {
return fmt.Errorf("failed to check stacks directory: %w", err)
}
}
return nil
}
{
_, err := os.Stat(a.Cnf.ApxStoragePath)
if err != nil {
if os.IsNotExist(err) {
err = os.MkdirAll(a.Cnf.ApxStoragePath, 0755)
if err != nil {
return fmt.Errorf("failed to create apx storage directory: %w", err)
}
} else {
return fmt.Errorf("failed to check apx storage directory: %w", err)
}
}
return nil
}
{
_, err := os.Stat(a.Cnf.UserPkgManagersPath)
if err != nil {
if os.IsNotExist(err) {
err = os.MkdirAll(a.Cnf.UserPkgManagersPath, 0755)
if err != nil {
return fmt.Errorf("failed to create apx user pkg managers directory: %w", err)
}
} else {
return fmt.Errorf("failed to check apx user pkg managers directory: %w", err)
}
}
return nil
}
{
apx = &Apx{
Cnf: cnf,
}
err := apx.EssentialChecks()
if err != nil {
// localisation features aren't available at this stage, so this error can't be translated
fmt.Println("ERROR: Unable to find apx configuration files")
return nil
}
return apx
}
{
cnf, err := settings.GetApxDefaultConfig()
if err != nil {
panic(err)
}
apx = &Apx{
Cnf: cnf,
}
err = apx.EssentialChecks()
if err != nil {
// localisation features aren't available at this stage, so this error can't be translated
fmt.Println("ERROR: Unable to find apx configuration files")
return nil
}
return apx
}
{
entrypoint := apx.Cnf.DistroboxPath
if useEngine {
entrypoint = d.EngineBinary
}
// we need to append engineFlags after command, otherwise they will be
// ignored in commands like "enter"
finalArgs := []string{command}
// NOTE: for engine-specific commands, we need to use pkexec for rootfull
// containers, since podman does not offer a dedicated flag for this.
if rootFull && useEngine {
entrypoint = "pkexec"
finalArgs = []string{d.EngineBinary, command}
}
cmd := exec.Command(entrypoint, finalArgs...)
if detachedMode {
cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}
}
if !captureOutput && !muteOutput {
cmd.Stdout = os.Stdout
}
if !muteOutput {
cmd.Stderr = os.Stderr
}
cmd.Stdin = os.Stdin
cmd.Env = os.Environ()
cmd.Env = append(cmd.Env, "DBX_SUDO_PROGRAM=pkexec")
// NOTE: the custom storage is not being used since it prevent other
// utilities, like VSCode, to access the container.
if d.Engine == "podman" {
cmd.Env = append(cmd.Env, "CONTAINER_STORAGE_DRIVER="+apx.Cnf.StorageDriver)
// cmd.Env = append(cmd.Env, "XDG_DATA_HOME="+apx.Cnf.ApxStoragePath)
} else if d.Engine == "docker" {
cmd.Env = append(cmd.Env, "DOCKER_STORAGE_DRIVER="+apx.Cnf.StorageDriver)
// cmd.Env = append(cmd.Env, "DOCKER_DATA_ROOT="+apx.Cnf.ApxStoragePath)
}
if len(engineFlags) > 0 {
cmd.Args = append(cmd.Args, "--additional-flags")
cmd.Args = append(cmd.Args, strings.Join(engineFlags, " "))
}
// NOTE: the root flag is not being used by the Apx CLI, but it's useful
// for those using Apx as a library, e.g. VSO.
if rootFull && !useEngine {
cmd.Args = append(cmd.Args, "--root")
}
cmd.Args = append(cmd.Args, args...)
if os.Getenv("APX_VERBOSE") == "1" {
fmt.Println("Runing a command:")
fmt.Println("\tCommand:", cmd.String())
fmt.Println("\tcaptureOutput:", captureOutput)
fmt.Println("\tmuteOutput:", muteOutput)
fmt.Println("\trootFull:", rootFull)
fmt.Println("\tdetachedMode:", detachedMode)
}
if detachedMode {
err := cmd.Start()
if err != nil {
return nil, err
}
return nil, nil
}
if captureOutput {
output, err := cmd.Output()
if err != nil {
if exitErr, ok := err.(*exec.ExitError); ok {
return output, errors.New(string(exitErr.Stderr))
}
}
return output, err
}
cmd.Stdout = ioutil.Discard
err := cmd.Run()
return nil, err
}
{
output, err := d.RunCommand("ps", []string{
"-a",
"--format", "{{.ID}}|{{.CreatedAt}}|{{.Status}}|{{.Labels}}|{{.Names}}",
}, []string{}, true, true, false, rootFull, false)
if err != nil {
return nil, err
}
rows := strings.Split(string(output), "\n")
containers := []dboxContainer{}
for _, row := range rows {
if row == "" {
continue
}
rowItems := strings.Split(row, "|")
if len(rowItems) != 5 {
continue
}
container := dboxContainer{
ID: strings.Trim(rowItems[0], "\""),
CreatedAt: strings.Trim(rowItems[1], "\""),
Status: strings.Trim(rowItems[2], "\""),
Name: strings.Trim(rowItems[4], "\""),
Labels: map[string]string{},
}
// example labels: map[manager:apx name:alpine stack:alpine]
labels := strings.ReplaceAll(rowItems[3], "map[", "")
labels = strings.ReplaceAll(labels, "]", "")
labelsItems := strings.Split(labels, " ")
for _, label := range labelsItems {
labelItems := strings.Split(label, ":")
if len(labelItems) != 2 {
continue
}
container.Labels[labelItems[0]] = labelItems[1]
}
containers = append(containers, container)
}
return containers, nil
}
{
containers, err := d.ListContainers(rootFull)
if err != nil {
return nil, err
}
for _, container := range containers {
// fmt.Println("found container", container.Name, "requested", name)
if container.Name == name {
return &container, nil
}
}
return nil, errors.New("container not found")
}
{
_, err := d.RunCommand("rm", []string{
"--force",
name,
}, []string{}, false, false, true, rootFull, false)
return err
}
{
args := []string{
"--image", image,
"--name", name,
"--no-entry",
"--yes",
"--pull",
}
if home != "" {
args = append(args, "--home", home)
}
if hasNvidiaGPU() && withNvidiaIntegration {
args = append(args, "--nvidia")
}
if withInit {
args = append(args, "--init")
}
if unshared {
args = append(args, "--unshare-all")
}
if hostname != "" {
args = append(args, "--hostname", hostname)
}
if len(additionalPackages) > 0 {
args = append(args, "--additional-packages")
args = append(args, strings.Join(additionalPackages, " "))
}
engineFlags := []string{}
for key, value := range labels {
engineFlags = append(engineFlags, fmt.Sprintf("--label=%s=%s", key, value))
}
engineFlags = append(engineFlags, "--label=manager=apx")
_, err := d.RunCommand("create", args, engineFlags, false, false, false, rootFull, false)
// fmt.Println(string(out))
return err
}
{
args := []string{
"--name", name,
"--",
}
args = append(args, command...)
_, err := d.RunCommand("run", args, []string{}, false, false, false, rootFull, detachedMode)
return err
}
{
finalArgs := []string{
// "--verbose",
name,
"--",
}
finalArgs = append(finalArgs, args...)
engineFlags := []string{}
out, err := d.RunCommand("enter", finalArgs, engineFlags, false, captureOutput, muteOutput, rootFull, detachedMode)
// if error 130, it means that the user pressed CTRL+D, so ignore
if err != nil && err.Error() == "exit status 130" {
return string(out), nil
}
return string(out), err
}
{
finalArgs := []string{
name,
}
engineFlags := []string{}
_, err := d.RunCommand("enter", finalArgs, engineFlags, false, false, false, rootFull, false)
// if error 130, it means that the user pressed CTRL+D, so ignore
if err != nil && err.Error() == "exit status 130" {
return nil
}
return err
}
{
_, err := d.RunCommand("start", []string{
name,
}, []string{}, true, false, false, rootFull, false)
return err
}
{
finalArgs := []string{
name,
"--yes",
}
engineFlags := []string{}
_, err := d.RunCommand("stop", finalArgs, engineFlags, false, false, false, rootFull, false)
return err
}
{
finalArgs := []string{"distrobox-export"}
if delete {
finalArgs = append(finalArgs, "--delete")
}
finalArgs = append(finalArgs, args...)
_, err := d.ContainerExec(name, true, true, rootFull, false, finalArgs...)
return err
}
{
args := []string{"--app", appName, "--export-label", label}
return d.ContainerExport(containerName, false, rootFull, args...)
}
{
args := []string{"--app", appName}
return d.ContainerExport(containerName, true, rootFull, args...)
}
{
args := []string{"--bin", binary, "--export-path", exportPath}
return d.ContainerExport(containerName, false, rootFull, args...)
}
{
args := []string{"--bin", binary}
return d.ContainerExport(containerName, true, rootFull, args...)
}
{
engineBinary, engine := getEngine()
version, err := dboxGetVersion()
if err != nil {
return nil, err
}
return &dbox{
Engine: engine,
EngineBinary: engineBinary,
Version: version,
}, nil
}
{
podmanBinary, err := exec.LookPath("podman")
if err == nil {
return podmanBinary, "podman"
}
dockerBinary, err := exec.LookPath("docker")
if err == nil {
return dockerBinary, "docker"
}
log.Fatal("no container engine found. Please install Podman or Docker.")
return "", ""
}
{
output, err := exec.Command(apx.Cnf.DistroboxPath, "version").Output()
if err != nil {
return "", err
}
splitted := strings.Split(string(output), "distrobox: ")
if len(splitted) != 2 {
return "", errors.New("can't retrieve distrobox version")
}
return splitted[1], nil
}
{
out, err := exec.Command("df", "-T", "/").Output()
if err != nil {
return false
}
return strings.Contains(string(out), "overlay")
}
{
if IsOverlayTypeFS() {
log.Default().Printf("Apx does not work with overlay type filesystem.")
os.Exit(1)
}
}
{
_, err := os.Stat("/dev/nvidia0")
return err == nil
}
PkgManager represents a package manager in Apx, a set of instructions to handle a package manager.
Save saves the package manager to the specified path.
{
data, err := yaml.Marshal(pkgManager)
if err != nil {
return err
}
filePath := SelectYamlFile(apx.Cnf.UserPkgManagersPath, pkgManager.Name)
err = os.WriteFile(filePath, data, 0644)
return err
}
Remove removes the package manager from the specified path.
{
if pkgManager.BuiltIn {
return errors.New("cannot remove built-in package manager")
}
filePath := SelectYamlFile(apx.Cnf.UserPkgManagersPath, pkgManager.Name)
err := os.Remove(filePath)
return err
}
GenCmd generates the command to run inside the container.
{
finalArgs := make([]string, 0)
if pkgManager.NeedSudo {
finalArgs = append(finalArgs, "sudo")
}
if pkgManager.Model == 0 || pkgManager.Model == 1 {
// no-translate (DEPRECATION WARNING)
fmt.Println("!!! DEPRECATION WARNING: Model 1 will be removed in the future, please update your Apx package manager to use model 2.")
finalArgs = append(finalArgs, pkgManager.Name)
finalArgs = append(finalArgs, cmd)
finalArgs = append(finalArgs, args...)
} else {
cmdItems := strings.Fields(cmd)
finalArgs = append(finalArgs, cmdItems...)
finalArgs = append(finalArgs, args...)
}
return finalArgs
}
Export exports the package manager to the specified path.
{
if _, err := os.Stat(path); os.IsNotExist(err) {
err = os.MkdirAll(path, 0755)
if err != nil {
return err
}
}
filePath := SelectYamlFile(path, pkgManager.Name)
data, err := yaml.Marshal(pkgManager)
if err != nil {
return err
}
err = os.WriteFile(filePath, data, 0644)
if err != nil {
return err
}
return nil
}
NewPkgManager creates a new PkgManager instance.
{
return &PkgManager{
Name: name,
NeedSudo: needSudo,
CmdAutoRemove: autoRemove,
CmdClean: clean,
CmdInstall: install,
CmdList: list,
CmdPurge: purge,
CmdRemove: remove,
CmdSearch: search,
CmdShow: show,
CmdUpdate: update,
CmdUpgrade: upgrade,
BuiltIn: builtIn,
Model: 2,
}
}
LoadPkgManager loads a package manager from the specified path.
{
userPkgFile := SelectYamlFile(apx.Cnf.UserPkgManagersPath, name)
pkgManager, err := loadPkgManagerFromPath(userPkgFile)
if err != nil {
pkgFile := SelectYamlFile(apx.Cnf.PkgManagersPath, name)
pkgManager, err = loadPkgManagerFromPath(pkgFile)
}
return pkgManager, err
}
loadPkgManagerFromPath loads a package manager from the specified path.
{
pkgManager := &PkgManager{}
f, err := os.Open(path)
if err != nil {
return nil, errors.New("package manager not found")
}
data, err := io.ReadAll(f)
if err != nil {
return nil, err
}
err = yaml.Unmarshal(data, pkgManager)
if err != nil {
return nil, err
}
return pkgManager, nil
}
ListPkgManagers lists all the package managers.
{
pkgManagers := make([]*PkgManager, 0)
pkgManagersFromEtc := listPkgManagersFromPath(apx.Cnf.UserPkgManagersPath)
pkgManagers = append(pkgManagers, pkgManagersFromEtc...)
if apx.Cnf.PkgManagersPath == apx.Cnf.UserPkgManagersPath {
// user install
return pkgManagers
}
pkgManagersFromShare := listPkgManagersFromPath(apx.Cnf.PkgManagersPath)
pkgManagers = append(pkgManagers, pkgManagersFromShare...)
return pkgManagers
}
listPkgManagersFromPath lists all the package managers from the specified path.
{
pkgManagers := make([]*PkgManager, 0)
files, err := os.ReadDir(path)
if err != nil {
return pkgManagers
}
for _, file := range files {
extension := filepath.Ext(file.Name())
if !file.IsDir() && (extension == ".yaml" || extension == ".yml") {
// Remove the ".yaml" or ".yml" extension
pkgManagerName := file.Name()[:(len(file.Name()) - len(extension))]
pkgManager, err := LoadPkgManager(pkgManagerName)
if err == nil {
pkgManagers = append(pkgManagers, pkgManager)
}
}
}
return pkgManagers
}
PkgManagerExists checks if the package manager exists.
{
_, err := LoadPkgManager(name)
return err == nil
}
LoadPkgManagerFromPath loads a package manager from the specified path.
{
pkgManager := &PkgManager{}
f, err := os.Open(path)
if err != nil {
return nil, errors.New("package manager not found")
}
data, err := io.ReadAll(f)
if err != nil {
return nil, err
}
err = yaml.Unmarshal(data, pkgManager)
if err != nil {
return nil, err
}
if pkgManager.Model == 0 {
pkgManager.Model = 1 // assuming old model if not specified
}
return pkgManager, nil
}
Stack represents a stack in Apx, a set of instructions to build a container.
Save saves the stack to a YAML file.
{
data, err := yaml.Marshal(stack)
if err != nil {
return err
}
filePath := SelectYamlFile(apx.Cnf.UserStacksPath, stack.Name)
err = os.WriteFile(filePath, data, 0644)
return err
}
GetPkgManager returns the package manager of the stack.
{
pkgManager, err := LoadPkgManager(stack.PkgManager)
if err != nil {
return nil, err
}
return pkgManager, nil
}
Remove removes the stack file.
{
if stack.BuiltIn {
return errors.New("cannot remove built-in stack")
}
filePath := SelectYamlFile(apx.Cnf.UserStacksPath, stack.Name)
err := os.Remove(filePath)
return err
}
Export exports the stack YAML to the specified path.
{
if _, err := os.Stat(path); os.IsNotExist(err) {
err = os.MkdirAll(path, 0755)
if err != nil {
return err
}
}
filePath := SelectYamlFile(path, stack.Name)
data, err := yaml.Marshal(stack)
if err != nil {
return err
}
err = os.WriteFile(filePath, data, 0644)
if err != nil {
return err
}
return nil
}
NewStack creates a new Stack instance.
{
return &Stack{
Name: name,
Base: base,
Packages: packages,
PkgManager: pkgManager,
BuiltIn: builtIn,
}
}
LoadStack loads a stack from the specified path.
{
usrStackFile := SelectYamlFile(apx.Cnf.UserStacksPath, name)
stack, err := LoadStackFromPath(usrStackFile)
if err != nil {
stackFile := SelectYamlFile(apx.Cnf.StacksPath, name)
stack, err = LoadStackFromPath(stackFile)
}
return stack, err
}
LoadStackFromPath loads a stack from the specified path.
{
stack := &Stack{}
f, err := os.Open(path)
if err != nil {
return nil, errors.New("stack not found")
}
data, err := io.ReadAll(f)
if err != nil {
return nil, err
}
err = yaml.Unmarshal(data, stack)
if err != nil {
return nil, err
}
if stack.Name == "" || stack.Base == "" || stack.PkgManager == "" {
return nil, errors.New("invalid stack file")
}
return stack, nil
}
ListStacks returns a list of all stacks.
{
stacks := make([]*Stack, 0)
stacksFromEtc := listStacksFromPath(apx.Cnf.UserStacksPath)
stacks = append(stacks, stacksFromEtc...)
if apx.Cnf.UserStacksPath == apx.Cnf.StacksPath {
// user install
return stacks
}
stacksFromShare := listStacksFromPath(apx.Cnf.StacksPath)
stacks = append(stacks, stacksFromShare...)
return stacks
}
ListStackForPkgManager returns a list of stacks for the specified package manager.
{
stacks := make([]*Stack, 0)
stacksFromEtc := listStacksFromPath(apx.Cnf.UserStacksPath)
for _, stack := range stacksFromEtc {
if stack.PkgManager == pkgManager {
stacks = append(stacks, stack)
}
}
stacksFromShare := listStacksFromPath(apx.Cnf.StacksPath)
for _, stack := range stacksFromShare {
if stack.PkgManager == pkgManager {
stacks = append(stacks, stack)
}
}
return stacks
}
listStacksFromPath returns a list of stacks from the specified path.
this func does not return an error, since Apx is meant to be portable and
the main directory can be missing, while the user directory is always created.
{
stacks := make([]*Stack, 0)
files, err := os.ReadDir(path)
if err != nil {
return stacks
}
for _, file := range files {
extension := filepath.Ext(file.Name())
if !file.IsDir() && (extension == ".yaml" || extension == ".yml") {
// Remove the ".yaml" or ".yml" extension
stackName := file.Name()[:(len(file.Name()) - len(extension))]
stack, err := LoadStack(stackName)
if err == nil {
stacks = append(stacks, stack)
}
}
}
return stacks
}
StackExists checks if a stack exists.
{
s, _ := LoadStack(name)
return s != nil
}
{
dbox, err := NewDbox()
if err != nil {
return err
}
labels := map[string]string{
"stack": strings.ReplaceAll(s.Stack.Name, " ", "\\ "),
"name": strings.ReplaceAll(s.Name, " ", "\\ "),
}
if s.IsManaged {
labels["managed"] = "true"
}
if s.HasInit {
labels["hasInit"] = "true"
}
if s.IsUnshared {
labels["unshared"] = "true"
}
if s.HasNvidiaIntegration {
labels["nvidia"] = "true"
}
err = dbox.CreateContainer(
s.InternalName,
s.Stack.Base,
s.Stack.Packages,
s.Home,
labels,
s.HasInit,
s.IsRootfull,
s.IsUnshared,
s.HasNvidiaIntegration,
s.Hostname,
)
if err != nil {
return err
}
return nil
}
{
dbox, err := NewDbox()
if err != nil {
return "", err
}
out, err := dbox.ContainerExec(s.InternalName, captureOutput, false, s.IsRootfull, detachedMode, args...)
if err != nil {
return "", err
}
if captureOutput {
return out, nil
}
return "", nil
}
{
dbox, err := NewDbox()
if err != nil {
return err
}
return dbox.ContainerEnter(s.InternalName, s.IsRootfull)
}
{
dbox, err := NewDbox()
if err != nil {
return err
}
return dbox.ContainerStart(s.InternalName, s.IsRootfull)
}
{
dbox, err := NewDbox()
if err != nil {
return err
}
return dbox.ContainerStop(s.InternalName, s.IsRootfull)
}
{
dbox, err := NewDbox()
if err != nil {
return err
}
return dbox.ContainerDelete(s.InternalName, s.IsRootfull)
}
{
err := s.Remove()
if err != nil {
return err
}
return s.Create()
}
{
dbox, err := NewDbox()
if err != nil {
return err
}
return dbox.ContainerExportDesktopEntry(s.InternalName, appName, fmt.Sprintf("on %s", s.Name), s.IsRootfull)
}
{
exportedN := 0
for _, appName := range args {
err := s.ExportDesktopEntry(appName)
if err != nil {
return exportedN, err
}
exportedN++
}
return exportedN, nil
}
{
exportedN := 0
for _, appName := range args {
err := s.UnexportDesktopEntry(appName)
if err != nil {
return exportedN, err
}
exportedN++
}
return exportedN, nil
}
{
if !strings.HasPrefix(binary, "/") {
binaryPath, err := s.Exec(true, false, "which", binary)
if err != nil {
return err
}
binary = strings.TrimSpace(binaryPath)
}
binaryName := filepath.Base(binary)
dbox, err := NewDbox()
if err != nil {
return err
}
homeDir, err := os.UserHomeDir()
if err != nil {
return err
}
if exportPath == "" {
exportPath = filepath.Join(homeDir, ".local", "bin")
}
joinedPath := filepath.Join(exportPath, binaryName)
if _, err := os.Stat(joinedPath); err == nil {
tmpExportPath := fmt.Sprintf("/tmp/%s", uuid.New().String())
err = os.MkdirAll(tmpExportPath, 0o755)
if err != nil {
return err
}
err = dbox.ContainerExportBin(s.InternalName, binary, tmpExportPath, s.IsRootfull)
if err != nil {
return err
}
err = CopyFile(filepath.Join(tmpExportPath, binaryName), filepath.Join(exportPath, fmt.Sprintf("%s-%s", binaryName, s.InternalName)))
if err != nil {
return err
}
err = os.RemoveAll(tmpExportPath)
if err != nil {
return err
}
err = os.Chmod(filepath.Join(exportPath, fmt.Sprintf("%s-%s", binaryName, s.InternalName)), 0o755)
if err != nil {
return err
}
return nil
}
err = os.MkdirAll(exportPath, 0o755)
if err != nil {
return err
}
err = dbox.ContainerExportBin(s.InternalName, binary, exportPath, s.IsRootfull)
if err != nil {
return err
}
return nil
}
{
dbox, err := NewDbox()
if err != nil {
return err
}
return dbox.ContainerUnexportDesktopEntry(s.InternalName, appName, s.IsRootfull)
}
{
if !strings.HasPrefix(binary, "/") {
binaryPath, err := s.Exec(true, false, "which", binary)
if err != nil {
return err
}
binary = strings.TrimSpace(binaryPath)
}
dbox, err := NewDbox()
if err != nil {
return err
}
return dbox.ContainerUnexportBin(s.InternalName, binary, s.IsRootfull)
}
{
internalName := genInternalName(name)
return &SubSystem{
InternalName: internalName,
Name: name,
Stack: stack,
Home: home,
HasInit: hasInit,
IsManaged: isManaged,
IsRootfull: isRootfull,
IsUnshared: isUnshared,
HasNvidiaIntegration: hasNvidiaIntegration,
Hostname: hostname,
}, nil
}
{
return fmt.Sprintf("apx-%s", strings.ReplaceAll(strings.ToLower(name), " ", "-"))
}
{
home, err := os.UserHomeDir()
if err != nil {
return map[string]map[string]string{}
}
binPath := filepath.Join(home, ".local", "bin")
if _, err := os.Stat(binPath); os.IsNotExist(err) {
return map[string]map[string]string{}
}
binDir := os.DirFS(binPath)
binaries := map[string]map[string]string{}
err = fs.WalkDir(binDir, ".", func(path string, d fs.DirEntry, err error) error {
if err != nil {
return err
}
if d.IsDir() {
return nil
}
file, err := os.Open(filepath.Join(binPath, path))
if err != nil {
return err
}
defer file.Close()
scanner := bufio.NewScanner(file)
const maxTokenSize = 1024 * 1024
buf := make([]byte, maxTokenSize)
scanner.Buffer(buf, maxTokenSize)
for scanner.Scan() {
if scanner.Text() == "# distrobox_binary" {
scanner.Scan()
if strings.HasSuffix(scanner.Text(), internalName) {
name := filepath.Base(path)
binaries[name] = map[string]string{
"Exec": path,
// "Icon": pIcon,
"Name": name,
// "GenericName": pGenericName,
}
}
break
}
}
if err := scanner.Err(); err != nil {
return err
}
return nil
})
if err != nil {
fmt.Printf("error reading binaries: %s\n", err)
return map[string]map[string]string{}
}
return binaries
}
{
home, err := os.UserHomeDir()
if err != nil {
return map[string]map[string]string{}
}
files, err := filepath.Glob(fmt.Sprintf("%s/.local/share/applications/%s-*.desktop", home, internalName))
if err != nil {
return map[string]map[string]string{}
}
programs := map[string]map[string]string{}
for _, file := range files {
f, err := os.Open(file)
if err != nil {
return map[string]map[string]string{}
}
defer f.Close()
data, err := io.ReadAll(f)
if err != nil {
continue
}
pName := ""
pExec := ""
pIcon := ""
pGenericName := ""
for _, line := range strings.Split(string(data), "\n") {
if strings.HasPrefix(line, "Name=") {
pName = strings.TrimPrefix(line, "Name=")
pName = strings.ReplaceAll(pName, fmt.Sprintf(" on %s", name), "")
}
if strings.HasPrefix(line, "Exec=") {
pExec = strings.TrimPrefix(line, "Exec=")
}
if strings.HasPrefix(line, "Icon=") {
pIcon = strings.TrimPrefix(line, "Icon=")
}
if strings.HasPrefix(line, "GenericName=") {
pGenericName = strings.TrimPrefix(line, "GenericName=")
}
}
if pName != "" && pExec != "" {
programs[pName] = map[string]string{
"Exec": pExec,
"Icon": pIcon,
"Name": pName,
"GenericName": pGenericName,
}
}
}
return programs
}
{
bins := findExportedBinaries(internalName)
progs := findExportedPrograms(internalName, name)
// If duplicate is found, give priority to application
for k, v := range progs {
bins[k] = v
}
return bins
}
{
dbox, err := NewDbox()
if err != nil {
return nil, err
}
internalName := genInternalName(name)
container, err := dbox.GetContainer(internalName, isRootFull)
if err != nil {
return nil, err
}
stack, err := LoadStack(container.Labels["stack"])
if err != nil {
return nil, err
}
return &SubSystem{
InternalName: internalName,
Name: container.Labels["name"],
Stack: stack,
Status: container.Status,
HasInit: container.Labels["hasInit"] == "true",
IsManaged: container.Labels["managed"] == "true",
IsRootfull: isRootFull,
IsUnshared: container.Labels["unshared"] == "true",
}, nil
}
{
dbox, err := NewDbox()
if err != nil {
return nil, err
}
containers, err := dbox.ListContainers(includeRootFull)
if err != nil {
return nil, err
}
subsystems := []*SubSystem{}
for _, container := range containers {
if _, ok := container.Labels["name"]; !ok {
// log.Printf("Container %s has no name label, skipping", container.Name)
continue
}
if !includeManaged {
if _, ok := container.Labels["managed"]; ok {
continue
}
}
stack, err := LoadStack(container.Labels["stack"])
if err != nil {
log.Printf("Error loading stack %s: %s", container.Labels["stack"], err)
continue
}
internalName := genInternalName(container.Labels["name"])
subsystem := &SubSystem{
InternalName: internalName,
Name: container.Labels["name"],
Stack: stack,
Status: container.Status,
ExportedPrograms: findExported(internalName, container.Labels["name"]),
}
subsystems = append(subsystems, subsystem)
}
return subsystems, nil
}
ListSubsystemForStack returns a list of subsystems for the specified stack.
{
dbox, err := NewDbox()
if err != nil {
return nil, err
}
containers, err := dbox.ListContainers(true)
if err != nil {
return nil, err
}
subsystems := []*SubSystem{}
for _, container := range containers {
if _, ok := container.Labels["name"]; !ok {
continue
}
stack, err := LoadStack(stackName)
if err != nil {
log.Printf("Error loading stack %s: %s", stackName, err)
continue
}
internalName := genInternalName(container.Labels["name"])
subsystem := &SubSystem{
InternalName: internalName,
Name: container.Labels["name"],
Stack: stack,
Status: container.Status,
ExportedPrograms: findExported(internalName, container.Labels["name"]),
}
if subsystem.Stack.Name == stack.Name {
subsystems = append(subsystems, subsystem)
}
}
return subsystems, nil
}
{
if os.Geteuid() != 0 {
if display {
fmt.Println("You must be root to run this command")
}
return false
}
return true
}
{
var response string
fmt.Print(s + " [y/N]: ")
fmt.Scanln(&response)
if response == "y" || response == "Y" {
return true
}
return false
}
{
user, err := user.Current()
if err != nil {
return "", err
}
cacheDir := filepath.Join(user.HomeDir, ".cache", "apx")
if _, err := os.Stat(cacheDir); os.IsNotExist(err) {
if err := os.MkdirAll(cacheDir, 0755); err != nil {
return "", err
}
}
fileName := filepath.Base(path)
newPath := filepath.Join(cacheDir, fileName)
pathContents, err := os.Open(path)
if err != nil {
return "", err
}
defer pathContents.Close()
newPathContents, err := os.Create(newPath)
if err != nil {
return "", err
}
defer newPathContents.Close()
_, err = newPathContents.ReadFrom(pathContents)
if err != nil {
return "", err
}
return newPath, nil
}
getPrettifiedDate returns a human readable date from a timestamp
{
t, err := time.Parse("2006-01-02 15:04:05.999999999 -0700 MST", date)
if err != nil {
return date
}
// If the date is less than 24h ago, return the time since
if t.After(time.Now().Add(-24 * time.Hour)) {
duration := time.Since(t).Round(time.Hour)
hours := int(duration.Hours())
return fmt.Sprintf("%dh ago", hours)
}
return t.Format("02 Jan 2006 15:04:05")
}
{
table := tablewriter.NewWriter(writer)
table.SetColumnSeparator("┊")
table.SetCenterSeparator("┼")
table.SetRowSeparator("┄")
table.SetHeaderLine(true)
table.SetAlignment(tablewriter.ALIGN_LEFT)
table.SetHeaderAlignment(tablewriter.ALIGN_LEFT)
table.SetRowLine(true)
return table
}
{
source, err := os.Open(src)
if err != nil {
return err
}
defer source.Close()
destination, err := os.Create(dst)
if err != nil {
return err
}
defer destination.Close()
_, err = io.Copy(destination, source)
if err != nil {
return err
}
return nil
}
{
const (
YML string = ".yml"
YAML string = ".yaml"
)
yamlFile := filepath.Join(basePath, fmt.Sprintf("%s%s", name, YAML))
ymlFile := filepath.Join(basePath, fmt.Sprintf("%s%s", name, YML))
if _, err := os.Stat(yamlFile); errors.Is(err, os.ErrNotExist) {
return ymlFile
}
return yamlFile
}
import "fmt"
import "github.com/vanilla-os/apx/v2/settings"
import "errors"
import "fmt"
import "log"
import "os"
import "os/exec"
import "strings"
import "syscall"
import "io/ioutil"
import "errors"
import "fmt"
import "log"
import "os"
import "os/exec"
import "strings"
import "errors"
import "fmt"
import "io"
import "os"
import "path/filepath"
import "strings"
import "gopkg.in/yaml.v2"
import "errors"
import "io"
import "os"
import "path/filepath"
import "gopkg.in/yaml.v2"
import "bufio"
import "fmt"
import "io"
import "io/fs"
import "log"
import "os"
import "path/filepath"
import "strings"
import "github.com/google/uuid"
import "errors"
import "fmt"
import "io"
import "os"
import "os/user"
import "path/filepath"
import "time"
import "github.com/olekukonko/tablewriter"