1 /*
2  * Copyright (C) 2009 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 /* this program is used to read a set of system properties and their values
18  * from the emulator program and set them in the currently-running emulated
19  * system. It does so by connecting to the 'boot-properties' qemud service.
20  *
21  * This program should be run as root and called from
22  * /system/etc/init.ranchu.rc exclusively.
23  */
24 
25 #include <android-base/properties.h>
26 #include <android-base/unique_fd.h>
27 #include <cutils/properties.h>
28 #include <debug.h>
29 #include <qemu_pipe_bp.h>
30 #include <qemud.h>
31 #include <stdio.h>
32 #include <string.h>
33 #include <unistd.h>
34 
35 #include <string_view>
36 
37 namespace {
38 constexpr char kBootPropertiesService[] = "boot-properties";
39 constexpr char kHeartbeatService[] = "QemuMiscPipe";
40 
41 // qemu-props will not set these properties.
42 const char* const k_properties_to_ignore[] = {
43     "dalvik.vm.heapsize",
44     "ro.opengles.version",
45     "qemu.adb.secure",
46     nullptr,
47 };
48 
49 // These properties will not be prefixed with "vendor.".
50 const char* const k_system_properties[] = {
51     "qemu.sf.lcd_density",
52     "qemu.hw.mainkeys",
53     nullptr,
54 };
55 
check_if_property_in_list(const char * prop_name,const char * const * prop_list)56 bool check_if_property_in_list(const char* prop_name, const char* const* prop_list) {
57     for (; *prop_list; ++prop_list) {
58         if (!strcmp(prop_name, *prop_list)) {
59             return true;
60         }
61     }
62     return false;
63 }
64 
65 // We don't want to rename properties which already have the prefix
66 // or the system properties.
need_prepend_prefix(const char * prop,const std::string_view prefix)67 bool need_prepend_prefix(const char* prop, const std::string_view prefix) {
68     return strncmp(prefix.data(), prop, prefix.size()) &&
69            !check_if_property_in_list(prop, k_system_properties);
70 }
71 
72 // Deprecated, consider replacing with androidboot
setBootProperties()73 int setBootProperties() {
74     using android::base::unique_fd;
75     unique_fd qemud;
76 
77     for (int tries = 5; tries > 0; --tries) {
78         qemud = unique_fd(qemud_channel_open(kBootPropertiesService));
79         if (qemud.ok()) {
80             break;
81         } else if (tries > 1) {
82             sleep(1);
83         } else {
84             return FAILURE(1);
85         }
86     }
87 
88     if (qemud_channel_send(qemud.get(), "list", -1) < 0) {
89         return FAILURE(1);
90     }
91 
92     while (true) {
93         char temp[PROPERTY_KEY_MAX + PROPERTY_VALUE_MAX + 2];
94         const int len = qemud_channel_recv(qemud.get(), temp, sizeof(temp) - 1);
95 
96         /* lone NUL-byte signals end of properties */
97         if (len < 0 || len > (sizeof(temp) - 1) || !temp[0]) {
98             break;
99         }
100 
101         temp[len] = '\0';
102         char* prop_value = strchr(temp, '=');
103         if (!prop_value) {
104             continue;
105         }
106 
107         *prop_value = 0;
108         ++prop_value;
109 
110         if (check_if_property_in_list(temp, k_properties_to_ignore)) {
111             continue;
112         }
113 
114         char renamed_property[sizeof(temp)];
115         const char* final_prop_name = nullptr;
116 
117         using namespace std::literals;
118         static constexpr std::string_view k_vendor_prefix = "vendor."sv;
119         if (need_prepend_prefix(temp, k_vendor_prefix)) {
120             snprintf(renamed_property, sizeof(renamed_property), "%.*s%s",
121                      int(k_vendor_prefix.size()), k_vendor_prefix.data(), temp);
122 
123             final_prop_name = renamed_property;
124         } else {
125             final_prop_name = temp;
126         }
127 
128         if (property_set(final_prop_name, prop_value) < 0) {
129             ALOGW("could not set property '%s' to '%s'", final_prop_name, prop_value);
130         } else {
131             ALOGI("successfully set property '%s' to '%s'", final_prop_name, prop_value);
132         }
133     }
134 
135     return 0;
136 }
137 
138 }  // namespace
139 
140 static int s_QemuMiscPipe = -1;
141 static void sendHeartBeat();
142 static void sendMessage(const char* mesg);
143 static void closeMiscPipe();
144 extern void parse_virtio_serial();
145 
main(const int argc,const char * argv[])146 int main(const int argc, const char* argv[])
147 {
148     if ((argc == 2) && !strcmp(argv[1], "bootcomplete")) {
149         sendMessage("bootcomplete");
150         return 0;
151     }
152 
153     int r = setBootProperties();
154     if (r) {
155         return r;
156     }
157 
158     parse_virtio_serial();
159 
160     sendHeartBeat();
161     while (s_QemuMiscPipe >= 0) {
162         if (android::base::WaitForProperty(
163                     "vendor.qemu.dev.bootcomplete", "1",
164                     /*relative_timeout=*/std::chrono::seconds(5))) {
165             break;
166         }
167         sendHeartBeat();
168     }
169 
170     while (s_QemuMiscPipe >= 0) {
171         usleep(30 * 1000000);
172         sendHeartBeat();
173     }
174 
175     closeMiscPipe();
176     return 0;
177 }
178 
sendHeartBeat()179 void sendHeartBeat() {
180     sendMessage("heartbeat");
181 }
182 
sendMessage(const char * mesg)183 void sendMessage(const char* mesg) {
184    if (s_QemuMiscPipe < 0) {
185         s_QemuMiscPipe = qemu_pipe_open_ns(NULL, kHeartbeatService, O_RDWR);
186         if (s_QemuMiscPipe < 0) {
187             ALOGE("failed to open %s", kHeartbeatService);
188             return;
189         }
190     }
191 
192     int32_t cmd_len = strlen(mesg) + 1; //including trailing '\0'
193     qemu_pipe_write_fully(s_QemuMiscPipe, &cmd_len, sizeof(cmd_len));
194     qemu_pipe_write_fully(s_QemuMiscPipe, mesg, cmd_len);
195 
196     int r = qemu_pipe_read_fully(s_QemuMiscPipe, &cmd_len, sizeof(cmd_len));
197     if (r || (cmd_len < 0)) {
198         closeMiscPipe();
199         return;
200     }
201 
202     while (cmd_len > 0) {
203         char buf[64];
204         const size_t chunk = std::min<size_t>(cmd_len, sizeof(buf));
205         r = qemu_pipe_read_fully(s_QemuMiscPipe, buf, chunk);
206         if (r) {
207             closeMiscPipe();
208             return;
209         } else {
210             cmd_len -= chunk;
211         }
212     }
213 }
214 
closeMiscPipe()215 void closeMiscPipe() {
216     if (s_QemuMiscPipe >= 0) {
217         close(s_QemuMiscPipe);
218         s_QemuMiscPipe = -1;
219     }
220 }
221