Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit da0b0116 authored by David Pursell's avatar David Pursell Committed by Gerrit Code Review
Browse files

Merge "Revert "adb: create shell protocol class.""

parents e4c1bb53 d9b74192
Loading
Loading
Loading
Loading
+1 −10
Original line number Diff line number Diff line
@@ -131,8 +131,6 @@ LOCAL_CFLAGS := -DADB_HOST=0 $(LIBADB_CFLAGS)
LOCAL_SRC_FILES := \
    $(LIBADB_TEST_SRCS) \
    $(LIBADB_TEST_linux_SRCS) \
    shell_service_protocol.cpp \
    shell_service_protocol_test.cpp \

LOCAL_SANITIZE := $(adb_target_sanitize)
LOCAL_STATIC_LIBRARIES := libadbd
@@ -147,12 +145,7 @@ LOCAL_MODULE := adb_test
LOCAL_CFLAGS := -DADB_HOST=1 $(LIBADB_CFLAGS)
LOCAL_CFLAGS_windows := $(LIBADB_windows_CFLAGS)
LOCAL_CFLAGS_linux := $(LIBADB_linux_CFLAGS)
LOCAL_SRC_FILES := \
    $(LIBADB_TEST_SRCS) \
    services.cpp \
    shell_service_protocol.cpp \
    shell_service_protocol_test.cpp \

LOCAL_SRC_FILES := $(LIBADB_TEST_SRCS) services.cpp
LOCAL_SRC_FILES_linux := $(LIBADB_TEST_linux_SRCS)
LOCAL_SRC_FILES_darwin := $(LIBADB_TEST_darwin_SRCS)
LOCAL_SANITIZE := $(adb_host_sanitize)
@@ -208,7 +201,6 @@ LOCAL_SRC_FILES := \
    adb_client.cpp \
    services.cpp \
    file_sync_client.cpp \
    shell_service_protocol.cpp \

LOCAL_CFLAGS += \
    $(ADB_COMMON_CFLAGS) \
@@ -257,7 +249,6 @@ LOCAL_SRC_FILES := \
    remount_service.cpp \
    set_verity_enable_state_service.cpp \
    shell_service.cpp \
    shell_service_protocol.cpp \

LOCAL_CFLAGS := \
    $(ADB_COMMON_CFLAGS) \
+0 −100
Original line number Diff line number Diff line
@@ -14,109 +14,9 @@
 * limitations under the License.
 */

// This file contains classes and functionality to launch shell subprocesses
// in adbd and communicate between those subprocesses and the adb client.
//
// The main features exposed here are:
//   1. A ShellPacket class to wrap data in a simple protocol. Both adbd and
//      the adb client use this class to transmit data between them.
//   2. Functions to launch a subprocess on the adbd side.

#ifndef SHELL_SERVICE_H_
#define SHELL_SERVICE_H_

#include <stdint.h>

#include <base/macros.h>

#include "adb.h"

// Class to send and receive shell protocol packets.
//
// To keep things simple and predictable, reads and writes block until an entire
// packet is complete.
//
// Example: read raw data from |fd| and send it in a packet.
//   ShellProtocol* p = new ShellProtocol(protocol_fd);
//   int len = adb_read(stdout_fd, p->data(), p->data_capacity());
//   packet->WritePacket(ShellProtocol::kIdStdout, len);
//
// Example: read a packet and print it to |stdout|.
//   ShellProtocol* p = new ShellProtocol(protocol_fd);
//   if (p->ReadPacket() && p->id() == kIdStdout) {
//       fwrite(p->data(), 1, p->data_length(), stdout);
//   }
class ShellProtocol {
  public:
    // This is an unscoped enum to make it easier to compare against raw bytes.
    enum Id : uint8_t {
        kIdStdin  = 0,
        kIdStdout = 1,
        kIdStderr = 2,
        kIdExit   = 3,
        kIdInvalid  = 255,  // Indicates an invalid or unknown packet.
    };

    // ShellPackets will probably be too large to allocate on the stack so they
    // should be dynamically allocated on the heap instead.
    //
    // |fd| is an open file descriptor to be used to send or receive packets.
    explicit ShellProtocol(int fd);
    virtual ~ShellProtocol();

    // Returns a pointer to the data buffer.
    const char* data() const { return buffer_ + kHeaderSize; }
    char* data() { return buffer_ + kHeaderSize; }

    // Returns the total capacity of the data buffer.
    size_t data_capacity() const { return buffer_end_ - data(); }

