• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2019 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 package com.android.tradefed.device.cloud;
17 
18 import com.android.tradefed.device.TestDeviceOptions;
19 import com.android.tradefed.util.CommandResult;
20 import com.android.tradefed.util.CommandStatus;
21 import com.android.tradefed.util.IRunUtil;
22 
23 import java.util.Arrays;
24 import java.util.List;
25 
26 /** Utility to create another user in Cuttlefish VM. New user will allow to run a second device. */
27 public class MultiUserSetupUtil {
28 
29     /** Files that must be copied between users to avoid conflicting ownership */
30     private static final List<String> FILE_TO_BE_COPIED =
31             Arrays.asList("android-info.txt", "*.img");
32 
33     /** Files that can simply be shared between the different users */
34     private static final List<String> FILE_TO_BE_LINKED = Arrays.asList("bin", "config", "lib64");
35 
36     /** Setup a new remote user on an existing Cuttlefish VM. */
prepareRemoteUser( String username, GceAvdInfo remoteInstance, TestDeviceOptions options, IRunUtil runUtil, long timeoutMs)37     public static CommandResult prepareRemoteUser(
38             String username,
39             GceAvdInfo remoteInstance,
40             TestDeviceOptions options,
41             IRunUtil runUtil,
42             long timeoutMs) {
43         // First create the user
44         String createUserCommand =
45                 "sudo useradd " + username + " -G sudo,kvm,cvdnetwork -m -s /bin/bash -p '*'";
46         CommandResult createUserRes =
47                 RemoteSshUtil.remoteSshCommandExec(
48                         remoteInstance, options, runUtil, timeoutMs, createUserCommand.split(" "));
49         if (!CommandStatus.SUCCESS.equals(createUserRes.getStatus())) {
50             return createUserRes;
51         }
52         return null;
53     }
54 
55     /** Create the 'cvd-XX' user on the remote device if missing. */
addExtraCvdUser( int userId, GceAvdInfo remoteInstance, TestDeviceOptions options, IRunUtil runUtil, long timeoutMs)56     public static CommandResult addExtraCvdUser(
57             int userId,
58             GceAvdInfo remoteInstance,
59             TestDeviceOptions options,
60             IRunUtil runUtil,
61             long timeoutMs) {
62         String useridString = getUserNumber(userId);
63         String username = String.format("cvd-%s", useridString);
64         String createUserCommand =
65                 "sudo useradd " + username + " -G plugdev -m -s /bin/bash -p '*'";
66         CommandResult createUserRes =
67                 RemoteSshUtil.remoteSshCommandExec(
68                         remoteInstance, options, runUtil, timeoutMs, createUserCommand.split(" "));
69         if (!CommandStatus.SUCCESS.equals(createUserRes.getStatus())) {
70             if (createUserRes
71                     .getStderr()
72                     .contains(String.format("user '%s' already exists", username))) {
73                 return null;
74             }
75             return createUserRes;
76         }
77         return null;
78     }
79 
80     /** Setup the tuntap interface required to start the Android devices if they are missing. */
setupNetworkInterface( int userId, GceAvdInfo remoteInstance, TestDeviceOptions options, IRunUtil runUtil, long timeoutMs)81     public static CommandResult setupNetworkInterface(
82             int userId,
83             GceAvdInfo remoteInstance,
84             TestDeviceOptions options,
85             IRunUtil runUtil,
86             long timeoutMs) {
87         if (userId < 9) {
88             // TODO: use 'tuntap show' to check if interface exists already or not.
89             return null;
90         }
91         String useridString = getUserNumber(userId);
92         String mtap = String.format("cvd-mtap-%s", useridString);
93         String wtap = String.format("cvd-wtap-%s", useridString);
94         String addNetworkInterface = "sudo ip tuntap add dev %s mode tap group cvdnetwork";
95         String mtapCommand = String.format(addNetworkInterface, mtap);
96         CommandResult addNetworkInterfaceRes =
97                 RemoteSshUtil.remoteSshCommandExec(
98                         remoteInstance, options, runUtil, timeoutMs, mtapCommand.split(" "));
99         if (!CommandStatus.SUCCESS.equals(addNetworkInterfaceRes.getStatus())) {
100             return addNetworkInterfaceRes;
101         }
102 
103         String wtapCommand = String.format(addNetworkInterface, wtap);
104         addNetworkInterfaceRes =
105                 RemoteSshUtil.remoteSshCommandExec(
106                         remoteInstance, options, runUtil, timeoutMs, wtapCommand.split(" "));
107         if (!CommandStatus.SUCCESS.equals(addNetworkInterfaceRes.getStatus())) {
108             return addNetworkInterfaceRes;
109         }
110         return null;
111     }
112 
113     /** Setup a new remote user on an existing Cuttlefish VM. */
prepareRemoteHomeDir( String mainRootUser, String username, GceAvdInfo remoteInstance, TestDeviceOptions options, IRunUtil runUtil, long timeoutMs)114     public static CommandResult prepareRemoteHomeDir(
115             String mainRootUser,
116             String username,
117             GceAvdInfo remoteInstance,
118             TestDeviceOptions options,
119             IRunUtil runUtil,
120             long timeoutMs) {
121         StringBuilder copyCommandBuilder = new StringBuilder("sudo cp ");
122         for (String file : FILE_TO_BE_COPIED) {
123             copyCommandBuilder.append(" /home/" + mainRootUser + "/" + file);
124         }
125         copyCommandBuilder.append(" /home/" + username + "/");
126         CommandResult cpRes =
127                 RemoteSshUtil.remoteSshCommandExec(
128                         remoteInstance,
129                         options,
130                         runUtil,
131                         timeoutMs,
132                         copyCommandBuilder.toString().split(" "));
133         if (!CommandStatus.SUCCESS.equals(cpRes.getStatus())) {
134             return cpRes;
135         }
136         // Own the copied files
137         String chownUser = getChownCommand(username);
138         CommandResult chownRes =
139                 RemoteSshUtil.remoteSshCommandExec(
140                         remoteInstance, options, runUtil, timeoutMs, chownUser);
141         if (!CommandStatus.SUCCESS.equals(chownRes.getStatus())) {
142             return chownRes;
143         }
144         // Link files that can be shared between users
145         for (String file : FILE_TO_BE_LINKED) {
146             String copyDevice =
147                     "sudo ln -s /home/" + mainRootUser + "/" + file + " /home/" + username + "/";
148             CommandResult copyRes =
149                     RemoteSshUtil.remoteSshCommandExec(
150                             remoteInstance, options, runUtil, timeoutMs, copyDevice.split(" "));
151             if (!CommandStatus.SUCCESS.equals(copyRes.getStatus())) {
152                 return copyRes;
153             }
154         }
155         return null;
156     }
157 
158     /** Gets the command for a user to own the main directory. */
getChownCommand(String username)159     public static String getChownCommand(String username) {
160         return "find /home/" + username + " | sudo xargs chown " + username;
161     }
162 
163     /** Returns the user id string version that follow the remote device notation. */
getUserNumber(int userId)164     public static String getUserNumber(int userId) {
165         return (userId > 9) ? Integer.toString(userId) : "0" + Integer.toString(userId);
166     }
167 }
168