1/*
2 * Copyright (C) 2010 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package vogar;
18
19import com.google.common.annotations.VisibleForTesting;
20import com.google.common.collect.ImmutableList;
21import java.io.File;
22import java.io.FileNotFoundException;
23import java.io.IOException;
24import java.util.List;
25import java.util.regex.Matcher;
26import java.util.regex.Pattern;
27import vogar.android.DeviceFilesystem;
28import vogar.commands.Command;
29
30/**
31 * Runs actions on a remote host using SSH.
32 */
33public final class SshTarget extends Target {
34    private final Log log;
35    private final String host;
36    private final int port;
37    private final DeviceFilesystem deviceFilesystem;
38    private final ImmutableList<String> sshCommandPrefixList;
39
40    @VisibleForTesting
41    public SshTarget(Log log, String hostAndPort) {
42        this.log = log;
43        int colon = hostAndPort.indexOf(":");
44        if (colon != -1) {
45            host = hostAndPort.substring(0, colon);
46            port = Integer.parseInt(hostAndPort.substring(colon + 1));
47        } else {
48            host = hostAndPort;
49            port = 22;
50        }
51        sshCommandPrefixList = ImmutableList.of("ssh", "-p", Integer.toString(port), host, "-C");
52        deviceFilesystem = new DeviceFilesystem(log, sshCommandPrefixList);
53    }
54
55    public static File defaultDeviceDir() {
56        return new File("/data/local/tmp/vogar");
57    }
58
59    @Override protected ImmutableList<String> targetProcessPrefix() {
60        return sshCommandPrefixList;
61    }
62
63    @Override public void await(File nonEmptyDirectory) {
64    }
65
66    @Override public void rm(File file) {
67        new Command.Builder(log)
68                .args(sshCommandPrefixList)
69                .args("rm", "-r", file.getPath())
70                .permitNonZeroExitStatus(true)
71                .execute();
72    }
73
74    @Override public String getDeviceUserName() {
75        // TODO: move this to device set up
76        // The default environment doesn't include $USER, so dalvikvm doesn't set "user.name".
77        // DeviceDalvikVm uses this to set "user.name" manually with -D.
78        String line = new Command.Builder(log)
79                .args(sshCommandPrefixList)
80                .args("id")
81                .execute().get(0);
82        // TODO: use 'id -un' when we don't need to support anything older than M
83        Matcher m = Pattern.compile("^uid=\\d+\\((\\S+)\\) gid=\\d+\\(\\S+\\).*").matcher(line);
84        return m.matches() ? m.group(1) : "root";
85    }
86
87    @Override public void mkdirs(File file) {
88        deviceFilesystem.mkdirs(file);
89    }
90
91    @Override public void forwardTcp(final int port) {
92        try {
93            new Command(log, "ssh", "-p", Integer.toString(port), host,
94                    "-L", port + ":" + host + ":" + port, "-N").start();
95        } catch (IOException e) {
96            throw new RuntimeException(e);
97        }
98    }
99
100    @Override public void push(File local, File remote) {
101        new Command(log, "scp", "-r", "-P", Integer.toString(port),
102                local.getPath(), host + ":" + remote.getPath()).execute();
103    }
104
105    @Override public List<File> ls(File directory) throws FileNotFoundException {
106        return deviceFilesystem.ls(directory);
107    }
108
109    @Override public void pull(File remote, File local) {
110        new Command(log, "scp", "-r", "-P", Integer.toString(port),
111                host + ":" + remote.getPath(), local.getPath()).execute();
112    }
113}
114