• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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