    // Reads a packet from the FD.
    //
    // If a packet is too big to fit in the buffer then Read() will split the
    // packet across multiple calls. For example, reading a 50-byte packet into
    // a 20-byte buffer would read 20 bytes, 20 bytes, then 10 bytes.
    //
    // Returns false if the FD closed or errored.
    bool Read();

    // Returns the ID of the packet in the buffer.
    int id() const { return buffer_[0]; }

    // Returns the number of bytes that have been read into the data buffer.
    size_t data_length() const { return data_length_; }

    // Writes the packet currently in the buffer to the FD.
    //
    // Returns false if the FD closed or errored.
    bool Write(Id id, size_t length);

  private:
    // Packets support 4-byte lengths.
    typedef uint32_t length_t;

    enum {
        // It's OK if MAX_PAYLOAD doesn't match on the sending and receiving
        // end, reading will split larger packets into multiple smaller ones.
        kBufferSize = MAX_PAYLOAD,

        // Header is 1 byte ID + 4 bytes length.
        kHeaderSize = sizeof(Id) + sizeof(length_t)
    };

    int fd_;
    char buffer_[kBufferSize];
    size_t data_length_ = 0, bytes_left_ = 0;

    // We need to be able to modify this value for testing purposes, but it
    // will stay constant during actual program use.
    char* buffer_end_ = buffer_ + sizeof(buffer_);

    friend class ShellProtocolTest;

    DISALLOW_COPY_AND_ASSIGN(ShellProtocol);
};

#if !ADB_HOST

