summaryrefslogtreecommitdiffhomepage
path: root/app/src/main/java/com/wireguard/android/util/ToolsInstaller.java
blob: fddcf98b70c67ba5865d31d4115e1e9d604643e1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
package com.wireguard.android.util;

import android.content.Context;
import android.system.ErrnoException;
import android.system.OsConstants;

import com.wireguard.android.Application.ApplicationContext;
import com.wireguard.android.Application.ApplicationScope;
import com.wireguard.android.util.RootShell.NoRootException;

import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;

import javax.inject.Inject;

/**
 * Helper to install WireGuard tools to the system partition.
 */

@ApplicationScope
public final class ToolsInstaller {
    private static final String[][] EXECUTABLES = {
            {"libwg.so", "wg"},
            {"libwg-quick.so", "wg-quick"},
    };
    private static final File[] INSTALL_DIRS = {
            new File("/system/xbin"),
            new File("/system/bin"),
    };
    private static final File INSTALL_DIR = getInstallDir();

    private final File localBinaryDir;
    private final File nativeLibraryDir;
    private final RootShell rootShell;

    @Inject
    public ToolsInstaller(@ApplicationContext final Context context, final RootShell rootShell) {
        localBinaryDir = new File(context.getCacheDir(), "bin");
        nativeLibraryDir = new File(context.getApplicationInfo().nativeLibraryDir);
        this.rootShell = rootShell;
    }

    private static File getInstallDir() {
        final String path = System.getenv("PATH");
        if (path == null)
            return INSTALL_DIRS[0];
        final List<String> paths = Arrays.asList(path.split(":"));
        for (final File dir : INSTALL_DIRS)
            if (paths.contains(dir.getPath()) && dir.isDirectory())
                return dir;
        return null;
    }

    public boolean areInstalled() {
        if (INSTALL_DIR == null)
            return false;
        final StringBuilder script = new StringBuilder();
        for (final String[] names : EXECUTABLES) {
            script.append(String.format("cmp -s '%s' '%s' && ",
                    new File(nativeLibraryDir, names[0]),
                    new File(INSTALL_DIR, names[1])));
        }
        script.append("exit ").append(OsConstants.EALREADY);
        try {
            return rootShell.run(null, script.toString()) == OsConstants.EALREADY;
        } catch (final ErrnoException | IOException | NoRootException ignored) {
            return false;
        }
    }

    public boolean areSymlinked() {
        final StringBuilder script = new StringBuilder();
        for (final String[] names : EXECUTABLES) {
            script.append(String.format("test '%s' -ef '%s' && ",
                    new File(nativeLibraryDir, names[0]),
                    new File(localBinaryDir, names[1])));
        }
        script.append("exit ").append(OsConstants.EALREADY);
        try {
            return rootShell.run(null, script.toString()) == OsConstants.EALREADY;
        } catch (final ErrnoException | IOException | NoRootException ignored) {
            return false;
        }
    }

    public int install() {
        if (INSTALL_DIR == null)
            return OsConstants.ENOENT;
        final StringBuilder script = new StringBuilder("set -ex"
                + "; trap 'mount -o ro,remount /system' EXIT; mount -o rw,remount /system");
        for (final String[] names : EXECUTABLES) {
            final File destination = new File(INSTALL_DIR, names[1]);
            script.append(String.format("; cp '%s' '%s'; chmod 755 '%s'; restorecon '%s' ",
                    new File(nativeLibraryDir, names[0]), destination, destination, destination));
        }
        try {
            return rootShell.run(null, script.toString());
        } catch (final ErrnoException e) {
            return e.errno;
        } catch (final IOException ignored) {
            return OsConstants.EXIT_FAILURE;
        } catch (final NoRootException ignored) {
            return OsConstants.EACCES;
        }
    }

    public int symlink() {
        final StringBuilder script = new StringBuilder("set -ex");
        for (final String[] names : EXECUTABLES) {
            script.append(String.format("; ln -fns '%s' '%s'",
                    new File(nativeLibraryDir, names[0]),
                    new File(localBinaryDir, names[1])));
        }
        try {
            return rootShell.run(null, script.toString());
        } catch (final ErrnoException e) {
            return e.errno;
        } catch (final IOException ignored) {
            return OsConstants.EXIT_FAILURE;
        } catch (final NoRootException ignored) {
            return OsConstants.EACCES;
        }
    }
}