Esempio n. 1
0
/// Makes the LocalConnection object listen.
/// 
/// The name is a symbolic name like "lc1", that is used by the
/// send() command to signify which local connection to send the
/// object to.
//
/// When connect is called, this object adds its domain + name plus some
/// other bits of information to the listeners portion of the shared memory.
/// It also sets the initial bytes of the shared memory to a set
/// pattern.
//
/// The connection will fail if a listener with the same id (domain + name)
/// already exists. ActionScript isn't informed of this failure.
void
LocalConnection_as::connect(const std::string& name)
{
    assert(!name.empty());

    _name = name;
    
    if (!_shm.attach()) {
        log_error("Failed to open shared memory segment");
        return;
    }

    SharedMem::iterator ptr = _shm.begin();

    // We can't connect if there is already a listener with the same name.
    if (!addListener(_domain + ":" + _name, _shm)) {
        return;
    }
        
    const char i[] = { 1, 0, 0, 0, 1, 0, 0, 0 };
    std::copy(i, i + 8, ptr);

    movie_root& mr = getRoot(owner());
    mr.addAdvanceCallback(this);

    _connected = true;
    
    return;
}
Esempio n. 2
0
void
LocalConnection_as::update()
{
    // Check whether local connection is disabled(!): brilliant choice of
    // function name.
    if (rcfile.getLocalConnection()) {
        log_security("Attempting to write to disabled LocalConnection!");
        movie_root& mr = getRoot(owner());
        mr.removeAdvanceCallback(this);
        return;
    }

    // No-op if already attached. Nothing to do if it fails, but we
    // should probably stop trying.
    if (!_shm.attach()) {
        log_error("Failed to attach shared memory segment");
        return;
    }

    // We need the lock to prevent simultaneous reads/writes from other
    // processes.
    SharedMem::Lock lock(_shm);
    if (!lock.locked()) {
        log_debug("Failed to get shm lock");
        return;
    }

    SharedMem::iterator ptr = _shm.begin();
    
    // First check timestamp data.

    // These are not network byte order by default, but not sure about 
    // host byte order.
    const boost::uint32_t timestamp = readLong(ptr + 8);
    const boost::uint32_t size = readLong(ptr + 12);

    // As long as there is a timestamp in the shared memory, we mustn't
    // write anything.
    //
    // We check if this is data we are listening for. If it is, read it and
    // mark for overwriting.
    //
    // If not, we keep checking until the data has been overwritten by
    // another listener or until it's expired. If it's expired, we
    // mark for overwriting.
    if (timestamp) {

        // Start after 16-byte header.
        const boost::uint8_t* b = ptr + 16;

        // End at reported size of AMF sequence.
        const boost::uint8_t* end = b + size;

        amf::Reader rd(b, end, getGlobal(owner()));
        as_value a;

        // Get the connection name. That's all we need to remove expired
        // data.
        if (!rd(a)) {
            log_error("Invalid connection name data");
            return;
        }
        const std::string& connection = a.to_string();

        // Now check if data we wrote has expired. There's no really
        // reliable way of checking that we genuinely wrote it.
        if (_lastTime == timestamp) {
            
            const size_t timeout = 4 * 1000;

            VM& vm = getVM(owner());
            const boost::uint32_t timeNow = getTimestamp(vm);

            if (timeNow - timestamp > timeout) {
                log_debug("Data %s expired at %s. Removing its target "
                        "as a listener", timestamp, timeNow);
                removeListener(connection, _shm);
                markRead(_shm);
                _lastTime = 0;
            }
        }

        // If we are listening and the data is for us, get the rest of it
        // and call the method.
        if (_connected && connection == _domain + ":" + _name) {
            executeAMFFunction(owner(), rd);
            // Zero the timestamp bytes to signal that the shared memory
            // can be written again.
            markRead(_shm);
        }
        else {
            // The data has not expired and we didn't read it. Leave it
            // alone until it's expired or someone else has read it.
            return;
        }
    }

    // If we have no data to send, there's nothing more to do.
    if (_queue.empty()) {
        // ...except remove the callback if we aren't listening for anything.
        if (!_connected) {
            movie_root& mr = getRoot(owner());
            mr.removeAdvanceCallback(this);
        }
        return;
    }

    // Get the first buffer.
    boost::shared_ptr<ConnectionData> cd = _queue.front();
    _queue.pop_front();

    // If the correct listener isn't there, iterate until we find one or
    // there aren't any left.
    while (!findListener(_domain + ":" + cd->name, _shm)) {
        if (_queue.empty()) {
            // Make sure we send the empty header later.
            cd->ts = 0;
            break;
        }
        cd = _queue.front();
        _queue.pop_front();
    }

    // Yes, there is data to send.
    const char i[] = { 1, 0, 0, 0, 1, 0, 0, 0 };
    std::copy(i, i + arraySize(i), ptr);

    SimpleBuffer& buf = cd->data;

    SharedMem::iterator tmp = ptr + 8;
    writeLong(tmp, cd->ts);
    writeLong(tmp, cd->ts ? buf.size() : 0);
    std::copy(buf.data(), buf.data() + buf.size(), tmp);

    // Note the timestamp of our last send. We will keep calling update()
    // until the data has expired or been read.
    _lastTime = cd->ts;

}