1 /*
2 * Copyright 2007 The Android Open Source Project
3 *
4 * Fake device support.
5 */
6 /*
7 Implementation notes:
8
9 There are a couple of basic scenarios, exemplified by the "fb" and
10 "events" devices. The framebuffer driver is pretty simple, handling a
11 few ioctl()s and managing a stretch of memory. We can just intercept a
12 few calls. The input event driver can be used in a select() or poll()
13 call with other file descriptors, which either requires us to do some
14 fancy tricks with select() and poll(), or requires that we return a real
15 file descriptor (perhaps based on a socketpair).
16
17 We have three basic approaches to dealing with "fake" file descriptors:
18
19 (1) Always use real fds. We can dup() an open /dev/null to get a number
20 for the cases where we don't need a socketpair.
21 (2) Always use fake fds with absurdly high numeric values. Testing to see
22 if the fd is one we handle is trivial (range check). This doesn't
23 work for select(), which uses fd bitmaps accessed through macros.
24 (3) Use a mix of real and fake fds, in a high range (512-1023). Because
25 it's in the "real" range, we can pass real fds around for things that
26 are handed to poll() and select(), but because of the high numeric
27 value we *should* be able to get away with a trivial range check.
28
29 Approach (1) is the most portable and least likely to break, but the
30 efficiencies gained in approach (2) make it more desirable. There is
31 a small risk of application fds wandering into our range, but we can
32 minimize that by asserting on a "guard zone" and/or obstructing dup2().
33 (We can also dup2(/dev/null) to "reserve" our fds, but that wastes
34 resources.)
35 */
36
37 #include "Common.h"
38
39 #include <stdlib.h>
40 #include <string.h>
41 #include <sys/types.h>
42 #include <sys/socket.h>
43 #include <assert.h>
44 #include <fnmatch.h>
45
46 /*
47 * Devices we intercept.
48 *
49 * Needed:
50 * /dev/alarm
51 * radio
52 */
53 typedef FakeDev* (*wsFileHook)(const char *path, int flags);
54
55 typedef struct FakedPath {
56 const char *pathexpr;
57 wsFileHook hook;
58 } FakedPath;
59
60 FakedPath fakedpaths[] =
61 {
62 { "/dev/graphics/fb0", wsOpenDevFb },
63 { "/dev/hw3d", NULL },
64 { "/dev/eac", wsOpenDevAudio },
65 { "/dev/tty0", wsOpenDevConsoleTty },
66 { "/dev/input/event0", wsOpenDevEvent },
67 { "/dev/input/*", NULL },
68 { "/dev/log/*", wsOpenDevLog },
69 { "/sys/class/power_supply/*", wsOpenDevPower },
70 { "/sys/power/state", wsOpenSysPower },
71 { "/sys/power/wake_lock", wsOpenSysPower },
72 { "/sys/power/wake_unlock", wsOpenSysPower },
73 { "/sys/devices/platform/android-vibrator/enable", wsOpenDevVibrator },
74 { "/sys/qemu_trace/*", NULL },
75 { NULL, NULL }
76 };
77
78
79 /*
80 * Generic drop-in for an unimplemented call.
81 *
82 * Returns -1, which conveniently is the same as MAP_FAILED for mmap.
83 */
notImplemented(FakeDev * dev,const char * callName)84 static int notImplemented(FakeDev* dev, const char* callName)
85 {
86 wsLog("WARNING: unimplemented %s() on '%s' %p\n",
87 callName, dev->debugName, dev->state);
88 errno = kNoHandlerError;
89 return -1;
90 }
91
92 /*
93 * Default implementations. We want to log as much information as we can
94 * so that we can fill in the missing implementation.
95 *
96 * TODO: for some or all of these we will want to display the full arg list.
97 */
noClose(FakeDev * dev,...)98 static int noClose(FakeDev* dev, ...)
99 {
100 return 0;
101 }
noDup(FakeDev * dev,...)102 static FakeDev* noDup(FakeDev* dev, ...)
103 {
104 notImplemented(dev, "dup");
105 return NULL;
106 }
noRead(FakeDev * dev,...)107 static int noRead(FakeDev* dev, ...)
108 {
109 return notImplemented(dev, "read");
110 }
noReadv(FakeDev * dev,...)111 static int noReadv(FakeDev* dev, ...)
112 {
113 return notImplemented(dev, "readv");
114 }
noWrite(FakeDev * dev,...)115 static int noWrite(FakeDev* dev, ...)
116 {
117 return notImplemented(dev, "write");
118 }
noWritev(FakeDev * dev,...)119 static int noWritev(FakeDev* dev, ...)
120 {
121 return notImplemented(dev, "writev");
122 }
noMmap(FakeDev * dev,...)123 static int noMmap(FakeDev* dev, ...)
124 {
125 return notImplemented(dev, "mmap");
126 }
noIoctl(FakeDev * dev,...)127 static int noIoctl(FakeDev* dev, ...)
128 {
129 return notImplemented(dev, "ioctl");
130 }
131
132
133 /*
134 * Create a new FakeDev entry.
135 *
136 * We mark the fd slot as "used" in the bitmap, but don't add it to the
137 * table yet since the entry is not fully prepared.
138 */
wsCreateFakeDev(const char * debugName)139 FakeDev* wsCreateFakeDev(const char* debugName)
140 {
141 FakeDev* newDev;
142 int cc;
143
144 assert(debugName != NULL);
145
146 newDev = (FakeDev*) calloc(1, sizeof(FakeDev));
147 if (newDev == NULL)
148 return NULL;
149
150 newDev->debugName = strdup(debugName);
151 newDev->state = NULL;
152
153 newDev->close = (Fake_close) noClose;
154 newDev->dup = (Fake_dup) noDup;
155 newDev->read = (Fake_read) noRead;
156 newDev->readv = (Fake_readv) noReadv;
157 newDev->write = (Fake_write) noWrite;
158 newDev->writev = (Fake_writev) noWritev;
159 newDev->mmap = (Fake_mmap) noMmap;
160 newDev->ioctl = (Fake_ioctl) noIoctl;
161
162 /*
163 * Allocate a new entry. The bit vector map is really only used as a
164 * performance boost in the current implementation.
165 */
166 cc = pthread_mutex_lock(&gWrapSim.fakeFdLock); assert(cc == 0);
167 int newfd = wsAllocBit(gWrapSim.fakeFdMap);
168 cc = pthread_mutex_unlock(&gWrapSim.fakeFdLock); assert(cc == 0);
169
170 if (newfd < 0) {
171 wsLog("WARNING: ran out of 'fake' file descriptors\n");
172 free(newDev);
173 return NULL;
174 }
175 newDev->fd = newfd + kFakeFdBase;
176 newDev->otherFd = -1;
177 assert(gWrapSim.fakeFdList[newDev->fd - kFakeFdBase] == NULL);
178
179 return newDev;
180 }
181
182 /*
183 * Create a new FakeDev entry, and open a file descriptor that actually
184 * works.
185 */
wsCreateRealFakeDev(const char * debugName)186 FakeDev* wsCreateRealFakeDev(const char* debugName)
187 {
188 FakeDev* newDev = wsCreateFakeDev(debugName);
189 if (newDev == NULL)
190 return newDev;
191
192 int fds[2];
193
194 if (socketpair(AF_UNIX, SOCK_STREAM, 0, fds) < 0) {
195 wsLog("socketpair() failed: %s\n", strerror(errno));
196 wsFreeFakeDev(newDev);
197 return NULL;
198 }
199
200 if (dup2(fds[0], newDev->fd) < 0) {
201 wsLog("dup2(%d,%d) failed: %s\n",
202 fds[0], newDev->fd, strerror(errno));
203 wsFreeFakeDev(newDev);
204 return NULL;
205 }
206 close(fds[0]);
207
208 /* okay to leave this one in the "normal" range; not visible to app */
209 newDev->otherFd = fds[1];
210
211 return newDev;
212 }
213
214 /*
215 * Free fake device entry.
216 */
wsFreeFakeDev(FakeDev * dev)217 void wsFreeFakeDev(FakeDev* dev)
218 {
219 if (dev == NULL)
220 return;
221
222 wsLog("## closing/freeing '%s' (%d/%d)\n",
223 dev->debugName, dev->fd, dev->otherFd);
224
225 /*
226 * If we assigned a file descriptor slot, free it up.
227 */
228 if (dev->fd >= 0) {
229 int cc;
230
231 gWrapSim.fakeFdList[dev->fd - kFakeFdBase] = NULL;
232
233 cc = pthread_mutex_lock(&gWrapSim.fakeFdLock); assert(cc == 0);
234 wsFreeBit(gWrapSim.fakeFdMap, dev->fd - kFakeFdBase);
235 cc = pthread_mutex_unlock(&gWrapSim.fakeFdLock); assert(cc == 0);
236 }
237 if (dev->otherFd >= 0)
238 close(dev->otherFd);
239
240 if (dev->debugName) free(dev->debugName);
241 free(dev);
242 }
243
244 /*
245 * Map a file descriptor to a fake device.
246 *
247 * Returns NULL if there's no corresponding entry.
248 */
wsFakeDevFromFd(int fd)249 FakeDev* wsFakeDevFromFd(int fd)
250 {
251 /* quick range test */
252 if (fd < kFakeFdBase || fd >= kFakeFdBase + kMaxFakeFdCount)
253 return NULL;
254
255 return gWrapSim.fakeFdList[fd - kFakeFdBase];
256 }
257
258
259 /*
260 * Check to see if we're opening a device that we want to fake out.
261 *
262 * We return a file descriptor >= 0 on success, -1 if we're not interested,
263 * or -2 if we explicitly want to pretend that the device doesn't exist.
264 */
wsInterceptDeviceOpen(const char * pathName,int flags)265 int wsInterceptDeviceOpen(const char* pathName, int flags)
266 {
267 FakedPath* p = fakedpaths;
268
269 while (p->pathexpr) {
270 if (fnmatch(p->pathexpr, pathName, 0) == 0) {
271 if (p->hook != NULL) {
272 FakeDev* dev = p->hook(pathName, flags);
273 if (dev != NULL) {
274 /*
275 * Now that the device entry is ready, add it to the list.
276 */
277 wsLog("## created fake dev %d: '%s' %p\n",
278 dev->fd, dev->debugName, dev->state);
279 gWrapSim.fakeFdList[dev->fd - kFakeFdBase] = dev;
280 return dev->fd;
281 }
282 } else {
283 wsLog("## rejecting attempt to open %s\n", pathName);
284 errno = ENOENT;
285 return -2;
286 }
287 break;
288 }
289 p++;
290 }
291 return -1;
292 }
293
294 /*
295 * Check to see if we're accessing a device that we want to fake out.
296 * Returns 0 if the device can be (fake) opened with the given mode,
297 * -1 if it can't, -2 if it can't and we don't want to allow fallback
298 * to the host-device either.
299 * TODO: actually check the mode.
300 */
wsInterceptDeviceAccess(const char * pathName,int mode)301 int wsInterceptDeviceAccess(const char *pathName, int mode)
302 {
303 FakedPath *p = fakedpaths;
304
305 while (p->pathexpr) {
306 if (fnmatch(p->pathexpr, pathName, 0) == 0) {
307 if (p->hook) {
308 return 0;
309 } else {
310 wsLog("## rejecting attempt to open %s\n", pathName);
311 errno = ENOENT;
312 return -2;
313 }
314 break;
315 }
316 p++;
317 }
318 errno = ENOENT;
319 return -1;
320 }
321