1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149
|
/*
* 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.
*/
// 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 <android-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,
// Close subprocess stdin if possible.
kIdCloseStdin = 4,
// Window size change (an ASCII version of struct winsize).
kIdWindowSizeChange = 5,
// Indicates an invalid or unknown packet.
kIdInvalid = 255,
};
// 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 {
kPty,
kRaw,
};
enum class SubprocessProtocol {
kNone,
kShell,
};
// Forks and starts a new shell subprocess. If |name| is empty an interactive
// shell is started, otherwise |name| is executed non-interactively.
//
// Returns an open FD connected to the subprocess or -1 on failure.
int StartSubprocess(const char* name, const char* terminal_type,
SubprocessType type, SubprocessProtocol protocol);
#endif // !ADB_HOST
#endif // SHELL_SERVICE_H_
|