1 /*
2 * Copyright 2021 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
17 #define LOG_TAG "VirtualMachine"
18
19 #include <aidl/android/system/virtualizationservice/IVirtualMachine.h>
20 #include <android-base/scopeguard.h>
21 #include <android-base/strings.h>
22 #include <android/binder_auto_utils.h>
23 #include <android/binder_ibinder_jni.h>
24 #include <fcntl.h>
25 #include <jni.h>
26 #include <log/log.h>
27 #include <nativehelper/JNIHelp.h>
28 #include <nativehelper/JNIPlatformHelp.h>
29 #include <nativehelper/ScopedLocalRef.h>
30 #include <pty.h>
31 #include <string.h>
32 #include <sys/stat.h>
33 #include <termios.h>
34 #include <unistd.h>
35
36 #include <binder_rpc_unstable.hpp>
37 #include <string>
38 #include <tuple>
39
40 #include "common.h"
41
42 namespace {
43
throwIOException(JNIEnv * env,const std::string & msg)44 void throwIOException(JNIEnv *env, const std::string &msg) {
45 jniThrowException(env, "java/io/IOException", msg.c_str());
46 }
47
48 } // namespace
49
50 extern "C" JNIEXPORT jobject JNICALL
Java_android_system_virtualmachine_VirtualMachine_nativeConnectToVsockServer(JNIEnv * env,jclass clazz,jobject vmBinder,jint port)51 Java_android_system_virtualmachine_VirtualMachine_nativeConnectToVsockServer(
52 JNIEnv* env, [[maybe_unused]] jclass clazz, jobject vmBinder, jint port) {
53 using aidl::android::system::virtualizationservice::IVirtualMachine;
54 using ndk::ScopedFileDescriptor;
55 using ndk::SpAIBinder;
56
57 auto vm = IVirtualMachine::fromBinder(SpAIBinder{AIBinder_fromJavaBinder(env, vmBinder)});
58
59 std::tuple args{env, vm.get(), port};
60 using Args = decltype(args);
61
62 auto requestFunc = [](void* param) {
63 auto [env, vm, port] = *static_cast<Args*>(param);
64
65 ScopedFileDescriptor fd;
66 if (auto status = vm->connectVsock(port, &fd); !status.isOk()) {
67 env->ThrowNew(env->FindClass("android/system/virtualmachine/VirtualMachineException"),
68 ("Failed to connect vsock: " + status.getDescription()).c_str());
69 return -1;
70 }
71
72 // take ownership
73 int ret = fd.get();
74 *fd.getR() = -1;
75
76 return ret;
77 };
78
79 RpcSessionHandle session;
80 // We need a thread pool to be able to support linkToDeath, or callbacks
81 // (b/268335700). These threads are currently created eagerly, so we don't
82 // want too many. The number 1 is chosen after some discussion, and to match
83 // the server-side default (mMaxThreads on RpcServer).
84 ARpcSession_setMaxIncomingThreads(session.get(), 1);
85 auto client = ARpcSession_setupPreconnectedClient(session.get(), requestFunc, &args);
86 return AIBinder_toJavaBinder(env, client);
87 }
88
89 extern "C" JNIEXPORT void JNICALL
Java_android_system_virtualmachine_VirtualMachine_nativeOpenPtyRawNonblock(JNIEnv * env,jclass clazz,jobject resultCallback)90 Java_android_system_virtualmachine_VirtualMachine_nativeOpenPtyRawNonblock(
91 JNIEnv *env, [[maybe_unused]] jclass clazz, jobject resultCallback) {
92 int pm, ps;
93 // man openpty says: "Nobody knows how much space should be reserved for name."
94 // but on modern Linux the format of the pts name is always `/dev/pts/[0-9]+`
95 // Realistically speaking, a buffer of 32 bytes leaves us with 22 digits for the pts number,
96 // which should be more than enough.
97 // NOTE: bionic implements openpty() with internal name buffer of size 32, musl 20.
98 char name[32];
99 if (openpty(&pm, &ps, name, nullptr, nullptr)) {
100 throwIOException(env, "openpty(): " + android::base::ErrnoNumberAsString(errno));
101 return;
102 }
103 fcntl(pm, F_SETFD, FD_CLOEXEC);
104 fcntl(ps, F_SETFD, FD_CLOEXEC);
105 name[sizeof(name) - 1] = '\0';
106 // Set world RW so adb shell can talk to the pts.
107 chmod(name, 0666);
108
109 if (int flags = fcntl(pm, F_GETFL, 0); flags < 0) {
110 throwIOException(env, "fcntl(F_GETFL): " + android::base::ErrnoNumberAsString(errno));
111 return;
112 } else if (fcntl(pm, F_SETFL, flags | O_NONBLOCK) < 0) {
113 throwIOException(env, "fcntl(F_SETFL): " + android::base::ErrnoNumberAsString(errno));
114 return;
115 }
116
117 android::base::ScopeGuard cleanup_handler([=] {
118 close(ps);
119 close(pm);
120 });
121
122 struct termios tio;
123 if (tcgetattr(pm, &tio)) {
124 throwIOException(env, "tcgetattr(): " + android::base::ErrnoNumberAsString(errno));
125 return;
126 }
127 cfmakeraw(&tio);
128 if (tcsetattr(pm, TCSANOW, &tio)) {
129 throwIOException(env, "tcsetattr(): " + android::base::ErrnoNumberAsString(errno));
130 return;
131 }
132
133 jobject mfd = jniCreateFileDescriptor(env, pm);
134 if (mfd == nullptr) {
135 return;
136 }
137 jobject sfd = jniCreateFileDescriptor(env, ps);
138 if (sfd == nullptr) {
139 return;
140 }
141 size_t len = strlen(name);
142 ScopedLocalRef<jbyteArray> ptsName(env, env->NewByteArray(len));
143 if (ptsName.get() != nullptr) {
144 env->SetByteArrayRegion(ptsName.get(), 0, len, (jbyte *)name);
145 }
146 ScopedLocalRef<jclass> callback_class(env, env->GetObjectClass(resultCallback));
147 jmethodID mid = env->GetMethodID(callback_class.get(), "apply",
148 "(Ljava/io/FileDescriptor;Ljava/io/FileDescriptor;[B)V");
149 if (mid == nullptr) {
150 return;
151 }
152
153 env->CallVoidMethod(resultCallback, mid, mfd, sfd, ptsName.get());
154 // FD ownership is transferred to the callback, reset the auto-close hander.
155 cleanup_handler.Disable();
156 }
157