Home | History | Annotate | Download | only in wrapsim
      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  */
     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  */
     98 static int noClose(FakeDev* dev, ...)
     99 {
    100     return 0;
    101 }
    102 static FakeDev* noDup(FakeDev* dev, ...)
    103 {
    104     notImplemented(dev, "dup");
    105     return NULL;
    106 }
    107 static int noRead(FakeDev* dev, ...)
    108 {
    109     return notImplemented(dev, "read");
    110 }
    111 static int noReadv(FakeDev* dev, ...)
    112 {
    113     return notImplemented(dev, "readv");
    114 }
    115 static int noWrite(FakeDev* dev, ...)
    116 {
    117     return notImplemented(dev, "write");
    118 }
    119 static int noWritev(FakeDev* dev, ...)
    120 {
    121     return notImplemented(dev, "writev");
    122 }
    123 static int noMmap(FakeDev* dev, ...)
    124 {
    125     return notImplemented(dev, "mmap");
    126 }
    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  */
    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  */
    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  */
    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  */
    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  */
    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  */
    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