enum class SubprocessType {

adb/shell_service_protocol.cpp

deleted100644 → 0
+0 −62
Original line number Diff line number Diff line
/*
 * Copyright (C) 2015 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "shell_service.h"

#include <string.h>

#include <algorithm>

#include "adb_io.h"

ShellProtocol::ShellProtocol(int fd) : fd_(fd) {
    buffer_[0] = kIdInvalid;
}

ShellProtocol::~ShellProtocol() {
}

bool ShellProtocol::Read() {
    // Only read a new header if we've finished the last packet.
    if (!bytes_left_) {
        if (!ReadFdExactly(fd_, buffer_, kHeaderSize)) {
            return false;
        }

        length_t packet_length;
        memcpy(&packet_length, &buffer_[1], sizeof(packet_length));
        bytes_left_ = packet_length;
        data_length_ = 0;
    }

    size_t read_length = std::min(bytes_left_, data_capacity());
    if (read_length && !ReadFdExactly(fd_, data(), read_length)) {
        return false;
    }

    bytes_left_ -= read_length;
    data_length_ = read_length;

    return true;
}

bool ShellProtocol::Write(Id id, size_t length) {
    buffer_[0] = id;
    length_t typed_length = length;
    memcpy(&buffer_[1], &typed_length, sizeof(typed_length));

    return WriteFdExactly(fd_, buffer_, kHeaderSize + length);
}
+0 −192
Original line number Diff line number Diff line
/*
 * Copyright (C) 2015 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "shell_service.h"

#include <gtest/gtest.h>

#include <signal.h>
#include <string.h>

#include "sysdeps.h"

class ShellProtocolTest : public ::testing::Test {
  public:
    static void SetUpTestCase() {
#if !defined(_WIN32)
        // This is normally done in main.cpp.
        saved_sigpipe_handler_ = signal(SIGPIPE, SIG_IGN);
#endif
    }

    static void TearDownTestCase() {
#if !defined(_WIN32)
        signal(SIGPIPE, saved_sigpipe_handler_);
#endif
    }

    // Initializes the socketpair and ShellProtocols needed for testing.
    void SetUp() {
        int fds[2];
        ASSERT_EQ(0, adb_socketpair(fds));
        read_fd_ = fds[0];
        write_fd_ = fds[1];

        write_protocol_ = new ShellProtocol(write_fd_);
        ASSERT_TRUE(write_protocol_ != nullptr);

        read_protocol_ = new ShellProtocol(read_fd_);
        ASSERT_TRUE(read_protocol_ != nullptr);
    }

    // Cleans up FDs and ShellProtocols. If an FD is closed manually during a
    // test, set it to -1 to prevent TearDown() trying to close it again.
    void TearDown() {
        for (int fd : {read_fd_, write_fd_}) {
            if (fd >= 0) {
                adb_close(fd);
            }
        }
        for (ShellProtocol* protocol : {read_protocol_, write_protocol_}) {
            if (protocol) {
                delete protocol;
            }
        }
    }

    // Fakes the buffer size so we can test filling buffers.
    void SetReadDataCapacity(size_t size) {
        read_protocol_->buffer_end_ = read_protocol_->data() + size;
    }

    static sighandler_t saved_sigpipe_handler_;

    int read_fd_ = -1, write_fd_ = -1;
    ShellProtocol *read_protocol_ = nullptr, *write_protocol_ = nullptr;
};

sighandler_t ShellProtocolTest::saved_sigpipe_handler_ = nullptr;

namespace {

// Returns true if the packet contains the given values.
bool PacketEquals(const ShellProtocol* protocol, ShellProtocol::Id id,
                    const void* data, size_t data_length) {
    return (protocol->id() == id &&
            protocol->data_length() == data_length &&
            !memcmp(data, protocol->data(), data_length));
}

}  // namespace

// Tests data that can fit in a single packet.
TEST_F(ShellProtocolTest, FullPacket) {
    ShellProtocol::Id id = ShellProtocol::kIdStdout;
    char data[] = "abc 123 \0\r\n";

    memcpy(write_protocol_->data(), data, sizeof(data));
    ASSERT_TRUE(write_protocol_->Write(id, sizeof(data)));

    ASSERT_TRUE(read_protocol_->Read());
    ASSERT_TRUE(PacketEquals(read_protocol_, id, data, sizeof(data)));
}

// Tests data that has to be read multiple times due to smaller read buffer.
TEST_F(ShellProtocolTest, ReadBufferOverflow) {
    ShellProtocol::Id id = ShellProtocol::kIdStdin;

    memcpy(write_protocol_->data(), "1234567890", 10);
    ASSERT_TRUE(write_protocol_->Write(id, 10));

    SetReadDataCapacity(4);
    ASSERT_TRUE(read_protocol_->Read());
    ASSERT_TRUE(PacketEquals(read_protocol_, id, "1234", 4));
    ASSERT_TRUE(read_protocol_->Read());
    ASSERT_TRUE(PacketEquals(read_protocol_, id, "5678", 4));
    ASSERT_TRUE(read_protocol_->Read());
    ASSERT_TRUE(PacketEquals(read_protocol_, id, "90", 2));
}

// Tests a zero length packet.
TEST_F(ShellProtocolTest, ZeroLengthPacket) {
    ShellProtocol::Id id = ShellProtocol::kIdStderr;

    ASSERT_TRUE(write_protocol_->Write(id, 0));
    ASSERT_TRUE(read_protocol_->Read());
    ASSERT_TRUE(PacketEquals(read_protocol_, id, nullptr, 0));
}

// Tests exit code packets.
TEST_F(ShellProtocolTest, ExitCodePacket) {
    write_protocol_->data()[0] = 20;
    ASSERT_TRUE(write_protocol_->Write(ShellProtocol::kIdExit, 1));

    ASSERT_TRUE(read_protocol_->Read());
    ASSERT_EQ(ShellProtocol::kIdExit, read_protocol_->id());
    ASSERT_EQ(20, read_protocol_->data()[0]);
}

// Tests writing to a closed pipe.
TEST_F(ShellProtocolTest, WriteToClosedPipeFail) {
    adb_close(read_fd_);
    read_fd_ = -1;

    ASSERT_FALSE(write_protocol_->Write(ShellProtocol::kIdStdout, 0));
}

// Tests writing to a closed FD.
TEST_F(ShellProtocolTest, WriteToClosedFdFail) {
    adb_close(write_fd_);
    write_fd_ = -1;

    ASSERT_FALSE(write_protocol_->Write(ShellProtocol::kIdStdout, 0));
}

// Tests reading from a closed pipe.
TEST_F(ShellProtocolTest, ReadFromClosedPipeFail) {
    adb_close(write_fd_);
    write_fd_ = -1;

    ASSERT_FALSE(read_protocol_->Read());
}

// Tests reading from a closed FD.
TEST_F(ShellProtocolTest, ReadFromClosedFdFail) {
    adb_close(read_fd_);
    read_fd_ = -1;

    ASSERT_FALSE(read_protocol_->Read());
}

// Tests reading from a closed pipe that has a packet waiting. This checks that
// even if the pipe closes before we can fully read its contents we will still
// be able to access the last packets.
TEST_F(ShellProtocolTest, ReadPacketFromClosedPipe) {
    ShellProtocol::Id id = ShellProtocol::kIdStdout;
    char data[] = "foo bar";

    memcpy(write_protocol_->data(), data, sizeof(data));
    ASSERT_TRUE(write_protocol_->Write(id, sizeof(data)));
    adb_close(write_fd_);
    write_fd_ = -1;

    // First read should grab the packet.
    ASSERT_TRUE(read_protocol_->Read());
    ASSERT_TRUE(PacketEquals(read_protocol_, id, data, sizeof(data)));

    // Second read should fail.
    ASSERT_FALSE(read_protocol_->Read());
}