qemu.c revision 5ce01674d44addab570009ceddeaa08d64534092
1/* 2 * Copyright (C) 2008 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 file contains various functions used by all libhardware modules 18 * that support QEMU emulation 19 */ 20#include "qemu.h" 21#define LOG_TAG "hardware-qemu" 22#include <cutils/log.h> 23#include <cutils/properties.h> 24#include <cutils/sockets.h> 25#include <errno.h> 26#include <fcntl.h> 27#include <termios.h> 28#include <stdio.h> 29#include <stdarg.h> 30 31#define QEMU_DEBUG 0 32 33#if QEMU_DEBUG 34# define D(...) LOGD(__VA_ARGS__) 35#else 36# define D(...) ((void)0) 37#endif 38 39 40int 41qemu_check(void) 42{ 43 static int in_qemu = -1; 44 45 if (__builtin_expect(in_qemu < 0,0)) { 46 char propBuf[PROPERTY_VALUE_MAX]; 47 property_get("ro.kernel.qemu", propBuf, ""); 48 in_qemu = (propBuf[0] == '1'); 49 } 50 return in_qemu; 51} 52 53 54int 55qemu_channel_open( QemuChannel* channel, 56 const char* name, 57 int mode ) 58{ 59 int fd = -1; 60 61 /* initialize the channel is needed */ 62 if (!channel->is_inited) 63 { 64 int done = 0; 65 66 // try to connect to qemud socket first 67 do { 68 snprintf(channel->device, sizeof channel->device, 69 "qemud_%s", name); 70 71 fd = socket_local_client( channel->device, 72 ANDROID_SOCKET_NAMESPACE_RESERVED, 73 SOCK_STREAM ); 74 if (fd < 0) { 75 D("no '%s' control socket available: %s", 76 channel->device, strerror(errno)); 77 break; 78 } 79 close(fd); 80 channel->is_qemud = 1; 81 done = 1; 82 } while (0); 83 84 // otherwise, look for a kernel-provided device name 85 if (!done) do { 86 char key[PROPERTY_KEY_MAX]; 87 char prop[PROPERTY_VALUE_MAX]; 88 int ret; 89 90 ret = snprintf(key, sizeof key, "ro.kernel.android.%s", name); 91 if (ret >= (int)sizeof key) 92 break; 93 94 if (property_get(key, prop, "") == 0) { 95 D("no kernel-provided %s device name", name); 96 break; 97 } 98 99 ret = snprintf(channel->device, sizeof channel->device, 100 "/dev/%s", prop); 101 if (ret >= (int)sizeof channel->device) { 102 D("%s device name too long: '%s'", name, prop); 103 break; 104 } 105 channel->is_tty = !memcmp("/dev/tty", channel->device, 8); 106 done = 1; 107 108 } while (0); 109 110 channel->is_available = done; 111 channel->is_inited = 1; 112 } 113 114 /* try to open the file */ 115 if (!channel->is_available) { 116 fd = -1; 117 errno = ENOENT; 118 } else if (channel->is_qemud) { 119 do { 120 fd = socket_local_client( channel->device, 121 ANDROID_SOCKET_NAMESPACE_RESERVED, 122 SOCK_STREAM ); 123 } while (fd < 0 && errno == EINTR); 124 } else { 125 do { 126 fd = open(channel->device, mode); 127 } while (fd < 0 && errno == EINTR); 128 129 /* disable ECHO on serial lines */ 130 if (fd >= 0 && channel->is_tty) { 131 struct termios ios; 132 tcgetattr( fd, &ios ); 133 ios.c_lflag = 0; /* disable ECHO, ICANON, etc... */ 134 tcsetattr( fd, TCSANOW, &ios ); 135 } 136 } 137 return fd; 138} 139 140 141static int 142qemu_command_vformat( char* buffer, 143 int buffer_size, 144 const char* format, 145 va_list args ) 146{ 147 char header[5]; 148 int len; 149 150 if (buffer_size < 6) 151 return -1; 152 153 len = vsnprintf(buffer+4, buffer_size-4, format, args); 154 if (len >= buffer_size-4) 155 return -1; 156 157 snprintf(header, sizeof header, "%04x", len); 158 memcpy(buffer, header, 4); 159 return len + 4; 160} 161 162extern int 163qemu_command_format( char* buffer, 164 int buffer_size, 165 const char* format, 166 ... ) 167{ 168 va_list args; 169 int ret; 170 171 va_start(args, format); 172 ret = qemu_command_format(buffer, buffer_size, format, args); 173 va_end(args); 174 return ret; 175} 176 177 178static int 179qemu_control_fd(void) 180{ 181 static QemuChannel channel[1]; 182 int fd; 183 184 fd = qemu_channel_open( channel, "control", O_RDWR ); 185 if (fd < 0) { 186 D("%s: could not open control channel: %s", __FUNCTION__, 187 strerror(errno)); 188 } 189 return fd; 190} 191 192static int 193qemu_control_write( int fd, const char* cmd, int len ) 194{ 195 int len2; 196 do { 197 len2 = write(fd, cmd, len); 198 } while (len2 < 0 && errno == EINTR); 199 return len2; 200} 201 202static int 203qemu_control_read( int fd, char* buff, int len ) 204{ 205 int len2; 206 do { 207 len2 = read(fd, buff, len); 208 } while (len2 < 0 && errno == EINTR); 209 return len2; 210} 211 212static int 213qemu_control_send(const char* cmd, int len) 214{ 215 int fd, len2; 216 217 if (len < 0) { 218 errno = EINVAL; 219 return -1; 220 } 221 222 fd = qemu_control_fd(); 223 if (fd < 0) 224 return -1; 225 226 len2 = qemu_control_write(fd, cmd, len); 227 close(fd); 228 if (len2 != len) { 229 D("%s: could not send everything %d < %d", 230 __FUNCTION__, len2, len); 231 return -1; 232 } 233 return 0; 234} 235 236 237int 238qemu_control_command( const char* fmt, ... ) 239{ 240 va_list args; 241 char command[256]; 242 int len, fd; 243 244 va_start(args, fmt); 245 len = qemu_command_vformat( command, sizeof command, fmt, args ); 246 va_end(args); 247 248 if (len < 0 || len >= (int)sizeof command) { 249 if (len < 0) { 250 D("%s: could not send: %s", __FUNCTION__, strerror(errno)); 251 } else { 252 D("%s: too large %d > %d", __FUNCTION__, len, (int)(sizeof command)); 253 } 254 errno = EINVAL; 255 return -1; 256 } 257 258 return qemu_control_send( command, len ); 259} 260 261extern int qemu_control_query( const char* question, int questionlen, 262 char* answer, int answersize ) 263{ 264 int ret, fd, len, result = -1; 265 char header[5], *end; 266 267 if (questionlen <= 0) { 268 errno = EINVAL; 269 return -1; 270 } 271 272 fd = qemu_control_fd(); 273 if (fd < 0) 274 return -1; 275 276 ret = qemu_control_write( fd, question, questionlen ); 277 if (ret != questionlen) { 278 D("%s: could not write all: %d < %d", __FUNCTION__, 279 ret, questionlen); 280 goto Exit; 281 } 282 283 /* read a 4-byte header giving the length of the following content */ 284 ret = qemu_control_read( fd, header, 4 ); 285 if (ret != 4) { 286 D("%s: could not read header (%d != 4)", 287 __FUNCTION__, ret); 288 goto Exit; 289 } 290 291 header[5] = 0; 292 len = strtol( header, &end, 16 ); 293 if ( len < 0 || end == NULL || end != header+4 || len > answersize ) { 294 D("%s: could not parse header: '%s'", 295 __FUNCTION__, header); 296 goto Exit; 297 } 298 299 /* read the answer */ 300 ret = qemu_control_read( fd, answer, len ); 301 if (ret != len) { 302 D("%s: could not read all of answer %d < %d", 303 __FUNCTION__, ret, len); 304 goto Exit; 305 } 306 307 result = len; 308 309Exit: 310 close(fd); 311 return result; 312} 313