Merge "Documented adb install --instant"
diff --git a/adb/Android.bp b/adb/Android.bp
index 46bc02b..99de54e 100644
--- a/adb/Android.bp
+++ b/adb/Android.bp
@@ -222,7 +222,6 @@
cc_binary_host {
name: "adb",
- tags: ["debug"],
defaults: ["adb_defaults"],
@@ -369,6 +368,7 @@
"libusb",
"libmdnssd",
],
+ test_suites: ["device-tests"],
}
python_binary_host {
diff --git a/adb/adb.cpp b/adb/adb.cpp
index 4fbfafb..3bf281c 100644
--- a/adb/adb.cpp
+++ b/adb/adb.cpp
@@ -132,6 +132,7 @@
{
D("adb: online");
t->online = 1;
+ t->SetConnectionEstablished(true);
}
void handle_offline(atransport *t)
@@ -1221,7 +1222,7 @@
std::string error;
atransport* t = acquire_one_transport(type, serial, transport_id, nullptr, &error);
if (!t) {
- return SendFail(reply_fd, error);
+ return -1;
}
int ret = handle_forward_request(service, t, reply_fd);
diff --git a/adb/test_adb.py b/adb/test_adb.py
index 3bb433d..32bf029 100644
--- a/adb/test_adb.py
+++ b/adb/test_adb.py
@@ -21,9 +21,11 @@
"""
from __future__ import print_function
+import binascii
import contextlib
import os
import random
+import select
import socket
import struct
import subprocess
@@ -33,6 +35,68 @@
import adb
+@contextlib.contextmanager
+def fake_adb_server(protocol=socket.AF_INET, port=0):
+ """Creates a fake ADB server that just replies with a CNXN packet."""
+
+ serversock = socket.socket(protocol, socket.SOCK_STREAM)
+ if protocol == socket.AF_INET:
+ serversock.bind(('127.0.0.1', port))
+ else:
+ serversock.bind(('::1', port))
+ serversock.listen(1)
+
+ # A pipe that is used to signal the thread that it should terminate.
+ readpipe, writepipe = os.pipe()
+
+ def _adb_packet(command, arg0, arg1, data):
+ bin_command = struct.unpack('I', command)[0]
+ buf = struct.pack('IIIIII', bin_command, arg0, arg1, len(data), 0,
+ bin_command ^ 0xffffffff)
+ buf += data
+ return buf
+
+ def _handle():
+ rlist = [readpipe, serversock]
+ cnxn_sent = {}
+ while True:
+ ready, _, _ = select.select(rlist, [], [])
+ for r in ready:
+ if r == readpipe:
+ # Closure pipe
+ os.close(r)
+ serversock.shutdown(socket.SHUT_RDWR)
+ serversock.close()
+ return
+ elif r == serversock:
+ # Server socket
+ conn, _ = r.accept()
+ rlist.append(conn)
+ else:
+ # Client socket
+ data = r.recv(1024)
+ if not data:
+ if r in cnxn_sent:
+ del cnxn_sent[r]
+ rlist.remove(r)
+ continue
+ if r in cnxn_sent:
+ continue
+ cnxn_sent[r] = True
+ r.sendall(_adb_packet('CNXN', 0x01000001, 1024 * 1024,
+ 'device::ro.product.name=fakeadb'))
+
+ port = serversock.getsockname()[1]
+ server_thread = threading.Thread(target=_handle)
+ server_thread.start()
+
+ try:
+ yield port
+ finally:
+ os.close(writepipe)
+ server_thread.join()
+
+
class NonApiTest(unittest.TestCase):
"""Tests for ADB that aren't a part of the AndroidDevice API."""
@@ -211,45 +275,32 @@
Bug: http://b/30313466
"""
- ipv4 = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
- ipv4.bind(('127.0.0.1', 0))
- ipv4.listen(1)
+ for protocol in (socket.AF_INET, socket.AF_INET6):
+ try:
+ with fake_adb_server(protocol=protocol) as port:
+ output = subprocess.check_output(
+ ['adb', 'connect', 'localhost:{}'.format(port)])
- ipv6 = socket.socket(socket.AF_INET6, socket.SOCK_STREAM)
- try:
- ipv6.bind(('::1', ipv4.getsockname()[1] + 1))
- ipv6.listen(1)
- except socket.error:
- print("IPv6 not available, skipping")
- return
+ self.assertEqual(
+ output.strip(), 'connected to localhost:{}'.format(port))
+ except socket.error:
+ print("IPv6 not available, skipping")
+ continue
- for s in (ipv4, ipv6):
- port = s.getsockname()[1]
+ def test_already_connected(self):
+ with fake_adb_server() as port:
output = subprocess.check_output(
['adb', 'connect', 'localhost:{}'.format(port)])
self.assertEqual(
output.strip(), 'connected to localhost:{}'.format(port))
- s.close()
- def test_already_connected(self):
- s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
- s.bind(('127.0.0.1', 0))
- s.listen(2)
+ # b/31250450: this always returns 0 but probably shouldn't.
+ output = subprocess.check_output(
+ ['adb', 'connect', 'localhost:{}'.format(port)])
- port = s.getsockname()[1]
- output = subprocess.check_output(
- ['adb', 'connect', 'localhost:{}'.format(port)])
-
- self.assertEqual(
- output.strip(), 'connected to localhost:{}'.format(port))
-
- # b/31250450: this always returns 0 but probably shouldn't.
- output = subprocess.check_output(
- ['adb', 'connect', 'localhost:{}'.format(port)])
-
- self.assertEqual(
- output.strip(), 'already connected to localhost:{}'.format(port))
+ self.assertEqual(
+ output.strip(), 'already connected to localhost:{}'.format(port))
def main():
random.seed(0)
diff --git a/adb/transport.cpp b/adb/transport.cpp
index 92c52e2..fa7cc8c 100644
--- a/adb/transport.cpp
+++ b/adb/transport.cpp
@@ -64,6 +64,21 @@
const char* const kFeatureLibusb = "libusb";
const char* const kFeaturePushSync = "push_sync";
+namespace {
+
+// A class that helps the Clang Thread Safety Analysis deal with
+// std::unique_lock. Given that std::unique_lock is movable, and the analysis
+// can not currently perform alias analysis, it is not annotated. In order to
+// assert that the mutex is held, a ScopedAssumeLocked can be created just after
+// the std::unique_lock.
+class SCOPED_CAPABILITY ScopedAssumeLocked {
+ public:
+ ScopedAssumeLocked(std::mutex& mutex) ACQUIRE(mutex) {}
+ ~ScopedAssumeLocked() RELEASE() {}
+};
+
+} // namespace
+
TransportId NextTransportId() {
static std::atomic<TransportId> next(1);
return next++;
@@ -77,8 +92,6 @@
Stop();
}
-static void AssumeLocked(std::mutex& mutex) ASSERT_CAPABILITY(mutex) {}
-
void BlockingConnectionAdapter::Start() {
std::lock_guard<std::mutex> lock(mutex_);
if (started_) {
@@ -103,12 +116,11 @@
LOG(INFO) << this->transport_name_ << ": write thread spawning";
while (true) {
std::unique_lock<std::mutex> lock(mutex_);
+ ScopedAssumeLocked assume_locked(mutex_);
cv_.wait(lock, [this]() REQUIRES(mutex_) {
return this->stopped_ || !this->write_queue_.empty();
});
- AssumeLocked(mutex_);
-
if (this->stopped_) {
return;
}
@@ -505,8 +517,8 @@
if (t->GetConnectionState() != kCsNoPerm) {
/* initial references are the two threads */
t->ref_count = 1;
- t->connection->SetTransportName(t->serial_name());
- t->connection->SetReadCallback([t](Connection*, std::unique_ptr<apacket> p) {
+ t->connection()->SetTransportName(t->serial_name());
+ t->connection()->SetReadCallback([t](Connection*, std::unique_ptr<apacket> p) {
if (!check_header(p.get(), t)) {
D("%s: remote read: bad header", t->serial);
return false;
@@ -519,7 +531,7 @@
fdevent_run_on_main_thread([packet, t]() { handle_packet(packet, t); });
return true;
});
- t->connection->SetErrorCallback([t](Connection*, const std::string& error) {
+ t->connection()->SetErrorCallback([t](Connection*, const std::string& error) {
D("%s: connection terminated: %s", t->serial, error.c_str());
fdevent_run_on_main_thread([t]() {
handle_offline(t);
@@ -527,7 +539,7 @@
});
});
- t->connection->Start();
+ t->connection()->Start();
#if ADB_HOST
send_connect(t);
#endif
@@ -596,7 +608,7 @@
t->ref_count--;
if (t->ref_count == 0) {
D("transport: %s unref (kicking and closing)", t->serial);
- t->connection->Stop();
+ t->connection()->Stop();
remove_transport(t);
} else {
D("transport: %s unref (count=%zu)", t->serial, t->ref_count);
@@ -721,15 +733,39 @@
return result;
}
+bool ConnectionWaitable::WaitForConnection(std::chrono::milliseconds timeout) {
+ std::unique_lock<std::mutex> lock(mutex_);
+ ScopedAssumeLocked assume_locked(mutex_);
+ return cv_.wait_for(lock, timeout, [&]() REQUIRES(mutex_) {
+ return connection_established_ready_;
+ }) && connection_established_;
+}
+
+void ConnectionWaitable::SetConnectionEstablished(bool success) {
+ {
+ std::lock_guard<std::mutex> lock(mutex_);
+ if (connection_established_ready_) return;
+ connection_established_ready_ = true;
+ connection_established_ = success;
+ D("connection established with %d", success);
+ }
+ cv_.notify_one();
+}
+
+atransport::~atransport() {
+ // If the connection callback had not been run before, run it now.
+ SetConnectionEstablished(false);
+}
+
int atransport::Write(apacket* p) {
- return this->connection->Write(std::unique_ptr<apacket>(p)) ? 0 : -1;
+ return this->connection()->Write(std::unique_ptr<apacket>(p)) ? 0 : -1;
}
void atransport::Kick() {
if (!kicked_) {
D("kicking transport %s", this->serial);
kicked_ = true;
- this->connection->Stop();
+ this->connection()->Stop();
}
}
@@ -742,6 +778,11 @@
connection_state_ = state;
}
+void atransport::SetConnection(std::unique_ptr<Connection> connection) {
+ std::lock_guard<std::mutex> lock(mutex_);
+ connection_ = std::shared_ptr<Connection>(std::move(connection));
+}
+
std::string atransport::connection_state_name() const {
ConnectionState state = GetConnectionState();
switch (state) {
@@ -873,6 +914,10 @@
qual_match(target.c_str(), "device:", device, false);
}
+void atransport::SetConnectionEstablished(bool success) {
+ connection_waitable_->SetConnectionEstablished(success);
+}
+
#if ADB_HOST
// We use newline as our delimiter, make sure to never output it.
@@ -974,7 +1019,7 @@
VLOG(TRANSPORT) << "socket transport " << transport->serial
<< " is already in pending_list and fails to register";
delete t;
- return -1;
+ return -EALREADY;
}
}
@@ -983,7 +1028,7 @@
VLOG(TRANSPORT) << "socket transport " << transport->serial
<< " is already in transport_list and fails to register";
delete t;
- return -1;
+ return -EALREADY;
}
}
@@ -992,8 +1037,15 @@
lock.unlock();
+ auto waitable = t->connection_waitable();
register_transport(t);
- return 0;
+
+ if (local == 1) {
+ // Do not wait for emulator transports.
+ return 0;
+ }
+
+ return waitable->WaitForConnection(std::chrono::seconds(10)) ? 0 : -1;
}
#if ADB_HOST
@@ -1052,8 +1104,9 @@
void unregister_usb_transport(usb_handle* usb) {
std::lock_guard<std::recursive_mutex> lock(transport_lock);
transport_list.remove_if([usb](atransport* t) {
- if (auto connection = dynamic_cast<UsbConnection*>(t->connection.get())) {
- return connection->handle_ == usb && t->GetConnectionState() == kCsNoPerm;
+ auto connection = t->connection();
+ if (auto usb_connection = dynamic_cast<UsbConnection*>(connection.get())) {
+ return usb_connection->handle_ == usb && t->GetConnectionState() == kCsNoPerm;
}
return false;
});
diff --git a/adb/transport.h b/adb/transport.h
index d18c362..ebc186b 100644
--- a/adb/transport.h
+++ b/adb/transport.h
@@ -20,6 +20,7 @@
#include <sys/types.h>
#include <atomic>
+#include <chrono>
#include <condition_variable>
#include <deque>
#include <functional>
@@ -30,6 +31,7 @@
#include <thread>
#include <unordered_set>
+#include <android-base/macros.h>
#include <android-base/thread_annotations.h>
#include <openssl/rsa.h>
@@ -160,6 +162,35 @@
usb_handle* handle_;
};
+// Waits for a transport's connection to be not pending. This is a separate
+// object so that the transport can be destroyed and another thread can be
+// notified of it in a race-free way.
+class ConnectionWaitable {
+ public:
+ ConnectionWaitable() = default;
+ ~ConnectionWaitable() = default;
+
+ // Waits until the first CNXN packet has been received by the owning
+ // atransport, or the specified timeout has elapsed. Can be called from any
+ // thread.
+ //
+ // Returns true if the CNXN packet was received in a timely fashion, false
+ // otherwise.
+ bool WaitForConnection(std::chrono::milliseconds timeout);
+
+ // Can be called from any thread when the connection stops being pending.
+ // Only the first invocation will be acknowledged, the rest will be no-ops.
+ void SetConnectionEstablished(bool success);
+
+ private:
+ bool connection_established_ GUARDED_BY(mutex_) = false;
+ bool connection_established_ready_ GUARDED_BY(mutex_) = false;
+ std::mutex mutex_;
+ std::condition_variable cv_;
+
+ DISALLOW_COPY_AND_ASSIGN(ConnectionWaitable);
+};
+
class atransport {
public:
// TODO(danalbert): We expose waaaaaaay too much stuff because this was
@@ -168,13 +199,16 @@
// it's better to do this piece by piece.
atransport(ConnectionState state = kCsOffline)
- : id(NextTransportId()), connection_state_(state) {
+ : id(NextTransportId()),
+ connection_state_(state),
+ connection_waitable_(std::make_shared<ConnectionWaitable>()),
+ connection_(nullptr) {
// Initialize protocol to min version for compatibility with older versions.
// Version will be updated post-connect.
protocol_version = A_VERSION_MIN;
max_payload = MAX_PAYLOAD;
}
- virtual ~atransport() {}
+ virtual ~atransport();
int Write(apacket* p);
void Kick();
@@ -183,13 +217,17 @@
ConnectionState GetConnectionState() const;
void SetConnectionState(ConnectionState state);
+ void SetConnection(std::unique_ptr<Connection> connection);
+ std::shared_ptr<Connection> connection() {
+ std::lock_guard<std::mutex> lock(mutex_);
+ return connection_;
+ }
+
const TransportId id;
size_t ref_count = 0;
bool online = false;
TransportType type = kTransportAny;
- std::unique_ptr<Connection> connection;
-
// Used to identify transports for clients.
char* serial = nullptr;
char* product = nullptr;
@@ -241,7 +279,14 @@
// This is to make it easier to use the same network target for both fastboot and adb.
bool MatchesTarget(const std::string& target) const;
-private:
+ // Notifies that the atransport is no longer waiting for the connection
+ // being established.
+ void SetConnectionEstablished(bool success);
+
+ // Gets a shared reference to the ConnectionWaitable.
+ std::shared_ptr<ConnectionWaitable> connection_waitable() { return connection_waitable_; }
+
+ private:
bool kicked_ = false;
// A set of features transmitted in the banner with the initial connection.
@@ -258,6 +303,15 @@
std::deque<std::shared_ptr<RSA>> keys_;
#endif
+ // A sharable object that can be used to wait for the atransport's
+ // connection to be established.
+ std::shared_ptr<ConnectionWaitable> connection_waitable_;
+
+ // The underlying connection object.
+ std::shared_ptr<Connection> connection_ GUARDED_BY(mutex_);
+
+ std::mutex mutex_;
+
DISALLOW_COPY_AND_ASSIGN(atransport);
};
diff --git a/adb/transport_local.cpp b/adb/transport_local.cpp
index c09fcb7..e81f27c 100644
--- a/adb/transport_local.cpp
+++ b/adb/transport_local.cpp
@@ -101,7 +101,11 @@
int ret = register_socket_transport(fd, serial.c_str(), port, 0);
if (ret < 0) {
adb_close(fd);
- *response = android::base::StringPrintf("already connected to %s", serial.c_str());
+ if (ret == -EALREADY) {
+ *response = android::base::StringPrintf("already connected to %s", serial.c_str());
+ } else {
+ *response = android::base::StringPrintf("failed to connect to %s", serial.c_str());
+ }
} else {
*response = android::base::StringPrintf("connected to %s", serial.c_str());
}
@@ -452,7 +456,8 @@
// Emulator connection.
if (local) {
auto emulator_connection = std::make_unique<EmulatorConnection>(std::move(fd), adb_port);
- t->connection = std::make_unique<BlockingConnectionAdapter>(std::move(emulator_connection));
+ t->SetConnection(
+ std::make_unique<BlockingConnectionAdapter>(std::move(emulator_connection)));
std::lock_guard<std::mutex> lock(local_transports_lock);
atransport* existing_transport = find_emulator_transport_by_adb_port_locked(adb_port);
if (existing_transport != NULL) {
@@ -472,6 +477,6 @@
// Regular tcp connection.
auto fd_connection = std::make_unique<FdConnection>(std::move(fd));
- t->connection = std::make_unique<BlockingConnectionAdapter>(std::move(fd_connection));
+ t->SetConnection(std::make_unique<BlockingConnectionAdapter>(std::move(fd_connection)));
return fail;
}
diff --git a/adb/transport_usb.cpp b/adb/transport_usb.cpp
index e9a75cd..94b2e37 100644
--- a/adb/transport_usb.cpp
+++ b/adb/transport_usb.cpp
@@ -176,7 +176,7 @@
void init_usb_transport(atransport* t, usb_handle* h) {
D("transport: usb");
auto connection = std::make_unique<UsbConnection>(h);
- t->connection = std::make_unique<BlockingConnectionAdapter>(std::move(connection));
+ t->SetConnection(std::make_unique<BlockingConnectionAdapter>(std::move(connection)));
t->type = kTransportUsb;
}
diff --git a/base/Android.bp b/base/Android.bp
index 12b3969..ec81f61 100644
--- a/base/Android.bp
+++ b/base/Android.bp
@@ -162,4 +162,5 @@
suffix: "64",
},
},
+ test_suites: ["device-tests"],
}
diff --git a/base/include/android-base/parseint.h b/base/include/android-base/parseint.h
index 2c8570e..1b7cc5f 100644
--- a/base/include/android-base/parseint.h
+++ b/base/include/android-base/parseint.h
@@ -19,6 +19,7 @@
#include <errno.h>
#include <stdlib.h>
+#include <string.h>
#include <limits>
#include <string>
@@ -31,14 +32,20 @@
// otherwise valid values will be rejected. Returns boolean success; 'out'
// is untouched if parsing fails.
template <typename T>
-bool ParseUint(const char* s, T* out,
- T max = std::numeric_limits<T>::max()) {
+bool ParseUint(const char* s, T* out, T max = std::numeric_limits<T>::max(),
+ bool allow_suffixes = false) {
int base = (s[0] == '0' && (s[1] == 'x' || s[1] == 'X')) ? 16 : 10;
errno = 0;
char* end;
unsigned long long int result = strtoull(s, &end, base);
- if (errno != 0 || s == end || *end != '\0') {
- return false;
+ if (errno != 0 || end == s) return false;
+ if (*end != '\0') {
+ const char* suffixes = "bkmgtpe";
+ const char* suffix;
+ if (!allow_suffixes || (suffix = strchr(suffixes, tolower(*end))) == nullptr) return false;
+#if __clang__ // TODO: win32 still builds with GCC :-(
+ if (__builtin_mul_overflow(result, 1ULL << (10 * (suffix - suffixes)), &result)) return false;
+#endif
}
if (max < result) {
return false;
@@ -49,9 +56,20 @@
// TODO: string_view
template <typename T>
-bool ParseUint(const std::string& s, T* out,
- T max = std::numeric_limits<T>::max()) {
- return ParseUint(s.c_str(), out, max);
+bool ParseUint(const std::string& s, T* out, T max = std::numeric_limits<T>::max(),
+ bool allow_suffixes = false) {
+ return ParseUint(s.c_str(), out, max, allow_suffixes);
+}
+
+template <typename T>
+bool ParseByteCount(const char* s, T* out, T max = std::numeric_limits<T>::max()) {
+ return ParseUint(s, out, max, true);
+}
+
+// TODO: string_view
+template <typename T>
+bool ParseByteCount(const std::string& s, T* out, T max = std::numeric_limits<T>::max()) {
+ return ParseByteCount(s.c_str(), out, max);
}
// Parses the signed decimal integer in the string 's' and sets 'out' to
diff --git a/base/parseint_test.cpp b/base/parseint_test.cpp
index 483b1d3..fb1c339 100644
--- a/base/parseint_test.cpp
+++ b/base/parseint_test.cpp
@@ -96,3 +96,44 @@
ASSERT_FALSE(android::base::ParseInt("456x", &u));
ASSERT_EQ(123u, u);
}
+
+TEST(parseint, ParseByteCount) {
+ uint64_t i = 0;
+ ASSERT_TRUE(android::base::ParseByteCount("123b", &i));
+ ASSERT_EQ(123ULL, i);
+
+ ASSERT_TRUE(android::base::ParseByteCount("8k", &i));
+ ASSERT_EQ(8ULL * 1024, i);
+
+ ASSERT_TRUE(android::base::ParseByteCount("8M", &i));
+ ASSERT_EQ(8ULL * 1024 * 1024, i);
+
+ ASSERT_TRUE(android::base::ParseByteCount("6g", &i));
+ ASSERT_EQ(6ULL * 1024 * 1024 * 1024, i);
+
+ ASSERT_TRUE(android::base::ParseByteCount("1T", &i));
+ ASSERT_EQ(1ULL * 1024 * 1024 * 1024 * 1024, i);
+
+ ASSERT_TRUE(android::base::ParseByteCount("2p", &i));
+ ASSERT_EQ(2ULL * 1024 * 1024 * 1024 * 1024 * 1024, i);
+
+ ASSERT_TRUE(android::base::ParseByteCount("4e", &i));
+ ASSERT_EQ(4ULL * 1024 * 1024 * 1024 * 1024 * 1024 * 1024, i);
+}
+
+TEST(parseint, ParseByteCount_invalid_suffix) {
+ unsigned u;
+ ASSERT_FALSE(android::base::ParseByteCount("1x", &u));
+}
+
+TEST(parseint, ParseByteCount_overflow) {
+ uint64_t u64;
+ ASSERT_FALSE(android::base::ParseByteCount("4294967295E", &u64));
+
+ uint16_t u16;
+ ASSERT_TRUE(android::base::ParseByteCount("63k", &u16));
+ ASSERT_EQ(63U * 1024, u16);
+ ASSERT_TRUE(android::base::ParseByteCount("65535b", &u16));
+ ASSERT_EQ(65535U, u16);
+ ASSERT_FALSE(android::base::ParseByteCount("65k", &u16));
+}
diff --git a/bootstat/boot_reason_test.sh b/bootstat/boot_reason_test.sh
index b194bbe..01b8948 100755
--- a/bootstat/boot_reason_test.sh
+++ b/bootstat/boot_reason_test.sh
@@ -312,6 +312,7 @@
init : processing action (ro.boot.bootreason=*) from (/system/etc/init/bootstat.rc
init : processing action (sys.boot_completed=1 && sys.logbootcomplete=1) from (/system/etc/init/bootstat.rc
(/system/bin/bootstat --record_boot_complete --record_boot_reason --record_time_since_factory_reset -l)'
+ (/system/bin/bootstat --set_system_boot_reason --record_boot_complete --record_boot_reason --record_time_since_factory_reset -l)'
(/system/bin/bootstat -r post_decrypt_time_elapsed)'
init : Command 'exec - system log -- /system/bin/bootstat --record_boot_complete' action=sys.boot_completed=1 && sys.logbootcomplete=1 (/system/etc/init/bootstat.rc:
init : Command 'exec - system log -- /system/bin/bootstat --record_boot_reason' action=sys.boot_completed=1 && sys.logbootcomplete=1 (/system/etc/init/bootstat.rc:
@@ -582,9 +583,9 @@
duration_test
case ${TEST} in
bootloader | recovery | cold | hard | warm ) reason=${TEST} ;;
- *) reason=reboot,${TEST} ;;
+ *) reason=reboot,${TEST#optional_} ;;
esac
- adb reboot ${TEST}
+ adb reboot ${TEST#optional_}
wait_for_screen
bootloader_reason=`validate_property ro.boot.bootreason`
EXPECT_PROPERTY ro.boot.bootreason ${bootloader_reason}
@@ -948,6 +949,20 @@
report_bootstat_logs reboot,adb
}
+[ "USAGE: test_rescueparty
+
+rescueparty test
+- adb reboot rescueparty
+- (wait until screen is up, boot has completed)
+- adb shell getprop sys.boot.reason
+- adb shell getprop ro.boot.bootreason
+- NB: should report reboot,rescueparty" ]
+test_optional_rescueparty() {
+ blind_reboot_test
+ echo "WARNING: legacy devices are allowed to fail following ro.boot.bootreason result" >&2
+ EXPECT_PROPERTY ro.boot.bootreason reboot,rescueparty
+}
+
[ "USAGE: test_Its_Just_So_Hard_reboot
Its Just So Hard reboot test:
diff --git a/bootstat/bootstat.cpp b/bootstat/bootstat.cpp
index c2688e9..1b13b21 100644
--- a/bootstat/bootstat.cpp
+++ b/bootstat/bootstat.cpp
@@ -232,7 +232,7 @@
{"shutdown,thermal,battery", 87},
{"reboot,its_just_so_hard", 88}, // produced by boot_reason_test
{"reboot,Its Just So Hard", 89}, // produced by boot_reason_test
- // {"usb", 90}, // Duplicate of enum 80 above. Immediate reuse possible.
+ {"reboot,rescueparty", 90},
{"charge", 91},
{"oem_tz_crash", 92},
{"uvlo", 93},
@@ -303,6 +303,9 @@
{"kernel_panic,init", 158},
{"kernel_panic,oom", 159},
{"kernel_panic,stack", 160},
+ {"kernel_panic,sysrq,livelock,alarm", 161}, // llkd
+ {"kernel_panic,sysrq,livelock,driver", 162}, // llkd
+ {"kernel_panic,sysrq,livelock,zombie", 163}, // llkd
};
// Converts a string value representing the reason the system booted to an
diff --git a/debuggerd/Android.bp b/debuggerd/Android.bp
index 7e6f24d..7c28b28 100644
--- a/debuggerd/Android.bp
+++ b/debuggerd/Android.bp
@@ -112,7 +112,6 @@
"libbase",
"libdebuggerd",
"libbacktrace",
- "libunwind",
"libunwindstack",
"libdexfile",
"liblzma",
@@ -158,7 +157,6 @@
static_libs: [
"libbacktrace",
- "libunwind",
"libunwindstack",
"liblzma",
"libbase",
@@ -221,6 +219,8 @@
stem: "debuggerd_test64",
},
},
+
+ test_suites: ["device-tests"],
}
cc_benchmark {
diff --git a/debuggerd/crash_dump.cpp b/debuggerd/crash_dump.cpp
index a1f0211..40cf6c3 100644
--- a/debuggerd/crash_dump.cpp
+++ b/debuggerd/crash_dump.cpp
@@ -190,6 +190,19 @@
static unique_fd g_tombstoned_socket;
static unique_fd g_output_fd;
+static void DefuseSignalHandlers() {
+ // Don't try to dump ourselves.
+ struct sigaction action = {};
+ action.sa_handler = SIG_DFL;
+ debuggerd_register_handlers(&action);
+
+ sigset_t mask;
+ sigemptyset(&mask);
+ if (sigprocmask(SIG_SETMASK, &mask, nullptr) != 0) {
+ PLOG(FATAL) << "failed to set signal mask";
+ }
+}
+
static void Initialize(char** argv) {
android::base::InitLogging(argv);
android::base::SetAborter([](const char* abort_msg) {
@@ -213,17 +226,6 @@
_exit(1);
});
-
- // Don't try to dump ourselves.
- struct sigaction action = {};
- action.sa_handler = SIG_DFL;
- debuggerd_register_handlers(&action);
-
- sigset_t mask;
- sigemptyset(&mask);
- if (sigprocmask(SIG_SETMASK, &mask, nullptr) != 0) {
- PLOG(FATAL) << "failed to set signal mask";
- }
}
static void ParseArgs(int argc, char** argv, pid_t* pseudothread_tid, DebuggerdDumpType* dump_type) {
@@ -321,6 +323,8 @@
}
int main(int argc, char** argv) {
+ DefuseSignalHandlers();
+
atrace_begin(ATRACE_TAG, "before reparent");
pid_t target_process = getppid();
diff --git a/debuggerd/debuggerd_test.cpp b/debuggerd/debuggerd_test.cpp
index e410be9..9b64be7 100644
--- a/debuggerd/debuggerd_test.cpp
+++ b/debuggerd/debuggerd_test.cpp
@@ -346,7 +346,9 @@
std::string result;
ConsumeFd(std::move(output_fd), &result);
- ASSERT_MATCH(result, R"(signal 11 \(SIGSEGV\), code 0 \(SI_USER\), fault addr --------)");
+ ASSERT_MATCH(
+ result,
+ R"(signal 11 \(SIGSEGV\), code 0 \(SI_USER from pid \d+, uid \d+\), fault addr --------)");
ASSERT_MATCH(result, R"(backtrace:)");
}
diff --git a/debuggerd/handler/debuggerd_handler.cpp b/debuggerd/handler/debuggerd_handler.cpp
index 05e6efa..c07a34a 100644
--- a/debuggerd/handler/debuggerd_handler.cpp
+++ b/debuggerd/handler/debuggerd_handler.cpp
@@ -169,24 +169,26 @@
return;
}
- const char* signal_name = get_signame(info->si_signo);
- bool has_address = signal_has_si_addr(info->si_signo, info->si_code);
-
- // Many signals don't have an address.
+ // Many signals don't have an address or sender.
char addr_desc[32] = ""; // ", fault addr 0x1234"
- if (has_address) {
+ if (signal_has_si_addr(info)) {
async_safe_format_buffer(addr_desc, sizeof(addr_desc), ", fault addr %p", info->si_addr);
}
+ pid_t self_pid = __getpid();
+ char sender_desc[32] = {}; // " from pid 1234, uid 666"
+ if (signal_has_sender(info, self_pid)) {
+ get_signal_sender(sender_desc, sizeof(sender_desc), info);
+ }
char main_thread_name[MAX_TASK_NAME_LEN + 1];
if (!get_main_thread_name(main_thread_name, sizeof(main_thread_name))) {
strncpy(main_thread_name, "<unknown>", sizeof(main_thread_name));
}
- async_safe_format_log(
- ANDROID_LOG_FATAL, "libc", "Fatal signal %d (%s), code %d (%s)%s in tid %d (%s), pid %d (%s)",
- info->si_signo, signal_name, info->si_code, get_sigcode(info->si_signo, info->si_code),
- addr_desc, __gettid(), thread_name, __getpid(), main_thread_name);
+ async_safe_format_log(ANDROID_LOG_FATAL, "libc",
+ "Fatal signal %d (%s), code %d (%s%s)%s in tid %d (%s), pid %d (%s)",
+ info->si_signo, get_signame(info), info->si_code, get_sigcode(info),
+ sender_desc, addr_desc, __gettid(), thread_name, self_pid, main_thread_name);
}
/*
diff --git a/debuggerd/libdebuggerd/include/libdebuggerd/utility.h b/debuggerd/libdebuggerd/include/libdebuggerd/utility.h
index 7b04e71..7c5304e 100644
--- a/debuggerd/libdebuggerd/include/libdebuggerd/utility.h
+++ b/debuggerd/libdebuggerd/include/libdebuggerd/utility.h
@@ -74,8 +74,10 @@
void drop_capabilities();
-bool signal_has_si_addr(int si_signo, int si_code);
-const char* get_signame(int sig);
-const char* get_sigcode(int signo, int code);
+bool signal_has_sender(const siginfo_t*, pid_t caller_pid);
+bool signal_has_si_addr(const siginfo_t*);
+void get_signal_sender(char* buf, size_t n, const siginfo_t*);
+const char* get_signame(const siginfo_t*);
+const char* get_sigcode(const siginfo_t*);
#endif // _DEBUGGERD_UTILITY_H
diff --git a/debuggerd/libdebuggerd/tombstone.cpp b/debuggerd/libdebuggerd/tombstone.cpp
index 55d6204..e11be1e 100644
--- a/debuggerd/libdebuggerd/tombstone.cpp
+++ b/debuggerd/libdebuggerd/tombstone.cpp
@@ -102,18 +102,24 @@
if (!cause.empty()) _LOG(log, logtype::HEADER, "Cause: %s\n", cause.c_str());
}
-static void dump_signal_info(log_t* log, const siginfo_t* si) {
+static void dump_signal_info(log_t* log, const ThreadInfo& thread_info) {
char addr_desc[32]; // ", fault addr 0x1234"
- if (signal_has_si_addr(si->si_signo, si->si_code)) {
- snprintf(addr_desc, sizeof(addr_desc), "%p", si->si_addr);
+ if (signal_has_si_addr(thread_info.siginfo)) {
+ snprintf(addr_desc, sizeof(addr_desc), "%p", thread_info.siginfo->si_addr);
} else {
snprintf(addr_desc, sizeof(addr_desc), "--------");
}
- _LOG(log, logtype::HEADER, "signal %d (%s), code %d (%s), fault addr %s\n", si->si_signo,
- get_signame(si->si_signo), si->si_code, get_sigcode(si->si_signo, si->si_code), addr_desc);
+ char sender_desc[32] = {}; // " from pid 1234, uid 666"
+ if (signal_has_sender(thread_info.siginfo, thread_info.pid)) {
+ get_signal_sender(sender_desc, sizeof(sender_desc), thread_info.siginfo);
+ }
- dump_probable_cause(log, si);
+ _LOG(log, logtype::HEADER, "signal %d (%s), code %d (%s%s), fault addr %s\n",
+ thread_info.siginfo->si_signo, get_signame(thread_info.siginfo),
+ thread_info.siginfo->si_code, get_sigcode(thread_info.siginfo), sender_desc, addr_desc);
+
+ dump_probable_cause(log, thread_info.siginfo);
}
static void dump_thread_info(log_t* log, const ThreadInfo& thread_info) {
@@ -247,14 +253,13 @@
length -= sizeof(size_t);
- std::vector<char> msg(length);
+ // The abort message should be null terminated already, but reserve a spot for NUL just in case.
+ std::vector<char> msg(length + 1);
if (!process_memory->ReadFully(address + sizeof(length), &msg[0], length)) {
_LOG(log, logtype::HEADER, "Failed to read abort message: %s\n", strerror(errno));
return;
}
- // The abort message should be null terminated already, but just in case...
- msg[length] = '\0';
_LOG(log, logtype::HEADER, "Abort message: '%s'\n", &msg[0]);
}
@@ -413,7 +418,7 @@
dump_thread_info(log, thread_info);
if (thread_info.siginfo) {
- dump_signal_info(log, thread_info.siginfo);
+ dump_signal_info(log, thread_info);
}
if (primary_thread) {
@@ -422,8 +427,10 @@
dump_registers(log, thread_info.registers.get());
+ // Unwind will mutate the registers, so make a copy first.
+ std::unique_ptr<Regs> regs_copy(thread_info.registers->Clone());
std::vector<backtrace_frame_data_t> frames;
- if (!Backtrace::Unwind(thread_info.registers.get(), map, &frames, 0, nullptr)) {
+ if (!Backtrace::Unwind(regs_copy.get(), map, &frames, 0, nullptr)) {
_LOG(log, logtype::THREAD, "Failed to unwind");
return false;
}
@@ -441,7 +448,7 @@
if (map) {
uint64_t addr = 0;
siginfo_t* si = thread_info.siginfo;
- if (signal_has_si_addr(si->si_signo, si->si_code)) {
+ if (signal_has_si_addr(si)) {
addr = reinterpret_cast<uint64_t>(si->si_addr);
}
dump_all_maps(log, map, process_memory, addr);
diff --git a/debuggerd/libdebuggerd/utility.cpp b/debuggerd/libdebuggerd/utility.cpp
index 1ad1800..1f6f3c8 100644
--- a/debuggerd/libdebuggerd/utility.cpp
+++ b/debuggerd/libdebuggerd/utility.cpp
@@ -254,13 +254,13 @@
}
}
-bool signal_has_si_addr(int si_signo, int si_code) {
+bool signal_has_si_addr(const siginfo_t* si) {
// Manually sent signals won't have si_addr.
- if (si_code == SI_USER || si_code == SI_QUEUE || si_code == SI_TKILL) {
+ if (si->si_code == SI_USER || si->si_code == SI_QUEUE || si->si_code == SI_TKILL) {
return false;
}
- switch (si_signo) {
+ switch (si->si_signo) {
case SIGBUS:
case SIGFPE:
case SIGILL:
@@ -272,8 +272,16 @@
}
}
-const char* get_signame(int sig) {
- switch (sig) {
+bool signal_has_sender(const siginfo_t* si, pid_t caller_pid) {
+ return SI_FROMUSER(si) && (si->si_pid != 0) && (si->si_pid != caller_pid);
+}
+
+void get_signal_sender(char* buf, size_t n, const siginfo_t* si) {
+ snprintf(buf, n, " from pid %d, uid %d", si->si_pid, si->si_uid);
+}
+
+const char* get_signame(const siginfo_t* si) {
+ switch (si->si_signo) {
case SIGABRT: return "SIGABRT";
case SIGBUS: return "SIGBUS";
case SIGFPE: return "SIGFPE";
@@ -290,11 +298,11 @@
}
}
-const char* get_sigcode(int signo, int code) {
+const char* get_sigcode(const siginfo_t* si) {
// Try the signal-specific codes...
- switch (signo) {
+ switch (si->si_signo) {
case SIGILL:
- switch (code) {
+ switch (si->si_code) {
case ILL_ILLOPC: return "ILL_ILLOPC";
case ILL_ILLOPN: return "ILL_ILLOPN";
case ILL_ILLADR: return "ILL_ILLADR";
@@ -307,7 +315,7 @@
static_assert(NSIGILL == ILL_BADSTK, "missing ILL_* si_code");
break;
case SIGBUS:
- switch (code) {
+ switch (si->si_code) {
case BUS_ADRALN: return "BUS_ADRALN";
case BUS_ADRERR: return "BUS_ADRERR";
case BUS_OBJERR: return "BUS_OBJERR";
@@ -317,7 +325,7 @@
static_assert(NSIGBUS == BUS_MCEERR_AO, "missing BUS_* si_code");
break;
case SIGFPE:
- switch (code) {
+ switch (si->si_code) {
case FPE_INTDIV: return "FPE_INTDIV";
case FPE_INTOVF: return "FPE_INTOVF";
case FPE_FLTDIV: return "FPE_FLTDIV";
@@ -330,7 +338,7 @@
static_assert(NSIGFPE == FPE_FLTSUB, "missing FPE_* si_code");
break;
case SIGSEGV:
- switch (code) {
+ switch (si->si_code) {
case SEGV_MAPERR: return "SEGV_MAPERR";
case SEGV_ACCERR: return "SEGV_ACCERR";
#if defined(SEGV_BNDERR)
@@ -350,21 +358,21 @@
break;
#if defined(SYS_SECCOMP) // Our glibc is too old, and we build this for the host too.
case SIGSYS:
- switch (code) {
+ switch (si->si_code) {
case SYS_SECCOMP: return "SYS_SECCOMP";
}
static_assert(NSIGSYS == SYS_SECCOMP, "missing SYS_* si_code");
break;
#endif
case SIGTRAP:
- switch (code) {
+ switch (si->si_code) {
case TRAP_BRKPT: return "TRAP_BRKPT";
case TRAP_TRACE: return "TRAP_TRACE";
case TRAP_BRANCH: return "TRAP_BRANCH";
case TRAP_HWBKPT: return "TRAP_HWBKPT";
}
- if ((code & 0xff) == SIGTRAP) {
- switch ((code >> 8) & 0xff) {
+ if ((si->si_code & 0xff) == SIGTRAP) {
+ switch ((si->si_code >> 8) & 0xff) {
case PTRACE_EVENT_FORK:
return "PTRACE_EVENT_FORK";
case PTRACE_EVENT_VFORK:
@@ -387,7 +395,7 @@
break;
}
// Then the other codes...
- switch (code) {
+ switch (si->si_code) {
case SI_USER: return "SI_USER";
case SI_KERNEL: return "SI_KERNEL";
case SI_QUEUE: return "SI_QUEUE";
diff --git a/debuggerd/tombstoned/tombstoned.cpp b/debuggerd/tombstoned/tombstoned.cpp
index 1bf8f14..15ae406 100644
--- a/debuggerd/tombstoned/tombstoned.cpp
+++ b/debuggerd/tombstoned/tombstoned.cpp
@@ -61,10 +61,11 @@
struct Crash {
~Crash() { event_free(crash_event); }
- unique_fd crash_fd;
+ std::string crash_tombstone_path;
+ unique_fd crash_tombstone_fd;
+ unique_fd crash_socket_fd;
pid_t crash_pid;
event* crash_event = nullptr;
- std::string crash_path;
DebuggerdDumpType crash_type;
};
@@ -109,24 +110,29 @@
return &queue;
}
- std::pair<unique_fd, std::string> get_output() {
- unique_fd result;
- std::string file_name = StringPrintf("%s%02d", file_name_prefix_.c_str(), next_artifact_);
-
- // Unlink and create the file, instead of using O_TRUNC, to avoid two processes
- // interleaving their output in case we ever get into that situation.
- if (unlinkat(dir_fd_, file_name.c_str(), 0) != 0 && errno != ENOENT) {
- PLOG(FATAL) << "failed to unlink tombstone at " << dir_path_ << "/" << file_name;
- }
-
- result.reset(openat(dir_fd_, file_name.c_str(),
- O_CREAT | O_EXCL | O_WRONLY | O_APPEND | O_CLOEXEC, 0640));
+ std::pair<std::string, unique_fd> get_output() {
+ std::string path;
+ unique_fd result(openat(dir_fd_, ".", O_WRONLY | O_APPEND | O_TMPFILE | O_CLOEXEC, 0640));
if (result == -1) {
- PLOG(FATAL) << "failed to create tombstone at " << dir_path_ << "/" << file_name;
- }
+ // We might not have O_TMPFILE. Try creating with an arbitrary filename instead.
+ static size_t counter = 0;
+ std::string tmp_filename = StringPrintf(".temporary%zu", counter++);
+ result.reset(openat(dir_fd_, tmp_filename.c_str(),
+ O_WRONLY | O_APPEND | O_CREAT | O_TRUNC | O_CLOEXEC, 0640));
+ if (result == -1) {
+ PLOG(FATAL) << "failed to create temporary tombstone in " << dir_path_;
+ }
+ path = StringPrintf("%s/%s", dir_path_.c_str(), tmp_filename.c_str());
+ }
+ return std::make_pair(std::move(path), std::move(result));
+ }
+
+ std::string get_next_artifact_path() {
+ std::string file_name =
+ StringPrintf("%s/%s%02d", dir_path_.c_str(), file_name_prefix_.c_str(), next_artifact_);
next_artifact_ = (next_artifact_ + 1) % max_artifacts_;
- return {std::move(result), dir_path_ + "/" + file_name};
+ return file_name;
}
bool maybe_enqueue_crash(Crash* crash) {
@@ -203,14 +209,17 @@
static void perform_request(Crash* crash) {
unique_fd output_fd;
- if (!intercept_manager->GetIntercept(crash->crash_pid, crash->crash_type, &output_fd)) {
- std::tie(output_fd, crash->crash_path) = CrashQueue::for_crash(crash)->get_output();
+ bool intercepted =
+ intercept_manager->GetIntercept(crash->crash_pid, crash->crash_type, &output_fd);
+ if (!intercepted) {
+ std::tie(crash->crash_tombstone_path, output_fd) = CrashQueue::for_crash(crash)->get_output();
+ crash->crash_tombstone_fd.reset(dup(output_fd.get()));
}
TombstonedCrashPacket response = {
.packet_type = CrashPacketType::kPerformDump
};
- ssize_t rc = send_fd(crash->crash_fd, &response, sizeof(response), std::move(output_fd));
+ ssize_t rc = send_fd(crash->crash_socket_fd, &response, sizeof(response), std::move(output_fd));
if (rc == -1) {
PLOG(WARNING) << "failed to send response to CrashRequest";
goto fail;
@@ -222,7 +231,7 @@
struct timeval timeout = { 10, 0 };
event_base* base = event_get_base(crash->crash_event);
- event_assign(crash->crash_event, base, crash->crash_fd, EV_TIMEOUT | EV_READ,
+ event_assign(crash->crash_event, base, crash->crash_socket_fd, EV_TIMEOUT | EV_READ,
crash_completed_cb, crash);
event_add(crash->crash_event, &timeout);
}
@@ -243,7 +252,7 @@
// and only native crashes on the native socket.
struct timeval timeout = { 1, 0 };
event* crash_event = event_new(base, sockfd, EV_TIMEOUT | EV_READ, crash_request_cb, crash);
- crash->crash_fd.reset(sockfd);
+ crash->crash_socket_fd.reset(sockfd);
crash->crash_event = crash_event;
event_add(crash_event, &timeout);
}
@@ -342,14 +351,37 @@
goto fail;
}
- if (!crash->crash_path.empty()) {
- if (crash->crash_type == kDebuggerdJavaBacktrace) {
- LOG(ERROR) << "Traces for pid " << crash->crash_pid << " written to: " << crash->crash_path;
+ if (crash->crash_tombstone_fd != -1) {
+ std::string fd_path = StringPrintf("/proc/self/fd/%d", crash->crash_tombstone_fd.get());
+ std::string tombstone_path = CrashQueue::for_crash(crash)->get_next_artifact_path();
+
+ // linkat doesn't let us replace a file, so we need to unlink first.
+ int rc = unlink(tombstone_path.c_str());
+ if (rc != 0 && errno != ENOENT) {
+ PLOG(ERROR) << "failed to unlink tombstone at " << tombstone_path;
+ goto fail;
+ }
+
+ rc = linkat(AT_FDCWD, fd_path.c_str(), AT_FDCWD, tombstone_path.c_str(), AT_SYMLINK_FOLLOW);
+ if (rc != 0) {
+ PLOG(ERROR) << "failed to link tombstone";
} else {
- // NOTE: Several tools parse this log message to figure out where the
- // tombstone associated with a given native crash was written. Any changes
- // to this message must be carefully considered.
- LOG(ERROR) << "Tombstone written to: " << crash->crash_path;
+ if (crash->crash_type == kDebuggerdJavaBacktrace) {
+ LOG(ERROR) << "Traces for pid " << crash->crash_pid << " written to: " << tombstone_path;
+ } else {
+ // NOTE: Several tools parse this log message to figure out where the
+ // tombstone associated with a given native crash was written. Any changes
+ // to this message must be carefully considered.
+ LOG(ERROR) << "Tombstone written to: " << tombstone_path;
+ }
+ }
+
+ // If we don't have O_TMPFILE, we need to clean up after ourselves.
+ if (!crash->crash_tombstone_path.empty()) {
+ rc = unlink(crash->crash_tombstone_path.c_str());
+ if (rc != 0) {
+ PLOG(ERROR) << "failed to unlink temporary tombstone at " << crash->crash_tombstone_path;
+ }
}
}
diff --git a/demangle/Android.bp b/demangle/Android.bp
index 8d5b135..cf6abfd 100644
--- a/demangle/Android.bp
+++ b/demangle/Android.bp
@@ -78,4 +78,9 @@
shared_libs: [
"libdemangle",
],
+
+ test_suites: ["device-tests"],
+ required: [
+ "libdemangle",
+ ],
}
diff --git a/demangle/Android.mk b/demangle/Android.mk
index e3cfc2a..d8082a9 100644
--- a/demangle/Android.mk
+++ b/demangle/Android.mk
@@ -19,7 +19,6 @@
include $(CLEAR_VARS)
LOCAL_MODULE := demangle_fuzzer
-LOCAL_MODULE_TAGS := optional
LOCAL_SRC_FILES := \
Demangler.cpp \
demangle_fuzzer.cpp \
diff --git a/fastboot/Android.mk b/fastboot/Android.mk
index 9bd0628..983e195 100644
--- a/fastboot/Android.mk
+++ b/fastboot/Android.mk
@@ -101,14 +101,13 @@
#
my_dist_files := $(HOST_OUT_EXECUTABLES)/fastboot
-my_dist_files += $(HOST_OUT_EXECUTABLES)/mke2fs$(HOST_EXECUTABLE_SUFFIX)
-my_dist_files += $(HOST_OUT_EXECUTABLES)/e2fsdroid$(HOST_EXECUTABLE_SUFFIX)
-my_dist_files += $(HOST_OUT_EXECUTABLES)/make_f2fs$(HOST_EXECUTABLE_SUFFIX)
-my_dist_files += $(HOST_OUT_EXECUTABLES)/sload_f2fs$(HOST_EXECUTABLE_SUFFIX)
+my_dist_files += $(HOST_OUT_EXECUTABLES)/mke2fs
+my_dist_files += $(HOST_OUT_EXECUTABLES)/e2fsdroid
+my_dist_files += $(HOST_OUT_EXECUTABLES)/make_f2fs
+my_dist_files += $(HOST_OUT_EXECUTABLES)/sload_f2fs
$(call dist-for-goals,dist_files sdk win_sdk,$(my_dist_files))
ifdef HOST_CROSS_OS
-# Archive fastboot.exe for win_sdk build.
-$(call dist-for-goals,win_sdk,$(ALL_MODULES.host_cross_fastboot.BUILT))
+$(call dist-for-goals,dist_files sdk win_sdk,$(ALL_MODULES.host_cross_fastboot.BUILT))
endif
my_dist_files :=
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index 41a3d6b..9463cc9 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -82,7 +82,7 @@
// libsparse will support INT_MAX, but this results in large allocations, so
// let's keep it at 1GB to avoid memory pressure on the host.
static constexpr int64_t RESPARSE_LIMIT = 1 * 1024 * 1024 * 1024;
-static int64_t sparse_limit = -1;
+static uint64_t sparse_limit = 0;
static int64_t target_sparse_limit = -1;
static unsigned g_base_addr = 0x10000000;
@@ -375,7 +375,7 @@
" -w Wipe userdata.\n"
" -s SERIAL Specify a USB device.\n"
" -s tcp|udp:HOST[:PORT] Specify a network device.\n"
- " -S SIZE[K|M|G] Use sparse files above this limit (0 to disable).\n"
+ " -S SIZE[K|M|G] Break into sparse files no larger than SIZE.\n"
" --slot SLOT Use SLOT; 'all' for both slots, 'other' for\n"
" non-current slot (default: current active slot).\n"
" --set-active[=SLOT] Sets the active slot before rebooting.\n"
@@ -693,10 +693,14 @@
fb_queue_notice("--------------------------------------------");
}
-static struct sparse_file** load_sparse_files(int fd, int max_size) {
+static struct sparse_file** load_sparse_files(int fd, int64_t max_size) {
struct sparse_file* s = sparse_file_import_auto(fd, false, true);
if (!s) die("cannot sparse read file");
+ if (max_size <= 0 || max_size > std::numeric_limits<uint32_t>::max()) {
+ die("invalid max size %" PRId64, max_size);
+ }
+
int files = sparse_file_resparse(s, max_size, nullptr, 0);
if (files < 0) die("Failed to resparse");
@@ -730,13 +734,10 @@
}
static int64_t get_sparse_limit(Transport* transport, int64_t size) {
- int64_t limit;
-
- if (sparse_limit == 0) {
- return 0;
- } else if (sparse_limit > 0) {
- limit = sparse_limit;
- } else {
+ int64_t limit = sparse_limit;
+ if (limit == 0) {
+ // Unlimited, so see what the target device's limit is.
+ // TODO: shouldn't we apply this limit even if you've used -S?
if (target_sparse_limit == -1) {
target_sparse_limit = get_target_sparse_limit(transport);
}
@@ -1197,47 +1198,6 @@
fb_queue_command(command, "");
}
-static int64_t parse_num(const char *arg)
-{
- char *endptr;
- unsigned long long num;
-
- num = strtoull(arg, &endptr, 0);
- if (endptr == arg) {
- return -1;
- }
-
- if (*endptr == 'k' || *endptr == 'K') {
- if (num >= (-1ULL) / 1024) {
- return -1;
- }
- num *= 1024LL;
- endptr++;
- } else if (*endptr == 'm' || *endptr == 'M') {
- if (num >= (-1ULL) / (1024 * 1024)) {
- return -1;
- }
- num *= 1024LL * 1024LL;
- endptr++;
- } else if (*endptr == 'g' || *endptr == 'G') {
- if (num >= (-1ULL) / (1024 * 1024 * 1024)) {
- return -1;
- }
- num *= 1024LL * 1024LL * 1024LL;
- endptr++;
- }
-
- if (*endptr != '\0') {
- return -1;
- }
-
- if (num > INT64_MAX) {
- return -1;
- }
-
- return num;
-}
-
static std::string fb_fix_numeric_var(std::string var) {
// Some bootloaders (angler, for example), send spurious leading whitespace.
var = android::base::Trim(var);
@@ -1471,8 +1431,9 @@
serial = optarg;
break;
case 'S':
- sparse_limit = parse_num(optarg);
- if (sparse_limit < 0) die("invalid sparse limit");
+ if (!android::base::ParseByteCount(optarg, &sparse_limit)) {
+ die("invalid sparse limit %s", optarg);
+ }
break;
case 'v':
set_verbose();
@@ -1683,20 +1644,20 @@
}
if (wants_wipe) {
- fb_queue_erase("userdata");
- if (set_fbe_marker) {
- fprintf(stderr, "setting FBE marker on initial userdata...\n");
- std::string initial_userdata_dir = create_fbemarker_tmpdir();
- fb_perform_format(transport, "userdata", 1, "", "", initial_userdata_dir);
- delete_fbemarker_tmpdir(initial_userdata_dir);
- } else {
- fb_perform_format(transport, "userdata", 1, "", "", "");
- }
-
- std::string cache_type;
- if (fb_getvar(transport, "partition-type:cache", &cache_type) && !cache_type.empty()) {
- fb_queue_erase("cache");
- fb_perform_format(transport, "cache", 1, "", "", "");
+ std::vector<std::string> partitions = { "userdata", "cache", "metadata" };
+ for (const auto& partition : partitions) {
+ std::string partition_type;
+ if (!fb_getvar(transport, std::string{"partition-type:"} + partition, &partition_type)) continue;
+ if (partition_type.empty()) continue;
+ fb_queue_erase(partition);
+ if (partition == "userdata" && set_fbe_marker) {
+ fprintf(stderr, "setting FBE marker on initial userdata...\n");
+ std::string initial_userdata_dir = create_fbemarker_tmpdir();
+ fb_perform_format(transport, partition, 1, "", "", initial_userdata_dir);
+ delete_fbemarker_tmpdir(initial_userdata_dir);
+ } else {
+ fb_perform_format(transport, partition, 1, "", "", "");
+ }
}
}
if (wants_set_active) {
diff --git a/fastboot/protocol.cpp b/fastboot/protocol.cpp
index a089567..fda6f5d 100644
--- a/fastboot/protocol.cpp
+++ b/fastboot/protocol.cpp
@@ -278,19 +278,14 @@
return _command_end(transport);
}
-#define TRANSPORT_BUF_SIZE 1024
+static constexpr size_t TRANSPORT_BUF_SIZE = 1024;
static char transport_buf[TRANSPORT_BUF_SIZE];
-static int transport_buf_len;
+static size_t transport_buf_len;
-static int fb_download_data_sparse_write(void *priv, const void *data, int len)
-{
- int r;
- Transport* transport = reinterpret_cast<Transport*>(priv);
- int to_write;
- const char* ptr = reinterpret_cast<const char*>(data);
-
+static int fb_download_data_sparse_write(void* priv, const void* data, size_t len) {
+ const char* ptr = static_cast<const char*>(data);
if (transport_buf_len) {
- to_write = std::min(TRANSPORT_BUF_SIZE - transport_buf_len, len);
+ size_t to_write = std::min(TRANSPORT_BUF_SIZE - transport_buf_len, len);
memcpy(transport_buf + transport_buf_len, ptr, to_write);
transport_buf_len += to_write;
@@ -298,9 +293,10 @@
len -= to_write;
}
+ Transport* transport = static_cast<Transport*>(priv);
if (transport_buf_len == TRANSPORT_BUF_SIZE) {
- r = _command_write_data(transport, transport_buf, TRANSPORT_BUF_SIZE);
- if (r != TRANSPORT_BUF_SIZE) {
+ int64_t r = _command_write_data(transport, transport_buf, TRANSPORT_BUF_SIZE);
+ if (r != static_cast<int64_t>(TRANSPORT_BUF_SIZE)) {
return -1;
}
transport_buf_len = 0;
@@ -311,9 +307,9 @@
g_error = "internal error: transport_buf not empty";
return -1;
}
- to_write = round_down(len, TRANSPORT_BUF_SIZE);
- r = _command_write_data(transport, ptr, to_write);
- if (r != to_write) {
+ size_t to_write = round_down(len, TRANSPORT_BUF_SIZE);
+ int64_t r = _command_write_data(transport, ptr, to_write);
+ if (r != static_cast<int64_t>(to_write)) {
return -1;
}
ptr += to_write;
@@ -344,12 +340,12 @@
}
int fb_download_data_sparse(Transport* transport, struct sparse_file* s) {
- int size = sparse_file_len(s, true, false);
- if (size <= 0) {
+ int64_t size = sparse_file_len(s, true, false);
+ if (size <= 0 || size > std::numeric_limits<uint32_t>::max()) {
return -1;
}
- std::string cmd(android::base::StringPrintf("download:%08x", size));
+ std::string cmd(android::base::StringPrintf("download:%08" PRIx64, size));
int r = _command_start(transport, cmd, size, 0);
if (r < 0) {
return -1;
diff --git a/fs_mgr/fs_mgr.cpp b/fs_mgr/fs_mgr.cpp
index 9aab0ba..e9040df 100644
--- a/fs_mgr/fs_mgr.cpp
+++ b/fs_mgr/fs_mgr.cpp
@@ -1383,7 +1383,7 @@
mount_point = basename(fstab->recs[i].mount_point);
}
- fs_mgr_verity_ioctl_init(io, mount_point, 0);
+ fs_mgr_dm_ioctl_init(io, DM_BUF_SIZE, mount_point);
const char* status;
if (ioctl(fd, DM_TABLE_STATUS, io)) {
diff --git a/fs_mgr/fs_mgr_avb.cpp b/fs_mgr/fs_mgr_avb.cpp
index 7824cfa..cf6b497 100644
--- a/fs_mgr/fs_mgr_avb.cpp
+++ b/fs_mgr/fs_mgr_avb.cpp
@@ -303,13 +303,14 @@
static bool load_verity_table(struct dm_ioctl* io, const std::string& dm_device_name, int fd,
uint64_t image_size, const std::string& verity_table) {
- fs_mgr_verity_ioctl_init(io, dm_device_name, DM_STATUS_TABLE_FLAG);
+ fs_mgr_dm_ioctl_init(io, DM_BUF_SIZE, dm_device_name);
// The buffer consists of [dm_ioctl][dm_target_spec][verity_params].
char* buffer = (char*)io;
// Builds the dm_target_spec arguments.
struct dm_target_spec* dm_target = (struct dm_target_spec*)&buffer[sizeof(struct dm_ioctl)];
+ io->flags = DM_READONLY_FLAG;
io->target_count = 1;
dm_target->status = 0;
dm_target->sector_start = 0;
@@ -359,14 +360,14 @@
alignas(dm_ioctl) char buffer[DM_BUF_SIZE];
struct dm_ioctl* io = (struct dm_ioctl*)buffer;
const std::string mount_point(basename(fstab_entry->mount_point));
- if (!fs_mgr_create_verity_device(io, mount_point, fd)) {
+ if (!fs_mgr_dm_create_device(io, mount_point, fd)) {
LERROR << "Couldn't create verity device!";
return false;
}
// Gets the name of the device file.
std::string verity_blk_name;
- if (!fs_mgr_get_verity_device_name(io, mount_point, fd, &verity_blk_name)) {
+ if (!fs_mgr_dm_get_device_name(io, mount_point, fd, &verity_blk_name)) {
LERROR << "Couldn't get verity device number!";
return false;
}
@@ -385,7 +386,7 @@
}
// Activates the device.
- if (!fs_mgr_resume_verity_table(io, mount_point, fd)) {
+ if (!fs_mgr_dm_resume_table(io, mount_point, fd)) {
return false;
}
diff --git a/fs_mgr/fs_mgr_dm_ioctl.cpp b/fs_mgr/fs_mgr_dm_ioctl.cpp
index 4cbd5a8..3a7fae4 100644
--- a/fs_mgr/fs_mgr_dm_ioctl.cpp
+++ b/fs_mgr/fs_mgr_dm_ioctl.cpp
@@ -23,21 +23,20 @@
#include "fs_mgr_priv.h"
#include "fs_mgr_priv_dm_ioctl.h"
-void fs_mgr_verity_ioctl_init(struct dm_ioctl* io, const std::string& name, unsigned flags) {
- memset(io, 0, DM_BUF_SIZE);
- io->data_size = DM_BUF_SIZE;
+void fs_mgr_dm_ioctl_init(struct dm_ioctl* io, size_t size, const std::string& name) {
+ memset(io, 0, size);
+ io->data_size = size;
io->data_start = sizeof(struct dm_ioctl);
io->version[0] = 4;
io->version[1] = 0;
io->version[2] = 0;
- io->flags = flags | DM_READONLY_FLAG;
if (!name.empty()) {
strlcpy(io->name, name.c_str(), sizeof(io->name));
}
}
-bool fs_mgr_create_verity_device(struct dm_ioctl* io, const std::string& name, int fd) {
- fs_mgr_verity_ioctl_init(io, name, 1);
+bool fs_mgr_dm_create_device(struct dm_ioctl* io, const std::string& name, int fd) {
+ fs_mgr_dm_ioctl_init(io, sizeof(*io), name);
if (ioctl(fd, DM_DEV_CREATE, io)) {
PERROR << "Error creating device mapping";
return false;
@@ -45,8 +44,8 @@
return true;
}
-bool fs_mgr_destroy_verity_device(struct dm_ioctl* io, const std::string& name, int fd) {
- fs_mgr_verity_ioctl_init(io, name, 0);
+bool fs_mgr_dm_destroy_device(struct dm_ioctl* io, const std::string& name, int fd) {
+ fs_mgr_dm_ioctl_init(io, sizeof(*io), name);
if (ioctl(fd, DM_DEV_REMOVE, io)) {
PERROR << "Error removing device mapping";
return false;
@@ -54,13 +53,13 @@
return true;
}
-bool fs_mgr_get_verity_device_name(struct dm_ioctl* io, const std::string& name, int fd,
- std::string* out_dev_name) {
+bool fs_mgr_dm_get_device_name(struct dm_ioctl* io, const std::string& name, int fd,
+ std::string* out_dev_name) {
FS_MGR_CHECK(out_dev_name != nullptr);
- fs_mgr_verity_ioctl_init(io, name, 0);
+ fs_mgr_dm_ioctl_init(io, sizeof(*io), name);
if (ioctl(fd, DM_DEV_STATUS, io)) {
- PERROR << "Error fetching verity device number";
+ PERROR << "Error fetching device-mapper device number";
return false;
}
@@ -70,10 +69,10 @@
return true;
}
-bool fs_mgr_resume_verity_table(struct dm_ioctl* io, const std::string& name, int fd) {
- fs_mgr_verity_ioctl_init(io, name, 0);
+bool fs_mgr_dm_resume_table(struct dm_ioctl* io, const std::string& name, int fd) {
+ fs_mgr_dm_ioctl_init(io, sizeof(*io), name);
if (ioctl(fd, DM_DEV_SUSPEND, io)) {
- PERROR << "Error activating verity device";
+ PERROR << "Error activating device table";
return false;
}
return true;
diff --git a/fs_mgr/fs_mgr_priv_dm_ioctl.h b/fs_mgr/fs_mgr_priv_dm_ioctl.h
index a00a9c1..792475d 100644
--- a/fs_mgr/fs_mgr_priv_dm_ioctl.h
+++ b/fs_mgr/fs_mgr_priv_dm_ioctl.h
@@ -20,15 +20,15 @@
#include <linux/dm-ioctl.h>
#include <string>
-void fs_mgr_verity_ioctl_init(struct dm_ioctl* io, const std::string& name, unsigned flags);
+void fs_mgr_dm_ioctl_init(struct dm_ioctl* io, size_t size, const std::string& name);
-bool fs_mgr_create_verity_device(struct dm_ioctl* io, const std::string& name, int fd);
+bool fs_mgr_dm_create_device(struct dm_ioctl* io, const std::string& name, int fd);
-bool fs_mgr_destroy_verity_device(struct dm_ioctl* io, const std::string& name, int fd);
+bool fs_mgr_dm_destroy_device(struct dm_ioctl* io, const std::string& name, int fd);
-bool fs_mgr_get_verity_device_name(struct dm_ioctl* io, const std::string& name, int fd,
- std::string* out_dev_name);
+bool fs_mgr_dm_get_device_name(struct dm_ioctl* io, const std::string& name, int fd,
+ std::string* out_dev_name);
-bool fs_mgr_resume_verity_table(struct dm_ioctl* io, const std::string& name, int fd);
+bool fs_mgr_dm_resume_table(struct dm_ioctl* io, const std::string& name, int fd);
#endif /* __CORE_FS_MGR_PRIV_DM_IOCTL_H */
diff --git a/fs_mgr/fs_mgr_verity.cpp b/fs_mgr/fs_mgr_verity.cpp
index 896b603..fe41f8a 100644
--- a/fs_mgr/fs_mgr_verity.cpp
+++ b/fs_mgr/fs_mgr_verity.cpp
@@ -258,12 +258,13 @@
char *buffer = (char*) io;
size_t bufsize;
- fs_mgr_verity_ioctl_init(io, name, DM_STATUS_TABLE_FLAG);
+ fs_mgr_dm_ioctl_init(io, DM_BUF_SIZE, name);
struct dm_target_spec *tgt = (struct dm_target_spec *) &buffer[sizeof(struct dm_ioctl)];
// set tgt arguments
io->target_count = 1;
+ io->flags = DM_READONLY_FLAG;
tgt->status = 0;
tgt->sector_start = 0;
tgt->length = device_size / 512;
@@ -804,13 +805,13 @@
}
// create the device
- if (!fs_mgr_create_verity_device(io, mount_point, fd)) {
+ if (!fs_mgr_dm_create_device(io, mount_point, fd)) {
LERROR << "Couldn't create verity device!";
goto out;
}
// get the name of the device file
- if (!fs_mgr_get_verity_device_name(io, mount_point, fd, &verity_blk_name)) {
+ if (!fs_mgr_dm_get_device_name(io, mount_point, fd, &verity_blk_name)) {
LERROR << "Couldn't get verity device number!";
goto out;
}
@@ -899,7 +900,7 @@
loaded:
// activate the device
- if (!fs_mgr_resume_verity_table(io, mount_point, fd)) {
+ if (!fs_mgr_dm_resume_table(io, mount_point, fd)) {
goto out;
}
@@ -922,7 +923,7 @@
if (!verified_at_boot) {
free(fstab->blk_device);
fstab->blk_device = strdup(verity_blk_name.c_str());
- } else if (!fs_mgr_destroy_verity_device(io, mount_point, fd)) {
+ } else if (!fs_mgr_dm_destroy_device(io, mount_point, fd)) {
LERROR << "Failed to remove verity device " << mount_point.c_str();
goto out;
}
diff --git a/healthd/OWNERS b/healthd/OWNERS
index 2375f7c..00df08a 100644
--- a/healthd/OWNERS
+++ b/healthd/OWNERS
@@ -1 +1,2 @@
+elsk@google.com
toddpoynor@google.com
diff --git a/init/Android.bp b/init/Android.bp
index 70a4ac6..a31c5a5 100644
--- a/init/Android.bp
+++ b/init/Android.bp
@@ -180,6 +180,7 @@
"util_test.cpp",
],
static_libs: ["libinit"],
+ test_suites: ["device-tests"],
}
cc_benchmark {
diff --git a/init/README.md b/init/README.md
index b14521c..c08b07a 100644
--- a/init/README.md
+++ b/init/README.md
@@ -193,7 +193,7 @@
`disabled`
> This service will not automatically start with its class.
- It must be explicitly started by name.
+ It must be explicitly started by name or by interface name.
`file <path> <type>`
> Open a file path and pass its fd to the launched process. _type_ must be
diff --git a/init/bootchart.cpp b/init/bootchart.cpp
index 379b4fa..c2cf573 100644
--- a/init/bootchart.cpp
+++ b/init/bootchart.cpp
@@ -32,12 +32,14 @@
#include <mutex>
#include <thread>
+#include <android-base/chrono_utils.h>
#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/properties.h>
#include <android-base/stringprintf.h>
using android::base::StringPrintf;
+using android::base::boot_clock;
using namespace std::chrono_literals;
namespace android {
@@ -50,9 +52,9 @@
static bool g_bootcharting_finished;
static long long get_uptime_jiffies() {
- std::string uptime;
- if (!android::base::ReadFileToString("/proc/uptime", &uptime)) return 0;
- return 100LL * strtod(uptime.c_str(), NULL);
+ constexpr int64_t kNanosecondsPerJiffy = 10000000;
+ boot_clock::time_point uptime = boot_clock::now();
+ return uptime.time_since_epoch().count() / kNanosecondsPerJiffy;
}
static std::unique_ptr<FILE, decltype(&fclose)> fopen_unique(const char* filename,
diff --git a/init/host_init_stubs.cpp b/init/host_init_stubs.cpp
index e6cc08a..4451ac8 100644
--- a/init/host_init_stubs.cpp
+++ b/init/host_init_stubs.cpp
@@ -49,6 +49,10 @@
}
// selinux.h
+bool SelinuxHasVendorInit() {
+ return true;
+}
+
void SelabelInitialize() {}
bool SelabelLookupFileContext(const std::string& key, int type, std::string* result) {
diff --git a/init/host_init_stubs.h b/init/host_init_stubs.h
index ddfb7ae..ad48602 100644
--- a/init/host_init_stubs.h
+++ b/init/host_init_stubs.h
@@ -56,6 +56,7 @@
const std::string& source_context, const ucred& cr, std::string* error);
// selinux.h
+bool SelinuxHasVendorInit();
void SelabelInitialize();
bool SelabelLookupFileContext(const std::string& key, int type, std::string* result);
diff --git a/init/init.cpp b/init/init.cpp
index 0d5690b..54bbb4b 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -31,6 +31,10 @@
#include <sys/types.h>
#include <unistd.h>
+#include <map>
+#include <memory>
+#include <optional>
+
#include <android-base/chrono_utils.h>
#include <android-base/file.h>
#include <android-base/logging.h>
@@ -43,9 +47,6 @@
#include <private/android_filesystem_config.h>
#include <selinux/android.h>
-#include <memory>
-#include <optional>
-
#include "action_parser.h"
#include "import_parser.h"
#include "init_first_stage.h"
@@ -130,12 +131,31 @@
}
}
-void register_epoll_handler(int fd, void (*fn)()) {
+static std::map<int, std::function<void()>> epoll_handlers;
+
+void register_epoll_handler(int fd, std::function<void()> handler) {
+ auto[it, inserted] = epoll_handlers.emplace(fd, std::move(handler));
+ if (!inserted) {
+ LOG(ERROR) << "Cannot specify two epoll handlers for a given FD";
+ return;
+ }
epoll_event ev;
ev.events = EPOLLIN;
- ev.data.ptr = reinterpret_cast<void*>(fn);
+ // std::map's iterators do not get invalidated until erased, so we use the pointer to the
+ // std::function in the map directly for epoll_ctl.
+ ev.data.ptr = reinterpret_cast<void*>(&it->second);
if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd, &ev) == -1) {
- PLOG(ERROR) << "epoll_ctl failed";
+ PLOG(ERROR) << "epoll_ctl failed to add fd";
+ epoll_handlers.erase(fd);
+ }
+}
+
+void unregister_epoll_handler(int fd) {
+ if (epoll_ctl(epoll_fd, EPOLL_CTL_DEL, fd, nullptr) == -1) {
+ PLOG(ERROR) << "epoll_ctl failed to remove fd";
+ }
+ if (epoll_handlers.erase(fd) != 1) {
+ LOG(ERROR) << "Attempting to remove epoll handler for FD without an existing handler";
}
}
@@ -334,8 +354,8 @@
return Success();
}
-static Result<Success> keychord_init_action(const BuiltinArguments& args) {
- keychord_init();
+static Result<Success> KeychordInitAction(const BuiltinArguments& args) {
+ KeychordInit();
return Success();
}
@@ -752,7 +772,7 @@
am.QueueBuiltinAction(MixHwrngIntoLinuxRngAction, "MixHwrngIntoLinuxRng");
am.QueueBuiltinAction(SetMmapRndBitsAction, "SetMmapRndBits");
am.QueueBuiltinAction(SetKptrRestrictAction, "SetKptrRestrict");
- am.QueueBuiltinAction(keychord_init_action, "keychord_init");
+ am.QueueBuiltinAction(KeychordInitAction, "KeychordInit");
am.QueueBuiltinAction(console_init_action, "console_init");
// Trigger all the boot actions to get us started.
@@ -809,7 +829,7 @@
if (nr == -1) {
PLOG(ERROR) << "epoll_wait failed";
} else if (nr == 1) {
- ((void (*)()) ev.data.ptr)();
+ std::invoke(*reinterpret_cast<std::function<void()>*>(ev.data.ptr));
}
}
diff --git a/init/init.h b/init/init.h
index d4a0e96..e7c4d8d 100644
--- a/init/init.h
+++ b/init/init.h
@@ -19,6 +19,7 @@
#include <sys/types.h>
+#include <functional>
#include <string>
#include <vector>
@@ -42,7 +43,8 @@
void property_changed(const std::string& name, const std::string& value);
-void register_epoll_handler(int fd, void (*fn)());
+void register_epoll_handler(int fd, std::function<void()> handler);
+void unregister_epoll_handler(int fd);
bool start_waiting_for_property(const char *name, const char *value);
diff --git a/init/init_first_stage.cpp b/init/init_first_stage.cpp
index 0f4cc68..1b6e97e 100644
--- a/init/init_first_stage.cpp
+++ b/init/init_first_stage.cpp
@@ -118,17 +118,14 @@
// -----------------
FirstStageMount::FirstStageMount()
: need_dm_verity_(false), device_tree_fstab_(fs_mgr_read_fstab_dt(), fs_mgr_free_fstab) {
- if (!device_tree_fstab_) {
- // The client of FirstStageMount should check the existence of fstab in device-tree
- // in advance, without parsing it. Reaching here means there is a FATAL error when
- // parsing the fstab. So stop here to expose the failure.
- LOG(FATAL) << "Failed to read fstab from device tree";
- return;
- }
- // Stores device_tree_fstab_->recs[] into mount_fstab_recs_ (vector<fstab_rec*>)
- // for easier manipulation later, e.g., range-base for loop.
- for (int i = 0; i < device_tree_fstab_->num_entries; i++) {
- mount_fstab_recs_.push_back(&device_tree_fstab_->recs[i]);
+ if (device_tree_fstab_) {
+ // Stores device_tree_fstab_->recs[] into mount_fstab_recs_ (vector<fstab_rec*>)
+ // for easier manipulation later, e.g., range-base for loop.
+ for (int i = 0; i < device_tree_fstab_->num_entries; i++) {
+ mount_fstab_recs_.push_back(&device_tree_fstab_->recs[i]);
+ }
+ } else {
+ LOG(INFO) << "Failed to read fstab from device tree";
}
}
@@ -141,8 +138,11 @@
}
bool FirstStageMount::DoFirstStageMount() {
- // Nothing to mount.
- if (mount_fstab_recs_.empty()) return true;
+ if (mount_fstab_recs_.empty()) {
+ // Nothing to mount.
+ LOG(INFO) << "First stage mount skipped (missing/incompatible/empty fstab in device tree)";
+ return true;
+ }
if (!InitDevices()) return false;
@@ -482,12 +482,6 @@
return true;
}
- // Firstly checks if device tree fstab entries are compatible.
- if (!is_android_dt_value_expected("fstab/compatible", "android,fstab")) {
- LOG(INFO) << "First stage mount skipped (missing/incompatible fstab in device tree)";
- return true;
- }
-
std::unique_ptr<FirstStageMount> handle = FirstStageMount::Create();
if (!handle) {
LOG(ERROR) << "Failed to create FirstStageMount";
diff --git a/init/keychords.cpp b/init/keychords.cpp
index e686ce1..f55d2c4 100644
--- a/init/keychords.cpp
+++ b/init/keychords.cpp
@@ -16,13 +16,20 @@
#include "keychords.h"
+#include <dirent.h>
#include <fcntl.h>
-#include <stdlib.h>
-#include <sys/stat.h>
+#include <linux/input.h>
+#include <sys/cdefs.h>
+#include <sys/ioctl.h>
#include <sys/types.h>
-#include <linux/keychord.h>
#include <unistd.h>
+#include <algorithm>
+#include <functional>
+#include <memory>
+#include <string>
+#include <vector>
+
#include <android-base/logging.h>
#include <android-base/properties.h>
@@ -31,51 +38,87 @@
namespace android {
namespace init {
-static struct input_keychord *keychords = 0;
-static int keychords_count = 0;
-static int keychords_length = 0;
-static int keychord_fd = -1;
+namespace {
-void add_service_keycodes(Service* svc)
-{
- struct input_keychord *keychord;
- size_t i, size;
+int keychords_count;
- if (!svc->keycodes().empty()) {
- /* add a new keychord to the list */
- size = sizeof(*keychord) + svc->keycodes().size() * sizeof(keychord->keycodes[0]);
- keychords = (input_keychord*) realloc(keychords, keychords_length + size);
- if (!keychords) {
- PLOG(ERROR) << "could not allocate keychords";
- keychords_length = 0;
- keychords_count = 0;
- return;
+struct KeychordEntry {
+ const std::vector<int> keycodes;
+ bool notified;
+ int id;
+
+ KeychordEntry(const std::vector<int>& keycodes, int id)
+ : keycodes(keycodes), notified(false), id(id) {}
+};
+
+std::vector<KeychordEntry> keychord_entries;
+
+// Bit management
+class KeychordMask {
+ private:
+ typedef unsigned int mask_t;
+ std::vector<mask_t> bits;
+ static constexpr size_t bits_per_byte = 8;
+
+ public:
+ explicit KeychordMask(size_t bit = 0) : bits((bit + sizeof(mask_t) - 1) / sizeof(mask_t), 0) {}
+
+ void SetBit(size_t bit, bool value = true) {
+ auto idx = bit / (bits_per_byte * sizeof(mask_t));
+ if (idx >= bits.size()) return;
+ if (value) {
+ bits[idx] |= mask_t(1) << (bit % (bits_per_byte * sizeof(mask_t)));
+ } else {
+ bits[idx] &= ~(mask_t(1) << (bit % (bits_per_byte * sizeof(mask_t))));
}
-
- keychord = (struct input_keychord *)((char *)keychords + keychords_length);
- keychord->version = KEYCHORD_VERSION;
- keychord->id = keychords_count + 1;
- keychord->count = svc->keycodes().size();
- svc->set_keychord_id(keychord->id);
-
- for (i = 0; i < svc->keycodes().size(); i++) {
- keychord->keycodes[i] = svc->keycodes()[i];
- }
- keychords_count++;
- keychords_length += size;
- }
-}
-
-static void handle_keychord() {
- int ret;
- __u16 id;
-
- ret = read(keychord_fd, &id, sizeof(id));
- if (ret != sizeof(id)) {
- PLOG(ERROR) << "could not read keychord id";
- return;
}
+ bool GetBit(size_t bit) const {
+ auto idx = bit / (bits_per_byte * sizeof(mask_t));
+ return bits[idx] & (mask_t(1) << (bit % (bits_per_byte * sizeof(mask_t))));
+ }
+
+ size_t bytesize() const { return bits.size() * sizeof(mask_t); }
+ void* data() { return bits.data(); }
+ size_t size() const { return bits.size() * sizeof(mask_t) * bits_per_byte; }
+ void resize(size_t bit) {
+ auto idx = bit / (bits_per_byte * sizeof(mask_t));
+ if (idx >= bits.size()) {
+ bits.resize(idx + 1, 0);
+ }
+ }
+
+ operator bool() const {
+ for (size_t i = 0; i < bits.size(); ++i) {
+ if (bits[i]) return true;
+ }
+ return false;
+ }
+
+ KeychordMask operator&(const KeychordMask& rval) const {
+ auto len = std::min(bits.size(), rval.bits.size());
+ KeychordMask ret;
+ ret.bits.resize(len);
+ for (size_t i = 0; i < len; ++i) {
+ ret.bits[i] = bits[i] & rval.bits[i];
+ }
+ return ret;
+ }
+
+ void operator|=(const KeychordMask& rval) {
+ size_t len = rval.bits.size();
+ bits.resize(len);
+ for (size_t i = 0; i < len; ++i) {
+ bits[i] |= rval.bits[i];
+ }
+ }
+};
+
+KeychordMask keychord_current;
+
+constexpr char kDevicePath[] = "/dev/input";
+
+void HandleKeychord(int id) {
// Only handle keychords if adb is enabled.
std::string adb_enabled = android::base::GetProperty("init.svc.adbd", "");
if (adb_enabled == "running") {
@@ -94,32 +137,125 @@
}
}
-void keychord_init() {
+void KeychordLambdaCheck() {
+ for (auto& e : keychord_entries) {
+ bool found = true;
+ for (auto& code : e.keycodes) {
+ if (!keychord_current.GetBit(code)) {
+ e.notified = false;
+ found = false;
+ break;
+ }
+ }
+ if (!found) continue;
+ if (e.notified) continue;
+ e.notified = true;
+ HandleKeychord(e.id);
+ }
+}
+
+void KeychordLambdaHandler(int fd) {
+ input_event event;
+ auto res = TEMP_FAILURE_RETRY(::read(fd, &event, sizeof(event)));
+ if ((res != sizeof(event)) || (event.type != EV_KEY)) return;
+ keychord_current.SetBit(event.code, event.value);
+ KeychordLambdaCheck();
+}
+
+bool KeychordGeteventEnable(int fd) {
+ static bool EviocsmaskSupported = true;
+
+ // Make sure it is an event channel, should pass this ioctl call
+ int version;
+ if (::ioctl(fd, EVIOCGVERSION, &version)) return false;
+
+ if (EviocsmaskSupported) {
+ KeychordMask mask(EV_KEY);
+ mask.SetBit(EV_KEY);
+ input_mask msg = {};
+ msg.type = EV_SYN;
+ msg.codes_size = mask.bytesize();
+ msg.codes_ptr = reinterpret_cast<uintptr_t>(mask.data());
+ if (::ioctl(fd, EVIOCSMASK, &msg) == -1) {
+ PLOG(WARNING) << "EVIOCSMASK not supported";
+ EviocsmaskSupported = false;
+ }
+ }
+
+ KeychordMask mask;
+ for (auto& e : keychord_entries) {
+ for (auto& code : e.keycodes) {
+ mask.resize(code);
+ mask.SetBit(code);
+ }
+ }
+
+ keychord_current.resize(mask.size());
+ KeychordMask available(mask.size());
+ auto res = ::ioctl(fd, EVIOCGBIT(EV_KEY, available.bytesize()), available.data());
+ if (res == -1) return false;
+ if (!(available & mask)) return false;
+
+ if (EviocsmaskSupported) {
+ input_mask msg = {};
+ msg.type = EV_KEY;
+ msg.codes_size = mask.bytesize();
+ msg.codes_ptr = reinterpret_cast<uintptr_t>(mask.data());
+ ::ioctl(fd, EVIOCSMASK, &msg);
+ }
+
+ KeychordMask set(mask.size());
+ res = ::ioctl(fd, EVIOCGKEY(res), set.data());
+ if (res > 0) {
+ keychord_current |= mask & available & set;
+ KeychordLambdaCheck();
+ }
+ register_epoll_handler(fd, [fd]() { KeychordLambdaHandler(fd); });
+ return true;
+}
+
+void GeteventOpenDevice(const std::string& device) {
+ auto fd = TEMP_FAILURE_RETRY(::open(device.c_str(), O_RDWR | O_CLOEXEC));
+ if (fd == -1) {
+ PLOG(ERROR) << "Can not open " << device;
+ return;
+ }
+ if (!KeychordGeteventEnable(fd)) {
+ ::close(fd);
+ }
+}
+
+void GeteventOpenDevice() {
+ std::unique_ptr<DIR, decltype(&closedir)> device(opendir(kDevicePath), closedir);
+ if (!device) return;
+
+ dirent* entry;
+ while ((entry = readdir(device.get()))) {
+ if (entry->d_name[0] == '.') continue;
+ std::string devname(kDevicePath);
+ devname += '/';
+ devname += entry->d_name;
+ GeteventOpenDevice(devname);
+ }
+}
+
+void AddServiceKeycodes(Service* svc) {
+ if (svc->keycodes().empty()) return;
+ for (auto& code : svc->keycodes()) {
+ if ((code < 0) || (code >= KEY_MAX)) return;
+ }
+ ++keychords_count;
+ keychord_entries.emplace_back(KeychordEntry(svc->keycodes(), keychords_count));
+ svc->set_keychord_id(keychords_count);
+}
+
+} // namespace
+
+void KeychordInit() {
for (const auto& service : ServiceList::GetInstance()) {
- add_service_keycodes(service.get());
+ AddServiceKeycodes(service.get());
}
-
- // Nothing to do if no services require keychords.
- if (!keychords) {
- return;
- }
-
- keychord_fd = TEMP_FAILURE_RETRY(open("/dev/keychord", O_RDWR | O_CLOEXEC));
- if (keychord_fd == -1) {
- PLOG(ERROR) << "could not open /dev/keychord";
- return;
- }
-
- int ret = write(keychord_fd, keychords, keychords_length);
- if (ret != keychords_length) {
- PLOG(ERROR) << "could not configure /dev/keychord " << ret;
- close(keychord_fd);
- }
-
- free(keychords);
- keychords = nullptr;
-
- register_epoll_handler(keychord_fd, handle_keychord);
+ if (keychords_count) GeteventOpenDevice();
}
} // namespace init
diff --git a/init/keychords.h b/init/keychords.h
index 1c34098..689a3b5 100644
--- a/init/keychords.h
+++ b/init/keychords.h
@@ -22,8 +22,7 @@
namespace android {
namespace init {
-void add_service_keycodes(Service* svc);
-void keychord_init();
+void KeychordInit();
} // namespace init
} // namespace android
diff --git a/init/property_service.cpp b/init/property_service.cpp
index 99d3c83..c3100a5 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -59,6 +59,7 @@
#include "init.h"
#include "persistent_properties.h"
#include "property_type.h"
+#include "selinux.h"
#include "subcontext.h"
#include "util.h"
@@ -542,7 +543,7 @@
size_t flen = 0;
const char* context = kInitContext.c_str();
- if (GetIntProperty("ro.vndk.version", 28) >= 28) {
+ if (SelinuxHasVendorInit()) {
for (const auto& [path_prefix, secontext] : paths_and_secontexts) {
if (StartsWith(filename, path_prefix)) {
context = secontext;
diff --git a/init/selinux.cpp b/init/selinux.cpp
index 6aba9c1..0ba5c4a 100644
--- a/init/selinux.cpp
+++ b/init/selinux.cpp
@@ -55,12 +55,14 @@
#include <android-base/chrono_utils.h>
#include <android-base/file.h>
#include <android-base/logging.h>
+#include <android-base/parseint.h>
#include <android-base/unique_fd.h>
#include <selinux/android.h>
#include "log.h"
#include "util.h"
+using android::base::ParseInt;
using android::base::Timer;
using android::base::unique_fd;
@@ -453,6 +455,31 @@
selinux_set_callback(SELINUX_CB_LOG, cb);
}
+// This function checks whether the sepolicy supports vendor init.
+bool SelinuxHasVendorInit() {
+ if (!IsSplitPolicyDevice()) {
+ // If this device does not split sepolicy files, vendor_init will be available in the latest
+ // monolithic sepolicy file.
+ return true;
+ }
+
+ std::string version;
+ if (!GetVendorMappingVersion(&version)) {
+ // Return true as the default if we failed to load the vendor sepolicy version.
+ return true;
+ }
+
+ int major_version;
+ std::string major_version_str(version, 0, version.find('.'));
+ if (!ParseInt(major_version_str, &major_version)) {
+ PLOG(ERROR) << "Failed to parse the vendor sepolicy major version " << major_version_str;
+ // Return true as the default if we failed to parse the major version.
+ return true;
+ }
+
+ return major_version >= 28;
+}
+
// selinux_android_file_context_handle() takes on the order of 10+ms to run, so we want to cache
// its value. selinux_android_restorecon() also needs an sehandle for file context look up. It
// will create and store its own copy, but selinux_android_set_sehandle() can be used to provide
diff --git a/init/selinux.h b/init/selinux.h
index 7b880ec..30069b5 100644
--- a/init/selinux.h
+++ b/init/selinux.h
@@ -27,6 +27,7 @@
void SelinuxRestoreContext();
void SelinuxSetupKernelLogging();
+bool SelinuxHasVendorInit();
void SelabelInitialize();
bool SelabelLookupFileContext(const std::string& key, int type, std::string* result);
diff --git a/init/subcontext.cpp b/init/subcontext.cpp
index 9c0c0bb..fdb4641 100644
--- a/init/subcontext.cpp
+++ b/init/subcontext.cpp
@@ -357,7 +357,7 @@
static std::vector<Subcontext> subcontexts;
std::vector<Subcontext>* InitializeSubcontexts() {
- if (GetIntProperty("ro.vndk.version", 28) >= 28) {
+ if (SelinuxHasVendorInit()) {
for (const auto& [path_prefix, secontext] : paths_and_secontexts) {
subcontexts.emplace_back(path_prefix, secontext);
}
diff --git a/libbacktrace/Android.bp b/libbacktrace/Android.bp
index 4bd01d2..11b8144 100644
--- a/libbacktrace/Android.bp
+++ b/libbacktrace/Android.bp
@@ -22,11 +22,6 @@
"-Werror",
],
- // The latest clang (r230699) does not allow SP/PC to be declared in inline asm lists.
- clang_cflags: ["-Wno-inline-asm"],
-
- include_dirs: ["external/libunwind/include/tdep"],
-
target: {
darwin: {
enabled: false,
@@ -92,7 +87,6 @@
shared_libs: [
"libbase",
"liblog",
- "libunwind",
"libunwindstack",
"libdexfile",
],
@@ -183,12 +177,16 @@
"art/runtime",
],
+ test_suites: ["device-tests"],
data: [
"testdata/arm/*",
"testdata/arm64/*",
"testdata/x86/*",
"testdata/x86_64/*",
],
+ required: [
+ "libbacktrace_test",
+ ],
}
cc_benchmark {
diff --git a/libcutils/Android.bp b/libcutils/Android.bp
index 6d00dc6..e823257 100644
--- a/libcutils/Android.bp
+++ b/libcutils/Android.bp
@@ -160,10 +160,20 @@
misc_undefined: ["integer"],
},
},
+
+ vendor: {
+ exclude_srcs: [
+ // qtaguid.cpp loads libnetd_client.so with dlopen(). Since
+ // the interface of libnetd_client.so may vary between AOSP
+ // releases, exclude qtaguid.cpp from the VNDK-SP variant.
+ "qtaguid.cpp",
+ ],
+ }
},
shared_libs: ["liblog"],
header_libs: [
+ "libbase_headers",
"libcutils_headers",
"libutils_headers",
],
diff --git a/libcutils/ashmem-host.cpp b/libcutils/ashmem-host.cpp
index b2bec99..bb990d5 100644
--- a/libcutils/ashmem-host.cpp
+++ b/libcutils/ashmem-host.cpp
@@ -24,7 +24,6 @@
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
-#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
diff --git a/libcutils/canned_fs_config.cpp b/libcutils/canned_fs_config.cpp
index 6b5763b..2772ef0 100644
--- a/libcutils/canned_fs_config.cpp
+++ b/libcutils/canned_fs_config.cpp
@@ -21,7 +21,6 @@
#include <errno.h>
#include <inttypes.h>
#include <limits.h>
-#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
diff --git a/libcutils/hashmap.cpp b/libcutils/hashmap.cpp
index 65b6ab1..10e3b25 100644
--- a/libcutils/hashmap.cpp
+++ b/libcutils/hashmap.cpp
@@ -21,7 +21,6 @@
#include <cutils/threads.h>
#include <stdlib.h>
#include <string.h>
-#include <stdbool.h>
#include <sys/types.h>
typedef struct Entry Entry;
diff --git a/libcutils/include/cutils/sched_policy.h b/libcutils/include/cutils/sched_policy.h
index 4c1113b..cf91b76 100644
--- a/libcutils/include/cutils/sched_policy.h
+++ b/libcutils/include/cutils/sched_policy.h
@@ -40,16 +40,17 @@
/* Keep in sync with THREAD_GROUP_* in frameworks/base/core/java/android/os/Process.java */
typedef enum {
- SP_DEFAULT = -1,
+ SP_DEFAULT = -1,
SP_BACKGROUND = 0,
SP_FOREGROUND = 1,
- SP_SYSTEM = 2, // can't be used with set_sched_policy()
- SP_AUDIO_APP = 3,
- SP_AUDIO_SYS = 4,
- SP_TOP_APP = 5,
- SP_RT_APP = 6,
+ SP_SYSTEM = 2, // can't be used with set_sched_policy()
+ SP_AUDIO_APP = 3,
+ SP_AUDIO_SYS = 4,
+ SP_TOP_APP = 5,
+ SP_RT_APP = 6,
+ SP_RESTRICTED = 7,
SP_CNT,
- SP_MAX = SP_CNT - 1,
+ SP_MAX = SP_CNT - 1,
SP_SYSTEM_DEFAULT = SP_FOREGROUND,
} SchedPolicy;
diff --git a/libcutils/include/cutils/trace.h b/libcutils/include/cutils/trace.h
index bbb150d..58b9f09 100644
--- a/libcutils/include/cutils/trace.h
+++ b/libcutils/include/cutils/trace.h
@@ -74,7 +74,8 @@
#define ATRACE_TAG_ADB (1<<22)
#define ATRACE_TAG_VIBRATOR (1<<23)
#define ATRACE_TAG_AIDL (1<<24)
-#define ATRACE_TAG_LAST ATRACE_TAG_AIDL
+#define ATRACE_TAG_NNAPI (1<<25)
+#define ATRACE_TAG_LAST ATRACE_TAG_NNAPI
// Reserved for initialization.
#define ATRACE_TAG_NOT_READY (1ULL<<63)
diff --git a/libcutils/include/private/android_filesystem_config.h b/libcutils/include/private/android_filesystem_config.h
index 8209167..3be8ad0 100644
--- a/libcutils/include/private/android_filesystem_config.h
+++ b/libcutils/include/private/android_filesystem_config.h
@@ -130,6 +130,7 @@
#define AID_INCIDENTD 1067 /* incidentd daemon */
#define AID_SECURE_ELEMENT 1068 /* secure element subsystem */
#define AID_LMKD 1069 /* low memory killer daemon */
+#define AID_LLKD 1070 /* live lock daemon */
/* Changes to this file must be made in AOSP, *not* in internal branches. */
#define AID_SHELL 2000 /* adb and debug shell user */
diff --git a/libcutils/sched_policy.cpp b/libcutils/sched_policy.cpp
index f5ce82f..3fa548f 100644
--- a/libcutils/sched_policy.cpp
+++ b/libcutils/sched_policy.cpp
@@ -25,6 +25,7 @@
#include <string.h>
#include <unistd.h>
+#include <android-base/macros.h>
#include <log/log.h>
/* Re-map SP_DEFAULT to the system default policy, and leave other values unchanged.
@@ -57,6 +58,7 @@
static int bg_cpuset_fd = -1;
static int fg_cpuset_fd = -1;
static int ta_cpuset_fd = -1; // special cpuset for top app
+static int rs_cpuset_fd = -1; // special cpuset for screen off restrictions
// File descriptors open to /dev/stune/../tasks, setup by initialize, or -1 on error
static int bg_schedboost_fd = -1;
@@ -151,6 +153,8 @@
system_bg_cpuset_fd = open(filename, O_WRONLY | O_CLOEXEC);
filename = "/dev/cpuset/top-app/tasks";
ta_cpuset_fd = open(filename, O_WRONLY | O_CLOEXEC);
+ filename = "/dev/cpuset/restricted/tasks";
+ rs_cpuset_fd = open(filename, O_WRONLY | O_CLOEXEC);
if (schedboost_enabled()) {
filename = "/dev/stune/top-app/tasks";
@@ -308,6 +312,9 @@
case SP_SYSTEM:
fd = system_bg_cpuset_fd;
break;
+ case SP_RESTRICTED:
+ fd = rs_cpuset_fd;
+ break;
default:
boost_fd = fd = -1;
break;
@@ -454,20 +461,16 @@
#endif
-const char *get_sched_policy_name(SchedPolicy policy)
-{
+const char* get_sched_policy_name(SchedPolicy policy) {
policy = _policy(policy);
- static const char * const strings[SP_CNT] = {
- [SP_BACKGROUND] = "bg",
- [SP_FOREGROUND] = "fg",
- [SP_SYSTEM] = " ",
- [SP_AUDIO_APP] = "aa",
- [SP_AUDIO_SYS] = "as",
- [SP_TOP_APP] = "ta",
- [SP_RT_APP] = "rt",
+ static const char* const kSchedPolicyNames[] = {
+ [SP_BACKGROUND] = "bg", [SP_FOREGROUND] = "fg", [SP_SYSTEM] = " ",
+ [SP_AUDIO_APP] = "aa", [SP_AUDIO_SYS] = "as", [SP_TOP_APP] = "ta",
+ [SP_RT_APP] = "rt", [SP_RESTRICTED] = "rs",
};
- if ((policy < SP_CNT) && (strings[policy] != NULL))
- return strings[policy];
- else
+ static_assert(arraysize(kSchedPolicyNames) == SP_CNT, "missing name");
+ if (policy < SP_BACKGROUND || policy >= SP_CNT) {
return "error";
+ }
+ return kSchedPolicyNames[policy];
}
diff --git a/libcutils/tests/sched_policy_test.cpp b/libcutils/tests/sched_policy_test.cpp
index 173174a..5942ee5 100644
--- a/libcutils/tests/sched_policy_test.cpp
+++ b/libcutils/tests/sched_policy_test.cpp
@@ -60,6 +60,12 @@
return sleepTimes[median];
}
+static void AssertPolicy(SchedPolicy expected_policy) {
+ SchedPolicy current_policy;
+ ASSERT_EQ(0, get_sched_policy(0, ¤t_policy));
+ EXPECT_EQ(expected_policy, current_policy);
+}
+
TEST(SchedPolicy, set_sched_policy) {
if (!hasCapSysNice()) {
GTEST_LOG_(INFO) << "skipping test that requires CAP_SYS_NICE";
@@ -76,23 +82,17 @@
const unsigned int BG_FG_SLACK_FACTOR = 100;
ASSERT_EQ(0, set_sched_policy(0, SP_BACKGROUND));
+ AssertPolicy(SP_BACKGROUND);
auto bgSleepTime = medianSleepTime();
ASSERT_EQ(0, set_sched_policy(0, SP_FOREGROUND));
+ AssertPolicy(SP_FOREGROUND);
auto fgSleepTime = medianSleepTime();
ASSERT_GT(bgSleepTime, fgSleepTime * BG_FG_SLACK_FACTOR);
}
-TEST(SchedPolicy, get_sched_policy) {
- SchedPolicy policy;
- ASSERT_EQ(0, get_sched_policy(0, &policy));
-
- const char *policyName = get_sched_policy_name(policy);
- EXPECT_NE(nullptr, policyName);
- EXPECT_STRNE("error", policyName);
-
- ASSERT_EQ(0, set_sched_policy(0, SP_BACKGROUND));
- SchedPolicy newPolicy;
- ASSERT_EQ(0, get_sched_policy(0, &newPolicy));
- EXPECT_EQ(SP_BACKGROUND, newPolicy);
+TEST(SchedPolicy, get_sched_policy_name) {
+ EXPECT_STREQ("bg", get_sched_policy_name(SP_BACKGROUND));
+ EXPECT_STREQ("error", get_sched_policy_name(SchedPolicy(-2)));
+ EXPECT_STREQ("error", get_sched_policy_name(SP_CNT));
}
diff --git a/libcutils/trace-dev.inc b/libcutils/trace-dev.inc
index f95c6c5..c9580af 100644
--- a/libcutils/trace-dev.inc
+++ b/libcutils/trace-dev.inc
@@ -24,7 +24,6 @@
#include <limits.h>
#include <pthread.h>
#include <stdatomic.h>
-#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
diff --git a/libkeyutils/Android.bp b/libkeyutils/Android.bp
index 0285259..f3593ff 100644
--- a/libkeyutils/Android.bp
+++ b/libkeyutils/Android.bp
@@ -13,4 +13,5 @@
cflags: ["-Werror"],
shared_libs: ["libkeyutils"],
srcs: ["keyutils_test.cpp"],
+ test_suites: ["device-tests"],
}
diff --git a/libmemunreachable/Android.bp b/libmemunreachable/Android.bp
index 619ee34..f164a19 100644
--- a/libmemunreachable/Android.bp
+++ b/libmemunreachable/Android.bp
@@ -89,6 +89,8 @@
enabled: false,
},
},
+
+ test_suites: ["device-tests"],
}
cc_test {
@@ -103,4 +105,5 @@
"libhwbinder",
"libutils",
],
+ test_suites: ["device-tests"],
}
diff --git a/libmetricslogger/Android.bp b/libmetricslogger/Android.bp
index 6549b8d..e6e17ce 100644
--- a/libmetricslogger/Android.bp
+++ b/libmetricslogger/Android.bp
@@ -29,6 +29,13 @@
defaults: ["metricslogger_defaults"],
}
+// static version of libmetricslogger, needed by a few art static binaries
+cc_library_static {
+ name: "libmetricslogger_static",
+ srcs: metricslogger_lib_src_files,
+ defaults: ["metricslogger_defaults"],
+}
+
// metricslogger shared library, debug
// -----------------------------------------------------------------------------
cc_library_shared {
diff --git a/libmetricslogger/include/metricslogger/metrics_logger.h b/libmetricslogger/include/metricslogger/metrics_logger.h
index 189bc4b..2c76869 100644
--- a/libmetricslogger/include/metricslogger/metrics_logger.h
+++ b/libmetricslogger/include/metricslogger/metrics_logger.h
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+#include <log/log_event_list.h>
#include <cstdint>
#include <string>
@@ -32,6 +33,34 @@
// |value| in the field |field|.
void LogMultiAction(int32_t category, int32_t field, const std::string& value);
+// Logs a Tron complex event.
+//
+// A complex event can include data in a structure not suppored by the other
+// log event types above.
+//
+// Note that instances of this class are single use. You must call Record()
+// to write the event to the event log.
+class ComplexEventLogger {
+ private:
+ android_log_event_list logger;
+
+ public:
+ // Create a complex event with category|category|.
+ explicit ComplexEventLogger(int category);
+ // Add tagged data to the event, with the given tag and integer value.
+ void AddTaggedData(int tag, int32_t value);
+ // Add tagged data to the event, with the given tag and string value.
+ void AddTaggedData(int tag, const std::string& value);
+ // Add tagged data to the event, with the given tag and integer value.
+ void AddTaggedData(int tag, int64_t value);
+ // Add tagged data to the event, with the given tag and float value.
+ void AddTaggedData(int tag, float value);
+ // Record this event. This method can only be used once per instance
+ // of ComplexEventLogger. Do not made any subsequent calls to AddTaggedData
+ // after recording an event.
+ void Record();
+};
+
// TODO: replace these with the metric_logger.proto definitions
enum {
LOGBUILDER_CATEGORY = 757,
@@ -44,11 +73,23 @@
ACTION_BOOT = 1098,
FIELD_PLATFORM_REASON = 1099,
+
+ ACTION_HIDDEN_API_ACCESSED = 1391,
+ FIELD_HIDDEN_API_ACCESS_METHOD = 1392,
+ FIELD_HIDDEN_API_ACCESS_DENIED = 1393,
+ FIELD_HIDDEN_API_SIGNATURE = 1394,
};
enum {
TYPE_ACTION = 4,
};
+enum {
+ ACCESS_METHOD_NONE = 0,
+ ACCESS_METHOD_REFLECTION = 1,
+ ACCESS_METHOD_JNI = 2,
+ ACCESS_METHOD_LINKING = 3,
+};
+
} // namespace metricslogger
} // namespace android
diff --git a/libmetricslogger/metrics_logger.cpp b/libmetricslogger/metrics_logger.cpp
index fdc4407..912fa12 100644
--- a/libmetricslogger/metrics_logger.cpp
+++ b/libmetricslogger/metrics_logger.cpp
@@ -23,9 +23,14 @@
namespace {
+#ifdef __ANDROID__
EventTagMap* kEventTagMap = android_openEventTagMap(nullptr);
const int kSysuiMultiActionTag = android_lookupEventTagNum(
kEventTagMap, "sysui_multi_action", "(content|4)", ANDROID_LOG_UNKNOWN);
+#else
+// android_openEventTagMap does not work on host builds.
+const int kSysuiMultiActionTag = 0;
+#endif
} // namespace
@@ -53,5 +58,29 @@
<< field << value << LOG_ID_EVENTS;
}
+ComplexEventLogger::ComplexEventLogger(int category) : logger(kSysuiMultiActionTag) {
+ logger << LOGBUILDER_CATEGORY << category;
+}
+
+void ComplexEventLogger::AddTaggedData(int tag, int32_t value) {
+ logger << tag << value;
+}
+
+void ComplexEventLogger::AddTaggedData(int tag, const std::string& value) {
+ logger << tag << value;
+}
+
+void ComplexEventLogger::AddTaggedData(int tag, int64_t value) {
+ logger << tag << value;
+}
+
+void ComplexEventLogger::AddTaggedData(int tag, float value) {
+ logger << tag << value;
+}
+
+void ComplexEventLogger::Record() {
+ logger << LOG_ID_EVENTS;
+}
+
} // namespace metricslogger
} // namespace android
diff --git a/libprocinfo/Android.bp b/libprocinfo/Android.bp
index 83b0a7f..b35882c 100644
--- a/libprocinfo/Android.bp
+++ b/libprocinfo/Android.bp
@@ -83,4 +83,6 @@
suffix: "64",
},
},
+
+ test_suites: ["device-tests"],
}
diff --git a/libsparse/include/sparse/sparse.h b/libsparse/include/sparse/sparse.h
index 356f65f..1b91ead 100644
--- a/libsparse/include/sparse/sparse.h
+++ b/libsparse/include/sparse/sparse.h
@@ -18,6 +18,7 @@
#define _LIBSPARSE_SPARSE_H_
#include <stdbool.h>
+#include <stddef.h>
#include <stdint.h>
#ifdef __cplusplus
@@ -26,6 +27,11 @@
struct sparse_file;
+// The callbacks in sparse_file_callback() and sparse_file_foreach_chunk() take
+// size_t as the length type (was `int` in past). This allows clients to keep
+// their codes compatibile with both versions as needed.
+#define SPARSE_CALLBACK_USES_SIZE_T
+
/**
* sparse_file_new - create a new sparse file cookie
*
@@ -201,7 +207,7 @@
* Returns 0 on success, negative errno on error.
*/
int sparse_file_callback(struct sparse_file *s, bool sparse, bool crc,
- int (*write)(void *priv, const void *data, int len), void *priv);
+ int (*write)(void *priv, const void *data, size_t len), void *priv);
/**
* sparse_file_foreach_chunk - call a callback for data blocks in sparse file
@@ -218,7 +224,7 @@
* Returns 0 on success, negative errno on error.
*/
int sparse_file_foreach_chunk(struct sparse_file *s, bool sparse, bool crc,
- int (*write)(void *priv, const void *data, int len, unsigned int block,
+ int (*write)(void *priv, const void *data, size_t len, unsigned int block,
unsigned int nr_blocks),
void *priv);
/**
diff --git a/libsparse/output_file.c b/libsparse/output_file.c
index 51e60ef..002ad27 100644
--- a/libsparse/output_file.c
+++ b/libsparse/output_file.c
@@ -109,7 +109,7 @@
struct output_file_callback {
struct output_file out;
void *priv;
- int (*write)(void *priv, const void *buf, int len);
+ int (*write)(void *priv, const void *buf, size_t len);
};
#define to_output_file_callback(_o) \
@@ -634,7 +634,8 @@
return &outn->out;
}
-struct output_file *output_file_open_callback(int (*write)(void *, const void *, int),
+struct output_file *output_file_open_callback(
+ int (*write)(void *, const void *, size_t),
void *priv, unsigned int block_size, int64_t len,
int gz __unused, int sparse, int chunks, int crc)
{
diff --git a/libsparse/output_file.h b/libsparse/output_file.h
index b67e94e..690f610 100644
--- a/libsparse/output_file.h
+++ b/libsparse/output_file.h
@@ -27,7 +27,8 @@
struct output_file *output_file_open_fd(int fd, unsigned int block_size, int64_t len,
int gz, int sparse, int chunks, int crc);
-struct output_file *output_file_open_callback(int (*write)(void *, const void *, int),
+struct output_file *output_file_open_callback(
+ int (*write)(void *, const void *, size_t),
void *priv, unsigned int block_size, int64_t len, int gz, int sparse,
int chunks, int crc);
int write_data_chunk(struct output_file *out, unsigned int len, void *data);
diff --git a/libsparse/sparse.c b/libsparse/sparse.c
index b175860..466435f 100644
--- a/libsparse/sparse.c
+++ b/libsparse/sparse.c
@@ -179,7 +179,7 @@
}
int sparse_file_callback(struct sparse_file *s, bool sparse, bool crc,
- int (*write)(void *priv, const void *data, int len), void *priv)
+ int (*write)(void *priv, const void *data, size_t len), void *priv)
{
int ret;
int chunks;
@@ -203,11 +203,11 @@
void *priv;
unsigned int block;
unsigned int nr_blocks;
- int (*write)(void *priv, const void *data, int len, unsigned int block,
- unsigned int nr_blocks);
+ int (*write)(void *priv, const void *data, size_t len,
+ unsigned int block, unsigned int nr_blocks);
};
-static int foreach_chunk_write(void *priv, const void *data, int len)
+static int foreach_chunk_write(void *priv, const void *data, size_t len)
{
struct chunk_data *chk = priv;
@@ -215,7 +215,7 @@
}
int sparse_file_foreach_chunk(struct sparse_file *s, bool sparse, bool crc,
- int (*write)(void *priv, const void *data, int len, unsigned int block,
+ int (*write)(void *priv, const void *data, size_t len, unsigned int block,
unsigned int nr_blocks),
void *priv)
{
@@ -250,7 +250,7 @@
return ret;
}
-static int out_counter_write(void *priv, const void *data __unused, int len)
+static int out_counter_write(void *priv, const void *data __unused, size_t len)
{
int64_t *count = priv;
*count += len;
diff --git a/libunwindstack/Android.bp b/libunwindstack/Android.bp
index 6868f18..d411cee 100644
--- a/libunwindstack/Android.bp
+++ b/libunwindstack/Android.bp
@@ -186,6 +186,7 @@
"libgmock",
],
+ test_suites: ["device-tests"],
data: [
"tests/files/elf32.xz",
"tests/files/elf64.xz",
diff --git a/libunwindstack/RegsArm.cpp b/libunwindstack/RegsArm.cpp
index 27cab43..de22bde 100644
--- a/libunwindstack/RegsArm.cpp
+++ b/libunwindstack/RegsArm.cpp
@@ -197,4 +197,8 @@
return true;
}
+Regs* RegsArm::Clone() {
+ return new RegsArm(*this);
+}
+
} // namespace unwindstack
diff --git a/libunwindstack/RegsArm64.cpp b/libunwindstack/RegsArm64.cpp
index 4a2a6c4..a68f6e0 100644
--- a/libunwindstack/RegsArm64.cpp
+++ b/libunwindstack/RegsArm64.cpp
@@ -148,4 +148,8 @@
return true;
}
+Regs* RegsArm64::Clone() {
+ return new RegsArm64(*this);
+}
+
} // namespace unwindstack
diff --git a/libunwindstack/RegsMips.cpp b/libunwindstack/RegsMips.cpp
index c87e69b..2e6908c 100644
--- a/libunwindstack/RegsMips.cpp
+++ b/libunwindstack/RegsMips.cpp
@@ -173,4 +173,8 @@
return true;
}
+Regs* RegsMips::Clone() {
+ return new RegsMips(*this);
+}
+
} // namespace unwindstack
diff --git a/libunwindstack/RegsMips64.cpp b/libunwindstack/RegsMips64.cpp
index 236a922..0b835a1 100644
--- a/libunwindstack/RegsMips64.cpp
+++ b/libunwindstack/RegsMips64.cpp
@@ -160,4 +160,8 @@
return true;
}
+Regs* RegsMips64::Clone() {
+ return new RegsMips64(*this);
+}
+
} // namespace unwindstack
diff --git a/libunwindstack/RegsX86.cpp b/libunwindstack/RegsX86.cpp
index f7e0614..9bb39d1 100644
--- a/libunwindstack/RegsX86.cpp
+++ b/libunwindstack/RegsX86.cpp
@@ -179,4 +179,8 @@
return false;
}
+Regs* RegsX86::Clone() {
+ return new RegsX86(*this);
+}
+
} // namespace unwindstack
diff --git a/libunwindstack/RegsX86_64.cpp b/libunwindstack/RegsX86_64.cpp
index 7d6ad86..ebad3f4 100644
--- a/libunwindstack/RegsX86_64.cpp
+++ b/libunwindstack/RegsX86_64.cpp
@@ -168,4 +168,8 @@
return true;
}
+Regs* RegsX86_64::Clone() {
+ return new RegsX86_64(*this);
+}
+
} // namespace unwindstack
diff --git a/libunwindstack/include/unwindstack/Regs.h b/libunwindstack/include/unwindstack/Regs.h
index 4bac473..878ced3 100644
--- a/libunwindstack/include/unwindstack/Regs.h
+++ b/libunwindstack/include/unwindstack/Regs.h
@@ -73,6 +73,8 @@
uint16_t total_regs() { return total_regs_; }
+ virtual Regs* Clone() = 0;
+
static ArchEnum CurrentArch();
static Regs* RemoteGet(pid_t pid);
static Regs* CreateFromUcontext(ArchEnum arch, void* ucontext);
diff --git a/libunwindstack/include/unwindstack/RegsArm.h b/libunwindstack/include/unwindstack/RegsArm.h
index 31e6797..44f6744 100644
--- a/libunwindstack/include/unwindstack/RegsArm.h
+++ b/libunwindstack/include/unwindstack/RegsArm.h
@@ -50,6 +50,8 @@
void set_pc(uint64_t pc) override;
void set_sp(uint64_t sp) override;
+ Regs* Clone() override final;
+
static Regs* Read(void* data);
static Regs* CreateFromUcontext(void* ucontext);
diff --git a/libunwindstack/include/unwindstack/RegsArm64.h b/libunwindstack/include/unwindstack/RegsArm64.h
index 0c45eba..a72f91f 100644
--- a/libunwindstack/include/unwindstack/RegsArm64.h
+++ b/libunwindstack/include/unwindstack/RegsArm64.h
@@ -50,6 +50,8 @@
void set_pc(uint64_t pc) override;
void set_sp(uint64_t sp) override;
+ Regs* Clone() override final;
+
static Regs* Read(void* data);
static Regs* CreateFromUcontext(void* ucontext);
diff --git a/libunwindstack/include/unwindstack/RegsMips.h b/libunwindstack/include/unwindstack/RegsMips.h
index 709f9e2..c9dd202 100644
--- a/libunwindstack/include/unwindstack/RegsMips.h
+++ b/libunwindstack/include/unwindstack/RegsMips.h
@@ -50,6 +50,8 @@
void set_pc(uint64_t pc) override;
void set_sp(uint64_t sp) override;
+ Regs* Clone() override final;
+
static Regs* Read(void* data);
static Regs* CreateFromUcontext(void* ucontext);
diff --git a/libunwindstack/include/unwindstack/RegsMips64.h b/libunwindstack/include/unwindstack/RegsMips64.h
index 1de83ea..7c42812 100644
--- a/libunwindstack/include/unwindstack/RegsMips64.h
+++ b/libunwindstack/include/unwindstack/RegsMips64.h
@@ -50,6 +50,8 @@
void set_pc(uint64_t pc) override;
void set_sp(uint64_t sp) override;
+ Regs* Clone() override final;
+
static Regs* Read(void* data);
static Regs* CreateFromUcontext(void* ucontext);
diff --git a/libunwindstack/include/unwindstack/RegsX86.h b/libunwindstack/include/unwindstack/RegsX86.h
index 586c9d8..d19e449 100644
--- a/libunwindstack/include/unwindstack/RegsX86.h
+++ b/libunwindstack/include/unwindstack/RegsX86.h
@@ -53,6 +53,8 @@
void set_pc(uint64_t pc) override;
void set_sp(uint64_t sp) override;
+ Regs* Clone() override final;
+
static Regs* Read(void* data);
static Regs* CreateFromUcontext(void* ucontext);
diff --git a/libunwindstack/include/unwindstack/RegsX86_64.h b/libunwindstack/include/unwindstack/RegsX86_64.h
index 061f479..dc9a220 100644
--- a/libunwindstack/include/unwindstack/RegsX86_64.h
+++ b/libunwindstack/include/unwindstack/RegsX86_64.h
@@ -53,6 +53,8 @@
void set_pc(uint64_t pc) override;
void set_sp(uint64_t sp) override;
+ Regs* Clone() override final;
+
static Regs* Read(void* data);
static Regs* CreateFromUcontext(void* ucontext);
diff --git a/libunwindstack/tests/RegsFake.h b/libunwindstack/tests/RegsFake.h
index ede16b3..d6ca9b7 100644
--- a/libunwindstack/tests/RegsFake.h
+++ b/libunwindstack/tests/RegsFake.h
@@ -58,6 +58,8 @@
void FakeSetReturnAddress(uint64_t return_address) { fake_return_address_ = return_address; }
void FakeSetReturnAddressValid(bool valid) { fake_return_address_valid_ = valid; }
+ Regs* Clone() override { return nullptr; }
+
private:
ArchEnum fake_arch_ = ARCH_UNKNOWN;
uint64_t fake_pc_ = 0;
@@ -83,6 +85,8 @@
bool SetPcFromReturnAddress(Memory*) override { return false; }
bool StepIfSignalHandler(uint64_t, Elf*, Memory*) override { return false; }
+ Regs* Clone() override { return nullptr; }
+
private:
uint64_t fake_pc_ = 0;
uint64_t fake_sp_ = 0;
diff --git a/libunwindstack/tests/RegsTest.cpp b/libunwindstack/tests/RegsTest.cpp
index d15823e..90c3fe6 100644
--- a/libunwindstack/tests/RegsTest.cpp
+++ b/libunwindstack/tests/RegsTest.cpp
@@ -286,4 +286,39 @@
EXPECT_EQ(ARCH_MIPS64, mips64_regs.Arch());
}
+template <typename RegisterType>
+void clone_test(Regs* regs) {
+ RegisterType* register_values = reinterpret_cast<RegisterType*>(regs->RawData());
+ int num_regs = regs->total_regs();
+ for (int i = 0; i < num_regs; ++i) {
+ register_values[i] = i;
+ }
+
+ std::unique_ptr<Regs> clone(regs->Clone());
+ ASSERT_EQ(regs->total_regs(), clone->total_regs());
+ RegisterType* clone_values = reinterpret_cast<RegisterType*>(clone->RawData());
+ for (int i = 0; i < num_regs; ++i) {
+ EXPECT_EQ(register_values[i], clone_values[i]);
+ EXPECT_NE(®ister_values[i], &clone_values[i]);
+ }
+}
+
+TEST_F(RegsTest, clone) {
+ std::vector<std::unique_ptr<Regs>> regs;
+ regs.emplace_back(new RegsArm());
+ regs.emplace_back(new RegsArm64());
+ regs.emplace_back(new RegsX86());
+ regs.emplace_back(new RegsX86_64());
+ regs.emplace_back(new RegsMips());
+ regs.emplace_back(new RegsMips64());
+
+ for (auto& r : regs) {
+ if (r->Is32Bit()) {
+ clone_test<uint32_t>(r.get());
+ } else {
+ clone_test<uint64_t>(r.get());
+ }
+ }
+}
+
} // namespace unwindstack
diff --git a/libziparchive/Android.bp b/libziparchive/Android.bp
index 075fb86..6c06618 100644
--- a/libziparchive/Android.bp
+++ b/libziparchive/Android.bp
@@ -61,6 +61,7 @@
vndk: {
enabled: true,
},
+ double_loadable: true,
defaults: [
"libziparchive_defaults",
@@ -92,6 +93,10 @@
host_supported: true,
defaults: ["libziparchive_flags"],
+ data: [
+ "testdata/**/*",
+ ],
+
srcs: [
"entry_name_utils_test.cc",
"zip_archive_test.cc",
diff --git a/libziparchive/zip_archive_test.cc b/libziparchive/zip_archive_test.cc
index ad673dc..377479f 100644
--- a/libziparchive/zip_archive_test.cc
+++ b/libziparchive/zip_archive_test.cc
@@ -34,7 +34,7 @@
#include <ziparchive/zip_archive.h>
#include <ziparchive/zip_archive_stream_entry.h>
-static std::string test_data_dir;
+static std::string test_data_dir = android::base::GetExecutableDirectory() + "/testdata";
static const std::string kMissingZip = "missing.zip";
static const std::string kValidZip = "valid.zip";
@@ -729,41 +729,3 @@
ASSERT_EQ(0u, writer.GetOutput().size());
}
}
-
-int main(int argc, char** argv) {
- ::testing::InitGoogleTest(&argc, argv);
-
- static struct option options[] = {{"test_data_dir", required_argument, nullptr, 't'},
- {nullptr, 0, nullptr, 0}};
-
- while (true) {
- int option_index;
- const int c = getopt_long_only(argc, argv, "", options, &option_index);
- if (c == -1) {
- break;
- }
-
- if (c == 't') {
- test_data_dir = optarg;
- }
- }
-
- if (test_data_dir.size() == 0) {
- printf("Test data flag (--test_data_dir) required\n\n");
- return -1;
- }
-
- if (test_data_dir[0] != '/') {
- std::vector<char> cwd_buffer(1024);
- const char* cwd = getcwd(cwd_buffer.data(), cwd_buffer.size() - 1);
- if (cwd == nullptr) {
- printf("Cannot get current working directory, use an absolute path instead, was %s\n\n",
- test_data_dir.c_str());
- return -2;
- }
- test_data_dir = '/' + test_data_dir;
- test_data_dir = cwd + test_data_dir;
- }
-
- return RUN_ALL_TESTS();
-}
diff --git a/llkd/Android.bp b/llkd/Android.bp
new file mode 100644
index 0000000..a6edd26
--- /dev/null
+++ b/llkd/Android.bp
@@ -0,0 +1,42 @@
+cc_library_headers {
+ name: "llkd_headers",
+
+ export_include_dirs: ["include"],
+}
+
+cc_library_static {
+ name: "libllkd",
+
+ srcs: [
+ "libllkd.cpp",
+ ],
+
+ shared_libs: [
+ "libbase",
+ "libcutils",
+ "liblog",
+ ],
+
+ export_include_dirs: ["include"],
+
+ cflags: ["-Werror"],
+}
+
+cc_binary {
+ name: "llkd",
+
+ srcs: [
+ "llkd.cpp",
+ ],
+ shared_libs: [
+ "libbase",
+ "libcutils",
+ "liblog",
+ ],
+ static_libs: [
+ "libllkd",
+ ],
+ cflags: ["-Werror"],
+
+ init_rc: ["llkd.rc"],
+}
diff --git a/llkd/OWNERS b/llkd/OWNERS
new file mode 100644
index 0000000..b6af537
--- /dev/null
+++ b/llkd/OWNERS
@@ -0,0 +1,2 @@
+salyzyn@google.com
+surenb@google.com
diff --git a/llkd/README.md b/llkd/README.md
new file mode 100644
index 0000000..b2ba2a2
--- /dev/null
+++ b/llkd/README.md
@@ -0,0 +1,117 @@
+Android Live-LocK Daemon
+========================
+
+Introduction
+------------
+
+Android Live-LocK Daemon (llkd) is used to catch kernel deadlocks and mitigate.
+
+Code is structured to allow integration into another service as either as part
+of the main loop, or spun off as a thread should that be necessary. A default
+standalone implementation is provided by llkd component.
+
+The 'C' interface from libllkd component is thus:
+
+ #include "llkd.h"
+ bool llkInit(const char* threadname) /* return true if enabled */
+ unsigned llkCheckMillseconds(void) /* ms to sleep for next check */
+
+If a threadname is provided, a thread will be automatically spawned, otherwise
+caller must call llkCheckMilliseconds in its main loop. Function will return
+the period of time before the next expected call to this handler.
+
+Operations
+----------
+
+If a thread is in D or Z state with no forward progress for longer than
+ro.llk.timeout_ms, or ro.llk.[D|Z].timeout_ms, kill the process or parent
+process respectively. If another scan shows the same process continues to
+exist, then have a confirmed live-lock condition and need to panic. Panic
+the kernel in a manner to provide the greatest bugreporting details as to the
+condition. Add a alarm self watchdog should llkd ever get locked up that is
+double the expected time to flow through the mainloop. Sampling is every
+ro.llk_sample_ms.
+
+Default will not monitor init, or [kthreadd] and all that [kthreadd] spawns.
+This reduces the effectiveness of llkd by limiting its coverage. If there is
+value in covering [kthreadd] spawned threads, the requirement will be that
+the drivers not remain in a persistent 'D' state, or that they have mechanisms
+to recover the thread should it be killed externally (this is good driver
+coding hygiene, a common request to add such to publicly reviewed kernel.org
+maintained drivers). For instance use wait_event_interruptible() instead of
+wait_event(). The blacklists can be adjusted accordingly if these
+conditions are met to cover kernel components.
+
+An accompanying gTest set have been added, and will setup a persistent D or Z
+process, with and without forward progress, but not in a live-lock state
+because that would require a buggy kernel, or a module or kernel modification
+to stimulate. The test will check that llkd will mitigate first by killing
+the appropriate process. D state is setup by vfork() waiting for exec() in
+child process. Z state is setup by fork() and an un-waited for child process.
+Should be noted that both of these conditions should never happen on Android
+on purpose, and llkd effectively sweeps up processes that create these
+conditions. If the test can, it will reconfigure llkd to expedite the test
+duration by adjusting the ro.llk.* Android properties. Tests run the D state
+with some scheduling progress to ensure that ABA checking prevents false
+triggers. If 100% reliable ABA on platform, then ro.llk.killtest can be
+set to false; however this will result in some of the unit tests to panic
+kernel instead of deal with more graceful kill operation.
+
+Android Properties
+------------------
+
+Android Properties llkd respond to (<prop>_ms parms are in milliseconds):
+
+#### ro.config.low_ram
+default false, if true do not sysrq t (dump all threads).
+
+#### ro.llk.enable
+default false, allow live-lock daemon to be enabled.
+
+#### llk.enable
+default ro.llk.enable, and evaluated for eng.
+
+#### ro.khungtask.enable
+default false, allow [khungtask] daemon to be enabled.
+
+#### khungtask.enable
+default ro.khungtask.enable and evaluated for eng.
+
+#### ro.llk.mlockall
+default false, enable call to mlockall().
+
+#### ro.khungtask.timeout
+default value 12 minutes, [khungtask] maximum timelimit.
+
+#### ro.llk.timeout_ms
+default 10 minutes, D or Z maximum timelimit, double this value and it sets
+the alarm watchdog for llkd.
+
+#### ro.llk.D.timeout_ms
+default ro.llk.timeout_ms, D maximum timelimit.
+
+#### ro.llk.Z.timeout_ms
+default ro.llk.timeout_ms, Z maximum timelimit.
+
+#### ro.llk.check_ms
+default 2 minutes samples of threads for D or Z.
+
+#### ro.llk.blacklist.process
+default 0,1,2 (kernel, init and [kthreadd]) plus process names
+init,[kthreadd],[khungtaskd],lmkd,lmkd.llkd,llkd,watchdogd,
+[watchdogd],[watchdogd/0],...,[watchdogd/<get_nprocs-1>].
+
+#### ro.llk.blacklist.parent
+default 0,2 (kernel and [kthreadd]).
+
+#### ro.llk.blacklist.uid
+default <empty>, comma separated list of uid numbers or names.
+
+Architectural Concerns
+----------------------
+
+- Create kernel module and associated gTest to actually test panic.
+- Create gTest to test out blacklist (ro.llk.blacklist.<properties> generally
+ not be inputs). Could require more test-only interfaces to libllkd.
+- Speed up gTest using something else than ro.llk.<properties>, which should
+ not be inputs.
diff --git a/llkd/include/llkd.h b/llkd/include/llkd.h
new file mode 100644
index 0000000..e3ae4bb
--- /dev/null
+++ b/llkd/include/llkd.h
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+#ifndef _LLKD_H_
+#define _LLKD_H_
+
+#ifndef LOG_TAG
+#define LOG_TAG "livelock"
+#endif
+
+#include <stdbool.h>
+#include <sys/cdefs.h>
+
+__BEGIN_DECLS
+
+bool llkInit(const char* threadname); /* threadname NULL, not spawned */
+unsigned llkCheckMilliseconds(void);
+
+/* clang-format off */
+#define LLK_ENABLE_WRITEABLE_PROPERTY "llk.enable"
+#define LLK_ENABLE_PROPERTY "ro." LLK_ENABLE_WRITEABLE_PROPERTY
+#define LLK_ENABLE_DEFAULT false /* "eng" and userdebug true */
+#define KHT_ENABLE_WRITEABLE_PROPERTY "khungtask.enable"
+#define KHT_ENABLE_PROPERTY "ro." KHT_ENABLE_WRITEABLE_PROPERTY
+#define LLK_MLOCKALL_PROPERTY "ro.llk.mlockall"
+#define LLK_MLOCKALL_DEFAULT true
+#define LLK_KILLTEST_PROPERTY "ro.llk.killtest"
+#define LLK_KILLTEST_DEFAULT true
+#define LLK_TIMEOUT_MS_PROPERTY "ro.llk.timeout_ms"
+#define KHT_TIMEOUT_PROPERTY "ro.khungtask.timeout"
+#define LLK_D_TIMEOUT_MS_PROPERTY "ro.llk.D.timeout_ms"
+#define LLK_Z_TIMEOUT_MS_PROPERTY "ro.llk.Z.timeout_ms"
+#define LLK_CHECK_MS_PROPERTY "ro.llk.check_ms"
+/* LLK_CHECK_MS_DEFAULT = actual timeout_ms / LLK_CHECKS_PER_TIMEOUT_DEFAULT */
+#define LLK_CHECKS_PER_TIMEOUT_DEFAULT 5
+#define LLK_BLACKLIST_PROCESS_PROPERTY "ro.llk.blacklist.process"
+#define LLK_BLACKLIST_PROCESS_DEFAULT \
+ "0,1,2,init,[kthreadd],[khungtaskd],lmkd,lmkd.llkd,llkd,watchdogd,[watchdogd],[watchdogd/0]"
+#define LLK_BLACKLIST_PARENT_PROPERTY "ro.llk.blacklist.parent"
+#define LLK_BLACKLIST_PARENT_DEFAULT "0,2,[kthreadd]"
+#define LLK_BLACKLIST_UID_PROPERTY "ro.llk.blacklist.uid"
+#define LLK_BLACKLIST_UID_DEFAULT ""
+/* clang-format on */
+
+__END_DECLS
+
+#ifdef __cplusplus
+extern "C++" { /* In case this included wrapped with __BEGIN_DECLS */
+
+#include <chrono>
+
+__BEGIN_DECLS
+/* C++ code allowed to not specify threadname argument for this C linkage */
+bool llkInit(const char* threadname = nullptr);
+__END_DECLS
+std::chrono::milliseconds llkCheck(bool checkRunning = false);
+
+/* clang-format off */
+#define LLK_TIMEOUT_MS_DEFAULT std::chrono::duration_cast<milliseconds>(std::chrono::minutes(10))
+#define LLK_TIMEOUT_MS_MINIMUM std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::seconds(10))
+#define LLK_CHECK_MS_MINIMUM std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::seconds(1))
+/* clang-format on */
+
+} /* extern "C++" */
+#endif /* __cplusplus */
+
+#endif /* _LLKD_H_ */
diff --git a/llkd/libllkd.cpp b/llkd/libllkd.cpp
new file mode 100644
index 0000000..f357cc2
--- /dev/null
+++ b/llkd/libllkd.cpp
@@ -0,0 +1,1168 @@
+/*
+ * Copyright (C) 2018 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 "llkd.h"
+
+#include <ctype.h>
+#include <dirent.h> // opendir() and readdir()
+#include <errno.h>
+#include <fcntl.h>
+#include <pthread.h>
+#include <pwd.h> // getpwuid()
+#include <signal.h>
+#include <stdint.h>
+#include <sys/cdefs.h> // ___STRING, __predict_true() and _predict_false()
+#include <sys/mman.h> // mlockall()
+#include <sys/prctl.h>
+#include <sys/stat.h> // lstat()
+#include <sys/syscall.h> // __NR_getdents64
+#include <sys/sysinfo.h> // get_nprocs_conf()
+#include <sys/types.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <chrono>
+#include <ios>
+#include <sstream>
+#include <string>
+#include <unordered_map>
+#include <unordered_set>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/parseint.h>
+#include <android-base/properties.h>
+#include <android-base/strings.h>
+#include <cutils/android_get_control_file.h>
+#include <log/log_main.h>
+
+#define ARRAY_SIZE(x) (sizeof(x) / sizeof(*(x)))
+
+#define TASK_COMM_LEN 16 // internal kernel, not uapi, from .../linux/include/linux/sched.h
+
+using namespace std::chrono_literals;
+using namespace std::chrono;
+
+namespace {
+
+constexpr pid_t kernelPid = 0;
+constexpr pid_t initPid = 1;
+constexpr pid_t kthreaddPid = 2;
+
+constexpr char procdir[] = "/proc/";
+
+// Configuration
+milliseconds llkUpdate; // last check ms signature
+milliseconds llkCycle; // ms to next thread check
+bool llkEnable = LLK_ENABLE_DEFAULT; // llk daemon enabled
+bool llkRunning = false; // thread is running
+bool llkMlockall = LLK_MLOCKALL_DEFAULT; // run mlocked
+bool llkTestWithKill = LLK_KILLTEST_DEFAULT; // issue test kills
+milliseconds llkTimeoutMs = LLK_TIMEOUT_MS_DEFAULT; // default timeout
+enum { llkStateD, llkStateZ, llkNumStates }; // state indexes
+milliseconds llkStateTimeoutMs[llkNumStates]; // timeout override for each detection state
+milliseconds llkCheckMs; // checking interval to inspect any
+ // persistent live-locked states
+bool llkLowRam; // ro.config.low_ram
+bool khtEnable = LLK_ENABLE_DEFAULT; // [khungtaskd] panic
+// [khungtaskd] should have a timeout beyond the granularity of llkTimeoutMs.
+// Provides a wide angle of margin b/c khtTimeout is also its granularity.
+seconds khtTimeout = duration_cast<seconds>(llkTimeoutMs * (1 + LLK_CHECKS_PER_TIMEOUT_DEFAULT) /
+ LLK_CHECKS_PER_TIMEOUT_DEFAULT);
+
+// Blacklist variables, initialized with comma separated lists of high false
+// positive and/or dangerous references, e.g. without self restart, for pid,
+// ppid, name and uid:
+
+// list of pids, or tids or names to skip. kernel pid (0), init pid (1),
+// [kthreadd] pid (2), ourselves, "init", "[kthreadd]", "lmkd", "llkd" or
+// combinations of watchdogd in kernel and user space.
+std::unordered_set<std::string> llkBlacklistProcess;
+// list of parent pids, comm or cmdline names to skip. default:
+// kernel pid (0), [kthreadd] (2), or ourselves, enforced and implied
+std::unordered_set<std::string> llkBlacklistParent;
+// list of uids, and uid names, to skip, default nothing
+std::unordered_set<std::string> llkBlacklistUid;
+
+class dir {
+ public:
+ enum level { proc, task, numLevels };
+
+ private:
+ int fd;
+ size_t available_bytes;
+ dirent* next;
+ // each directory level picked to be just north of 4K in size
+ static constexpr size_t buffEntries = 15;
+ static dirent buff[numLevels][buffEntries];
+
+ bool fill(enum level index) {
+ if (index >= numLevels) return false;
+ if (available_bytes != 0) return true;
+ if (__predict_false(fd < 0)) return false;
+ // getdents64 has no libc wrapper
+ auto rc = TEMP_FAILURE_RETRY(syscall(__NR_getdents64, fd, buff[index], sizeof(buff[0]), 0));
+ if (rc <= 0) return false;
+ available_bytes = rc;
+ next = buff[index];
+ return true;
+ }
+
+ public:
+ dir() : fd(-1), available_bytes(0), next(nullptr) {}
+
+ explicit dir(const char* directory)
+ : fd(__predict_true(directory != nullptr)
+ ? ::open(directory, O_CLOEXEC | O_DIRECTORY | O_RDONLY)
+ : -1),
+ available_bytes(0),
+ next(nullptr) {}
+
+ explicit dir(const std::string&& directory)
+ : fd(::open(directory.c_str(), O_CLOEXEC | O_DIRECTORY | O_RDONLY)),
+ available_bytes(0),
+ next(nullptr) {}
+
+ explicit dir(const std::string& directory)
+ : fd(::open(directory.c_str(), O_CLOEXEC | O_DIRECTORY | O_RDONLY)),
+ available_bytes(0),
+ next(nullptr) {}
+
+ // Don't need any copy or move constructors.
+ explicit dir(const dir& c) = delete;
+ explicit dir(dir& c) = delete;
+ explicit dir(dir&& c) = delete;
+
+ ~dir() {
+ if (fd >= 0) {
+ ::close(fd);
+ }
+ }
+
+ operator bool() const { return fd >= 0; }
+
+ void reset(void) {
+ if (fd >= 0) {
+ ::close(fd);
+ fd = -1;
+ available_bytes = 0;
+ next = nullptr;
+ }
+ }
+
+ dir& reset(const char* directory) {
+ reset();
+ // available_bytes will _always_ be zero here as its value is
+ // intimately tied to fd < 0 or not.
+ fd = ::open(directory, O_CLOEXEC | O_DIRECTORY | O_RDONLY);
+ return *this;
+ }
+
+ void rewind(void) {
+ if (fd >= 0) {
+ ::lseek(fd, off_t(0), SEEK_SET);
+ available_bytes = 0;
+ next = nullptr;
+ }
+ }
+
+ dirent* read(enum level index = proc, dirent* def = nullptr) {
+ if (!fill(index)) return def;
+ auto ret = next;
+ available_bytes -= next->d_reclen;
+ next = reinterpret_cast<dirent*>(reinterpret_cast<char*>(next) + next->d_reclen);
+ return ret;
+ }
+} llkTopDirectory;
+
+dirent dir::buff[dir::numLevels][dir::buffEntries];
+
+// helper functions
+
+bool llkIsMissingExeLink(pid_t tid) {
+ char c;
+ // CAP_SYS_PTRACE is required to prevent ret == -1, but ENOENT is signal
+ auto ret = ::readlink((procdir + std::to_string(tid) + "/exe").c_str(), &c, sizeof(c));
+ return (ret == -1) && (errno == ENOENT);
+}
+
+// Common routine where caller accepts empty content as error/passthrough.
+// Reduces the churn of reporting read errors in the callers.
+std::string ReadFile(std::string&& path) {
+ std::string content;
+ if (!android::base::ReadFileToString(path, &content)) {
+ PLOG(DEBUG) << "Read " << path << " failed";
+ content = "";
+ }
+ return content;
+}
+
+std::string llkProcGetName(pid_t tid, const char* node = "/cmdline") {
+ std::string content = ReadFile(procdir + std::to_string(tid) + node);
+ static constexpr char needles[] = " \t\r\n"; // including trailing nul
+ auto pos = content.find_first_of(needles, 0, sizeof(needles));
+ if (pos != std::string::npos) {
+ content.erase(pos);
+ }
+ return content;
+}
+
+uid_t llkProcGetUid(pid_t tid) {
+ // Get the process' uid. The following read from /status is admittedly
+ // racy, prone to corruption due to shape-changes. The consequences are
+ // not catastrophic as we sample a few times before taking action.
+ //
+ // If /loginuid worked on reliably, or on Android (all tasks report -1)...
+ // Android lmkd causes /cgroup to contain memory:/<dom>/uid_<uid>/pid_<pid>
+ // which is tighter, but also not reliable.
+ std::string content = ReadFile(procdir + std::to_string(tid) + "/status");
+ static constexpr char Uid[] = "\nUid:";
+ auto pos = content.find(Uid);
+ if (pos == std::string::npos) {
+ return -1;
+ }
+ pos += ::strlen(Uid);
+ while ((pos < content.size()) && ::isblank(content[pos])) {
+ ++pos;
+ }
+ content.erase(0, pos);
+ for (pos = 0; (pos < content.size()) && ::isdigit(content[pos]); ++pos) {
+ ;
+ }
+ // Content of form 'Uid: 0 0 0 0', newline is error
+ if ((pos >= content.size()) || !::isblank(content[pos])) {
+ return -1;
+ }
+ content.erase(pos);
+ uid_t ret;
+ if (!android::base::ParseInt(content, &ret, uid_t(0))) {
+ return -1;
+ }
+ return ret;
+}
+
+struct proc {
+ pid_t tid; // monitored thread id (in Z or D state).
+ nanoseconds schedUpdate; // /proc/<tid>/sched "se.avg.lastUpdateTime",
+ uint64_t nrSwitches; // /proc/<tid>/sched "nr_switches" for
+ // refined ABA problem detection, determine
+ // forward scheduling progress.
+ milliseconds update; // llkUpdate millisecond signature of last.
+ milliseconds count; // duration in state.
+ pid_t pid; // /proc/<pid> before iterating through
+ // /proc/<pid>/task/<tid> for threads.
+ pid_t ppid; // /proc/<tid>/stat field 4 parent pid.
+ uid_t uid; // /proc/<tid>/status Uid: field.
+ unsigned time; // sum of /proc/<tid>/stat field 14 utime &
+ // 15 stime for coarse ABA problem detection.
+ std::string cmdline; // cached /cmdline content
+ char state; // /proc/<tid>/stat field 3: Z or D
+ // (others we do not monitor: S, R, T or ?)
+ char comm[TASK_COMM_LEN + 3]; // space for adding '[' and ']'
+ bool exeMissingValid; // exeMissing has been cached
+ bool cmdlineValid; // cmdline has been cached
+ bool updated; // cleared before monitoring pass.
+ bool killed; // sent a kill to this thread, next panic...
+
+ void setComm(const char* _comm) { strncpy(comm + 1, _comm, sizeof(comm) - 2); }
+
+ proc(pid_t tid, pid_t pid, pid_t ppid, const char* _comm, int time, char state)
+ : tid(tid),
+ schedUpdate(0),
+ nrSwitches(0),
+ update(llkUpdate),
+ count(0),
+ pid(pid),
+ ppid(ppid),
+ uid(-1),
+ time(time),
+ state(state),
+ exeMissingValid(false),
+ cmdlineValid(false),
+ updated(true),
+ killed(!llkTestWithKill) {
+ memset(comm, '\0', sizeof(comm));
+ setComm(_comm);
+ }
+
+ const char* getComm(void) {
+ if (comm[1] == '\0') { // comm Valid?
+ strncpy(comm + 1, llkProcGetName(tid, "/comm").c_str(), sizeof(comm) - 2);
+ }
+ if (!exeMissingValid) {
+ if (llkIsMissingExeLink(tid)) {
+ comm[0] = '[';
+ }
+ exeMissingValid = true;
+ }
+ size_t len = strlen(comm + 1);
+ if (__predict_true(len < (sizeof(comm) - 1))) {
+ if (comm[0] == '[') {
+ if ((comm[len] != ']') && __predict_true(len < (sizeof(comm) - 2))) {
+ comm[++len] = ']';
+ comm[++len] = '\0';
+ }
+ } else {
+ if (comm[len] == ']') {
+ comm[len] = '\0';
+ }
+ }
+ }
+ return &comm[comm[0] != '['];
+ }
+
+ const char* getCmdline(void) {
+ if (!cmdlineValid) {
+ cmdline = llkProcGetName(tid);
+ cmdlineValid = true;
+ }
+ return cmdline.c_str();
+ }
+
+ uid_t getUid(void) {
+ if (uid <= 0) { // Churn on root user, because most likely to setuid()
+ uid = llkProcGetUid(tid);
+ }
+ return uid;
+ }
+
+ void reset(void) { // reset cache, if we detected pid rollover
+ uid = -1;
+ state = '?';
+ cmdline = "";
+ comm[0] = '\0';
+ exeMissingValid = false;
+ cmdlineValid = false;
+ }
+};
+
+std::unordered_map<pid_t, proc> tids;
+
+// Check range and setup defaults, in order of propagation:
+// llkTimeoutMs
+// llkCheckMs
+// ...
+// KISS to keep it all self-contained, and called multiple times as parameters
+// are interpreted so that defaults, llkCheckMs and llkCycle make sense.
+void llkValidate() {
+ if (llkTimeoutMs == 0ms) {
+ llkTimeoutMs = LLK_TIMEOUT_MS_DEFAULT;
+ }
+ llkTimeoutMs = std::max(llkTimeoutMs, LLK_TIMEOUT_MS_MINIMUM);
+ if (llkCheckMs == 0ms) {
+ llkCheckMs = llkTimeoutMs / LLK_CHECKS_PER_TIMEOUT_DEFAULT;
+ }
+ llkCheckMs = std::min(llkCheckMs, llkTimeoutMs);
+
+ for (size_t state = 0; state < ARRAY_SIZE(llkStateTimeoutMs); ++state) {
+ if (llkStateTimeoutMs[state] == 0ms) {
+ llkStateTimeoutMs[state] = llkTimeoutMs;
+ }
+ llkStateTimeoutMs[state] =
+ std::min(std::max(llkStateTimeoutMs[state], LLK_TIMEOUT_MS_MINIMUM), llkTimeoutMs);
+ llkCheckMs = std::min(llkCheckMs, llkStateTimeoutMs[state]);
+ }
+
+ llkCheckMs = std::max(llkCheckMs, LLK_CHECK_MS_MINIMUM);
+ if (llkCycle == 0ms) {
+ llkCycle = llkCheckMs;
+ }
+ llkCycle = std::min(llkCycle, llkCheckMs);
+}
+
+milliseconds llkGetTimespecDiffMs(timespec* from, timespec* to) {
+ return duration_cast<milliseconds>(seconds(to->tv_sec - from->tv_sec)) +
+ duration_cast<milliseconds>(nanoseconds(to->tv_nsec - from->tv_nsec));
+}
+
+std::string llkProcGetName(pid_t tid, const char* comm, const char* cmdline) {
+ if ((cmdline != nullptr) && (*cmdline != '\0')) {
+ return cmdline;
+ }
+ if ((comm != nullptr) && (*comm != '\0')) {
+ return comm;
+ }
+
+ // UNLIKELY! Here because killed before we kill it?
+ // Assume change is afoot, do not call llkTidAlloc
+
+ // cmdline ?
+ std::string content = llkProcGetName(tid);
+ if (content.size() != 0) {
+ return content;
+ }
+ // Comm instead?
+ content = llkProcGetName(tid, "/comm");
+ if (llkIsMissingExeLink(tid) && (content.size() != 0)) {
+ return '[' + content + ']';
+ }
+ return content;
+}
+
+int llkKillOneProcess(pid_t pid, char state, pid_t tid, const char* tcomm = nullptr,
+ const char* tcmdline = nullptr, const char* pcomm = nullptr,
+ const char* pcmdline = nullptr) {
+ std::string forTid;
+ if (tid != pid) {
+ forTid = " for '" + llkProcGetName(tid, tcomm, tcmdline) + "' (" + std::to_string(tid) + ")";
+ }
+ LOG(INFO) << "Killing '" << llkProcGetName(pid, pcomm, pcmdline) << "' (" << pid
+ << ") to check forward scheduling progress in " << state << " state" << forTid;
+ // CAP_KILL required
+ errno = 0;
+ auto r = ::kill(pid, SIGKILL);
+ if (r) {
+ PLOG(ERROR) << "kill(" << pid << ")=" << r << ' ';
+ }
+
+ return r;
+}
+
+// Kill one process
+int llkKillOneProcess(pid_t pid, proc* tprocp) {
+ return llkKillOneProcess(pid, tprocp->state, tprocp->tid, tprocp->getComm(),
+ tprocp->getCmdline());
+}
+
+// Kill one process specified by kprocp
+int llkKillOneProcess(proc* kprocp, proc* tprocp) {
+ if (kprocp == nullptr) {
+ return -2;
+ }
+
+ return llkKillOneProcess(kprocp->tid, tprocp->state, tprocp->tid, tprocp->getComm(),
+ tprocp->getCmdline(), kprocp->getComm(), kprocp->getCmdline());
+}
+
+// Acquire file descriptor from environment, or open and cache it.
+// NB: cache is unnecessary in our current context, pedantically
+// required to prevent leakage of file descriptors in the future.
+int llkFileToWriteFd(const std::string& file) {
+ static std::unordered_map<std::string, int> cache;
+ auto search = cache.find(file);
+ if (search != cache.end()) return search->second;
+ auto fd = android_get_control_file(file.c_str());
+ if (fd >= 0) return fd;
+ fd = TEMP_FAILURE_RETRY(::open(file.c_str(), O_WRONLY | O_CLOEXEC));
+ if (fd >= 0) cache.emplace(std::make_pair(file, fd));
+ return fd;
+}
+
+// Wrap android::base::WriteStringToFile to use android_get_control_file.
+bool llkWriteStringToFile(const std::string& string, const std::string& file) {
+ auto fd = llkFileToWriteFd(file);
+ if (fd < 0) return false;
+ return android::base::WriteStringToFd(string, fd);
+}
+
+bool llkWriteStringToFileConfirm(const std::string& string, const std::string& file) {
+ auto fd = llkFileToWriteFd(file);
+ auto ret = (fd < 0) ? false : android::base::WriteStringToFd(string, fd);
+ std::string content;
+ if (!android::base::ReadFileToString(file, &content)) return ret;
+ return android::base::Trim(content) == string;
+}
+
+void llkPanicKernel(bool dump, pid_t tid, const char* state) __noreturn;
+void llkPanicKernel(bool dump, pid_t tid, const char* state) {
+ auto sysrqTriggerFd = llkFileToWriteFd("/proc/sysrq-trigger");
+ if (sysrqTriggerFd < 0) {
+ // DYB
+ llkKillOneProcess(initPid, 'R', tid);
+ // The answer to life, the universe and everything
+ ::exit(42);
+ // NOTREACHED
+ }
+ ::sync();
+ if (dump) {
+ // Show all locks that are held
+ android::base::WriteStringToFd("d", sysrqTriggerFd);
+ // This can trigger hardware watchdog, that is somewhat _ok_.
+ // But useless if pstore configured for <256KB, low ram devices ...
+ if (!llkLowRam) {
+ android::base::WriteStringToFd("t", sysrqTriggerFd);
+ }
+ ::usleep(200000); // let everything settle
+ }
+ llkWriteStringToFile(std::string("SysRq : Trigger a crash : 'livelock,") + state + "'\n",
+ "/dev/kmsg");
+ android::base::WriteStringToFd("c", sysrqTriggerFd);
+ // NOTREACHED
+ // DYB
+ llkKillOneProcess(initPid, 'R', tid);
+ // I sat at my desk, stared into the garden and thought '42 will do'.
+ // I typed it out. End of story
+ ::exit(42);
+ // NOTREACHED
+}
+
+void llkAlarmHandler(int) {
+ llkPanicKernel(false, ::getpid(), "alarm");
+}
+
+milliseconds GetUintProperty(const std::string& key, milliseconds def) {
+ return milliseconds(android::base::GetUintProperty(key, static_cast<uint64_t>(def.count()),
+ static_cast<uint64_t>(def.max().count())));
+}
+
+seconds GetUintProperty(const std::string& key, seconds def) {
+ return seconds(android::base::GetUintProperty(key, static_cast<uint64_t>(def.count()),
+ static_cast<uint64_t>(def.max().count())));
+}
+
+proc* llkTidLookup(pid_t tid) {
+ auto search = tids.find(tid);
+ if (search == tids.end()) {
+ return nullptr;
+ }
+ return &search->second;
+}
+
+void llkTidRemove(pid_t tid) {
+ tids.erase(tid);
+}
+
+proc* llkTidAlloc(pid_t tid, pid_t pid, pid_t ppid, const char* comm, int time, char state) {
+ auto it = tids.emplace(std::make_pair(tid, proc(tid, pid, ppid, comm, time, state)));
+ return &it.first->second;
+}
+
+std::string llkFormat(milliseconds ms) {
+ auto sec = duration_cast<seconds>(ms);
+ std::ostringstream s;
+ s << sec.count() << '.';
+ auto f = s.fill('0');
+ auto w = s.width(3);
+ s << std::right << (ms - sec).count();
+ s.width(w);
+ s.fill(f);
+ s << 's';
+ return s.str();
+}
+
+std::string llkFormat(seconds s) {
+ return std::to_string(s.count()) + 's';
+}
+
+std::string llkFormat(bool flag) {
+ return flag ? "true" : "false";
+}
+
+std::string llkFormat(const std::unordered_set<std::string>& blacklist) {
+ std::string ret;
+ for (auto entry : blacklist) {
+ if (ret.size()) {
+ ret += ",";
+ }
+ ret += entry;
+ }
+ return ret;
+}
+
+// We only officially support comma separators, but wetware being what they
+// are will take some liberty and I do not believe they should be punished.
+std::unordered_set<std::string> llkSplit(const std::string& s,
+ const std::string& delimiters = ", \t:") {
+ std::unordered_set<std::string> result;
+
+ size_t base = 0;
+ size_t found;
+ while (true) {
+ found = s.find_first_of(delimiters, base);
+ result.emplace(s.substr(base, found - base));
+ if (found == s.npos) break;
+ base = found + 1;
+ }
+ return result;
+}
+
+bool llkSkipName(const std::string& name,
+ const std::unordered_set<std::string>& blacklist = llkBlacklistProcess) {
+ if ((name.size() == 0) || (blacklist.size() == 0)) {
+ return false;
+ }
+
+ return blacklist.find(name) != blacklist.end();
+}
+
+bool llkSkipPid(pid_t pid) {
+ return llkSkipName(std::to_string(pid), llkBlacklistProcess);
+}
+
+bool llkSkipPpid(pid_t ppid) {
+ return llkSkipName(std::to_string(ppid), llkBlacklistParent);
+}
+
+bool llkSkipUid(uid_t uid) {
+ // Match by number?
+ if (llkSkipName(std::to_string(uid), llkBlacklistUid)) {
+ return true;
+ }
+
+ // Match by name?
+ auto pwd = ::getpwuid(uid);
+ return (pwd != nullptr) && __predict_true(pwd->pw_name != nullptr) &&
+ __predict_true(pwd->pw_name[0] != '\0') && llkSkipName(pwd->pw_name, llkBlacklistUid);
+}
+
+bool getValidTidDir(dirent* dp, std::string* piddir) {
+ if (!::isdigit(dp->d_name[0])) {
+ return false;
+ }
+
+ // Corner case can not happen in reality b/c of above ::isdigit check
+ if (__predict_false(dp->d_type != DT_DIR)) {
+ if (__predict_false(dp->d_type == DT_UNKNOWN)) { // can't b/c procfs
+ struct stat st;
+ *piddir = procdir;
+ *piddir += dp->d_name;
+ return (lstat(piddir->c_str(), &st) == 0) && (st.st_mode & S_IFDIR);
+ }
+ return false;
+ }
+
+ *piddir = procdir;
+ *piddir += dp->d_name;
+ return true;
+}
+
+bool llkIsMonitorState(char state) {
+ return (state == 'Z') || (state == 'D');
+}
+
+// returns -1 if not found
+long long getSchedValue(const std::string& schedString, const char* key) {
+ auto pos = schedString.find(key);
+ if (pos == std::string::npos) {
+ return -1;
+ }
+ pos = schedString.find(':', pos);
+ if (__predict_false(pos == std::string::npos)) {
+ return -1;
+ }
+ while ((++pos < schedString.size()) && ::isblank(schedString[pos])) {
+ ;
+ }
+ long long ret;
+ if (!android::base::ParseInt(schedString.substr(pos), &ret, static_cast<long long>(0))) {
+ return -1;
+ }
+ return ret;
+}
+
+// Primary ABA mitigation watching last time schedule activity happened
+void llkCheckSchedUpdate(proc* procp, const std::string& piddir) {
+ // Audit finds /proc/<tid>/sched is just over 1K, and
+ // is rarely larger than 2K, even less on Android.
+ // For example, the "se.avg.lastUpdateTime" field we are
+ // interested in typically within the primary set in
+ // the first 1K.
+ //
+ // Proc entries can not be read >1K atomically via libbase,
+ // but if there are problems we assume at least a few
+ // samples of reads occur before we take any real action.
+ std::string schedString = ReadFile(piddir + "/sched");
+ if (schedString.size() == 0) {
+ // /schedstat is not as standardized, but in 3.1+
+ // Android devices, the third field is nr_switches
+ // from /sched:
+ schedString = ReadFile(piddir + "/schedstat");
+ if (schedString.size() == 0) {
+ return;
+ }
+ auto val = static_cast<unsigned long long>(-1);
+ if (((::sscanf(schedString.c_str(), "%*d %*d %llu", &val)) == 1) &&
+ (val != static_cast<unsigned long long>(-1)) && (val != 0) &&
+ (val != procp->nrSwitches)) {
+ procp->nrSwitches = val;
+ procp->count = 0ms;
+ procp->killed = !llkTestWithKill;
+ }
+ return;
+ }
+
+ auto val = getSchedValue(schedString, "\nse.avg.lastUpdateTime");
+ if (val == -1) {
+ val = getSchedValue(schedString, "\nse.svg.last_update_time");
+ }
+ if (val != -1) {
+ auto schedUpdate = nanoseconds(val);
+ if (schedUpdate != procp->schedUpdate) {
+ procp->schedUpdate = schedUpdate;
+ procp->count = 0ms;
+ procp->killed = !llkTestWithKill;
+ }
+ }
+
+ val = getSchedValue(schedString, "\nnr_switches");
+ if (val != -1) {
+ if (static_cast<uint64_t>(val) != procp->nrSwitches) {
+ procp->nrSwitches = val;
+ procp->count = 0ms;
+ procp->killed = !llkTestWithKill;
+ }
+ }
+}
+
+void llkLogConfig(void) {
+ LOG(INFO) << "ro.config.low_ram=" << llkFormat(llkLowRam) << "\n"
+ << LLK_ENABLE_PROPERTY "=" << llkFormat(llkEnable) << "\n"
+ << KHT_ENABLE_PROPERTY "=" << llkFormat(khtEnable) << "\n"
+ << LLK_MLOCKALL_PROPERTY "=" << llkFormat(llkMlockall) << "\n"
+ << LLK_KILLTEST_PROPERTY "=" << llkFormat(llkTestWithKill) << "\n"
+ << KHT_TIMEOUT_PROPERTY "=" << llkFormat(khtTimeout) << "\n"
+ << LLK_TIMEOUT_MS_PROPERTY "=" << llkFormat(llkTimeoutMs) << "\n"
+ << LLK_D_TIMEOUT_MS_PROPERTY "=" << llkFormat(llkStateTimeoutMs[llkStateD]) << "\n"
+ << LLK_Z_TIMEOUT_MS_PROPERTY "=" << llkFormat(llkStateTimeoutMs[llkStateZ]) << "\n"
+ << LLK_CHECK_MS_PROPERTY "=" << llkFormat(llkCheckMs) << "\n"
+ << LLK_BLACKLIST_PROCESS_PROPERTY "=" << llkFormat(llkBlacklistProcess) << "\n"
+ << LLK_BLACKLIST_PARENT_PROPERTY "=" << llkFormat(llkBlacklistParent) << "\n"
+ << LLK_BLACKLIST_UID_PROPERTY "=" << llkFormat(llkBlacklistUid);
+}
+
+void* llkThread(void* obj) {
+ LOG(INFO) << "started";
+
+ std::string name = std::to_string(::gettid());
+ if (!llkSkipName(name)) {
+ llkBlacklistProcess.emplace(name);
+ }
+ name = static_cast<const char*>(obj);
+ prctl(PR_SET_NAME, name.c_str());
+ if (__predict_false(!llkSkipName(name))) {
+ llkBlacklistProcess.insert(name);
+ }
+ // No longer modifying llkBlacklistProcess.
+ llkRunning = true;
+ llkLogConfig();
+ while (llkRunning) {
+ ::usleep(duration_cast<microseconds>(llkCheck(true)).count());
+ }
+ // NOTREACHED
+ LOG(INFO) << "exiting";
+ return nullptr;
+}
+
+} // namespace
+
+milliseconds llkCheck(bool checkRunning) {
+ if (!llkEnable || (checkRunning != llkRunning)) {
+ return milliseconds::max();
+ }
+
+ // Reset internal watchdog, which is a healthy engineering margin of
+ // double the maximum wait or cycle time for the mainloop that calls us.
+ //
+ // This alarm is effectively the live lock detection of llkd, as
+ // we understandably can not monitor ourselves otherwise.
+ ::alarm(duration_cast<seconds>(llkTimeoutMs * 2).count());
+
+ // kernel jiffy precision fastest acquisition
+ static timespec last;
+ timespec now;
+ ::clock_gettime(CLOCK_MONOTONIC_COARSE, &now);
+ auto ms = llkGetTimespecDiffMs(&last, &now);
+ if (ms < llkCycle) {
+ return llkCycle - ms;
+ }
+ last = now;
+
+ LOG(VERBOSE) << "opendir(\"" << procdir << "\")";
+ if (__predict_false(!llkTopDirectory)) {
+ // gid containing AID_READPROC required
+ llkTopDirectory.reset(procdir);
+ if (__predict_false(!llkTopDirectory)) {
+ // Most likely reason we could be here is a resource limit.
+ // Keep our processing down to a minimum, but not so low that
+ // we do not recover in a timely manner should the issue be
+ // transitory.
+ LOG(DEBUG) << "opendir(\"" << procdir << "\") failed";
+ return llkTimeoutMs;
+ }
+ }
+
+ for (auto& it : tids) {
+ it.second.updated = false;
+ }
+
+ auto prevUpdate = llkUpdate;
+ llkUpdate += ms;
+ ms -= llkCycle;
+ auto myPid = ::getpid();
+ auto myTid = ::gettid();
+ for (auto dp = llkTopDirectory.read(); dp != nullptr; dp = llkTopDirectory.read()) {
+ std::string piddir;
+
+ if (!getValidTidDir(dp, &piddir)) {
+ continue;
+ }
+
+ // Get the process tasks
+ std::string taskdir = piddir + "/task/";
+ int pid = -1;
+ LOG(VERBOSE) << "+opendir(\"" << taskdir << "\")";
+ dir taskDirectory(taskdir);
+ if (__predict_false(!taskDirectory)) {
+ LOG(DEBUG) << "+opendir(\"" << taskdir << "\") failed";
+ }
+ for (auto tp = taskDirectory.read(dir::task, dp); tp != nullptr;
+ tp = taskDirectory.read(dir::task)) {
+ if (!getValidTidDir(tp, &piddir)) {
+ continue;
+ }
+
+ // Get the process stat
+ std::string stat = ReadFile(piddir + "/stat");
+ if (stat.size() == 0) {
+ continue;
+ }
+ unsigned tid = -1;
+ char pdir[TASK_COMM_LEN + 1];
+ char state = '?';
+ unsigned ppid = -1;
+ unsigned utime = -1;
+ unsigned stime = -1;
+ int dummy;
+ pdir[0] = '\0';
+ // tid should not change value
+ auto match = ::sscanf(
+ stat.c_str(),
+ "%u (%" ___STRING(
+ TASK_COMM_LEN) "[^)]) %c %u %*d %*d %*d %*d %*d %*d %*d %*d %*d %u %u %d",
+ &tid, pdir, &state, &ppid, &utime, &stime, &dummy);
+ if (pid == -1) {
+ pid = tid;
+ }
+ LOG(VERBOSE) << "match " << match << ' ' << tid << " (" << pdir << ") " << state << ' '
+ << ppid << " ... " << utime << ' ' << stime << ' ' << dummy;
+ if (match != 7) {
+ continue;
+ }
+
+ auto procp = llkTidLookup(tid);
+ if (procp == nullptr) {
+ procp = llkTidAlloc(tid, pid, ppid, pdir, utime + stime, state);
+ } else {
+ // comm can change ...
+ procp->setComm(pdir);
+ procp->updated = true;
+ // pid/ppid/tid wrap?
+ if (((procp->update != prevUpdate) && (procp->update != llkUpdate)) ||
+ (procp->ppid != ppid) || (procp->pid != pid)) {
+ procp->reset();
+ } else if (procp->time != (utime + stime)) { // secondary ABA.
+ // watching utime+stime granularity jiffy
+ procp->state = '?';
+ }
+ procp->update = llkUpdate;
+ procp->pid = pid;
+ procp->ppid = ppid;
+ procp->time = utime + stime;
+ if (procp->state != state) {
+ procp->count = 0ms;
+ procp->killed = !llkTestWithKill;
+ procp->state = state;
+ } else {
+ procp->count += llkCycle;
+ }
+ }
+
+ // Filter checks in intuitive order of CPU cost to evaluate
+ // If tid unique continue, if ppid or pid unique break
+
+ if (pid == myPid) {
+ break;
+ }
+ if (!llkIsMonitorState(state)) {
+ continue;
+ }
+ if ((tid == myTid) || llkSkipPid(tid)) {
+ continue;
+ }
+ if (llkSkipPpid(ppid)) {
+ break;
+ }
+
+ if (llkSkipName(procp->getComm())) {
+ continue;
+ }
+ if (llkSkipName(procp->getCmdline())) {
+ break;
+ }
+
+ auto pprocp = llkTidLookup(ppid);
+ if (pprocp == nullptr) {
+ pprocp = llkTidAlloc(ppid, ppid, 0, "", 0, '?');
+ }
+ if ((pprocp != nullptr) && (llkSkipName(pprocp->getComm(), llkBlacklistParent) ||
+ llkSkipName(pprocp->getCmdline(), llkBlacklistParent))) {
+ break;
+ }
+
+ if ((llkBlacklistUid.size() != 0) && llkSkipUid(procp->getUid())) {
+ continue;
+ }
+
+ // ABA mitigation watching last time schedule activity happened
+ llkCheckSchedUpdate(procp, piddir);
+
+ // Can only fall through to here if registered D or Z state !!!
+ if (procp->count < llkStateTimeoutMs[(state == 'Z') ? llkStateZ : llkStateD]) {
+ LOG(VERBOSE) << state << ' ' << llkFormat(procp->count) << ' ' << ppid << "->"
+ << pid << "->" << tid << ' ' << procp->getComm();
+ continue;
+ }
+
+ // We have to kill it to determine difference between live lock
+ // and persistent state blocked on a resource. Is there something
+ // wrong with a process that has no forward scheduling progress in
+ // Z or D? Yes, generally means improper accounting in the
+ // process, but not always ...
+ //
+ // Whomever we hit with a test kill must accept the Android
+ // Aphorism that everything can be burned to the ground and
+ // must survive.
+ if (procp->killed == false) {
+ procp->killed = true;
+ // confirm: re-read uid before committing to a panic.
+ procp->uid = -1;
+ switch (state) {
+ case 'Z': // kill ppid to free up a Zombie
+ // Killing init will kernel panic without diagnostics
+ // so skip right to controlled kernel panic with
+ // diagnostics.
+ if (ppid == initPid) {
+ break;
+ }
+ LOG(WARNING) << "Z " << llkFormat(procp->count) << ' ' << ppid << "->"
+ << pid << "->" << tid << ' ' << procp->getComm() << " [kill]";
+ if ((llkKillOneProcess(pprocp, procp) >= 0) ||
+ (llkKillOneProcess(ppid, procp) >= 0)) {
+ continue;
+ }
+ break;
+
+ case 'D': // kill tid to free up an uninterruptible D
+ // If ABA is doing its job, we would not need or
+ // want the following. Test kill is a Hail Mary
+ // to make absolutely sure there is no forward
+ // scheduling progress. The cost when ABA is
+ // not working is we kill a process that likes to
+ // stay in 'D' state, instead of panicing the
+ // kernel (worse).
+ LOG(WARNING) << "D " << llkFormat(procp->count) << ' ' << pid << "->" << tid
+ << ' ' << procp->getComm() << " [kill]";
+ if ((llkKillOneProcess(llkTidLookup(pid), procp) >= 0) ||
+ (llkKillOneProcess(pid, 'D', tid) >= 0) ||
+ (llkKillOneProcess(procp, procp) >= 0) ||
+ (llkKillOneProcess(tid, 'D', tid) >= 0)) {
+ continue;
+ }
+ break;
+ }
+ }
+ // We are here because we have confirmed kernel live-lock
+ LOG(ERROR) << state << ' ' << llkFormat(procp->count) << ' ' << ppid << "->" << pid
+ << "->" << tid << ' ' << procp->getComm() << " [panic]";
+ llkPanicKernel(true, tid, (state == 'Z') ? "zombie" : "driver");
+ }
+ LOG(VERBOSE) << "+closedir()";
+ }
+ llkTopDirectory.rewind();
+ LOG(VERBOSE) << "closedir()";
+
+ // garbage collection of old process references
+ for (auto p = tids.begin(); p != tids.end();) {
+ if (!p->second.updated) {
+ IF_ALOG(LOG_VERBOSE, LOG_TAG) {
+ std::string ppidCmdline = llkProcGetName(p->second.ppid, nullptr, nullptr);
+ if (ppidCmdline.size()) {
+ ppidCmdline = "(" + ppidCmdline + ")";
+ }
+ std::string pidCmdline;
+ if (p->second.pid != p->second.tid) {
+ pidCmdline = llkProcGetName(p->second.pid, nullptr, p->second.getCmdline());
+ if (pidCmdline.size()) {
+ pidCmdline = "(" + pidCmdline + ")";
+ }
+ }
+ std::string tidCmdline =
+ llkProcGetName(p->second.tid, p->second.getComm(), p->second.getCmdline());
+ if (tidCmdline.size()) {
+ tidCmdline = "(" + tidCmdline + ")";
+ }
+ LOG(VERBOSE) << "thread " << p->second.ppid << ppidCmdline << "->" << p->second.pid
+ << pidCmdline << "->" << p->second.tid << tidCmdline << " removed";
+ }
+ p = tids.erase(p);
+ } else {
+ ++p;
+ }
+ }
+ if (__predict_false(tids.empty())) {
+ llkTopDirectory.reset();
+ }
+
+ llkCycle = llkCheckMs;
+
+ timespec end;
+ ::clock_gettime(CLOCK_MONOTONIC_COARSE, &end);
+ auto milli = llkGetTimespecDiffMs(&now, &end);
+ LOG((milli > 10s) ? ERROR : (milli > 1s) ? WARNING : VERBOSE) << "sample " << llkFormat(milli);
+
+ // cap to minimum sleep for 1 second since last cycle
+ if (llkCycle < (ms + 1s)) {
+ return 1s;
+ }
+ return llkCycle - ms;
+}
+
+unsigned llkCheckMilliseconds() {
+ return duration_cast<milliseconds>(llkCheck()).count();
+}
+
+bool llkInit(const char* threadname) {
+ llkLowRam = android::base::GetBoolProperty("ro.config.low_ram", false);
+ if (!LLK_ENABLE_DEFAULT && android::base::GetBoolProperty("ro.debuggable", false)) {
+ llkEnable = android::base::GetProperty(LLK_ENABLE_PROPERTY, "eng") == "eng";
+ khtEnable = android::base::GetProperty(KHT_ENABLE_PROPERTY, "eng") == "eng";
+ }
+ llkEnable = android::base::GetBoolProperty(LLK_ENABLE_PROPERTY, llkEnable);
+ if (llkEnable && !llkTopDirectory.reset(procdir)) {
+ // Most likely reason we could be here is llkd was started
+ // incorrectly without the readproc permissions. Keep our
+ // processing down to a minimum.
+ llkEnable = false;
+ }
+ khtEnable = android::base::GetBoolProperty(KHT_ENABLE_PROPERTY, khtEnable);
+ llkMlockall = android::base::GetBoolProperty(LLK_MLOCKALL_PROPERTY, llkMlockall);
+ llkTestWithKill = android::base::GetBoolProperty(LLK_KILLTEST_PROPERTY, llkTestWithKill);
+ // if LLK_TIMOUT_MS_PROPERTY was not set, we will use a set
+ // KHT_TIMEOUT_PROPERTY as co-operative guidance for the default value.
+ khtTimeout = GetUintProperty(KHT_TIMEOUT_PROPERTY, khtTimeout);
+ if (khtTimeout == 0s) {
+ khtTimeout = duration_cast<seconds>(llkTimeoutMs * (1 + LLK_CHECKS_PER_TIMEOUT_DEFAULT) /
+ LLK_CHECKS_PER_TIMEOUT_DEFAULT);
+ }
+ llkTimeoutMs =
+ khtTimeout * LLK_CHECKS_PER_TIMEOUT_DEFAULT / (1 + LLK_CHECKS_PER_TIMEOUT_DEFAULT);
+ llkTimeoutMs = GetUintProperty(LLK_TIMEOUT_MS_PROPERTY, llkTimeoutMs);
+ llkValidate(); // validate llkTimeoutMs, llkCheckMs and llkCycle
+ llkStateTimeoutMs[llkStateD] = GetUintProperty(LLK_D_TIMEOUT_MS_PROPERTY, llkTimeoutMs);
+ llkStateTimeoutMs[llkStateZ] = GetUintProperty(LLK_Z_TIMEOUT_MS_PROPERTY, llkTimeoutMs);
+ llkCheckMs = GetUintProperty(LLK_CHECK_MS_PROPERTY, llkCheckMs);
+ llkValidate(); // validate all (effectively minus llkTimeoutMs)
+ std::string defaultBlacklistProcess(
+ std::to_string(kernelPid) + "," + std::to_string(initPid) + "," +
+ std::to_string(kthreaddPid) + "," + std::to_string(::getpid()) + "," +
+ std::to_string(::gettid()) + "," LLK_BLACKLIST_PROCESS_DEFAULT);
+ if (threadname) {
+ defaultBlacklistProcess += std::string(",") + threadname;
+ }
+ for (int cpu = 1; cpu < get_nprocs_conf(); ++cpu) {
+ defaultBlacklistProcess += ",[watchdog/" + std::to_string(cpu) + "]";
+ }
+ defaultBlacklistProcess =
+ android::base::GetProperty(LLK_BLACKLIST_PROCESS_PROPERTY, defaultBlacklistProcess);
+ llkBlacklistProcess = llkSplit(defaultBlacklistProcess);
+ if (!llkSkipName("[khungtaskd]")) { // ALWAYS ignore as special
+ llkBlacklistProcess.emplace("[khungtaskd]");
+ }
+ llkBlacklistParent = llkSplit(android::base::GetProperty(
+ LLK_BLACKLIST_PARENT_PROPERTY, std::to_string(kernelPid) + "," + std::to_string(kthreaddPid) +
+ "," LLK_BLACKLIST_PARENT_DEFAULT));
+ llkBlacklistUid =
+ llkSplit(android::base::GetProperty(LLK_BLACKLIST_UID_PROPERTY, LLK_BLACKLIST_UID_DEFAULT));
+
+ // internal watchdog
+ ::signal(SIGALRM, llkAlarmHandler);
+
+ // kernel hung task configuration? Otherwise leave it as-is
+ if (khtEnable) {
+ // EUID must be AID_ROOT to write to /proc/sys/kernel/ nodes, there
+ // are no capability overrides. For security reasons we do not want
+ // to run as AID_ROOT. We may not be able to write them successfully,
+ // we will try, but the least we can do is read the values back to
+ // confirm expectations and report whether configured or not.
+ auto configured = llkWriteStringToFileConfirm(std::to_string(khtTimeout.count()),
+ "/proc/sys/kernel/hung_task_timeout_secs");
+ if (configured) {
+ llkWriteStringToFile("65535", "/proc/sys/kernel/hung_task_warnings");
+ llkWriteStringToFile("65535", "/proc/sys/kernel/hung_task_check_count");
+ configured = llkWriteStringToFileConfirm("1", "/proc/sys/kernel/hung_task_panic");
+ }
+ if (configured) {
+ LOG(INFO) << "[khungtaskd] configured";
+ } else {
+ LOG(WARNING) << "[khungtaskd] not configurable";
+ }
+ }
+
+ bool logConfig = true;
+ if (llkEnable) {
+ if (llkMlockall &&
+ // MCL_ONFAULT pins pages as they fault instead of loading
+ // everything immediately all at once. (Which would be bad,
+ // because as of this writing, we have a lot of mapped pages we
+ // never use.) Old kernels will see MCL_ONFAULT and fail with
+ // EINVAL; we ignore this failure.
+ //
+ // N.B. read the man page for mlockall. MCL_CURRENT | MCL_ONFAULT
+ // pins ⊆ MCL_CURRENT, converging to just MCL_CURRENT as we fault
+ // in pages.
+
+ // CAP_IPC_LOCK required
+ mlockall(MCL_CURRENT | MCL_FUTURE | MCL_ONFAULT) && (errno != EINVAL)) {
+ PLOG(WARNING) << "mlockall failed ";
+ }
+
+ if (threadname) {
+ pthread_attr_t attr;
+
+ if (!pthread_attr_init(&attr)) {
+ sched_param param;
+
+ memset(¶m, 0, sizeof(param));
+ pthread_attr_setschedparam(&attr, ¶m);
+ pthread_attr_setschedpolicy(&attr, SCHED_BATCH);
+ if (!pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED)) {
+ pthread_t thread;
+ if (!pthread_create(&thread, &attr, llkThread, const_cast<char*>(threadname))) {
+ // wait a second for thread to start
+ for (auto retry = 50; retry && !llkRunning; --retry) {
+ ::usleep(20000);
+ }
+ logConfig = !llkRunning; // printed in llkd context?
+ } else {
+ LOG(ERROR) << "failed to spawn llkd thread";
+ }
+ } else {
+ LOG(ERROR) << "failed to detach llkd thread";
+ }
+ pthread_attr_destroy(&attr);
+ } else {
+ LOG(ERROR) << "failed to allocate attibutes for llkd thread";
+ }
+ }
+ } else {
+ LOG(DEBUG) << "[khungtaskd] left unconfigured";
+ }
+ if (logConfig) {
+ llkLogConfig();
+ }
+
+ return llkEnable;
+}
diff --git a/llkd/llkd.cpp b/llkd/llkd.cpp
new file mode 100644
index 0000000..f10253d
--- /dev/null
+++ b/llkd/llkd.cpp
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2018 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 "llkd.h"
+
+#include <sched.h>
+#include <unistd.h>
+
+#include <chrono>
+
+#include <android-base/logging.h>
+
+using namespace std::chrono;
+
+int main(int, char**) {
+ LOG(INFO) << "started";
+
+ bool enabled = llkInit();
+
+ // Would like this policy to be automatic as part of libllkd,
+ // but that would be presumptuous and bad side-effect.
+ struct sched_param param;
+ memset(¶m, 0, sizeof(param));
+ sched_setscheduler(0, SCHED_BATCH, ¶m);
+
+ while (true) {
+ if (enabled) {
+ ::usleep(duration_cast<microseconds>(llkCheck()).count());
+ } else {
+ ::pause();
+ }
+ }
+ // NOTREACHED
+
+ LOG(INFO) << "exiting";
+ return 0;
+}
diff --git a/llkd/llkd.rc b/llkd/llkd.rc
new file mode 100644
index 0000000..e538cdb
--- /dev/null
+++ b/llkd/llkd.rc
@@ -0,0 +1,49 @@
+# eng default for ro.llk.enable and ro.khungtask.enable
+on property:ro.debuggable=*
+ setprop llk.enable ${ro.llk.enable:-0}
+ setprop khungtask.enable ${ro.khungtask.enable:-0}
+
+on property:ro.debuggable=1
+ setprop llk.enable ${ro.llk.enable:-1}
+ setprop khungtask.enable ${ro.khungtask.enable:-1}
+
+on property:ro.llk.enable=eng
+ setprop llk.enable ${ro.debuggable:-0}
+
+on property:ro.khungtask.enable=eng
+ setprop khungtask.enable ${ro.debuggable:-0}
+
+on property:llk.enable=1
+ setprop llk.enable true
+
+on property:llk.enable=0
+ setprop llk.enable false
+
+on property:khungtask.enable=1
+ setprop khungtask.enable true
+
+on property:khungtask.enable=0
+ setprop khungtask.enable false
+
+# Configure [khungtaskd]
+on property:khungtask.enable=true
+ write /proc/sys/kernel/hung_task_timeout_secs ${ro.khungtask.timeout:-720}
+ write /proc/sys/kernel/hung_task_warnings 65535
+ write /proc/sys/kernel/hung_task_check_count 65535
+ write /proc/sys/kernel/hung_task_panic 1
+
+on property:khungtask.enable=false
+ write /proc/sys/kernel/hung_task_panic 0
+
+on property:llk.enable=true
+ start llkd
+
+service llkd /system/bin/llkd
+ class late_start
+ disabled
+ user llkd
+ group llkd readproc
+ capabilities KILL IPC_LOCK
+ file /dev/kmsg w
+ file /proc/sysrq-trigger w
+ writepid /dev/cpuset/system-background/tasks
diff --git a/llkd/tests/Android.bp b/llkd/tests/Android.bp
new file mode 100644
index 0000000..6dd5938
--- /dev/null
+++ b/llkd/tests/Android.bp
@@ -0,0 +1,41 @@
+// Copyright (C) 2018 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.
+
+cc_test {
+ name: "llkd_unit_test",
+
+ shared_libs: [
+ "libbase",
+ "liblog",
+ ],
+ header_libs: [
+ "llkd_headers",
+ ],
+
+ target: {
+ android: {
+ srcs: [
+ "llkd_test.cpp",
+ ],
+ },
+ },
+
+ cflags: [
+ "-Wall",
+ "-Wextra",
+ "-Werror",
+ ],
+
+ compile_multilib: "first",
+}
diff --git a/llkd/tests/llkd_test.cpp b/llkd/tests/llkd_test.cpp
new file mode 100644
index 0000000..3a15ff1
--- /dev/null
+++ b/llkd/tests/llkd_test.cpp
@@ -0,0 +1,287 @@
+/*
+ * Copyright (C) 2018 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 <signal.h>
+#include <stdint.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include <chrono>
+#include <iostream>
+#include <string>
+
+#include <android-base/properties.h>
+#include <gtest/gtest.h>
+#include <log/log_time.h> // for MS_PER_SEC and US_PER_SEC
+
+#include "llkd.h"
+
+using namespace std::chrono;
+using namespace std::chrono_literals;
+
+namespace {
+
+milliseconds GetUintProperty(const std::string& key, milliseconds def) {
+ return milliseconds(android::base::GetUintProperty(key, static_cast<uint64_t>(def.count()),
+ static_cast<uint64_t>(def.max().count())));
+}
+
+seconds GetUintProperty(const std::string& key, seconds def) {
+ return seconds(android::base::GetUintProperty(key, static_cast<uint64_t>(def.count()),
+ static_cast<uint64_t>(def.max().count())));
+}
+
+// GTEST_LOG_(WARNING) output is fugly, this has much less noise
+// ToDo: look into fixing googletest to produce output that matches style of
+// all the other status messages, and can switch off __line__ and
+// __function__ noise
+#define GTEST_LOG_WARNING std::cerr << "[ WARNING ] "
+#define GTEST_LOG_INFO std::cerr << "[ INFO ] "
+
+// Properties is _not_ a high performance ABI!
+void rest() {
+ usleep(200000);
+}
+
+void execute(const char* command) {
+ if (getuid() || system(command)) {
+ system((std::string("su root ") + command).c_str());
+ }
+}
+
+seconds llkdSleepPeriod(char state) {
+ auto default_eng = android::base::GetProperty(LLK_ENABLE_PROPERTY, "eng") == "eng";
+ auto default_enable = LLK_ENABLE_DEFAULT;
+ if (!LLK_ENABLE_DEFAULT && default_eng &&
+ android::base::GetBoolProperty("ro.debuggable", false)) {
+ default_enable = true;
+ }
+ default_enable = android::base::GetBoolProperty(LLK_ENABLE_PROPERTY, default_enable);
+ if (default_eng) {
+ GTEST_LOG_INFO << LLK_ENABLE_PROPERTY " defaults to \"eng\" thus "
+ << (default_enable ? "true" : "false") << "\n";
+ }
+ // Hail Mary hope is unconfigured.
+ if ((GetUintProperty(LLK_TIMEOUT_MS_PROPERTY, LLK_TIMEOUT_MS_DEFAULT) !=
+ duration_cast<milliseconds>(120s)) ||
+ (GetUintProperty(LLK_CHECK_MS_PROPERTY,
+ LLK_TIMEOUT_MS_DEFAULT / LLK_CHECKS_PER_TIMEOUT_DEFAULT) !=
+ duration_cast<milliseconds>(10s))) {
+ execute("stop llkd");
+ rest();
+ std::string setprop("setprop ");
+ execute((setprop + LLK_ENABLE_WRITEABLE_PROPERTY + " false").c_str());
+ rest();
+ execute((setprop + LLK_TIMEOUT_MS_PROPERTY + " 120000").c_str());
+ rest();
+ execute((setprop + KHT_TIMEOUT_PROPERTY + " 130").c_str());
+ rest();
+ execute((setprop + LLK_CHECK_MS_PROPERTY + " 10000").c_str());
+ rest();
+ execute((setprop + LLK_ENABLE_PROPERTY + " true").c_str());
+ rest();
+ execute((setprop + LLK_ENABLE_WRITEABLE_PROPERTY + " true").c_str());
+ rest();
+ }
+ default_enable = LLK_ENABLE_DEFAULT;
+ if (!LLK_ENABLE_DEFAULT && (android::base::GetProperty(LLK_ENABLE_PROPERTY, "eng") == "eng") &&
+ android::base::GetBoolProperty("ro.debuggable", false)) {
+ default_enable = true;
+ }
+ default_enable = android::base::GetBoolProperty(LLK_ENABLE_PROPERTY, default_enable);
+ if (default_enable) {
+ execute("start llkd");
+ rest();
+ GTEST_LOG_INFO << "llkd enabled\n";
+ } else {
+ GTEST_LOG_WARNING << "llkd disabled\n";
+ }
+
+ /* KISS follows llk_init() */
+ milliseconds llkTimeoutMs = LLK_TIMEOUT_MS_DEFAULT;
+ seconds khtTimeout = duration_cast<seconds>(
+ llkTimeoutMs * (1 + LLK_CHECKS_PER_TIMEOUT_DEFAULT) / LLK_CHECKS_PER_TIMEOUT_DEFAULT);
+ khtTimeout = GetUintProperty(KHT_TIMEOUT_PROPERTY, khtTimeout);
+ llkTimeoutMs =
+ khtTimeout * LLK_CHECKS_PER_TIMEOUT_DEFAULT / (1 + LLK_CHECKS_PER_TIMEOUT_DEFAULT);
+ llkTimeoutMs = GetUintProperty(LLK_TIMEOUT_MS_PROPERTY, llkTimeoutMs);
+ if (llkTimeoutMs < LLK_TIMEOUT_MS_MINIMUM) {
+ llkTimeoutMs = LLK_TIMEOUT_MS_MINIMUM;
+ }
+ milliseconds llkCheckMs = llkTimeoutMs / LLK_CHECKS_PER_TIMEOUT_DEFAULT;
+ auto timeout = GetUintProperty(
+ (state == 'Z') ? LLK_Z_TIMEOUT_MS_PROPERTY : LLK_D_TIMEOUT_MS_PROPERTY, llkTimeoutMs);
+ if (timeout < LLK_TIMEOUT_MS_MINIMUM) {
+ timeout = LLK_TIMEOUT_MS_MINIMUM;
+ }
+
+ if (llkCheckMs > timeout) {
+ llkCheckMs = timeout;
+ }
+ llkCheckMs = GetUintProperty(LLK_CHECK_MS_PROPERTY, llkCheckMs);
+ timeout += llkCheckMs;
+ auto sec = duration_cast<seconds>(timeout);
+ if (sec == 0s) {
+ ++sec;
+ } else if (sec > 59s) {
+ GTEST_LOG_WARNING << "llkd is configured for about " << duration_cast<minutes>(sec).count()
+ << " minutes to react\n";
+ }
+
+ // 33% margin for the test to naturally timeout waiting for llkd to respond
+ return (sec * 4 + 2s) / 3;
+}
+
+inline void waitForPid(pid_t child_pid) {
+ int wstatus;
+ ASSERT_LE(0, waitpid(child_pid, &wstatus, 0));
+ EXPECT_FALSE(WIFEXITED(wstatus)) << "[ INFO ] exit=" << WEXITSTATUS(wstatus);
+ ASSERT_TRUE(WIFSIGNALED(wstatus));
+ ASSERT_EQ(WTERMSIG(wstatus), SIGKILL);
+}
+
+bool checkKill(const char* reason) {
+ if (android::base::GetBoolProperty(LLK_KILLTEST_PROPERTY, LLK_KILLTEST_DEFAULT)) {
+ return false;
+ }
+ auto bootreason = android::base::GetProperty("sys.boot.reason", "nothing");
+ if (bootreason == reason) {
+ GTEST_LOG_INFO << "Expected test result confirmed " << reason << "\n";
+ return true;
+ }
+ GTEST_LOG_WARNING << "Expected test result is " << reason << "\n";
+
+ // apct adjustment if needed (set LLK_KILLTEST_PROPERTY to "off" to allow test)
+ //
+ // if (android::base::GetProperty(LLK_KILLTEST_PROPERTY, "") == "false") {
+ // GTEST_LOG_WARNING << "Bypassing test\n";
+ // return true;
+ // }
+
+ return false;
+}
+
+} // namespace
+
+// The tests that use this helper are to simulate processes stuck in 'D'
+// state that are experiencing forward scheduled progress. As such the
+// expectation is that llkd will _not_ perform any mitigations. The sleepfor
+// argument helps us set the amount of forward scheduler progress.
+static void llkd_driver_ABA(const microseconds sleepfor) {
+ const auto period = llkdSleepPeriod('D');
+ if (period <= sleepfor) {
+ GTEST_LOG_WARNING << "llkd configuration too short for "
+ << duration_cast<milliseconds>(sleepfor).count() << "ms work cycle\n";
+ return;
+ }
+
+ auto child_pid = fork();
+ ASSERT_LE(0, child_pid);
+ int wstatus;
+ if (!child_pid) {
+ auto ratio = period / sleepfor;
+ ASSERT_LT(0, ratio);
+ // vfork() parent is uninterruptable D state waiting for child to exec()
+ while (--ratio > 0) {
+ auto driver_pid = vfork();
+ ASSERT_LE(0, driver_pid);
+ if (driver_pid) { // parent
+ waitpid(driver_pid, &wstatus, 0);
+ if (!WIFEXITED(wstatus)) {
+ exit(42);
+ }
+ if (WEXITSTATUS(wstatus) != 42) {
+ exit(42);
+ }
+ } else {
+ usleep(sleepfor.count());
+ exit(42);
+ }
+ }
+ exit(0);
+ }
+ ASSERT_LE(0, waitpid(child_pid, &wstatus, 0));
+ EXPECT_TRUE(WIFEXITED(wstatus));
+ if (WIFEXITED(wstatus)) {
+ EXPECT_EQ(0, WEXITSTATUS(wstatus));
+ }
+ ASSERT_FALSE(WIFSIGNALED(wstatus)) << "[ INFO ] signo=" << WTERMSIG(wstatus);
+}
+
+TEST(llkd, driver_ABA_fast) {
+ llkd_driver_ABA(5ms);
+}
+
+TEST(llkd, driver_ABA_slow) {
+ llkd_driver_ABA(1s);
+}
+
+TEST(llkd, driver_ABA_glacial) {
+ llkd_driver_ABA(1min);
+}
+
+// Following tests must be last in this file to capture possible errant
+// kernel_panic mitigation failure.
+
+// The following tests simulate processes stick in 'Z' or 'D' state with
+// no forward scheduling progress, but interruptible. As such the expectation
+// is that llkd will perform kill mitigation and not progress to kernel_panic.
+
+TEST(llkd, zombie) {
+ if (checkKill("kernel_panic,sysrq,livelock,zombie")) {
+ return;
+ }
+
+ const auto period = llkdSleepPeriod('Z');
+
+ /* Create a Persistent Zombie Process */
+ pid_t child_pid = fork();
+ ASSERT_LE(0, child_pid);
+ if (!child_pid) {
+ auto zombie_pid = fork();
+ ASSERT_LE(0, zombie_pid);
+ if (!zombie_pid) {
+ sleep(1);
+ exit(0);
+ }
+ sleep(period.count());
+ exit(42);
+ }
+
+ waitForPid(child_pid);
+}
+
+TEST(llkd, driver) {
+ if (checkKill("kernel_panic,sysrq,livelock,driver")) {
+ return;
+ }
+
+ const auto period = llkdSleepPeriod('D');
+
+ /* Create a Persistent Device Process */
+ auto child_pid = fork();
+ ASSERT_LE(0, child_pid);
+ if (!child_pid) {
+ // vfork() parent is uninterruptable D state waiting for child to exec()
+ auto driver_pid = vfork();
+ ASSERT_LE(0, driver_pid);
+ sleep(period.count());
+ exit(driver_pid ? 42 : 0);
+ }
+
+ waitForPid(child_pid);
+}
diff --git a/logcat/event.logtags b/logcat/event.logtags
index 0983676..750761f 100644
--- a/logcat/event.logtags
+++ b/logcat/event.logtags
@@ -67,8 +67,9 @@
# ZygoteInit class preloading ends:
3030 boot_progress_preload_end (time|2|3)
-# Dalvik VM
+# Dalvik VM / ART
20003 dvm_lock_sample (process|3),(main|1|5),(thread|3),(time|1|3),(file|3),(line|1|5),(ownerfile|3),(ownerline|1|5),(sample_percent|1|6)
+20004 art_hidden_api_access (access_method|1),(flags|1),(class|3),(member|3),(type_signature|3)
75000 sqlite_mem_alarm_current (current|1|2)
75001 sqlite_mem_alarm_max (max|1|2)
@@ -119,6 +120,9 @@
70200 aggregation (aggregation time|2|3)
70201 aggregation_test (field1|1|2),(field2|1|2),(field3|1|2),(field4|1|2),(field5|1|2)
+# gms refuses to register this log tag, b/30156345
+70220 gms_unknown
+
# libc failure logging
80100 bionic_event_memcpy_buffer_overflow (uid|1)
80105 bionic_event_strcat_buffer_overflow (uid|1)
diff --git a/logd/LogAudit.cpp b/logd/LogAudit.cpp
index b76160d..27cd9a8 100755
--- a/logd/LogAudit.cpp
+++ b/logd/LogAudit.cpp
@@ -112,7 +112,7 @@
std::map<std::string, std::string> LogAudit::populateDenialMap() {
std::ifstream bug_file("/system/etc/selinux/selinux_denial_metadata");
std::string line;
- // allocate a map for the static map pointer in logParse to keep track of,
+ // allocate a map for the static map pointer in auditParse to keep track of,
// this function only runs once
std::map<std::string, std::string> denial_to_bug;
if (bug_file.good()) {
@@ -140,7 +140,8 @@
return "";
}
-void LogAudit::logParse(const std::string& string, std::string* bug_num) {
+void LogAudit::auditParse(const std::string& string, uid_t uid,
+ std::string* bug_num) {
if (!__android_log_is_debuggable()) {
bug_num->assign("");
return;
@@ -162,6 +163,11 @@
} else {
bug_num->assign("");
}
+
+ if (uid >= AID_APP_START && uid <= AID_APP_END) {
+ bug_num->append(" app=");
+ bug_num->append(android::uidToName(uid));
+ }
}
int LogAudit::logPrint(const char* fmt, ...) {
@@ -190,8 +196,27 @@
while ((cp = strstr(str, " "))) {
memmove(cp, cp + 1, strlen(cp + 1) + 1);
}
+ pid_t pid = getpid();
+ pid_t tid = gettid();
+ uid_t uid = AID_LOGD;
+ static const char pid_str[] = " pid=";
+ char* pidptr = strstr(str, pid_str);
+ if (pidptr && isdigit(pidptr[sizeof(pid_str) - 1])) {
+ cp = pidptr + sizeof(pid_str) - 1;
+ pid = 0;
+ while (isdigit(*cp)) {
+ pid = (pid * 10) + (*cp - '0');
+ ++cp;
+ }
+ tid = pid;
+ logbuf->wrlock();
+ uid = logbuf->pidToUid(pid);
+ logbuf->unlock();
+ memmove(pidptr, cp, strlen(cp) + 1);
+ }
+
bool info = strstr(str, " permissive=1") || strstr(str, " policy loaded ");
- static std::string bug_metadata;
+ static std::string denial_metadata;
if ((fdDmesg >= 0) && initialized) {
struct iovec iov[4];
static const char log_info[] = { KMSG_PRIORITY(LOG_INFO) };
@@ -228,8 +253,8 @@
last_info ? sizeof(log_info) : sizeof(log_warning);
iov[1].iov_base = last_str;
iov[1].iov_len = strlen(last_str);
- iov[2].iov_base = const_cast<char*>(bug_metadata.c_str());
- iov[2].iov_len = bug_metadata.length();
+ iov[2].iov_base = const_cast<char*>(denial_metadata.c_str());
+ iov[2].iov_len = denial_metadata.length();
if (count > 1) {
iov[3].iov_base = const_cast<char*>(resume);
iov[3].iov_len = strlen(resume);
@@ -249,14 +274,14 @@
last_info = info;
}
if (count == 0) {
- logParse(str, &bug_metadata);
+ auditParse(str, uid, &denial_metadata);
iov[0].iov_base = info ? const_cast<char*>(log_info)
: const_cast<char*>(log_warning);
iov[0].iov_len = info ? sizeof(log_info) : sizeof(log_warning);
iov[1].iov_base = str;
iov[1].iov_len = strlen(str);
- iov[2].iov_base = const_cast<char*>(bug_metadata.c_str());
- iov[2].iov_len = bug_metadata.length();
+ iov[2].iov_base = const_cast<char*>(denial_metadata.c_str());
+ iov[2].iov_len = denial_metadata.length();
iov[3].iov_base = const_cast<char*>(newline);
iov[3].iov_len = strlen(newline);
@@ -269,9 +294,6 @@
return 0;
}
- pid_t pid = getpid();
- pid_t tid = gettid();
- uid_t uid = AID_LOGD;
log_time now;
static const char audit_str[] = " audit(";
@@ -296,29 +318,13 @@
now = log_time(CLOCK_REALTIME);
}
- static const char pid_str[] = " pid=";
- char* pidptr = strstr(str, pid_str);
- if (pidptr && isdigit(pidptr[sizeof(pid_str) - 1])) {
- cp = pidptr + sizeof(pid_str) - 1;
- pid = 0;
- while (isdigit(*cp)) {
- pid = (pid * 10) + (*cp - '0');
- ++cp;
- }
- tid = pid;
- logbuf->wrlock();
- uid = logbuf->pidToUid(pid);
- logbuf->unlock();
- memmove(pidptr, cp, strlen(cp) + 1);
- }
-
// log to events
size_t str_len = strnlen(str, LOGGER_ENTRY_MAX_PAYLOAD);
if (((fdDmesg < 0) || !initialized) && !hasMetadata(str, str_len))
- logParse(str, &bug_metadata);
- str_len = (str_len + bug_metadata.length() <= LOGGER_ENTRY_MAX_PAYLOAD)
- ? str_len + bug_metadata.length()
+ auditParse(str, uid, &denial_metadata);
+ str_len = (str_len + denial_metadata.length() <= LOGGER_ENTRY_MAX_PAYLOAD)
+ ? str_len + denial_metadata.length()
: LOGGER_ENTRY_MAX_PAYLOAD;
size_t message_len = str_len + sizeof(android_log_event_string_t);
@@ -331,10 +337,10 @@
reinterpret_cast<android_log_event_string_t*>(buffer);
event->header.tag = htole32(AUDITD_LOG_TAG);
event->type = EVENT_TYPE_STRING;
- event->length = htole32(message_len);
- memcpy(event->data, str, str_len - bug_metadata.length());
- memcpy(event->data + str_len - bug_metadata.length(),
- bug_metadata.c_str(), bug_metadata.length());
+ event->length = htole32(str_len);
+ memcpy(event->data, str, str_len - denial_metadata.length());
+ memcpy(event->data + str_len - denial_metadata.length(),
+ denial_metadata.c_str(), denial_metadata.length());
rc = logbuf->log(
LOG_ID_EVENTS, now, uid, pid, tid, reinterpret_cast<char*>(event),
@@ -380,7 +386,8 @@
prefix_len = LOGGER_ENTRY_MAX_PAYLOAD;
}
size_t suffix_len = strnlen(ecomm, LOGGER_ENTRY_MAX_PAYLOAD - prefix_len);
- message_len = str_len + prefix_len + suffix_len + bug_metadata.length() + 2;
+ message_len =
+ str_len + prefix_len + suffix_len + denial_metadata.length() + 2;
if (main) { // begin scope for main buffer
char newstr[message_len];
@@ -390,7 +397,7 @@
strncpy(newstr + 1 + str_len, str, prefix_len);
strncpy(newstr + 1 + str_len + prefix_len, ecomm, suffix_len);
strncpy(newstr + 1 + str_len + prefix_len + suffix_len,
- bug_metadata.c_str(), bug_metadata.length());
+ denial_metadata.c_str(), denial_metadata.length());
rc = logbuf->log(LOG_ID_MAIN, now, uid, pid, tid, newstr,
(message_len <= USHRT_MAX) ? (unsigned short)message_len
diff --git a/logd/LogAudit.h b/logd/LogAudit.h
index 5904966..c3d7a3e 100644
--- a/logd/LogAudit.h
+++ b/logd/LogAudit.h
@@ -48,7 +48,7 @@
std::map<std::string, std::string> populateDenialMap();
std::string denialParse(const std::string& denial, char terminator,
const std::string& search_term);
- void logParse(const std::string& string, std::string* bug_num);
+ void auditParse(const std::string& string, uid_t uid, std::string* bug_num);
int logPrint(const char* fmt, ...)
__attribute__((__format__(__printf__, 2, 3)));
};
diff --git a/logd/LogKlog.cpp b/logd/LogKlog.cpp
index 7a7ac7d..ab980ac 100755
--- a/logd/LogKlog.cpp
+++ b/logd/LogKlog.cpp
@@ -825,7 +825,7 @@
(unsigned short)n);
// notify readers
- if (!rc) {
+ if (rc > 0) {
reader->notifyNewLog(static_cast<log_mask_t>(1 << LOG_ID_KERNEL));
}
diff --git a/logd/main.cpp b/logd/main.cpp
index 4af0d21..606aa63 100644
--- a/logd/main.cpp
+++ b/logd/main.cpp
@@ -33,7 +33,6 @@
#include <syslog.h>
#include <unistd.h>
-#include <cstdbool>
#include <memory>
#include <android-base/macros.h>
diff --git a/logwrapper/Android.bp b/logwrapper/Android.bp
index 54506dc..d4ba4f4 100644
--- a/logwrapper/Android.bp
+++ b/logwrapper/Android.bp
@@ -41,14 +41,11 @@
defaults: ["logwrapper_common"],
}
-// Build vendor logwrapper.
-// TODO: Add vendor_available to "logwrapper" module and remove "logwrapper_vendor" module
-// when vendor_available is fully supported.
cc_binary {
name: "logwrapper_vendor",
+ defaults: ["logwrapper_common"],
stem: "logwrapper",
vendor: true,
- defaults: ["logwrapper_common"],
}
// ========================================================
diff --git a/property_service/libpropertyinfoserializer/Android.bp b/property_service/libpropertyinfoserializer/Android.bp
index 72ae19a..3c4bdc3 100644
--- a/property_service/libpropertyinfoserializer/Android.bp
+++ b/property_service/libpropertyinfoserializer/Android.bp
@@ -35,4 +35,5 @@
"property_info_serializer_test.cpp",
],
static_libs: ["libpropertyinfoserializer"],
+ test_suites: ["device-tests"],
}
diff --git a/rootdir/etc/public.libraries.iot.txt b/rootdir/etc/public.libraries.iot.txt
new file mode 100644
index 0000000..ff0813d
--- /dev/null
+++ b/rootdir/etc/public.libraries.iot.txt
@@ -0,0 +1,27 @@
+# See https://android.googlesource.com/platform/ndk/+/master/docs/PlatformApis.md
+libandroid.so
+libandroidthings.so
+libaaudio.so
+libc.so
+libcamera2ndk.so
+libdl.so
+libEGL.so
+libGLESv1_CM.so
+libGLESv2.so
+libGLESv3.so
+libicui18n.so
+libicuuc.so
+libjnigraphics.so
+liblog.so
+libmediandk.so
+libm.so
+libnativewindow.so
+libneuralnetworks.so
+libOpenMAXAL.so
+libOpenSLES.so
+libRS.so
+libstdc++.so
+libsync.so
+libvulkan.so
+libwebviewchromium_plat_support.so
+libz.so
diff --git a/shell_and_utilities/README.md b/shell_and_utilities/README.md
index c423c69..678b853 100644
--- a/shell_and_utilities/README.md
+++ b/shell_and_utilities/README.md
@@ -179,15 +179,41 @@
toybox: acpi base64 basename blockdev cal cat chcon chgrp chmod chown
chroot chrt cksum clear cmp comm cp cpio cut date df diff dirname dmesg
-dos2unix du echo env expand expr fallocate false file find flock free
+dos2unix du echo env expand expr fallocate false file find flock fmt free
getenforce groups gunzip gzip head hostname hwclock id ifconfig inotifyd
-insmod ionice iorenice kill killall ln load\_policy log logname losetup
-ls lsmod lsof lspci lsusb md5sum microcom mkdir mkfifo mknod mkswap
-mktemp modinfo modprobe more mount mountpoint mv netstat nice nl nohup
-od paste patch pgrep pidof pkill pmap printenv printf ps pwd readlink
-realpath renice restorecon rm rmdir rmmod runcon sed sendevent seq
-setenforce setprop setsid sha1sum sha224sum sha256sum sha384sum
-sha512sum sleep sort split start stat stop strings swapoff swapon sync
-sysctl tac tail tar taskset tee time timeout top touch tr true truncate
-tty ulimit umount uname uniq unix2dos uptime usleep uudecode uuencode
-vmstat wc which whoami xargs xxd yes zcat
+insmod ionice iorenice kill killall ln load\_policy log logname losetup ls
+lsmod lsof lspci lsusb md5sum microcom mkdir mkfifo mknod mkswap mktemp
+modinfo modprobe more mount mountpoint mv netstat nice nl nohup od paste
+patch pgrep pidof pkill pmap printenv printf ps pwd readlink realpath
+renice restorecon rm rmdir rmmod runcon sed sendevent seq setenforce
+setprop setsid sha1sum sha224sum sha256sum sha384sum sha512sum sleep
+sort split start stat stop strings stty swapoff swapon sync sysctl tac
+tail tar taskset tee time timeout top touch tr true truncate tty ulimit
+umount uname uniq unix2dos uptime usleep uudecode uuencode vmstat wc
+which whoami xargs xxd yes zcat
+
+Android Q
+---------
+
+bzip2: bzcat bzip2 bunzip2
+
+one-true-awk: awk
+
+PCRE: egrep fgrep grep
+
+toolbox: getevent getprop newfs\_msdos
+
+toybox: acpi base64 basename blockdev cal cat chcon chgrp chmod chown
+chroot chrt cksum clear cmp comm cp cpio cut date dd df diff dirname
+dmesg dos2unix du echo env expand expr fallocate false file find flock
+fmt free getenforce groups gunzip gzip head hostname hwclock id ifconfig
+inotifyd insmod ionice iorenice kill killall ln load\_policy log logname
+losetup ls lsmod lsof lspci lsusb md5sum microcom mkdir mkfifo mknod
+mkswap mktemp modinfo modprobe more mount mountpoint mv nc netcat netstat
+nice nl nohup nsenter od paste patch pgrep pidof pkill pmap printenv
+printf ps pwd readlink realpath renice restorecon rm rmdir rmmod runcon
+sed sendevent seq setenforce setprop setsid sha1sum sha224sum sha256sum
+sha384sum sha512sum sleep sort split start stat stop strings stty swapoff
+swapon sync sysctl tac tail tar taskset tee time timeout top touch tr
+true truncate tty ulimit umount uname uniq unix2dos unshare uptime usleep
+uudecode uuencode vmstat wc which whoami xargs xxd yes zcat
diff --git a/toolbox/Android.bp b/toolbox/Android.bp
index ddd95b2..077f542 100644
--- a/toolbox/Android.bp
+++ b/toolbox/Android.bp
@@ -5,32 +5,8 @@
"-Werror",
"-Wno-unused-parameter",
"-Wno-unused-const-variable",
- "-include bsd-compatibility.h",
"-D_FILE_OFFSET_BITS=64",
- ],
- local_include_dirs: ["upstream-netbsd/include/"],
-}
-
-cc_library_static {
- name: "libtoolbox_dd",
- defaults: ["toolbox_defaults"],
- vendor_available: true,
- srcs: [
- "upstream-netbsd/bin/dd/args.c",
- "upstream-netbsd/bin/dd/conv.c",
- "upstream-netbsd/bin/dd/dd.c",
- "upstream-netbsd/bin/dd/dd_hostops.c",
- "upstream-netbsd/bin/dd/misc.c",
- "upstream-netbsd/bin/dd/position.c",
- "upstream-netbsd/lib/libc/gen/getbsize.c",
- "upstream-netbsd/lib/libc/gen/humanize_number.c",
- "upstream-netbsd/lib/libc/stdlib/strsuftoll.c",
- "upstream-netbsd/lib/libc/string/swab.c",
- "upstream-netbsd/lib/libutil/raise_default_signal.c",
- ],
- cflags: [
- "-Dmain=dd_main",
- "-DNO_CONV",
+ "-DWITHOUT_NLS",
],
}
@@ -55,15 +31,12 @@
generated_headers: [
"toolbox_input_labels",
],
- whole_static_libs: ["libtoolbox_dd"],
shared_libs: [
"libbase",
- "libcutils",
],
static_libs: ["libpropertyinfoparser"],
symlinks: [
- "dd",
"getevent",
"getprop",
"newfs_msdos",
@@ -89,38 +62,9 @@
srcs: ["r.c"],
}
-// We build BSD grep separately, so it can provide egrep and fgrep too.
-cc_defaults {
- name: "grep_common",
+cc_binary_host {
+ name: "newfs_msdos",
defaults: ["toolbox_defaults"],
- srcs: [
- "upstream-netbsd/usr.bin/grep/fastgrep.c",
- "upstream-netbsd/usr.bin/grep/file.c",
- "upstream-netbsd/usr.bin/grep/grep.c",
- "upstream-netbsd/usr.bin/grep/queue.c",
- "upstream-netbsd/usr.bin/grep/util.c",
- ],
- symlinks: [
- "egrep",
- "fgrep",
- ],
-
- sanitize: {
- integer_overflow: false,
- },
-}
-
-cc_binary {
- name: "grep",
- defaults: ["grep_common"],
-}
-
-// Build vendor grep.
-// TODO: Add vendor_available to "grep" module and remove "grep_vendor" module
-// when vendor_available is fully supported.
-cc_binary {
- name: "grep_vendor",
- stem: "grep",
- vendor: true,
- defaults: ["grep_common"],
+ srcs: ["newfs_msdos.c"],
+ cflags: ["-Dnewfs_msdos_main=main"]
}
diff --git a/toolbox/bsd-compatibility.h b/toolbox/bsd-compatibility.h
deleted file mode 100644
index 7c3ddd4..0000000
--- a/toolbox/bsd-compatibility.h
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
- * Copyright (C) 2014, The Android Open Source Project
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
- * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
- * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
- * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
- * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
- * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#include <stdbool.h>
-#include <sys/types.h>
-
-/* We want chown to support user.group as well as user:group. */
-#define SUPPORT_DOT
-
-/* We don't localize /system/bin! */
-#define WITHOUT_NLS
-
-// NetBSD uses _DIAGASSERT to null-check arguments and the like.
-#include <assert.h>
-#define _DIAGASSERT(e) ((e) ? (void) 0 : __assert2(__FILE__, __LINE__, __func__, #e))
-
-// TODO: update our <sys/cdefs.h> to support this properly.
-#define __type_fit(t, a) (0 == 0)
-
-// TODO: should this be in our <sys/cdefs.h>?
-#define __arraycount(a) (sizeof(a) / sizeof((a)[0]))
-
-// This at least matches GNU dd(1) behavior.
-#define SIGINFO SIGUSR1
-
-#define S_ISWHT(x) false
-
-__BEGIN_DECLS
-
-/* From NetBSD <stdlib.h>. */
-#define HN_DECIMAL 0x01
-#define HN_NOSPACE 0x02
-#define HN_B 0x04
-#define HN_DIVISOR_1000 0x08
-#define HN_GETSCALE 0x10
-#define HN_AUTOSCALE 0x20
-int humanize_number(char *, size_t, int64_t, const char *, int, int);
-int dehumanize_number(const char *, int64_t *);
-char *getbsize(int *, long *);
-long long strsuftoll(const char *, const char *, long long, long long);
-long long strsuftollx(const char *, const char *, long long, long long,
- char *, size_t);
-
-/* From NetBSD <string.h>. */
-void strmode(mode_t, char*);
-
-/* From NetBSD <sys/param.h>. */
-#define MAXBSIZE 65536
-
-/* From NetBSD <sys/stat.h>. */
-#define DEFFILEMODE (S_IRUSR | S_IWUSR)
-
-/* From NetBSD <unistd.h>. */
-void swab(const void * __restrict, void * __restrict, ssize_t);
-
-/* From NetBSD <util.h>. */
-int raise_default_signal(int);
-
-__END_DECLS
diff --git a/toolbox/newfs_msdos.c b/toolbox/newfs_msdos.c
index d7047e2..5fc8b02 100644
--- a/toolbox/newfs_msdos.c
+++ b/toolbox/newfs_msdos.c
@@ -32,15 +32,17 @@
#include <sys/param.h>
-#ifndef ANDROID
+#ifdef __APPLE__
+#elif defined(ANDROID)
+#include <linux/fs.h>
+#include <linux/hdreg.h>
+#include <stdarg.h>
+#include <sys/ioctl.h>
+#else
#include <sys/fdcio.h>
#include <sys/disk.h>
#include <sys/disklabel.h>
#include <sys/mount.h>
-#else
-#include <stdarg.h>
-#include <linux/fs.h>
-#include <linux/hdreg.h>
#endif
#include <sys/stat.h>
@@ -58,6 +60,10 @@
#include <time.h>
#include <unistd.h>
+#ifndef __unused
+#define __unused __attribute__((__unused__))
+#endif
+
#define MAXU16 0xffff /* maximum unsigned 16-bit quantity */
#define BPN 4 /* bits per nibble */
#define NPB 2 /* nibbles per byte */
@@ -794,7 +800,10 @@
* Get disk slice, partition, and geometry information.
*/
-#ifdef ANDROID
+#ifdef __APPLE__
+static void getdiskinfo(__unused int fd, __unused const char* fname, __unused const char* dtype,
+ __unused int oflag, __unused struct bpb* bpb) {}
+#elif ANDROID
static void getdiskinfo(int fd, const char *fname, const char *dtype,
__unused int oflag,struct bpb *bpb)
{
diff --git a/toolbox/tools.h b/toolbox/tools.h
index 505f528..3d4bc3e 100644
--- a/toolbox/tools.h
+++ b/toolbox/tools.h
@@ -1,4 +1,3 @@
-TOOL(dd)
TOOL(getevent)
TOOL(getprop)
TOOL(newfs_msdos)
diff --git a/toolbox/upstream-netbsd/bin/dd/args.c b/toolbox/upstream-netbsd/bin/dd/args.c
deleted file mode 100644
index 207e300..0000000
--- a/toolbox/upstream-netbsd/bin/dd/args.c
+++ /dev/null
@@ -1,391 +0,0 @@
-/* $NetBSD: args.c,v 1.38 2013/07/17 12:55:48 christos Exp $ */
-
-/*-
- * Copyright (c) 1991, 1993, 1994
- * The Regents of the University of California. All rights reserved.
- *
- * This code is derived from software contributed to Berkeley by
- * Keith Muller of the University of California, San Diego and Lance
- * Visser of Convex Computer Corporation.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. Neither the name of the University nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#include <sys/cdefs.h>
-#ifndef lint
-#if 0
-static char sccsid[] = "@(#)args.c 8.3 (Berkeley) 4/2/94";
-#else
-__RCSID("$NetBSD: args.c,v 1.38 2013/07/17 12:55:48 christos Exp $");
-#endif
-#endif /* not lint */
-
-#include <sys/types.h>
-#include <sys/time.h>
-
-#include <err.h>
-#include <errno.h>
-#include <limits.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include "dd.h"
-#include "extern.h"
-
-static int c_arg(const void *, const void *);
-
-#ifdef NO_MSGFMT
-static void f_msgfmt(char *) __dead;
-#else
-static void f_msgfmt(char *);
-#endif /* NO_MSGFMT */
-
-#ifdef NO_CONV
-static void f_conv(char *) __dead;
-#else
-static void f_conv(char *);
-static int c_conv(const void *, const void *);
-#endif /* NO_CONV */
-
-static void f_bs(char *);
-static void f_cbs(char *);
-static void f_count(char *);
-static void f_files(char *);
-static void f_ibs(char *);
-static void f_if(char *);
-static void f_obs(char *);
-static void f_of(char *);
-static void f_seek(char *);
-static void f_skip(char *);
-static void f_progress(char *);
-
-static const struct arg {
- const char *name;
- void (*f)(char *);
- u_int set, noset;
-} args[] = {
- /* the array needs to be sorted by the first column so
- bsearch() can be used to find commands quickly */
- { "bs", f_bs, C_BS, C_BS|C_IBS|C_OBS|C_OSYNC },
- { "cbs", f_cbs, C_CBS, C_CBS },
- { "conv", f_conv, 0, 0 },
- { "count", f_count, C_COUNT, C_COUNT },
- { "files", f_files, C_FILES, C_FILES },
- { "ibs", f_ibs, C_IBS, C_BS|C_IBS },
- { "if", f_if, C_IF, C_IF },
- { "iseek", f_skip, C_SKIP, C_SKIP },
- { "msgfmt", f_msgfmt, 0, 0 },
- { "obs", f_obs, C_OBS, C_BS|C_OBS },
- { "of", f_of, C_OF, C_OF },
- { "oseek", f_seek, C_SEEK, C_SEEK },
- { "progress", f_progress, 0, 0 },
- { "seek", f_seek, C_SEEK, C_SEEK },
- { "skip", f_skip, C_SKIP, C_SKIP },
-};
-
-/*
- * args -- parse JCL syntax of dd.
- */
-void
-jcl(char **argv)
-{
- struct arg *ap, tmp;
- char *oper, *arg;
-
- in.dbsz = out.dbsz = 512;
-
- while ((oper = *++argv) != NULL) {
- if ((oper = strdup(oper)) == NULL) {
- errx(EXIT_FAILURE,
- "unable to allocate space for the argument %s",
- *argv);
- /* NOTREACHED */
- }
- if ((arg = strchr(oper, '=')) == NULL) {
- errx(EXIT_FAILURE, "unknown operand %s", oper);
- /* NOTREACHED */
- }
- *arg++ = '\0';
- if (!*arg) {
- errx(EXIT_FAILURE, "no value specified for %s", oper);
- /* NOTREACHED */
- }
- tmp.name = oper;
- if (!(ap = bsearch(&tmp, args,
- __arraycount(args), sizeof(*args), c_arg))) {
- errx(EXIT_FAILURE, "unknown operand %s", tmp.name);
- /* NOTREACHED */
- }
- if (ddflags & ap->noset) {
- errx(EXIT_FAILURE,
- "%s: illegal argument combination or already set",
- tmp.name);
- /* NOTREACHED */
- }
- ddflags |= ap->set;
- ap->f(arg);
- }
-
- /* Final sanity checks. */
-
- if (ddflags & C_BS) {
- /*
- * Bs is turned off by any conversion -- we assume the user
- * just wanted to set both the input and output block sizes
- * and didn't want the bs semantics, so we don't warn.
- */
- if (ddflags & (C_BLOCK | C_LCASE | C_SWAB | C_UCASE |
- C_UNBLOCK | C_OSYNC | C_ASCII | C_EBCDIC | C_SPARSE)) {
- ddflags &= ~C_BS;
- ddflags |= C_IBS|C_OBS;
- }
-
- /* Bs supersedes ibs and obs. */
- if (ddflags & C_BS && ddflags & (C_IBS|C_OBS))
- warnx("bs supersedes ibs and obs");
- }
-
- /*
- * Ascii/ebcdic and cbs implies block/unblock.
- * Block/unblock requires cbs and vice-versa.
- */
- if (ddflags & (C_BLOCK|C_UNBLOCK)) {
- if (!(ddflags & C_CBS)) {
- errx(EXIT_FAILURE, "record operations require cbs");
- /* NOTREACHED */
- }
- cfunc = ddflags & C_BLOCK ? block : unblock;
- } else if (ddflags & C_CBS) {
- if (ddflags & (C_ASCII|C_EBCDIC)) {
- if (ddflags & C_ASCII) {
- ddflags |= C_UNBLOCK;
- cfunc = unblock;
- } else {
- ddflags |= C_BLOCK;
- cfunc = block;
- }
- } else {
- errx(EXIT_FAILURE,
- "cbs meaningless if not doing record operations");
- /* NOTREACHED */
- }
- } else
- cfunc = def;
-
- /* Read, write and seek calls take off_t as arguments.
- *
- * The following check is not done because an off_t is a quad
- * for current NetBSD implementations.
- *
- * if (in.offset > INT_MAX/in.dbsz || out.offset > INT_MAX/out.dbsz)
- * errx(1, "seek offsets cannot be larger than %d", INT_MAX);
- */
-}
-
-static int
-c_arg(const void *a, const void *b)
-{
-
- return (strcmp(((const struct arg *)a)->name,
- ((const struct arg *)b)->name));
-}
-
-static void
-f_bs(char *arg)
-{
-
- in.dbsz = out.dbsz = strsuftoll("block size", arg, 1, UINT_MAX);
-}
-
-static void
-f_cbs(char *arg)
-{
-
- cbsz = strsuftoll("conversion record size", arg, 1, UINT_MAX);
-}
-
-static void
-f_count(char *arg)
-{
-
- cpy_cnt = strsuftoll("block count", arg, 0, LLONG_MAX);
- if (!cpy_cnt)
- terminate(0);
-}
-
-static void
-f_files(char *arg)
-{
-
- files_cnt = (u_int)strsuftoll("file count", arg, 0, UINT_MAX);
- if (!files_cnt)
- terminate(0);
-}
-
-static void
-f_ibs(char *arg)
-{
-
- if (!(ddflags & C_BS))
- in.dbsz = strsuftoll("input block size", arg, 1, UINT_MAX);
-}
-
-static void
-f_if(char *arg)
-{
-
- in.name = arg;
-}
-
-#ifdef NO_MSGFMT
-/* Build a small version (i.e. for a ramdisk root) */
-static void
-f_msgfmt(char *arg)
-{
-
- errx(EXIT_FAILURE, "msgfmt option disabled");
- /* NOTREACHED */
-}
-#else /* NO_MSGFMT */
-static void
-f_msgfmt(char *arg)
-{
-
- /*
- * If the format string is not valid, dd_write_msg() will print
- * an error and exit.
- */
- dd_write_msg(arg, 0);
-
- msgfmt = arg;
-}
-#endif /* NO_MSGFMT */
-
-static void
-f_obs(char *arg)
-{
-
- if (!(ddflags & C_BS))
- out.dbsz = strsuftoll("output block size", arg, 1, UINT_MAX);
-}
-
-static void
-f_of(char *arg)
-{
-
- out.name = arg;
-}
-
-static void
-f_seek(char *arg)
-{
-
- out.offset = strsuftoll("seek blocks", arg, 0, LLONG_MAX);
-}
-
-static void
-f_skip(char *arg)
-{
-
- in.offset = strsuftoll("skip blocks", arg, 0, LLONG_MAX);
-}
-
-static void
-f_progress(char *arg)
-{
-
- progress = strsuftoll("progress blocks", arg, 0, LLONG_MAX);
-}
-
-#ifdef NO_CONV
-/* Build a small version (i.e. for a ramdisk root) */
-static void
-f_conv(char *arg)
-{
-
- errx(EXIT_FAILURE, "conv option disabled");
- /* NOTREACHED */
-}
-#else /* NO_CONV */
-
-static const struct conv {
- const char *name;
- u_int set, noset;
- const u_char *ctab;
-} clist[] = {
- { "ascii", C_ASCII, C_EBCDIC, e2a_POSIX },
- { "block", C_BLOCK, C_UNBLOCK, NULL },
- { "ebcdic", C_EBCDIC, C_ASCII, a2e_POSIX },
- { "ibm", C_EBCDIC, C_ASCII, a2ibm_POSIX },
- { "lcase", C_LCASE, C_UCASE, NULL },
- { "noerror", C_NOERROR, 0, NULL },
- { "notrunc", C_NOTRUNC, 0, NULL },
- { "oldascii", C_ASCII, C_EBCDIC, e2a_32V },
- { "oldebcdic", C_EBCDIC, C_ASCII, a2e_32V },
- { "oldibm", C_EBCDIC, C_ASCII, a2ibm_32V },
- { "osync", C_OSYNC, C_BS, NULL },
- { "sparse", C_SPARSE, 0, NULL },
- { "swab", C_SWAB, 0, NULL },
- { "sync", C_SYNC, 0, NULL },
- { "ucase", C_UCASE, C_LCASE, NULL },
- { "unblock", C_UNBLOCK, C_BLOCK, NULL },
- /* If you add items to this table, be sure to add the
- * conversions to the C_BS check in the jcl routine above.
- */
-};
-
-static void
-f_conv(char *arg)
-{
- struct conv *cp, tmp;
-
- while (arg != NULL) {
- tmp.name = strsep(&arg, ",");
- if (!(cp = bsearch(&tmp, clist,
- __arraycount(clist), sizeof(*clist), c_conv))) {
- errx(EXIT_FAILURE, "unknown conversion %s", tmp.name);
- /* NOTREACHED */
- }
- if (ddflags & cp->noset) {
- errx(EXIT_FAILURE,
- "%s: illegal conversion combination", tmp.name);
- /* NOTREACHED */
- }
- ddflags |= cp->set;
- if (cp->ctab)
- ctab = cp->ctab;
- }
-}
-
-static int
-c_conv(const void *a, const void *b)
-{
-
- return (strcmp(((const struct conv *)a)->name,
- ((const struct conv *)b)->name));
-}
-
-#endif /* NO_CONV */
diff --git a/toolbox/upstream-netbsd/bin/dd/conv.c b/toolbox/upstream-netbsd/bin/dd/conv.c
deleted file mode 100644
index d4a8a09..0000000
--- a/toolbox/upstream-netbsd/bin/dd/conv.c
+++ /dev/null
@@ -1,283 +0,0 @@
-/* $NetBSD: conv.c,v 1.17 2003/08/07 09:05:10 agc Exp $ */
-
-/*-
- * Copyright (c) 1991, 1993, 1994
- * The Regents of the University of California. All rights reserved.
- *
- * This code is derived from software contributed to Berkeley by
- * Keith Muller of the University of California, San Diego and Lance
- * Visser of Convex Computer Corporation.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. Neither the name of the University nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#include <sys/cdefs.h>
-#ifndef lint
-#if 0
-static char sccsid[] = "@(#)conv.c 8.3 (Berkeley) 4/2/94";
-#else
-__RCSID("$NetBSD: conv.c,v 1.17 2003/08/07 09:05:10 agc Exp $");
-#endif
-#endif /* not lint */
-
-#include <sys/param.h>
-#include <sys/time.h>
-
-#include <err.h>
-#include <string.h>
-#include <stdlib.h>
-
-#include "dd.h"
-#include "extern.h"
-
-/*
- * def --
- * Copy input to output. Input is buffered until reaches obs, and then
- * output until less than obs remains. Only a single buffer is used.
- * Worst case buffer calculation is (ibs + obs - 1).
- */
-void
-def(void)
-{
- uint64_t cnt;
- u_char *inp;
- const u_char *t;
-
- if ((t = ctab) != NULL)
- for (inp = in.dbp - (cnt = in.dbrcnt); cnt--; ++inp)
- *inp = t[*inp];
-
- /* Make the output buffer look right. */
- out.dbp = in.dbp;
- out.dbcnt = in.dbcnt;
-
- if (in.dbcnt >= out.dbsz) {
- /* If the output buffer is full, write it. */
- dd_out(0);
-
- /*
- * Ddout copies the leftover output to the beginning of
- * the buffer and resets the output buffer. Reset the
- * input buffer to match it.
- */
- in.dbp = out.dbp;
- in.dbcnt = out.dbcnt;
- }
-}
-
-void
-def_close(void)
-{
-
- /* Just update the count, everything is already in the buffer. */
- if (in.dbcnt)
- out.dbcnt = in.dbcnt;
-}
-
-#ifdef NO_CONV
-/* Build a smaller version (i.e. for a miniroot) */
-/* These can not be called, but just in case... */
-static const char no_block[] = "unblock and -DNO_CONV?";
-void block(void) { errx(EXIT_FAILURE, "%s", no_block + 2); }
-void block_close(void) { errx(EXIT_FAILURE, "%s", no_block + 2); }
-void unblock(void) { errx(EXIT_FAILURE, "%s", no_block); }
-void unblock_close(void) { errx(EXIT_FAILURE, "%s", no_block); }
-#else /* NO_CONV */
-
-/*
- * Copy variable length newline terminated records with a max size cbsz
- * bytes to output. Records less than cbs are padded with spaces.
- *
- * max in buffer: MAX(ibs, cbsz)
- * max out buffer: obs + cbsz
- */
-void
-block(void)
-{
- static int intrunc;
- int ch = 0; /* pacify gcc */
- uint64_t cnt, maxlen;
- u_char *inp, *outp;
- const u_char *t;
-
- /*
- * Record truncation can cross block boundaries. If currently in a
- * truncation state, keep tossing characters until reach a newline.
- * Start at the beginning of the buffer, as the input buffer is always
- * left empty.
- */
- if (intrunc) {
- for (inp = in.db, cnt = in.dbrcnt;
- cnt && *inp++ != '\n'; --cnt);
- if (!cnt) {
- in.dbcnt = 0;
- in.dbp = in.db;
- return;
- }
- intrunc = 0;
- /* Adjust the input buffer numbers. */
- in.dbcnt = cnt - 1;
- in.dbp = inp + cnt - 1;
- }
-
- /*
- * Copy records (max cbsz size chunks) into the output buffer. The
- * translation is done as we copy into the output buffer.
- */
- for (inp = in.dbp - in.dbcnt, outp = out.dbp; in.dbcnt;) {
- maxlen = MIN(cbsz, in.dbcnt);
- if ((t = ctab) != NULL)
- for (cnt = 0;
- cnt < maxlen && (ch = *inp++) != '\n'; ++cnt)
- *outp++ = t[ch];
- else
- for (cnt = 0;
- cnt < maxlen && (ch = *inp++) != '\n'; ++cnt)
- *outp++ = ch;
- /*
- * Check for short record without a newline. Reassemble the
- * input block.
- */
- if (ch != '\n' && in.dbcnt < cbsz) {
- (void)memmove(in.db, in.dbp - in.dbcnt, in.dbcnt);
- break;
- }
-
- /* Adjust the input buffer numbers. */
- in.dbcnt -= cnt;
- if (ch == '\n')
- --in.dbcnt;
-
- /* Pad short records with spaces. */
- if (cnt < cbsz)
- (void)memset(outp, ctab ? ctab[' '] : ' ', cbsz - cnt);
- else {
- /*
- * If the next character wouldn't have ended the
- * block, it's a truncation.
- */
- if (!in.dbcnt || *inp != '\n')
- ++st.trunc;
-
- /* Toss characters to a newline. */
- for (; in.dbcnt && *inp++ != '\n'; --in.dbcnt);
- if (!in.dbcnt)
- intrunc = 1;
- else
- --in.dbcnt;
- }
-
- /* Adjust output buffer numbers. */
- out.dbp += cbsz;
- if ((out.dbcnt += cbsz) >= out.dbsz)
- dd_out(0);
- outp = out.dbp;
- }
- in.dbp = in.db + in.dbcnt;
-}
-
-void
-block_close(void)
-{
-
- /*
- * Copy any remaining data into the output buffer and pad to a record.
- * Don't worry about truncation or translation, the input buffer is
- * always empty when truncating, and no characters have been added for
- * translation. The bottom line is that anything left in the input
- * buffer is a truncated record. Anything left in the output buffer
- * just wasn't big enough.
- */
- if (in.dbcnt) {
- ++st.trunc;
- (void)memmove(out.dbp, in.dbp - in.dbcnt, in.dbcnt);
- (void)memset(out.dbp + in.dbcnt,
- ctab ? ctab[' '] : ' ', cbsz - in.dbcnt);
- out.dbcnt += cbsz;
- }
-}
-
-/*
- * Convert fixed length (cbsz) records to variable length. Deletes any
- * trailing blanks and appends a newline.
- *
- * max in buffer: MAX(ibs, cbsz) + cbsz
- * max out buffer: obs + cbsz
- */
-void
-unblock(void)
-{
- uint64_t cnt;
- u_char *inp;
- const u_char *t;
-
- /* Translation and case conversion. */
- if ((t = ctab) != NULL)
- for (cnt = in.dbrcnt, inp = in.dbp - 1; cnt--; inp--)
- *inp = t[*inp];
- /*
- * Copy records (max cbsz size chunks) into the output buffer. The
- * translation has to already be done or we might not recognize the
- * spaces.
- */
- for (inp = in.db; in.dbcnt >= cbsz; inp += cbsz, in.dbcnt -= cbsz) {
- for (t = inp + cbsz - 1; t >= inp && *t == ' '; --t);
- if (t >= inp) {
- cnt = t - inp + 1;
- (void)memmove(out.dbp, inp, cnt);
- out.dbp += cnt;
- out.dbcnt += cnt;
- }
- ++out.dbcnt;
- *out.dbp++ = '\n';
- if (out.dbcnt >= out.dbsz)
- dd_out(0);
- }
- if (in.dbcnt)
- (void)memmove(in.db, in.dbp - in.dbcnt, in.dbcnt);
- in.dbp = in.db + in.dbcnt;
-}
-
-void
-unblock_close(void)
-{
- uint64_t cnt;
- u_char *t;
-
- if (in.dbcnt) {
- warnx("%s: short input record", in.name);
- for (t = in.db + in.dbcnt - 1; t >= in.db && *t == ' '; --t);
- if (t >= in.db) {
- cnt = t - in.db + 1;
- (void)memmove(out.dbp, in.db, cnt);
- out.dbp += cnt;
- out.dbcnt += cnt;
- }
- ++out.dbcnt;
- *out.dbp++ = '\n';
- }
-}
-
-#endif /* NO_CONV */
diff --git a/toolbox/upstream-netbsd/bin/dd/dd.c b/toolbox/upstream-netbsd/bin/dd/dd.c
deleted file mode 100644
index 03d080c..0000000
--- a/toolbox/upstream-netbsd/bin/dd/dd.c
+++ /dev/null
@@ -1,598 +0,0 @@
-/* $NetBSD: dd.c,v 1.49 2012/02/21 01:49:01 matt Exp $ */
-
-/*-
- * Copyright (c) 1991, 1993, 1994
- * The Regents of the University of California. All rights reserved.
- *
- * This code is derived from software contributed to Berkeley by
- * Keith Muller of the University of California, San Diego and Lance
- * Visser of Convex Computer Corporation.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. Neither the name of the University nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#include <sys/cdefs.h>
-#ifndef lint
-__COPYRIGHT("@(#) Copyright (c) 1991, 1993, 1994\
- The Regents of the University of California. All rights reserved.");
-#endif /* not lint */
-
-#ifndef lint
-#if 0
-static char sccsid[] = "@(#)dd.c 8.5 (Berkeley) 4/2/94";
-#else
-__RCSID("$NetBSD: dd.c,v 1.49 2012/02/21 01:49:01 matt Exp $");
-#endif
-#endif /* not lint */
-
-#include <sys/param.h>
-#include <sys/stat.h>
-#include <sys/ioctl.h>
-#include <sys/mtio.h>
-#include <sys/time.h>
-
-#include <ctype.h>
-#include <err.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <locale.h>
-#include <signal.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-#include "dd.h"
-#include "extern.h"
-
-static void dd_close(void);
-static void dd_in(void);
-static void getfdtype(IO *);
-static void redup_clean_fd(IO *);
-static void setup(void);
-
-int main(int, char *[]);
-
-IO in, out; /* input/output state */
-STAT st; /* statistics */
-void (*cfunc)(void); /* conversion function */
-uint64_t cpy_cnt; /* # of blocks to copy */
-static off_t pending = 0; /* pending seek if sparse */
-u_int ddflags; /* conversion options */
-uint64_t cbsz; /* conversion block size */
-u_int files_cnt = 1; /* # of files to copy */
-uint64_t progress = 0; /* display sign of life */
-const u_char *ctab; /* conversion table */
-sigset_t infoset; /* a set blocking SIGINFO */
-const char *msgfmt = "posix"; /* default summary() message format */
-
-/*
- * Ops for stdin/stdout and crunch'd dd. These are always host ops.
- */
-static const struct ddfops ddfops_stdfd = {
- .op_open = open,
- .op_close = close,
- .op_fcntl = fcntl,
- .op_ioctl = ioctl,
- .op_fstat = fstat,
- .op_fsync = fsync,
- .op_ftruncate = ftruncate,
- .op_lseek = lseek,
- .op_read = read,
- .op_write = write,
-};
-extern const struct ddfops ddfops_prog;
-
-int
-main(int argc, char *argv[])
-{
- int ch;
-
- setprogname(argv[0]);
- (void)setlocale(LC_ALL, "");
-
- while ((ch = getopt(argc, argv, "")) != -1) {
- switch (ch) {
- default:
- errx(EXIT_FAILURE, "usage: dd [operand ...]");
- /* NOTREACHED */
- }
- }
- argc -= (optind - 1);
- argv += (optind - 1);
-
- jcl(argv);
-#ifndef CRUNCHOPS
- if (ddfops_prog.op_init && ddfops_prog.op_init() == -1)
- err(1, "prog init");
-#endif
- setup();
-
- (void)signal(SIGINFO, summaryx);
- (void)signal(SIGINT, terminate);
- (void)sigemptyset(&infoset);
- (void)sigaddset(&infoset, SIGINFO);
-
- (void)atexit(summary);
-
- while (files_cnt--)
- dd_in();
-
- dd_close();
- exit(0);
- /* NOTREACHED */
-}
-
-static void
-setup(void)
-{
-#ifdef CRUNCHOPS
- const struct ddfops *prog_ops = &ddfops_stdfd;
-#else
- const struct ddfops *prog_ops = &ddfops_prog;
-#endif
-
- if (in.name == NULL) {
- in.name = "stdin";
- in.fd = STDIN_FILENO;
- in.ops = &ddfops_stdfd;
- } else {
- in.ops = prog_ops;
- in.fd = ddop_open(in, in.name, O_RDONLY, 0);
- if (in.fd < 0)
- err(EXIT_FAILURE, "%s", in.name);
- /* NOTREACHED */
-
- /* Ensure in.fd is outside the stdio descriptor range */
- redup_clean_fd(&in);
- }
-
- getfdtype(&in);
-
- if (files_cnt > 1 && !(in.flags & ISTAPE)) {
- errx(EXIT_FAILURE, "files is not supported for non-tape devices");
- /* NOTREACHED */
- }
-
- if (out.name == NULL) {
- /* No way to check for read access here. */
- out.fd = STDOUT_FILENO;
- out.name = "stdout";
- out.ops = &ddfops_stdfd;
- } else {
- out.ops = prog_ops;
-#define OFLAGS \
- (O_CREAT | (ddflags & (C_SEEK | C_NOTRUNC) ? 0 : O_TRUNC))
- out.fd = ddop_open(out, out.name, O_RDWR | OFLAGS, DEFFILEMODE);
- /*
- * May not have read access, so try again with write only.
- * Without read we may have a problem if output also does
- * not support seeks.
- */
- if (out.fd < 0) {
- out.fd = ddop_open(out, out.name, O_WRONLY | OFLAGS,
- DEFFILEMODE);
- out.flags |= NOREAD;
- }
- if (out.fd < 0) {
- err(EXIT_FAILURE, "%s", out.name);
- /* NOTREACHED */
- }
-
- /* Ensure out.fd is outside the stdio descriptor range */
- redup_clean_fd(&out);
- }
-
- getfdtype(&out);
-
- /*
- * Allocate space for the input and output buffers. If not doing
- * record oriented I/O, only need a single buffer.
- */
- if (!(ddflags & (C_BLOCK|C_UNBLOCK))) {
- size_t dbsz = out.dbsz;
- if (!(ddflags & C_BS))
- dbsz += in.dbsz - 1;
- if ((in.db = malloc(dbsz)) == NULL) {
- err(EXIT_FAILURE, NULL);
- /* NOTREACHED */
- }
- out.db = in.db;
- } else if ((in.db =
- malloc((u_int)(MAX(in.dbsz, cbsz) + cbsz))) == NULL ||
- (out.db = malloc((u_int)(out.dbsz + cbsz))) == NULL) {
- err(EXIT_FAILURE, NULL);
- /* NOTREACHED */
- }
- in.dbp = in.db;
- out.dbp = out.db;
-
- /* Position the input/output streams. */
- if (in.offset)
- pos_in();
- if (out.offset)
- pos_out();
-
- /*
- * Truncate the output file; ignore errors because it fails on some
- * kinds of output files, tapes, for example.
- */
- if ((ddflags & (C_OF | C_SEEK | C_NOTRUNC)) == (C_OF | C_SEEK))
- (void)ddop_ftruncate(out, out.fd, (off_t)out.offset * out.dbsz);
-
- /*
- * If converting case at the same time as another conversion, build a
- * table that does both at once. If just converting case, use the
- * built-in tables.
- */
- if (ddflags & (C_LCASE|C_UCASE)) {
-#ifdef NO_CONV
- /* Should not get here, but just in case... */
- errx(EXIT_FAILURE, "case conv and -DNO_CONV");
- /* NOTREACHED */
-#else /* NO_CONV */
- u_int cnt;
-
- if (ddflags & C_ASCII || ddflags & C_EBCDIC) {
- if (ddflags & C_LCASE) {
- for (cnt = 0; cnt < 256; ++cnt)
- casetab[cnt] = tolower(ctab[cnt]);
- } else {
- for (cnt = 0; cnt < 256; ++cnt)
- casetab[cnt] = toupper(ctab[cnt]);
- }
- } else {
- if (ddflags & C_LCASE) {
- for (cnt = 0; cnt < 256; ++cnt)
- casetab[cnt] = tolower(cnt);
- } else {
- for (cnt = 0; cnt < 256; ++cnt)
- casetab[cnt] = toupper(cnt);
- }
- }
-
- ctab = casetab;
-#endif /* NO_CONV */
- }
-
- (void)gettimeofday(&st.start, NULL); /* Statistics timestamp. */
-}
-
-static void
-getfdtype(IO *io)
-{
- struct mtget mt;
- struct stat sb;
-
- if (io->ops->op_fstat(io->fd, &sb)) {
- err(EXIT_FAILURE, "%s", io->name);
- /* NOTREACHED */
- }
- if (S_ISCHR(sb.st_mode))
- io->flags |= io->ops->op_ioctl(io->fd, MTIOCGET, &mt)
- ? ISCHR : ISTAPE;
- else if (io->ops->op_lseek(io->fd, (off_t)0, SEEK_CUR) == -1
- && errno == ESPIPE)
- io->flags |= ISPIPE; /* XXX fixed in 4.4BSD */
-}
-
-/*
- * Move the parameter file descriptor to a descriptor that is outside the
- * stdio descriptor range, if necessary. This is required to avoid
- * accidentally outputting completion or error messages into the
- * output file that were intended for the tty.
- */
-static void
-redup_clean_fd(IO *io)
-{
- int fd = io->fd;
- int newfd;
-
- if (fd != STDIN_FILENO && fd != STDOUT_FILENO &&
- fd != STDERR_FILENO)
- /* File descriptor is ok, return immediately. */
- return;
-
- /*
- * 3 is the first descriptor greater than STD*_FILENO. Any
- * free descriptor valued 3 or above is acceptable...
- */
- newfd = io->ops->op_fcntl(fd, F_DUPFD, 3);
- if (newfd < 0) {
- err(EXIT_FAILURE, "dupfd IO");
- /* NOTREACHED */
- }
-
- io->ops->op_close(fd);
- io->fd = newfd;
-}
-
-static void
-dd_in(void)
-{
- int flags;
- int64_t n;
-
- for (flags = ddflags;;) {
- if (cpy_cnt && (st.in_full + st.in_part) >= cpy_cnt)
- return;
-
- /*
- * Clear the buffer first if doing "sync" on input.
- * If doing block operations use spaces. This will
- * affect not only the C_NOERROR case, but also the
- * last partial input block which should be padded
- * with zero and not garbage.
- */
- if (flags & C_SYNC) {
- if (flags & (C_BLOCK|C_UNBLOCK))
- (void)memset(in.dbp, ' ', in.dbsz);
- else
- (void)memset(in.dbp, 0, in.dbsz);
- }
-
- n = ddop_read(in, in.fd, in.dbp, in.dbsz);
- if (n == 0) {
- in.dbrcnt = 0;
- return;
- }
-
- /* Read error. */
- if (n < 0) {
-
- /*
- * If noerror not specified, die. POSIX requires that
- * the warning message be followed by an I/O display.
- */
- if (!(flags & C_NOERROR)) {
- err(EXIT_FAILURE, "%s", in.name);
- /* NOTREACHED */
- }
- warn("%s", in.name);
- summary();
-
- /*
- * If it's not a tape drive or a pipe, seek past the
- * error. If your OS doesn't do the right thing for
- * raw disks this section should be modified to re-read
- * in sector size chunks.
- */
- if (!(in.flags & (ISPIPE|ISTAPE)) &&
- ddop_lseek(in, in.fd, (off_t)in.dbsz, SEEK_CUR))
- warn("%s", in.name);
-
- /* If sync not specified, omit block and continue. */
- if (!(ddflags & C_SYNC))
- continue;
-
- /* Read errors count as full blocks. */
- in.dbcnt += in.dbrcnt = in.dbsz;
- ++st.in_full;
-
- /* Handle full input blocks. */
- } else if ((uint64_t)n == in.dbsz) {
- in.dbcnt += in.dbrcnt = n;
- ++st.in_full;
-
- /* Handle partial input blocks. */
- } else {
- /* If sync, use the entire block. */
- if (ddflags & C_SYNC)
- in.dbcnt += in.dbrcnt = in.dbsz;
- else
- in.dbcnt += in.dbrcnt = n;
- ++st.in_part;
- }
-
- /*
- * POSIX states that if bs is set and no other conversions
- * than noerror, notrunc or sync are specified, the block
- * is output without buffering as it is read.
- */
- if (ddflags & C_BS) {
- out.dbcnt = in.dbcnt;
- dd_out(1);
- in.dbcnt = 0;
- continue;
- }
-
- if (ddflags & C_SWAB) {
- if ((n = in.dbrcnt) & 1) {
- ++st.swab;
- --n;
- }
- swab(in.dbp, in.dbp, n);
- }
-
- in.dbp += in.dbrcnt;
- (*cfunc)();
- }
-}
-
-/*
- * Cleanup any remaining I/O and flush output. If necessary, output file
- * is truncated.
- */
-static void
-dd_close(void)
-{
-
- if (cfunc == def)
- def_close();
- else if (cfunc == block)
- block_close();
- else if (cfunc == unblock)
- unblock_close();
- if (ddflags & C_OSYNC && out.dbcnt < out.dbsz) {
- (void)memset(out.dbp, 0, out.dbsz - out.dbcnt);
- out.dbcnt = out.dbsz;
- }
- /* If there are pending sparse blocks, make sure
- * to write out the final block un-sparse
- */
- if ((out.dbcnt == 0) && pending) {
- memset(out.db, 0, out.dbsz);
- out.dbcnt = out.dbsz;
- out.dbp = out.db + out.dbcnt;
- pending -= out.dbsz;
- }
- if (out.dbcnt)
- dd_out(1);
-
- /*
- * Reporting nfs write error may be deferred until next
- * write(2) or close(2) system call. So, we need to do an
- * extra check. If an output is stdout, the file structure
- * may be shared with other processes and close(2) just
- * decreases the reference count.
- */
- if (out.fd == STDOUT_FILENO && ddop_fsync(out, out.fd) == -1
- && errno != EINVAL) {
- err(EXIT_FAILURE, "fsync stdout");
- /* NOTREACHED */
- }
- if (ddop_close(out, out.fd) == -1) {
- err(EXIT_FAILURE, "close");
- /* NOTREACHED */
- }
-}
-
-void
-dd_out(int force)
-{
- static int warned;
- int64_t cnt, n, nw;
- u_char *outp;
-
- /*
- * Write one or more blocks out. The common case is writing a full
- * output block in a single write; increment the full block stats.
- * Otherwise, we're into partial block writes. If a partial write,
- * and it's a character device, just warn. If a tape device, quit.
- *
- * The partial writes represent two cases. 1: Where the input block
- * was less than expected so the output block was less than expected.
- * 2: Where the input block was the right size but we were forced to
- * write the block in multiple chunks. The original versions of dd(1)
- * never wrote a block in more than a single write, so the latter case
- * never happened.
- *
- * One special case is if we're forced to do the write -- in that case
- * we play games with the buffer size, and it's usually a partial write.
- */
- outp = out.db;
- for (n = force ? out.dbcnt : out.dbsz;; n = out.dbsz) {
- for (cnt = n;; cnt -= nw) {
-
- if (!force && ddflags & C_SPARSE) {
- int sparse, i;
- sparse = 1; /* Is buffer sparse? */
- for (i = 0; i < cnt; i++)
- if (outp[i] != 0) {
- sparse = 0;
- break;
- }
- if (sparse) {
- pending += cnt;
- outp += cnt;
- nw = 0;
- break;
- }
- }
- if (pending != 0) {
- if (ddop_lseek(out,
- out.fd, pending, SEEK_CUR) == -1)
- err(EXIT_FAILURE, "%s: seek error creating sparse file",
- out.name);
- }
- nw = bwrite(&out, outp, cnt);
- if (nw <= 0) {
- if (nw == 0)
- errx(EXIT_FAILURE,
- "%s: end of device", out.name);
- /* NOTREACHED */
- if (errno != EINTR)
- err(EXIT_FAILURE, "%s", out.name);
- /* NOTREACHED */
- nw = 0;
- }
- if (pending) {
- st.bytes += pending;
- st.sparse += pending/out.dbsz;
- st.out_full += pending/out.dbsz;
- pending = 0;
- }
- outp += nw;
- st.bytes += nw;
- if (nw == n) {
- if ((uint64_t)n != out.dbsz)
- ++st.out_part;
- else
- ++st.out_full;
- break;
- }
- ++st.out_part;
- if (nw == cnt)
- break;
- if (out.flags & ISCHR && !warned) {
- warned = 1;
- warnx("%s: short write on character device", out.name);
- }
- if (out.flags & ISTAPE)
- errx(EXIT_FAILURE,
- "%s: short write on tape device", out.name);
- /* NOTREACHED */
-
- }
- if ((out.dbcnt -= n) < out.dbsz)
- break;
- }
-
- /* Reassemble the output block. */
- if (out.dbcnt)
- (void)memmove(out.db, out.dbp - out.dbcnt, out.dbcnt);
- out.dbp = out.db + out.dbcnt;
-
- if (progress && (st.out_full + st.out_part) % progress == 0)
- (void)write(STDERR_FILENO, ".", 1);
-}
-
-/*
- * A protected against SIGINFO write
- */
-ssize_t
-bwrite(IO *io, const void *buf, size_t len)
-{
- sigset_t oset;
- ssize_t rv;
- int oerrno;
-
- (void)sigprocmask(SIG_BLOCK, &infoset, &oset);
- rv = io->ops->op_write(io->fd, buf, len);
- oerrno = errno;
- (void)sigprocmask(SIG_SETMASK, &oset, NULL);
- errno = oerrno;
- return (rv);
-}
diff --git a/toolbox/upstream-netbsd/bin/dd/dd.h b/toolbox/upstream-netbsd/bin/dd/dd.h
deleted file mode 100644
index b01c7b3..0000000
--- a/toolbox/upstream-netbsd/bin/dd/dd.h
+++ /dev/null
@@ -1,127 +0,0 @@
-/* $NetBSD: dd.h,v 1.15 2011/02/04 19:42:12 pooka Exp $ */
-
-/*-
- * Copyright (c) 1991, 1993, 1994
- * The Regents of the University of California. All rights reserved.
- *
- * This code is derived from software contributed to Berkeley by
- * Keith Muller of the University of California, San Diego and Lance
- * Visser of Convex Computer Corporation.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. Neither the name of the University nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- *
- * @(#)dd.h 8.3 (Berkeley) 4/2/94
- */
-
-#include <sys/stat.h>
-
-struct ddfops {
- int (*op_init)(void);
-
- int (*op_open)(const char *, int, ...);
- int (*op_close)(int);
-
- int (*op_fcntl)(int, int, ...);
-#ifdef __ANDROID__
- int (*op_ioctl)(int, int, ...);
-#else
- int (*op_ioctl)(int, unsigned long, ...);
-#endif
-
- int (*op_fstat)(int, struct stat *);
- int (*op_fsync)(int);
- int (*op_ftruncate)(int, off_t);
-
- off_t (*op_lseek)(int, off_t, int);
-
- ssize_t (*op_read)(int, void *, size_t);
- ssize_t (*op_write)(int, const void *, size_t);
-};
-
-#define ddop_open(dir, a1, a2, ...) dir.ops->op_open(a1, a2, __VA_ARGS__)
-#define ddop_close(dir, a1) dir.ops->op_close(a1)
-#define ddop_fcntl(dir, a1, a2, ...) dir.ops->op_fcntl(a1, a2, __VA_ARGS__)
-#define ddop_ioctl(dir, a1, a2, ...) dir.ops->op_ioctl(a1, a2, __VA_ARGS__)
-#define ddop_fsync(dir, a1) dir.ops->op_fsync(a1)
-#define ddop_ftruncate(dir, a1, a2) dir.ops->op_ftruncate(a1, a2)
-#define ddop_lseek(dir, a1, a2, a3) dir.ops->op_lseek(a1, a2, a3)
-#define ddop_read(dir, a1, a2, a3) dir.ops->op_read(a1, a2, a3)
-#define ddop_write(dir, a1, a2, a3) dir.ops->op_write(a1, a2, a3)
-
-/* Input/output stream state. */
-typedef struct {
- u_char *db; /* buffer address */
- u_char *dbp; /* current buffer I/O address */
- uint64_t dbcnt; /* current buffer byte count */
- int64_t dbrcnt; /* last read byte count */
- uint64_t dbsz; /* buffer size */
-
-#define ISCHR 0x01 /* character device (warn on short) */
-#define ISPIPE 0x02 /* pipe (not truncatable) */
-#define ISTAPE 0x04 /* tape (not seekable) */
-#define NOREAD 0x08 /* not readable */
- u_int flags;
-
- const char *name; /* name */
- int fd; /* file descriptor */
- uint64_t offset; /* # of blocks to skip */
- struct ddfops const *ops; /* ops to use with fd */
-} IO;
-
-typedef struct {
- uint64_t in_full; /* # of full input blocks */
- uint64_t in_part; /* # of partial input blocks */
- uint64_t out_full; /* # of full output blocks */
- uint64_t out_part; /* # of partial output blocks */
- uint64_t trunc; /* # of truncated records */
- uint64_t swab; /* # of odd-length swab blocks */
- uint64_t sparse; /* # of sparse output blocks */
- uint64_t bytes; /* # of bytes written */
- struct timeval start; /* start time of dd */
-} STAT;
-
-/* Flags (in ddflags). */
-#define C_ASCII 0x00001
-#define C_BLOCK 0x00002
-#define C_BS 0x00004
-#define C_CBS 0x00008
-#define C_COUNT 0x00010
-#define C_EBCDIC 0x00020
-#define C_FILES 0x00040
-#define C_IBS 0x00080
-#define C_IF 0x00100
-#define C_LCASE 0x00200
-#define C_NOERROR 0x00400
-#define C_NOTRUNC 0x00800
-#define C_OBS 0x01000
-#define C_OF 0x02000
-#define C_SEEK 0x04000
-#define C_SKIP 0x08000
-#define C_SWAB 0x10000
-#define C_SYNC 0x20000
-#define C_UCASE 0x40000
-#define C_UNBLOCK 0x80000
-#define C_OSYNC 0x100000
-#define C_SPARSE 0x200000
diff --git a/toolbox/upstream-netbsd/bin/dd/dd_hostops.c b/toolbox/upstream-netbsd/bin/dd/dd_hostops.c
deleted file mode 100644
index d6e7a89..0000000
--- a/toolbox/upstream-netbsd/bin/dd/dd_hostops.c
+++ /dev/null
@@ -1,53 +0,0 @@
-/* $NetBSD: dd_hostops.c,v 1.1 2011/02/04 19:42:12 pooka Exp $ */
-
-/*-
- * Copyright (c) 2010 The NetBSD Foundation, Inc.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
- * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
- * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
- * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include <sys/cdefs.h>
-#ifndef lint
-__RCSID("$NetBSD: dd_hostops.c,v 1.1 2011/02/04 19:42:12 pooka Exp $");
-#endif /* !lint */
-
-#include <sys/types.h>
-#include <sys/ioctl.h>
-
-#include <fcntl.h>
-#include <unistd.h>
-
-#include "dd.h"
-
-const struct ddfops ddfops_prog = {
- .op_open = open,
- .op_close = close,
- .op_fcntl = fcntl,
- .op_ioctl = ioctl,
- .op_fstat = fstat,
- .op_fsync = fsync,
- .op_ftruncate = ftruncate,
- .op_lseek = lseek,
- .op_read = read,
- .op_write = write,
-};
diff --git a/toolbox/upstream-netbsd/bin/dd/extern.h b/toolbox/upstream-netbsd/bin/dd/extern.h
deleted file mode 100644
index 9c59021..0000000
--- a/toolbox/upstream-netbsd/bin/dd/extern.h
+++ /dev/null
@@ -1,82 +0,0 @@
-/* $NetBSD: extern.h,v 1.22 2011/11/07 22:24:23 jym Exp $ */
-
-/*-
- * Copyright (c) 1991, 1993, 1994
- * The Regents of the University of California. All rights reserved.
- *
- * This code is derived from software contributed to Berkeley by
- * Keith Muller of the University of California, San Diego and Lance
- * Visser of Convex Computer Corporation.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. Neither the name of the University nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- *
- * @(#)extern.h 8.3 (Berkeley) 4/2/94
- */
-
-#include <sys/cdefs.h>
-
-#ifdef NO_CONV
-__dead void block(void);
-__dead void block_close(void);
-__dead void unblock(void);
-__dead void unblock_close(void);
-#else
-void block(void);
-void block_close(void);
-void unblock(void);
-void unblock_close(void);
-#endif
-
-#ifndef NO_MSGFMT
-int dd_write_msg(const char *, int);
-#endif
-
-void dd_out(int);
-void def(void);
-void def_close(void);
-void jcl(char **);
-void pos_in(void);
-void pos_out(void);
-void summary(void);
-void summaryx(int);
-__dead void terminate(int);
-void unblock(void);
-void unblock_close(void);
-ssize_t bwrite(IO *, const void *, size_t);
-
-extern IO in, out;
-extern STAT st;
-extern void (*cfunc)(void);
-extern uint64_t cpy_cnt;
-extern uint64_t cbsz;
-extern u_int ddflags;
-extern u_int files_cnt;
-extern uint64_t progress;
-extern const u_char *ctab;
-extern const u_char a2e_32V[], a2e_POSIX[];
-extern const u_char e2a_32V[], e2a_POSIX[];
-extern const u_char a2ibm_32V[], a2ibm_POSIX[];
-extern u_char casetab[];
-extern const char *msgfmt;
diff --git a/toolbox/upstream-netbsd/bin/dd/misc.c b/toolbox/upstream-netbsd/bin/dd/misc.c
deleted file mode 100644
index 0fac98b..0000000
--- a/toolbox/upstream-netbsd/bin/dd/misc.c
+++ /dev/null
@@ -1,342 +0,0 @@
-/* $NetBSD: misc.c,v 1.23 2011/11/07 22:24:23 jym Exp $ */
-
-/*-
- * Copyright (c) 1991, 1993, 1994
- * The Regents of the University of California. All rights reserved.
- *
- * This code is derived from software contributed to Berkeley by
- * Keith Muller of the University of California, San Diego and Lance
- * Visser of Convex Computer Corporation.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. Neither the name of the University nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#include <sys/cdefs.h>
-#ifndef lint
-#if 0
-static char sccsid[] = "@(#)misc.c 8.3 (Berkeley) 4/2/94";
-#else
-__RCSID("$NetBSD: misc.c,v 1.23 2011/11/07 22:24:23 jym Exp $");
-#endif
-#endif /* not lint */
-
-#include <sys/param.h>
-#include <sys/types.h>
-#include <sys/time.h>
-
-#include <err.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include <util.h>
-#include <inttypes.h>
-
-#include "dd.h"
-#include "extern.h"
-
-#define tv2mS(tv) ((tv).tv_sec * 1000LL + ((tv).tv_usec + 500) / 1000)
-
-static void posix_summary(void);
-#ifndef NO_MSGFMT
-static void custom_summary(void);
-static void human_summary(void);
-static void quiet_summary(void);
-
-static void buffer_write(const char *, size_t, int);
-#endif /* NO_MSGFMT */
-
-void
-summary(void)
-{
-
- if (progress)
- (void)write(STDERR_FILENO, "\n", 1);
-
-#ifdef NO_MSGFMT
- return posix_summary();
-#else /* NO_MSGFMT */
- if (strncmp(msgfmt, "human", sizeof("human")) == 0)
- return human_summary();
-
- if (strncmp(msgfmt, "posix", sizeof("posix")) == 0)
- return posix_summary();
-
- if (strncmp(msgfmt, "quiet", sizeof("quiet")) == 0)
- return quiet_summary();
-
- return custom_summary();
-#endif /* NO_MSGFMT */
-}
-
-static void
-posix_summary(void)
-{
- char buf[100];
- int64_t mS;
- struct timeval tv;
-
- if (progress)
- (void)write(STDERR_FILENO, "\n", 1);
-
- (void)gettimeofday(&tv, NULL);
- mS = tv2mS(tv) - tv2mS(st.start);
- if (mS == 0)
- mS = 1;
-
- /* Use snprintf(3) so that we don't reenter stdio(3). */
- (void)snprintf(buf, sizeof(buf),
- "%llu+%llu records in\n%llu+%llu records out\n",
- (unsigned long long)st.in_full, (unsigned long long)st.in_part,
- (unsigned long long)st.out_full, (unsigned long long)st.out_part);
- (void)write(STDERR_FILENO, buf, strlen(buf));
- if (st.swab) {
- (void)snprintf(buf, sizeof(buf), "%llu odd length swab %s\n",
- (unsigned long long)st.swab,
- (st.swab == 1) ? "block" : "blocks");
- (void)write(STDERR_FILENO, buf, strlen(buf));
- }
- if (st.trunc) {
- (void)snprintf(buf, sizeof(buf), "%llu truncated %s\n",
- (unsigned long long)st.trunc,
- (st.trunc == 1) ? "block" : "blocks");
- (void)write(STDERR_FILENO, buf, strlen(buf));
- }
- if (st.sparse) {
- (void)snprintf(buf, sizeof(buf), "%llu sparse output %s\n",
- (unsigned long long)st.sparse,
- (st.sparse == 1) ? "block" : "blocks");
- (void)write(STDERR_FILENO, buf, strlen(buf));
- }
- (void)snprintf(buf, sizeof(buf),
- "%llu bytes transferred in %lu.%03d secs (%llu bytes/sec)\n",
- (unsigned long long) st.bytes,
- (long) (mS / 1000),
- (int) (mS % 1000),
- (unsigned long long) (st.bytes * 1000LL / mS));
- (void)write(STDERR_FILENO, buf, strlen(buf));
-}
-
-/* ARGSUSED */
-void
-summaryx(int notused)
-{
-
- summary();
-}
-
-/* ARGSUSED */
-void
-terminate(int signo)
-{
-
- summary();
- (void)raise_default_signal(signo);
- _exit(127);
-}
-
-#ifndef NO_MSGFMT
-/*
- * Buffer write(2) calls
- */
-static void
-buffer_write(const char *str, size_t size, int flush)
-{
- static char wbuf[128];
- static size_t cnt = 0; /* Internal counter to allow wbuf to wrap */
-
- unsigned int i;
-
- for (i = 0; i < size; i++) {
- if (str != NULL) {
- wbuf[cnt++] = str[i];
- }
- if (cnt >= sizeof(wbuf)) {
- (void)write(STDERR_FILENO, wbuf, cnt);
- cnt = 0;
- }
- }
-
- if (flush != 0) {
- (void)write(STDERR_FILENO, wbuf, cnt);
- cnt = 0;
- }
-}
-
-/*
- * Write summary to stderr according to format 'fmt'. If 'enable' is 0, it
- * will not attempt to write anything. Can be used to validate the
- * correctness of the 'fmt' string.
- */
-int
-dd_write_msg(const char *fmt, int enable)
-{
- char hbuf[7], nbuf[32];
- const char *ptr;
- int64_t mS;
- struct timeval tv;
-
- (void)gettimeofday(&tv, NULL);
- mS = tv2mS(tv) - tv2mS(st.start);
- if (mS == 0)
- mS = 1;
-
-#define ADDC(c) do { if (enable != 0) buffer_write(&c, 1, 0); } \
- while (/*CONSTCOND*/0)
-#define ADDS(p) do { if (enable != 0) buffer_write(p, strlen(p), 0); } \
- while (/*CONSTCOND*/0)
-
- for (ptr = fmt; *ptr; ptr++) {
- if (*ptr != '%') {
- ADDC(*ptr);
- continue;
- }
-
- switch (*++ptr) {
- case 'b':
- (void)snprintf(nbuf, sizeof(nbuf), "%llu",
- (unsigned long long)st.bytes);
- ADDS(nbuf);
- break;
- case 'B':
- if (humanize_number(hbuf, sizeof(hbuf),
- st.bytes, "B",
- HN_AUTOSCALE, HN_DECIMAL) == -1)
- warnx("humanize_number (bytes transferred)");
- ADDS(hbuf);
- break;
- case 'e':
- (void)snprintf(nbuf, sizeof(nbuf), "%llu",
- (unsigned long long) (st.bytes * 1000LL / mS));
- ADDS(nbuf);
- break;
- case 'E':
- if (humanize_number(hbuf, sizeof(hbuf),
- st.bytes * 1000LL / mS, "B",
- HN_AUTOSCALE, HN_DECIMAL) == -1)
- warnx("humanize_number (bytes per second)");
- ADDS(hbuf); ADDS("/sec");
- break;
- case 'i':
- (void)snprintf(nbuf, sizeof(nbuf), "%llu",
- (unsigned long long)st.in_part);
- ADDS(nbuf);
- break;
- case 'I':
- (void)snprintf(nbuf, sizeof(nbuf), "%llu",
- (unsigned long long)st.in_full);
- ADDS(nbuf);
- break;
- case 'o':
- (void)snprintf(nbuf, sizeof(nbuf), "%llu",
- (unsigned long long)st.out_part);
- ADDS(nbuf);
- break;
- case 'O':
- (void)snprintf(nbuf, sizeof(nbuf), "%llu",
- (unsigned long long)st.out_full);
- ADDS(nbuf);
- break;
- case 's':
- (void)snprintf(nbuf, sizeof(nbuf), "%li.%03d",
- (long) (mS / 1000), (int) (mS % 1000));
- ADDS(nbuf);
- break;
- case 'p':
- (void)snprintf(nbuf, sizeof(nbuf), "%llu",
- (unsigned long long)st.sparse);
- ADDS(nbuf);
- break;
- case 't':
- (void)snprintf(nbuf, sizeof(nbuf), "%llu",
- (unsigned long long)st.trunc);
- ADDS(nbuf);
- break;
- case 'w':
- (void)snprintf(nbuf, sizeof(nbuf), "%llu",
- (unsigned long long)st.swab);
- ADDS(nbuf);
- break;
- case 'P':
- ADDS("block");
- if (st.sparse != 1) ADDS("s");
- break;
- case 'T':
- ADDS("block");
- if (st.trunc != 1) ADDS("s");
- break;
- case 'W':
- ADDS("block");
- if (st.swab != 1) ADDS("s");
- break;
- case '%':
- ADDC(*ptr);
- break;
- default:
- if (*ptr == '\0')
- goto done;
- errx(EXIT_FAILURE, "unknown specifier '%c' in "
- "msgfmt string", *ptr);
- /* NOTREACHED */
- }
- }
-
-done:
- /* flush buffer */
- buffer_write(NULL, 0, 1);
- return 0;
-}
-
-static void
-custom_summary(void)
-{
-
- dd_write_msg(msgfmt, 1);
-}
-
-static void
-human_summary(void)
-{
- (void)dd_write_msg("%I+%i records in\n%O+%o records out\n", 1);
- if (st.swab) {
- (void)dd_write_msg("%w odd length swab %W\n", 1);
- }
- if (st.trunc) {
- (void)dd_write_msg("%t truncated %T\n", 1);
- }
- if (st.sparse) {
- (void)dd_write_msg("%p sparse output %P\n", 1);
- }
- (void)dd_write_msg("%b bytes (%B) transferred in %s secs "
- "(%e bytes/sec - %E)\n", 1);
-}
-
-static void
-quiet_summary(void)
-{
-
- /* stay quiet */
-}
-#endif /* NO_MSGFMT */
diff --git a/toolbox/upstream-netbsd/bin/dd/position.c b/toolbox/upstream-netbsd/bin/dd/position.c
deleted file mode 100644
index 36dd580..0000000
--- a/toolbox/upstream-netbsd/bin/dd/position.c
+++ /dev/null
@@ -1,185 +0,0 @@
-/* $NetBSD: position.c,v 1.18 2010/11/22 21:04:28 pooka Exp $ */
-
-/*-
- * Copyright (c) 1991, 1993, 1994
- * The Regents of the University of California. All rights reserved.
- *
- * This code is derived from software contributed to Berkeley by
- * Keith Muller of the University of California, San Diego and Lance
- * Visser of Convex Computer Corporation.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. Neither the name of the University nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#include <sys/cdefs.h>
-#ifndef lint
-#if 0
-static char sccsid[] = "@(#)position.c 8.3 (Berkeley) 4/2/94";
-#else
-__RCSID("$NetBSD: position.c,v 1.18 2010/11/22 21:04:28 pooka Exp $");
-#endif
-#endif /* not lint */
-
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <sys/ioctl.h>
-#include <sys/mtio.h>
-#include <sys/time.h>
-
-#include <err.h>
-#include <errno.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-#include "dd.h"
-#include "extern.h"
-
-/*
- * Position input/output data streams before starting the copy. Device type
- * dependent. Seekable devices use lseek, and the rest position by reading.
- * Seeking past the end of file can cause null blocks to be written to the
- * output.
- */
-void
-pos_in(void)
-{
- int bcnt, cnt, nr, warned;
-
- /* If not a pipe or tape device, try to seek on it. */
- if (!(in.flags & (ISPIPE|ISTAPE))) {
- if (ddop_lseek(in, in.fd,
- (off_t)in.offset * (off_t)in.dbsz, SEEK_CUR) == -1) {
- err(EXIT_FAILURE, "%s", in.name);
- /* NOTREACHED */
- }
- return;
- /* NOTREACHED */
- }
-
- /*
- * Read the data. If a pipe, read until satisfy the number of bytes
- * being skipped. No differentiation for reading complete and partial
- * blocks for other devices.
- */
- for (bcnt = in.dbsz, cnt = in.offset, warned = 0; cnt;) {
- if ((nr = ddop_read(in, in.fd, in.db, bcnt)) > 0) {
- if (in.flags & ISPIPE) {
- if (!(bcnt -= nr)) {
- bcnt = in.dbsz;
- --cnt;
- }
- } else
- --cnt;
- continue;
- }
-
- if (nr == 0) {
- if (files_cnt > 1) {
- --files_cnt;
- continue;
- }
- errx(EXIT_FAILURE, "skip reached end of input");
- /* NOTREACHED */
- }
-
- /*
- * Input error -- either EOF with no more files, or I/O error.
- * If noerror not set die. POSIX requires that the warning
- * message be followed by an I/O display.
- */
- if (ddflags & C_NOERROR) {
- if (!warned) {
-
- warn("%s", in.name);
- warned = 1;
- summary();
- }
- continue;
- }
- err(EXIT_FAILURE, "%s", in.name);
- /* NOTREACHED */
- }
-}
-
-void
-pos_out(void)
-{
- struct mtop t_op;
- int n;
- uint64_t cnt;
-
- /*
- * If not a tape, try seeking on the file. Seeking on a pipe is
- * going to fail, but don't protect the user -- they shouldn't
- * have specified the seek operand.
- */
- if (!(out.flags & ISTAPE)) {
- if (ddop_lseek(out, out.fd,
- (off_t)out.offset * (off_t)out.dbsz, SEEK_SET) == -1)
- err(EXIT_FAILURE, "%s", out.name);
- /* NOTREACHED */
- return;
- }
-
- /* If no read access, try using mtio. */
- if (out.flags & NOREAD) {
- t_op.mt_op = MTFSR;
- t_op.mt_count = out.offset;
-
- if (ddop_ioctl(out, out.fd, MTIOCTOP, &t_op) < 0)
- err(EXIT_FAILURE, "%s", out.name);
- /* NOTREACHED */
- return;
- }
-
- /* Read it. */
- for (cnt = 0; cnt < out.offset; ++cnt) {
- if ((n = ddop_read(out, out.fd, out.db, out.dbsz)) > 0)
- continue;
-
- if (n < 0)
- err(EXIT_FAILURE, "%s", out.name);
- /* NOTREACHED */
-
- /*
- * If reach EOF, fill with NUL characters; first, back up over
- * the EOF mark. Note, cnt has not yet been incremented, so
- * the EOF read does not count as a seek'd block.
- */
- t_op.mt_op = MTBSR;
- t_op.mt_count = 1;
- if (ddop_ioctl(out, out.fd, MTIOCTOP, &t_op) == -1)
- err(EXIT_FAILURE, "%s", out.name);
- /* NOTREACHED */
-
- while (cnt++ < out.offset)
- if ((uint64_t)(n = bwrite(&out,
- out.db, out.dbsz)) != out.dbsz)
- err(EXIT_FAILURE, "%s", out.name);
- /* NOTREACHED */
- break;
- }
-}
diff --git a/toolbox/upstream-netbsd/include/namespace.h b/toolbox/upstream-netbsd/include/namespace.h
deleted file mode 100644
index e69de29..0000000
--- a/toolbox/upstream-netbsd/include/namespace.h
+++ /dev/null
diff --git a/toolbox/upstream-netbsd/include/sys/extattr.h b/toolbox/upstream-netbsd/include/sys/extattr.h
deleted file mode 100644
index e69de29..0000000
--- a/toolbox/upstream-netbsd/include/sys/extattr.h
+++ /dev/null
diff --git a/toolbox/upstream-netbsd/include/util.h b/toolbox/upstream-netbsd/include/util.h
deleted file mode 100644
index e69de29..0000000
--- a/toolbox/upstream-netbsd/include/util.h
+++ /dev/null
diff --git a/toolbox/upstream-netbsd/lib/libc/gen/getbsize.c b/toolbox/upstream-netbsd/lib/libc/gen/getbsize.c
deleted file mode 100644
index a9ce2c1..0000000
--- a/toolbox/upstream-netbsd/lib/libc/gen/getbsize.c
+++ /dev/null
@@ -1,118 +0,0 @@
-/* $NetBSD: getbsize.c,v 1.17 2012/06/25 22:32:43 abs Exp $ */
-
-/*-
- * Copyright (c) 1991, 1993
- * The Regents of the University of California. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. Neither the name of the University nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#include <sys/cdefs.h>
-#if defined(LIBC_SCCS) && !defined(lint)
-#if 0
-static char sccsid[] = "@(#)getbsize.c 8.1 (Berkeley) 6/4/93";
-#else
-__RCSID("$NetBSD: getbsize.c,v 1.17 2012/06/25 22:32:43 abs Exp $");
-#endif
-#endif /* not lint */
-
-#include "namespace.h"
-
-#include <assert.h>
-#include <err.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#ifdef __weak_alias
-__weak_alias(getbsize,_getbsize)
-#endif
-
-char *
-getbsize(int *headerlenp, long *blocksizep)
-{
- static char header[20];
- long n, max, mul, blocksize;
- char *ep, *p;
- const char *form;
-
-#define KB (1024L)
-#define MB (1024L * 1024L)
-#define GB (1024L * 1024L * 1024L)
-#define MAXB GB /* No tera, peta, nor exa. */
- form = "";
- if ((p = getenv("BLOCKSIZE")) != NULL && *p != '\0') {
- if ((n = strtol(p, &ep, 10)) < 0)
- goto underflow;
- if (n == 0)
- n = 1;
- if (*ep && ep[1])
- goto fmterr;
- switch (*ep) {
- case 'G': case 'g':
- form = "G";
- max = MAXB / GB;
- mul = GB;
- break;
- case 'K': case 'k':
- form = "K";
- max = MAXB / KB;
- mul = KB;
- break;
- case 'M': case 'm':
- form = "M";
- max = MAXB / MB;
- mul = MB;
- break;
- case '\0':
- max = MAXB;
- mul = 1;
- break;
- default:
-fmterr: warnx("%s: unknown blocksize", p);
- n = 512;
- mul = 1;
- max = 0;
- break;
- }
- if (n > max) {
- warnx("maximum blocksize is %ldG", MAXB / GB);
- n = max;
- }
- if ((blocksize = n * mul) < 512) {
-underflow: warnx("%s: minimum blocksize is 512", p);
- form = "";
- blocksize = n = 512;
- }
- } else
- blocksize = n = 512;
-
- if (headerlenp)
- *headerlenp =
- snprintf(header, sizeof(header), "%ld%s-blocks", n, form);
- if (blocksizep)
- *blocksizep = blocksize;
- return (header);
-}
diff --git a/toolbox/upstream-netbsd/lib/libc/gen/humanize_number.c b/toolbox/upstream-netbsd/lib/libc/gen/humanize_number.c
deleted file mode 100644
index 533560f..0000000
--- a/toolbox/upstream-netbsd/lib/libc/gen/humanize_number.c
+++ /dev/null
@@ -1,161 +0,0 @@
-/* $NetBSD: humanize_number.c,v 1.16 2012/03/17 20:01:14 christos Exp $ */
-
-/*
- * Copyright (c) 1997, 1998, 1999, 2002 The NetBSD Foundation, Inc.
- * All rights reserved.
- *
- * This code is derived from software contributed to The NetBSD Foundation
- * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility,
- * NASA Ames Research Center, by Luke Mewburn and by Tomas Svensson.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
- * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
- * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
- * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include <sys/cdefs.h>
-#if defined(LIBC_SCCS) && !defined(lint)
-__RCSID("$NetBSD: humanize_number.c,v 1.16 2012/03/17 20:01:14 christos Exp $");
-#endif /* LIBC_SCCS and not lint */
-
-#include "namespace.h"
-#include <assert.h>
-#include <inttypes.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <locale.h>
-
-int
-humanize_number(char *buf, size_t len, int64_t bytes,
- const char *suffix, int scale, int flags)
-{
- const char *prefixes, *sep;
- int b, r, s1, s2, sign;
- int64_t divisor, max, post = 1;
- size_t i, baselen, maxscale;
-
- _DIAGASSERT(buf != NULL);
- _DIAGASSERT(suffix != NULL);
- _DIAGASSERT(scale >= 0);
-
- if (flags & HN_DIVISOR_1000) {
- /* SI for decimal multiplies */
- divisor = 1000;
- if (flags & HN_B)
- prefixes = "B\0k\0M\0G\0T\0P\0E";
- else
- prefixes = "\0\0k\0M\0G\0T\0P\0E";
- } else {
- /*
- * binary multiplies
- * XXX IEC 60027-2 recommends Ki, Mi, Gi...
- */
- divisor = 1024;
- if (flags & HN_B)
- prefixes = "B\0K\0M\0G\0T\0P\0E";
- else
- prefixes = "\0\0K\0M\0G\0T\0P\0E";
- }
-
-#define SCALE2PREFIX(scale) (&prefixes[(scale) << 1])
- maxscale = 7;
-
- if ((size_t)scale >= maxscale &&
- (scale & (HN_AUTOSCALE | HN_GETSCALE)) == 0)
- return (-1);
-
- if (buf == NULL || suffix == NULL)
- return (-1);
-
- if (len > 0)
- buf[0] = '\0';
- if (bytes < 0) {
- sign = -1;
- baselen = 3; /* sign, digit, prefix */
- if (-bytes < INT64_MAX / 100)
- bytes *= -100;
- else {
- bytes = -bytes;
- post = 100;
- baselen += 2;
- }
- } else {
- sign = 1;
- baselen = 2; /* digit, prefix */
- if (bytes < INT64_MAX / 100)
- bytes *= 100;
- else {
- post = 100;
- baselen += 2;
- }
- }
- if (flags & HN_NOSPACE)
- sep = "";
- else {
- sep = " ";
- baselen++;
- }
- baselen += strlen(suffix);
-
- /* Check if enough room for `x y' + suffix + `\0' */
- if (len < baselen + 1)
- return (-1);
-
- if (scale & (HN_AUTOSCALE | HN_GETSCALE)) {
- /* See if there is additional columns can be used. */
- for (max = 100, i = len - baselen; i-- > 0;)
- max *= 10;
-
- /*
- * Divide the number until it fits the given column.
- * If there will be an overflow by the rounding below,
- * divide once more.
- */
- for (i = 0; bytes >= max - 50 && i < maxscale; i++)
- bytes /= divisor;
-
- if (scale & HN_GETSCALE) {
- _DIAGASSERT(__type_fit(int, i));
- return (int)i;
- }
- } else
- for (i = 0; i < (size_t)scale && i < maxscale; i++)
- bytes /= divisor;
- bytes *= post;
-
- /* If a value <= 9.9 after rounding and ... */
- if (bytes < 995 && i > 0 && flags & HN_DECIMAL) {
- /* baselen + \0 + .N */
- if (len < baselen + 1 + 2)
- return (-1);
- b = ((int)bytes + 5) / 10;
- s1 = b / 10;
- s2 = b % 10;
- r = snprintf(buf, len, "%d%s%d%s%s%s",
- sign * s1, localeconv()->decimal_point, s2,
- sep, SCALE2PREFIX(i), suffix);
- } else
- r = snprintf(buf, len, "%" PRId64 "%s%s%s",
- sign * ((bytes + 50) / 100),
- sep, SCALE2PREFIX(i), suffix);
-
- return (r);
-}
diff --git a/toolbox/upstream-netbsd/lib/libc/stdlib/strsuftoll.c b/toolbox/upstream-netbsd/lib/libc/stdlib/strsuftoll.c
deleted file mode 100644
index 80fc52f..0000000
--- a/toolbox/upstream-netbsd/lib/libc/stdlib/strsuftoll.c
+++ /dev/null
@@ -1,249 +0,0 @@
-/* $NetBSD: strsuftoll.c,v 1.9 2011/10/22 22:08:47 christos Exp $ */
-/*-
- * Copyright (c) 2001-2002,2004 The NetBSD Foundation, Inc.
- * All rights reserved.
- *
- * This code is derived from software contributed to The NetBSD Foundation
- * by Luke Mewburn.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
- * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
- * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
- * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
-/*-
- * Copyright (c) 1991, 1993, 1994
- * The Regents of the University of California. All rights reserved.
- *
- * This code is derived from software contributed to Berkeley by
- * Keith Muller of the University of California, San Diego and Lance
- * Visser of Convex Computer Corporation.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. Neither the name of the University nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#if HAVE_NBTOOL_CONFIG_H
-#include "nbtool_config.h"
-#endif
-
-#include <sys/cdefs.h>
-
-#if defined(LIBC_SCCS) && !defined(lint)
-__RCSID("$NetBSD: strsuftoll.c,v 1.9 2011/10/22 22:08:47 christos Exp $");
-#endif /* LIBC_SCCS and not lint */
-
-#ifdef _LIBC
-#include "namespace.h"
-#endif
-
-#if !HAVE_STRSUFTOLL
-
-#include <sys/types.h>
-#include <sys/time.h>
-
-#include <assert.h>
-#include <ctype.h>
-#include <err.h>
-#include <errno.h>
-#include <limits.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#ifdef _LIBC
-# ifdef __weak_alias
-__weak_alias(strsuftoll, _strsuftoll)
-__weak_alias(strsuftollx, _strsuftollx)
-# endif
-#endif /* LIBC */
-
-/*
- * Convert an expression of the following forms to a (u)int64_t.
- * 1) A positive decimal number.
- * 2) A positive decimal number followed by a b (mult by 512).
- * 3) A positive decimal number followed by a k (mult by 1024).
- * 4) A positive decimal number followed by a m (mult by 1048576).
- * 5) A positive decimal number followed by a g (mult by 1073741824).
- * 6) A positive decimal number followed by a t (mult by 1099511627776).
- * 7) A positive decimal number followed by a w (mult by sizeof int)
- * 8) Two or more positive decimal numbers (with/without k,b or w).
- * separated by x (also * for backwards compatibility), specifying
- * the product of the indicated values.
- * Returns the result upon successful conversion, or exits with an
- * appropriate error.
- *
- */
-/* LONGLONG */
-long long
-strsuftoll(const char *desc, const char *val,
- long long min, long long max)
-{
- long long result;
- char errbuf[100];
-
- result = strsuftollx(desc, val, min, max, errbuf, sizeof(errbuf));
- if (*errbuf != '\0')
- errx(EXIT_FAILURE, "%s", errbuf);
- return result;
-}
-
-/*
- * As strsuftoll(), but returns the error message into the provided buffer
- * rather than exiting with it.
- */
-/* LONGLONG */
-static long long
-__strsuftollx(const char *desc, const char *val,
- long long min, long long max, char *ebuf, size_t ebuflen, size_t depth)
-{
- long long num, t;
- char *expr;
-
- _DIAGASSERT(desc != NULL);
- _DIAGASSERT(val != NULL);
- _DIAGASSERT(ebuf != NULL);
-
- if (depth > 16) {
- snprintf(ebuf, ebuflen, "%s: Recursion limit exceeded", desc);
- return 0;
- }
-
- while (isspace((unsigned char)*val)) /* Skip leading space */
- val++;
-
- errno = 0;
- num = strtoll(val, &expr, 10);
- if (errno == ERANGE)
- goto erange; /* Overflow */
-
- if (expr == val) /* No digits */
- goto badnum;
-
- switch (*expr) {
- case 'b':
- t = num;
- num *= 512; /* 1 block */
- if (t > num)
- goto erange;
- ++expr;
- break;
- case 'k':
- t = num;
- num *= 1024; /* 1 kibibyte */
- if (t > num)
- goto erange;
- ++expr;
- break;
- case 'm':
- t = num;
- num *= 1048576; /* 1 mebibyte */
- if (t > num)
- goto erange;
- ++expr;
- break;
- case 'g':
- t = num;
- num *= 1073741824; /* 1 gibibyte */
- if (t > num)
- goto erange;
- ++expr;
- break;
- case 't':
- t = num;
- num *= 1099511627776LL; /* 1 tebibyte */
- if (t > num)
- goto erange;
- ++expr;
- break;
- case 'w':
- t = num;
- num *= sizeof(int); /* 1 word */
- if (t > num)
- goto erange;
- ++expr;
- break;
- }
-
- switch (*expr) {
- case '\0':
- break;
- case '*': /* Backward compatible */
- case 'x':
- t = num;
- num *= __strsuftollx(desc, expr + 1, min, max, ebuf, ebuflen,
- depth + 1);
- if (*ebuf != '\0')
- return 0;
- if (t > num) {
- erange:
- errno = ERANGE;
- snprintf(ebuf, ebuflen, "%s: %s", desc, strerror(errno));
- return 0;
- }
- break;
- default:
- badnum:
- snprintf(ebuf, ebuflen, "%s `%s': illegal number", desc, val);
- return 0;
- }
- if (num < min) {
- /* LONGLONG */
- snprintf(ebuf, ebuflen, "%s %lld is less than %lld.",
- desc, (long long)num, (long long)min);
- return 0;
- }
- if (num > max) {
- /* LONGLONG */
- snprintf(ebuf, ebuflen, "%s %lld is greater than %lld.",
- desc, (long long)num, (long long)max);
- return 0;
- }
- *ebuf = '\0';
- return num;
-}
-
-long long
-strsuftollx(const char *desc, const char *val,
- long long min, long long max, char *ebuf, size_t ebuflen)
-{
- return __strsuftollx(desc, val, min, max, ebuf, ebuflen, 0);
-}
-#endif /* !HAVE_STRSUFTOLL */
diff --git a/toolbox/upstream-netbsd/lib/libc/string/swab.c b/toolbox/upstream-netbsd/lib/libc/string/swab.c
deleted file mode 100644
index 392b186..0000000
--- a/toolbox/upstream-netbsd/lib/libc/string/swab.c
+++ /dev/null
@@ -1,80 +0,0 @@
-/* $NetBSD: swab.c,v 1.18 2011/01/04 17:14:07 martin Exp $ */
-
-/*
- * Copyright (c) 1988, 1993
- * The Regents of the University of California. All rights reserved.
- *
- * This code is derived from software contributed to Berkeley by
- * Jeffrey Mogul.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. Neither the name of the University nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#include <sys/cdefs.h>
-#if defined(LIBC_SCCS) && !defined(lint)
-#if 0
-static char sccsid[] = "@(#)swab.c 8.1 (Berkeley) 6/4/93";
-#else
-__RCSID("$NetBSD: swab.c,v 1.18 2011/01/04 17:14:07 martin Exp $");
-#endif
-#endif /* LIBC_SCCS and not lint */
-
-#include <assert.h>
-#include <unistd.h>
-
-void
-swab(const void * __restrict from, void * __restrict to, ssize_t len)
-{
- char temp;
- const char *fp;
- char *tp;
-
- if (len <= 1)
- return;
-
- _DIAGASSERT(from != NULL);
- _DIAGASSERT(to != NULL);
-
- len /= 2;
- fp = (const char *)from;
- tp = (char *)to;
-#define STEP temp = *fp++,*tp++ = *fp++,*tp++ = temp
-
- if (__predict_false(len == 1)) {
- STEP;
- return;
- }
-
- /* round to multiple of 8 */
- while ((--len % 8) != 0)
- STEP;
- len /= 8;
- if (len == 0)
- return;
- while (len-- != 0) {
- STEP; STEP; STEP; STEP;
- STEP; STEP; STEP; STEP;
- }
-}
diff --git a/toolbox/upstream-netbsd/lib/libutil/raise_default_signal.c b/toolbox/upstream-netbsd/lib/libutil/raise_default_signal.c
deleted file mode 100644
index 50cffd4..0000000
--- a/toolbox/upstream-netbsd/lib/libutil/raise_default_signal.c
+++ /dev/null
@@ -1,117 +0,0 @@
-/* $NetBSD: raise_default_signal.c,v 1.3 2008/04/28 20:23:03 martin Exp $ */
-
-/*-
- * Copyright (c) 2007 The NetBSD Foundation, Inc.
- * All rights reserved.
- *
- * This code is derived from software contributed to The NetBSD Foundation
- * by Luke Mewburn.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
- * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
- * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
- * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
-
-#if HAVE_NBTOOL_CONFIG_H
-#include "nbtool_config.h"
-#endif
-
-#include <sys/cdefs.h>
-#if defined(LIBC_SCCS) && !defined(lint)
-__RCSID("$NetBSD: raise_default_signal.c,v 1.3 2008/04/28 20:23:03 martin Exp $");
-#endif
-
-#include <errno.h>
-#include <signal.h>
-#include <stdio.h>
-#include <string.h>
-#include <util.h>
-
-#if ! HAVE_RAISE_DEFAULT_SIGNAL
-/*
- * raise_default_signal sig
- * Raise the default signal handler for sig, by
- * - block all signals
- * - set the signal handler to SIG_DFL
- * - raise the signal
- * - unblock the signal to deliver it
- *
- * The original signal mask and signal handler is restored on exit
- * (whether successful or not).
- *
- * Returns 0 on success, or -1 on failure with errno set to
- * on of the values for sigemptyset(), sigaddset(), sigprocmask(),
- * sigaction(), or raise().
- */
-int
-raise_default_signal(int sig)
-{
- struct sigaction origact, act;
- sigset_t origmask, fullmask, mask;
- int retval, oerrno;
-
- retval = -1;
-
- /* Setup data structures */
- /* XXX memset(3) isn't async-safe according to signal(7) */
- (void)memset(&act, 0, sizeof(act));
- act.sa_handler = SIG_DFL;
- act.sa_flags = 0;
- if ((sigemptyset(&act.sa_mask) == -1) ||
- (sigfillset(&fullmask) == -1) ||
- (sigemptyset(&mask) == -1) ||
- (sigaddset(&mask, sig) == -1))
- goto restore_none;
-
- /* Block all signals */
- if (sigprocmask(SIG_BLOCK, &fullmask, &origmask) == -1)
- goto restore_none;
- /* (use 'goto restore_mask' to restore state) */
-
- /* Enable the SIG_DFL handler */
- if (sigaction(sig, &act, &origact) == -1)
- goto restore_mask;
- /* (use 'goto restore_act' to restore state) */
-
- /* Raise the signal, and unblock the signal to deliver it */
- if ((raise(sig) == -1) ||
- (sigprocmask(SIG_UNBLOCK, &mask, NULL) == -1))
- goto restore_act;
-
- /* Flag successful raise() */
- retval = 0;
-
- /* Restore the original handler */
- restore_act:
- oerrno = errno;
- (void)sigaction(sig, &origact, NULL);
- errno = oerrno;
-
- /* Restore the original mask */
- restore_mask:
- oerrno = errno;
- (void)sigprocmask(SIG_SETMASK, &origmask, NULL);
- errno = oerrno;
-
- restore_none:
- return retval;
-}
-
-#endif /* ! HAVE_RAISE_DEFAULT_SIGNAL */
diff --git a/toolbox/upstream-netbsd/usr.bin/grep/fastgrep.c b/toolbox/upstream-netbsd/usr.bin/grep/fastgrep.c
deleted file mode 100644
index 2fcd864..0000000
--- a/toolbox/upstream-netbsd/usr.bin/grep/fastgrep.c
+++ /dev/null
@@ -1,336 +0,0 @@
-/* $OpenBSD: util.c,v 1.36 2007/10/02 17:59:18 otto Exp $ */
-/* $FreeBSD: head/usr.bin/grep/fastgrep.c 211496 2010-08-19 09:28:59Z des $ */
-
-/*-
- * Copyright (c) 1999 James Howard and Dag-Erling Coïdan Smørgrav
- * Copyright (C) 2008 Gabor Kovesdan <gabor@FreeBSD.org>
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-/*
- * XXX: This file is a speed up for grep to cover the defects of the
- * regex library. These optimizations should practically be implemented
- * there keeping this code clean. This is a future TODO, but for the
- * meantime, we need to use this workaround.
- */
-
-#if HAVE_NBTOOL_CONFIG_H
-#include "nbtool_config.h"
-#endif
-
-#include <sys/cdefs.h>
-__RCSID("$NetBSD: fastgrep.c,v 1.5 2011/04/18 03:27:40 joerg Exp $");
-
-#include <limits.h>
-#include <stdbool.h>
-#include <stdlib.h>
-#include <string.h>
-#include <wchar.h>
-#include <wctype.h>
-
-#include "grep.h"
-
-static inline int grep_cmp(const unsigned char *, const unsigned char *, size_t);
-static inline void grep_revstr(unsigned char *, int);
-
-void
-fgrepcomp(fastgrep_t *fg, const char *pat)
-{
- unsigned int i;
-
- /* Initialize. */
- fg->len = strlen(pat);
- fg->bol = false;
- fg->eol = false;
- fg->reversed = false;
-
- fg->pattern = (unsigned char *)grep_strdup(pat);
-
- /* Preprocess pattern. */
- for (i = 0; i <= UCHAR_MAX; i++)
- fg->qsBc[i] = fg->len;
- for (i = 1; i < fg->len; i++)
- fg->qsBc[fg->pattern[i]] = fg->len - i;
-}
-
-/*
- * Returns: -1 on failure, 0 on success
- */
-int
-fastcomp(fastgrep_t *fg, const char *pat)
-{
- unsigned int i;
- int firstHalfDot = -1;
- int firstLastHalfDot = -1;
- int hasDot = 0;
- int lastHalfDot = 0;
- int shiftPatternLen;
-
- /* Initialize. */
- fg->len = strlen(pat);
- fg->bol = false;
- fg->eol = false;
- fg->reversed = false;
- fg->word = wflag;
-
- /* Remove end-of-line character ('$'). */
- if (fg->len > 0 && pat[fg->len - 1] == '$') {
- fg->eol = true;
- fg->len--;
- }
-
- /* Remove beginning-of-line character ('^'). */
- if (pat[0] == '^') {
- fg->bol = true;
- fg->len--;
- pat++;
- }
-
- if (fg->len >= 14 &&
- memcmp(pat, "[[:<:]]", 7) == 0 &&
- memcmp(pat + fg->len - 7, "[[:>:]]", 7) == 0) {
- fg->len -= 14;
- pat += 7;
- /* Word boundary is handled separately in util.c */
- fg->word = true;
- }
-
- /*
- * pat has been adjusted earlier to not include '^', '$' or
- * the word match character classes at the beginning and ending
- * of the string respectively.
- */
- fg->pattern = grep_malloc(fg->len + 1);
- memcpy(fg->pattern, pat, fg->len);
- fg->pattern[fg->len] = '\0';
-
- /* Look for ways to cheat...er...avoid the full regex engine. */
- for (i = 0; i < fg->len; i++) {
- /* Can still cheat? */
- if (fg->pattern[i] == '.') {
- hasDot = i;
- if (i < fg->len / 2) {
- if (firstHalfDot < 0)
- /* Closest dot to the beginning */
- firstHalfDot = i;
- } else {
- /* Closest dot to the end of the pattern. */
- lastHalfDot = i;
- if (firstLastHalfDot < 0)
- firstLastHalfDot = i;
- }
- } else {
- /* Free memory and let others know this is empty. */
- free(fg->pattern);
- fg->pattern = NULL;
- return (-1);
- }
- }
-
- /*
- * Determine if a reverse search would be faster based on the placement
- * of the dots.
- */
- if ((!(lflag || cflag)) && ((!(fg->bol || fg->eol)) &&
- ((lastHalfDot) && ((firstHalfDot < 0) ||
- ((fg->len - (lastHalfDot + 1)) < (size_t)firstHalfDot)))) &&
- !oflag && !color) {
- fg->reversed = true;
- hasDot = fg->len - (firstHalfDot < 0 ?
- firstLastHalfDot : firstHalfDot) - 1;
- grep_revstr(fg->pattern, fg->len);
- }
-
- /*
- * Normal Quick Search would require a shift based on the position the
- * next character after the comparison is within the pattern. With
- * wildcards, the position of the last dot effects the maximum shift
- * distance.
- * The closer to the end the wild card is the slower the search. A
- * reverse version of this algorithm would be useful for wildcards near
- * the end of the string.
- *
- * Examples:
- * Pattern Max shift
- * ------- ---------
- * this 5
- * .his 4
- * t.is 3
- * th.s 2
- * thi. 1
- */
-
- /* Adjust the shift based on location of the last dot ('.'). */
- shiftPatternLen = fg->len - hasDot;
-
- /* Preprocess pattern. */
- for (i = 0; i <= (signed)UCHAR_MAX; i++)
- fg->qsBc[i] = shiftPatternLen;
- for (i = hasDot + 1; i < fg->len; i++) {
- fg->qsBc[fg->pattern[i]] = fg->len - i;
- }
-
- /*
- * Put pattern back to normal after pre-processing to allow for easy
- * comparisons later.
- */
- if (fg->reversed)
- grep_revstr(fg->pattern, fg->len);
-
- return (0);
-}
-
-int
-grep_search(fastgrep_t *fg, const unsigned char *data, size_t len, regmatch_t *pmatch)
-{
- unsigned int j;
- int ret = REG_NOMATCH;
-
- if (pmatch->rm_so == (ssize_t)len)
- return (ret);
-
- if (fg->bol && pmatch->rm_so != 0) {
- pmatch->rm_so = len;
- pmatch->rm_eo = len;
- return (ret);
- }
-
- /* No point in going farther if we do not have enough data. */
- if (len < fg->len)
- return (ret);
-
- /* Only try once at the beginning or ending of the line. */
- if (fg->bol || fg->eol) {
- /* Simple text comparison. */
- /* Verify data is >= pattern length before searching on it. */
- if (len >= fg->len) {
- /* Determine where in data to start search at. */
- j = fg->eol ? len - fg->len : 0;
- if (!((fg->bol && fg->eol) && (len != fg->len)))
- if (grep_cmp(fg->pattern, data + j,
- fg->len) == -1) {
- pmatch->rm_so = j;
- pmatch->rm_eo = j + fg->len;
- ret = 0;
- }
- }
- } else if (fg->reversed) {
- /* Quick Search algorithm. */
- j = len;
- do {
- if (grep_cmp(fg->pattern, data + j - fg->len,
- fg->len) == -1) {
- pmatch->rm_so = j - fg->len;
- pmatch->rm_eo = j;
- ret = 0;
- break;
- }
- /* Shift if within bounds, otherwise, we are done. */
- if (j == fg->len)
- break;
- j -= fg->qsBc[data[j - fg->len - 1]];
- } while (j >= fg->len);
- } else {
- /* Quick Search algorithm. */
- j = pmatch->rm_so;
- do {
- if (grep_cmp(fg->pattern, data + j, fg->len) == -1) {
- pmatch->rm_so = j;
- pmatch->rm_eo = j + fg->len;
- ret = 0;
- break;
- }
-
- /* Shift if within bounds, otherwise, we are done. */
- if (j + fg->len == len)
- break;
- else
- j += fg->qsBc[data[j + fg->len]];
- } while (j <= (len - fg->len));
- }
-
- return (ret);
-}
-
-/*
- * Returns: i >= 0 on failure (position that it failed)
- * -1 on success
- */
-static inline int
-grep_cmp(const unsigned char *pat, const unsigned char *data, size_t len)
-{
- size_t size;
- wchar_t *wdata, *wpat;
- unsigned int i;
-
- if (iflag) {
- if ((size = mbstowcs(NULL, (const char *)data, 0)) ==
- ((size_t) - 1))
- return (-1);
-
- wdata = grep_malloc(size * sizeof(wint_t));
-
- if (mbstowcs(wdata, (const char *)data, size) ==
- ((size_t) - 1))
- return (-1);
-
- if ((size = mbstowcs(NULL, (const char *)pat, 0)) ==
- ((size_t) - 1))
- return (-1);
-
- wpat = grep_malloc(size * sizeof(wint_t));
-
- if (mbstowcs(wpat, (const char *)pat, size) == ((size_t) - 1))
- return (-1);
- for (i = 0; i < len; i++) {
- if ((towlower(wpat[i]) == towlower(wdata[i])) ||
- ((grepbehave != GREP_FIXED) && wpat[i] == L'.'))
- continue;
- free(wpat);
- free(wdata);
- return (i);
- }
- } else {
- for (i = 0; i < len; i++) {
- if ((pat[i] == data[i]) || ((grepbehave != GREP_FIXED) &&
- pat[i] == '.'))
- continue;
- return (i);
- }
- }
- return (-1);
-}
-
-static inline void
-grep_revstr(unsigned char *str, int len)
-{
- int i;
- char c;
-
- for (i = 0; i < len / 2; i++) {
- c = str[i];
- str[i] = str[len - i - 1];
- str[len - i - 1] = c;
- }
-}
diff --git a/toolbox/upstream-netbsd/usr.bin/grep/file.c b/toolbox/upstream-netbsd/usr.bin/grep/file.c
deleted file mode 100644
index cf4a0fa..0000000
--- a/toolbox/upstream-netbsd/usr.bin/grep/file.c
+++ /dev/null
@@ -1,271 +0,0 @@
-/* $NetBSD: file.c,v 1.7 2011/04/18 22:46:48 joerg Exp $ */
-/* $FreeBSD: head/usr.bin/grep/file.c 211496 2010-08-19 09:28:59Z des $ */
-/* $OpenBSD: file.c,v 1.11 2010/07/02 20:48:48 nicm Exp $ */
-
-/*-
- * Copyright (c) 1999 James Howard and Dag-Erling Coïdan Smørgrav
- * Copyright (C) 2008-2010 Gabor Kovesdan <gabor@FreeBSD.org>
- * Copyright (C) 2010 Dimitry Andric <dimitry@andric.com>
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#if HAVE_NBTOOL_CONFIG_H
-#include "nbtool_config.h"
-#endif
-
-#include <sys/cdefs.h>
-__RCSID("$NetBSD: file.c,v 1.7 2011/04/18 22:46:48 joerg Exp $");
-
-#include <sys/param.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-
-#ifndef __ANDROID__
-#include <bzlib.h>
-#endif
-#include <err.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <stddef.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include <wchar.h>
-#include <wctype.h>
-#ifndef __ANDROID__
-#include <zlib.h>
-#endif
-
-#include "grep.h"
-
-#define MAXBUFSIZ (32 * 1024)
-#define LNBUFBUMP 80
-
-#ifndef __ANDROID__
-static gzFile gzbufdesc;
-static BZFILE* bzbufdesc;
-#endif
-
-static unsigned char buffer[MAXBUFSIZ];
-static unsigned char *bufpos;
-static size_t bufrem;
-
-static unsigned char *lnbuf;
-static size_t lnbuflen;
-
-static inline int
-grep_refill(struct file *f)
-{
- ssize_t nr;
-#ifndef __ANDROID__
- int bzerr;
-#endif
-
- bufpos = buffer;
- bufrem = 0;
-
-#ifndef __ANDROID__
- if (filebehave == FILE_GZIP)
- nr = gzread(gzbufdesc, buffer, MAXBUFSIZ);
- else if (filebehave == FILE_BZIP && bzbufdesc != NULL) {
- nr = BZ2_bzRead(&bzerr, bzbufdesc, buffer, MAXBUFSIZ);
- switch (bzerr) {
- case BZ_OK:
- case BZ_STREAM_END:
- /* No problem, nr will be okay */
- break;
- case BZ_DATA_ERROR_MAGIC:
- /*
- * As opposed to gzread(), which simply returns the
- * plain file data, if it is not in the correct
- * compressed format, BZ2_bzRead() instead aborts.
- *
- * So, just restart at the beginning of the file again,
- * and use plain reads from now on.
- */
- BZ2_bzReadClose(&bzerr, bzbufdesc);
- bzbufdesc = NULL;
- if (lseek(f->fd, 0, SEEK_SET) == -1)
- return (-1);
- nr = read(f->fd, buffer, MAXBUFSIZ);
- break;
- default:
- /* Make sure we exit with an error */
- nr = -1;
- }
- } else
-#endif
- nr = read(f->fd, buffer, MAXBUFSIZ);
-
- if (nr < 0)
- return (-1);
-
- bufrem = nr;
- return (0);
-}
-
-static inline int
-grep_lnbufgrow(size_t newlen)
-{
-
- if (lnbuflen < newlen) {
- lnbuf = grep_realloc(lnbuf, newlen);
- lnbuflen = newlen;
- }
-
- return (0);
-}
-
-char *
-grep_fgetln(struct file *f, size_t *lenp)
-{
- unsigned char *p;
- char *ret;
- size_t len;
- size_t off;
- ptrdiff_t diff;
-
- /* Fill the buffer, if necessary */
- if (bufrem == 0 && grep_refill(f) != 0)
- goto error;
-
- if (bufrem == 0) {
- /* Return zero length to indicate EOF */
- *lenp = 0;
- return ((char *)bufpos);
- }
-
- /* Look for a newline in the remaining part of the buffer */
- if ((p = memchr(bufpos, line_sep, bufrem)) != NULL) {
- ++p; /* advance over newline */
- ret = (char *)bufpos;
- len = p - bufpos;
- bufrem -= len;
- bufpos = p;
- *lenp = len;
- return (ret);
- }
-
- /* We have to copy the current buffered data to the line buffer */
- for (len = bufrem, off = 0; ; len += bufrem) {
- /* Make sure there is room for more data */
- if (grep_lnbufgrow(len + LNBUFBUMP))
- goto error;
- memcpy(lnbuf + off, bufpos, len - off);
- off = len;
- if (grep_refill(f) != 0)
- goto error;
- if (bufrem == 0)
- /* EOF: return partial line */
- break;
- if ((p = memchr(bufpos, line_sep, bufrem)) == NULL)
- continue;
- /* got it: finish up the line (like code above) */
- ++p;
- diff = p - bufpos;
- len += diff;
- if (grep_lnbufgrow(len))
- goto error;
- memcpy(lnbuf + off, bufpos, diff);
- bufrem -= diff;
- bufpos = p;
- break;
- }
- *lenp = len;
- return ((char *)lnbuf);
-
-error:
- *lenp = 0;
- return (NULL);
-}
-
-static inline struct file *
-grep_file_init(struct file *f)
-{
-
-#ifndef __ANDROID__
- if (filebehave == FILE_GZIP &&
- (gzbufdesc = gzdopen(f->fd, "r")) == NULL)
- goto error;
-
- if (filebehave == FILE_BZIP &&
- (bzbufdesc = BZ2_bzdopen(f->fd, "r")) == NULL)
- goto error;
-#endif
-
- /* Fill read buffer, also catches errors early */
- if (grep_refill(f) != 0)
- goto error;
-
- /* Check for binary stuff, if necessary */
- if (!nulldataflag && binbehave != BINFILE_TEXT &&
- memchr(bufpos, '\0', bufrem) != NULL)
- f->binary = true;
-
- return (f);
-error:
- close(f->fd);
- free(f);
- return (NULL);
-}
-
-/*
- * Opens a file for processing.
- */
-struct file *
-grep_open(const char *path)
-{
- struct file *f;
-
- f = grep_malloc(sizeof *f);
- memset(f, 0, sizeof *f);
- if (path == NULL) {
- /* Processing stdin implies --line-buffered. */
- lbflag = true;
- f->fd = STDIN_FILENO;
- } else if ((f->fd = open(path, O_RDONLY)) == -1) {
- free(f);
- return (NULL);
- }
-
- return (grep_file_init(f));
-}
-
-/*
- * Closes a file.
- */
-void
-grep_close(struct file *f)
-{
-
- close(f->fd);
-
- /* Reset read buffer and line buffer */
- bufpos = buffer;
- bufrem = 0;
-
- free(lnbuf);
- lnbuf = NULL;
- lnbuflen = 0;
-}
diff --git a/toolbox/upstream-netbsd/usr.bin/grep/grep.c b/toolbox/upstream-netbsd/usr.bin/grep/grep.c
deleted file mode 100644
index 1ea6ed3..0000000
--- a/toolbox/upstream-netbsd/usr.bin/grep/grep.c
+++ /dev/null
@@ -1,707 +0,0 @@
-/* $NetBSD: grep.c,v 1.12 2014/07/11 16:30:45 christos Exp $ */
-/* $FreeBSD: head/usr.bin/grep/grep.c 211519 2010-08-19 22:55:17Z delphij $ */
-/* $OpenBSD: grep.c,v 1.42 2010/07/02 22:18:03 tedu Exp $ */
-
-/*-
- * Copyright (c) 1999 James Howard and Dag-Erling Coïdan Smørgrav
- * Copyright (C) 2008-2009 Gabor Kovesdan <gabor@FreeBSD.org>
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#if HAVE_NBTOOL_CONFIG_H
-#include "nbtool_config.h"
-#endif
-
-#include <sys/cdefs.h>
-__RCSID("$NetBSD: grep.c,v 1.12 2014/07/11 16:30:45 christos Exp $");
-
-#include <sys/stat.h>
-#include <sys/types.h>
-
-#include <ctype.h>
-#include <err.h>
-#include <errno.h>
-#include <getopt.h>
-#include <limits.h>
-#include <libgen.h>
-#include <locale.h>
-#include <stdbool.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-#include "grep.h"
-
-#ifndef WITHOUT_NLS
-#include <nl_types.h>
-nl_catd catalog;
-#endif
-
-/*
- * Default messags to use when NLS is disabled or no catalogue
- * is found.
- */
-const char *errstr[] = {
- "",
-/* 1*/ "(standard input)",
-/* 2*/ "cannot read bzip2 compressed file",
-/* 3*/ "unknown %s option",
-/* 4*/ "usage: %s [-abcDEFGHhIiJLlmnOoPqRSsUVvwxZz] [-A num] [-B num] [-C[num]]\n",
-/* 5*/ "\t[-e pattern] [-f file] [--binary-files=value] [--color=when]\n",
-/* 6*/ "\t[--context[=num]] [--directories=action] [--label] [--line-buffered]\n",
-/* 7*/ "\t[pattern] [file ...]\n",
-/* 8*/ "Binary file %s matches\n",
-/* 9*/ "%s (BSD grep) %s\n",
-};
-
-/* Flags passed to regcomp() and regexec() */
-int cflags = 0;
-int eflags = REG_STARTEND;
-
-/* Searching patterns */
-unsigned int patterns, pattern_sz;
-char **pattern;
-regex_t *r_pattern;
-fastgrep_t *fg_pattern;
-
-/* Filename exclusion/inclusion patterns */
-unsigned int fpatterns, fpattern_sz;
-unsigned int dpatterns, dpattern_sz;
-struct epat *dpattern, *fpattern;
-
-/* For regex errors */
-char re_error[RE_ERROR_BUF + 1];
-
-/* Command-line flags */
-unsigned long long Aflag; /* -A x: print x lines trailing each match */
-unsigned long long Bflag; /* -B x: print x lines leading each match */
-bool Hflag; /* -H: always print file name */
-bool Lflag; /* -L: only show names of files with no matches */
-bool bflag; /* -b: show block numbers for each match */
-bool cflag; /* -c: only show a count of matching lines */
-bool hflag; /* -h: don't print filename headers */
-bool iflag; /* -i: ignore case */
-bool lflag; /* -l: only show names of files with matches */
-bool mflag; /* -m x: stop reading the files after x matches */
-unsigned long long mcount; /* count for -m */
-bool nflag; /* -n: show line numbers in front of matching lines */
-bool oflag; /* -o: print only matching part */
-bool qflag; /* -q: quiet mode (don't output anything) */
-bool sflag; /* -s: silent mode (ignore errors) */
-bool vflag; /* -v: only show non-matching lines */
-bool wflag; /* -w: pattern must start and end on word boundaries */
-bool xflag; /* -x: pattern must match entire line */
-bool lbflag; /* --line-buffered */
-bool nullflag; /* --null */
-bool nulldataflag; /* --null-data */
-unsigned char line_sep = '\n'; /* 0 for --null-data */
-char *label; /* --label */
-const char *color; /* --color */
-int grepbehave = GREP_BASIC; /* -EFGP: type of the regex */
-int binbehave = BINFILE_BIN; /* -aIU: handling of binary files */
-int filebehave = FILE_STDIO; /* -JZ: normal, gzip or bzip2 file */
-int devbehave = DEV_READ; /* -D: handling of devices */
-int dirbehave = DIR_READ; /* -dRr: handling of directories */
-int linkbehave = LINK_READ; /* -OpS: handling of symlinks */
-
-bool dexclude, dinclude; /* --exclude-dir and --include-dir */
-bool fexclude, finclude; /* --exclude and --include */
-
-enum {
- BIN_OPT = CHAR_MAX + 1,
- COLOR_OPT,
- DECOMPRESS_OPT,
- HELP_OPT,
- MMAP_OPT,
- LINEBUF_OPT,
- LABEL_OPT,
- R_EXCLUDE_OPT,
- R_INCLUDE_OPT,
- R_DEXCLUDE_OPT,
- R_DINCLUDE_OPT
-};
-
-static inline const char *init_color(const char *);
-
-/* Housekeeping */
-int tail; /* lines left to print */
-bool notfound; /* file not found */
-
-extern char *__progname;
-
-/*
- * Prints usage information and returns 2.
- */
-__dead static void
-usage(void)
-{
- fprintf(stderr, getstr(4), __progname);
- fprintf(stderr, "%s", getstr(5));
- fprintf(stderr, "%s", getstr(6));
- fprintf(stderr, "%s", getstr(7));
- exit(2);
-}
-
-static const char optstr[] =
- "0123456789A:B:C:D:EFGHIJLOPSRUVZabcd:e:f:hilm:nopqrsuvwxyz";
-
-struct option long_options[] =
-{
- {"binary-files", required_argument, NULL, BIN_OPT},
- {"decompress", no_argument, NULL, DECOMPRESS_OPT},
- {"help", no_argument, NULL, HELP_OPT},
- {"mmap", no_argument, NULL, MMAP_OPT},
- {"line-buffered", no_argument, NULL, LINEBUF_OPT},
- {"label", required_argument, NULL, LABEL_OPT},
- {"color", optional_argument, NULL, COLOR_OPT},
- {"colour", optional_argument, NULL, COLOR_OPT},
- {"exclude", required_argument, NULL, R_EXCLUDE_OPT},
- {"include", required_argument, NULL, R_INCLUDE_OPT},
- {"exclude-dir", required_argument, NULL, R_DEXCLUDE_OPT},
- {"include-dir", required_argument, NULL, R_DINCLUDE_OPT},
- {"after-context", required_argument, NULL, 'A'},
- {"text", no_argument, NULL, 'a'},
- {"before-context", required_argument, NULL, 'B'},
- {"byte-offset", no_argument, NULL, 'b'},
- {"context", optional_argument, NULL, 'C'},
- {"count", no_argument, NULL, 'c'},
- {"devices", required_argument, NULL, 'D'},
- {"directories", required_argument, NULL, 'd'},
- {"extended-regexp", no_argument, NULL, 'E'},
- {"regexp", required_argument, NULL, 'e'},
- {"fixed-strings", no_argument, NULL, 'F'},
- {"file", required_argument, NULL, 'f'},
- {"basic-regexp", no_argument, NULL, 'G'},
- {"no-filename", no_argument, NULL, 'h'},
- {"with-filename", no_argument, NULL, 'H'},
- {"ignore-case", no_argument, NULL, 'i'},
- {"bz2decompress", no_argument, NULL, 'J'},
- {"files-with-matches", no_argument, NULL, 'l'},
- {"files-without-match", no_argument, NULL, 'L'},
- {"max-count", required_argument, NULL, 'm'},
- {"line-number", no_argument, NULL, 'n'},
- {"only-matching", no_argument, NULL, 'o'},
- {"quiet", no_argument, NULL, 'q'},
- {"silent", no_argument, NULL, 'q'},
- {"recursive", no_argument, NULL, 'r'},
- {"no-messages", no_argument, NULL, 's'},
- {"binary", no_argument, NULL, 'U'},
- {"unix-byte-offsets", no_argument, NULL, 'u'},
- {"invert-match", no_argument, NULL, 'v'},
- {"version", no_argument, NULL, 'V'},
- {"word-regexp", no_argument, NULL, 'w'},
- {"line-regexp", no_argument, NULL, 'x'},
- {"null", no_argument, NULL, 'Z'},
- {"null-data", no_argument, NULL, 'z'},
- {NULL, no_argument, NULL, 0}
-};
-
-/*
- * Adds a searching pattern to the internal array.
- */
-static void
-add_pattern(char *pat, size_t len)
-{
-
- /* TODO: Check for empty patterns and shortcut */
-
- /* Increase size if necessary */
- if (patterns == pattern_sz) {
- pattern_sz *= 2;
- pattern = grep_realloc(pattern, ++pattern_sz *
- sizeof(*pattern));
- }
- if (len > 0 && pat[len - 1] == '\n')
- --len;
- /* pat may not be NUL-terminated */
- pattern[patterns] = grep_malloc(len + 1);
- memcpy(pattern[patterns], pat, len);
- pattern[patterns][len] = '\0';
- ++patterns;
-}
-
-/*
- * Adds a file include/exclude pattern to the internal array.
- */
-static void
-add_fpattern(const char *pat, int mode)
-{
-
- /* Increase size if necessary */
- if (fpatterns == fpattern_sz) {
- fpattern_sz *= 2;
- fpattern = grep_realloc(fpattern, ++fpattern_sz *
- sizeof(struct epat));
- }
- fpattern[fpatterns].pat = grep_strdup(pat);
- fpattern[fpatterns].mode = mode;
- ++fpatterns;
-}
-
-/*
- * Adds a directory include/exclude pattern to the internal array.
- */
-static void
-add_dpattern(const char *pat, int mode)
-{
-
- /* Increase size if necessary */
- if (dpatterns == dpattern_sz) {
- dpattern_sz *= 2;
- dpattern = grep_realloc(dpattern, ++dpattern_sz *
- sizeof(struct epat));
- }
- dpattern[dpatterns].pat = grep_strdup(pat);
- dpattern[dpatterns].mode = mode;
- ++dpatterns;
-}
-
-/*
- * Reads searching patterns from a file and adds them with add_pattern().
- */
-static void
-read_patterns(const char *fn)
-{
- FILE *f;
- char *line;
- size_t len;
- ssize_t rlen;
-
- if ((f = fopen(fn, "r")) == NULL)
- err(2, "%s", fn);
- line = NULL;
- len = 0;
- while ((rlen = getline(&line, &len, f)) != -1)
- add_pattern(line, *line == '\n' ? 0 : (size_t)rlen);
- free(line);
- if (ferror(f))
- err(2, "%s", fn);
- fclose(f);
-}
-
-static inline const char *
-init_color(const char *d)
-{
- char *c;
-
- c = getenv("GREP_COLOR");
- return (c != NULL ? c : d);
-}
-
-int
-main(int argc, char *argv[])
-{
- char **aargv, **eargv, *eopts;
- char *ep;
- unsigned long long l;
- unsigned int aargc, eargc, i, j;
- int c, lastc, needpattern, newarg, prevoptind;
-
- setlocale(LC_ALL, "");
-
-#ifndef WITHOUT_NLS
- catalog = catopen("grep", NL_CAT_LOCALE);
-#endif
-
- /* Check what is the program name of the binary. In this
- way we can have all the funcionalities in one binary
- without the need of scripting and using ugly hacks. */
- switch (__progname[0]) {
- case 'e':
- grepbehave = GREP_EXTENDED;
- break;
- case 'f':
- grepbehave = GREP_FIXED;
- break;
- case 'g':
- grepbehave = GREP_BASIC;
- break;
- case 'z':
- filebehave = FILE_GZIP;
- switch(__progname[1]) {
- case 'e':
- grepbehave = GREP_EXTENDED;
- break;
- case 'f':
- grepbehave = GREP_FIXED;
- break;
- case 'g':
- grepbehave = GREP_BASIC;
- break;
- }
- break;
- }
-
- lastc = '\0';
- newarg = 1;
- prevoptind = 1;
- needpattern = 1;
-
- eopts = getenv("GREP_OPTIONS");
-
- /* support for extra arguments in GREP_OPTIONS */
- eargc = 0;
- if (eopts != NULL) {
- char *str;
-
- /* make an estimation of how many extra arguments we have */
- for (j = 0; j < strlen(eopts); j++)
- if (eopts[j] == ' ')
- eargc++;
-
- eargv = (char **)grep_malloc(sizeof(char *) * (eargc + 1));
-
- eargc = 0;
- /* parse extra arguments */
- while ((str = strsep(&eopts, " ")) != NULL)
- eargv[eargc++] = grep_strdup(str);
-
- aargv = (char **)grep_calloc(eargc + argc + 1,
- sizeof(char *));
-
- aargv[0] = argv[0];
- for (i = 0; i < eargc; i++)
- aargv[i + 1] = eargv[i];
- for (j = 1; j < (unsigned int)argc; j++, i++)
- aargv[i + 1] = argv[j];
-
- aargc = eargc + argc;
- } else {
- aargv = argv;
- aargc = argc;
- }
-
- while (((c = getopt_long(aargc, aargv, optstr, long_options, NULL)) !=
- -1)) {
- switch (c) {
- case '0': case '1': case '2': case '3': case '4':
- case '5': case '6': case '7': case '8': case '9':
- if (newarg || !isdigit(lastc))
- Aflag = 0;
- else if (Aflag > LLONG_MAX / 10) {
- errno = ERANGE;
- err(2, NULL);
- }
- Aflag = Bflag = (Aflag * 10) + (c - '0');
- break;
- case 'C':
- if (optarg == NULL) {
- Aflag = Bflag = 2;
- break;
- }
- /* FALLTHROUGH */
- case 'A':
- /* FALLTHROUGH */
- case 'B':
- errno = 0;
- l = strtoull(optarg, &ep, 10);
- if (((errno == ERANGE) && (l == ULLONG_MAX)) ||
- ((errno == EINVAL) && (l == 0)))
- err(2, NULL);
- else if (ep[0] != '\0') {
- errno = EINVAL;
- err(2, NULL);
- }
- if (c == 'A')
- Aflag = l;
- else if (c == 'B')
- Bflag = l;
- else
- Aflag = Bflag = l;
- break;
- case 'a':
- binbehave = BINFILE_TEXT;
- break;
- case 'b':
- bflag = true;
- break;
- case 'c':
- cflag = true;
- break;
- case 'D':
- if (strcasecmp(optarg, "skip") == 0)
- devbehave = DEV_SKIP;
- else if (strcasecmp(optarg, "read") == 0)
- devbehave = DEV_READ;
- else
- errx(2, getstr(3), "--devices");
- break;
- case 'd':
- if (strcasecmp("recurse", optarg) == 0) {
- Hflag = true;
- dirbehave = DIR_RECURSE;
- } else if (strcasecmp("skip", optarg) == 0)
- dirbehave = DIR_SKIP;
- else if (strcasecmp("read", optarg) == 0)
- dirbehave = DIR_READ;
- else
- errx(2, getstr(3), "--directories");
- break;
- case 'E':
- grepbehave = GREP_EXTENDED;
- break;
- case 'e':
- add_pattern(optarg, strlen(optarg));
- needpattern = 0;
- break;
- case 'F':
- grepbehave = GREP_FIXED;
- break;
- case 'f':
- read_patterns(optarg);
- needpattern = 0;
- break;
- case 'G':
- grepbehave = GREP_BASIC;
- break;
- case 'H':
- Hflag = true;
- break;
- case 'h':
- Hflag = false;
- hflag = true;
- break;
- case 'I':
- binbehave = BINFILE_SKIP;
- break;
- case 'i':
- case 'y':
- iflag = true;
- cflags |= REG_ICASE;
- break;
- case 'J':
- filebehave = FILE_BZIP;
- break;
- case 'L':
- lflag = false;
- Lflag = true;
- break;
- case 'l':
- Lflag = false;
- lflag = true;
- break;
- case 'm':
- mflag = true;
- errno = 0;
- mcount = strtoull(optarg, &ep, 10);
- if (((errno == ERANGE) && (mcount == ULLONG_MAX)) ||
- ((errno == EINVAL) && (mcount == 0)))
- err(2, NULL);
- else if (ep[0] != '\0') {
- errno = EINVAL;
- err(2, NULL);
- }
- break;
- case 'n':
- nflag = true;
- break;
- case 'O':
- linkbehave = LINK_EXPLICIT;
- break;
- case 'o':
- oflag = true;
- break;
- case 'p':
- linkbehave = LINK_SKIP;
- break;
- case 'q':
- qflag = true;
- break;
- case 'S':
- linkbehave = LINK_READ;
- break;
- case 'R':
- case 'r':
- dirbehave = DIR_RECURSE;
- Hflag = true;
- break;
- case 's':
- sflag = true;
- break;
- case 'U':
- binbehave = BINFILE_BIN;
- break;
- case 'u':
- case MMAP_OPT:
- /* noop, compatibility */
- break;
- case 'V':
- printf(getstr(9), __progname, VERSION);
- exit(0);
- case 'v':
- vflag = true;
- break;
- case 'w':
- wflag = true;
- break;
- case 'x':
- xflag = true;
- break;
- case 'Z':
- nullflag = true;
- break;
- case 'z':
- nulldataflag = true;
- line_sep = '\0';
- break;
- case BIN_OPT:
- if (strcasecmp("binary", optarg) == 0)
- binbehave = BINFILE_BIN;
- else if (strcasecmp("without-match", optarg) == 0)
- binbehave = BINFILE_SKIP;
- else if (strcasecmp("text", optarg) == 0)
- binbehave = BINFILE_TEXT;
- else
- errx(2, getstr(3), "--binary-files");
- break;
- case COLOR_OPT:
- color = NULL;
- if (optarg == NULL || strcasecmp("auto", optarg) == 0 ||
- strcasecmp("tty", optarg) == 0 ||
- strcasecmp("if-tty", optarg) == 0) {
- char *term;
-
- term = getenv("TERM");
- if (isatty(STDOUT_FILENO) && term != NULL &&
- strcasecmp(term, "dumb") != 0)
- color = init_color("01;31");
- } else if (strcasecmp("always", optarg) == 0 ||
- strcasecmp("yes", optarg) == 0 ||
- strcasecmp("force", optarg) == 0) {
- color = init_color("01;31");
- } else if (strcasecmp("never", optarg) != 0 &&
- strcasecmp("none", optarg) != 0 &&
- strcasecmp("no", optarg) != 0)
- errx(2, getstr(3), "--color");
- break;
- case DECOMPRESS_OPT:
- filebehave = FILE_GZIP;
- break;
- case LABEL_OPT:
- label = optarg;
- break;
- case LINEBUF_OPT:
- lbflag = true;
- break;
- case R_INCLUDE_OPT:
- finclude = true;
- add_fpattern(optarg, INCL_PAT);
- break;
- case R_EXCLUDE_OPT:
- fexclude = true;
- add_fpattern(optarg, EXCL_PAT);
- break;
- case R_DINCLUDE_OPT:
- dinclude = true;
- add_dpattern(optarg, INCL_PAT);
- break;
- case R_DEXCLUDE_OPT:
- dexclude = true;
- add_dpattern(optarg, EXCL_PAT);
- break;
- case HELP_OPT:
- default:
- usage();
- }
- lastc = c;
- newarg = optind != prevoptind;
- prevoptind = optind;
- }
- aargc -= optind;
- aargv += optind;
-
- /* Fail if we don't have any pattern */
- if (aargc == 0 && needpattern)
- usage();
-
- /* Process patterns from command line */
- if (aargc != 0 && needpattern) {
- add_pattern(*aargv, strlen(*aargv));
- --aargc;
- ++aargv;
- }
-
- switch (grepbehave) {
- case GREP_FIXED:
- case GREP_BASIC:
- break;
- case GREP_EXTENDED:
- cflags |= REG_EXTENDED;
- break;
- default:
- /* NOTREACHED */
- usage();
- }
-
- fg_pattern = grep_calloc(patterns, sizeof(*fg_pattern));
- r_pattern = grep_calloc(patterns, sizeof(*r_pattern));
-/*
- * XXX: fgrepcomp() and fastcomp() are workarounds for regexec() performance.
- * Optimizations should be done there.
- */
- /* Check if cheating is allowed (always is for fgrep). */
- if (grepbehave == GREP_FIXED) {
- for (i = 0; i < patterns; ++i)
- fgrepcomp(&fg_pattern[i], pattern[i]);
- } else {
- for (i = 0; i < patterns; ++i) {
- if (fastcomp(&fg_pattern[i], pattern[i])) {
- /* Fall back to full regex library */
- c = regcomp(&r_pattern[i], pattern[i], cflags);
- if (c != 0) {
- regerror(c, &r_pattern[i], re_error,
- RE_ERROR_BUF);
- errx(2, "%s", re_error);
- }
- }
- }
- }
-
- if (lbflag)
- setlinebuf(stdout);
-
- if ((aargc == 0 || aargc == 1) && !Hflag)
- hflag = true;
-
- if (aargc == 0)
- exit(!procfile("-"));
-
- if (dirbehave == DIR_RECURSE)
- c = grep_tree(aargv);
- else
- for (c = 0; aargc--; ++aargv) {
- if ((finclude || fexclude) && !file_matching(*aargv))
- continue;
- c+= procfile(*aargv);
- }
-
-#ifndef WITHOUT_NLS
- catclose(catalog);
-#endif
-
- /* Find out the correct return value according to the
- results and the command line option. */
- exit(c ? (notfound ? (qflag ? 0 : 2) : 0) : (notfound ? 2 : 1));
-}
diff --git a/toolbox/upstream-netbsd/usr.bin/grep/grep.h b/toolbox/upstream-netbsd/usr.bin/grep/grep.h
deleted file mode 100644
index fa2a3e3..0000000
--- a/toolbox/upstream-netbsd/usr.bin/grep/grep.h
+++ /dev/null
@@ -1,162 +0,0 @@
-/* $NetBSD: grep.h,v 1.8 2012/05/06 22:27:00 joerg Exp $ */
-/* $OpenBSD: grep.h,v 1.15 2010/04/05 03:03:55 tedu Exp $ */
-/* $FreeBSD: head/usr.bin/grep/grep.h 211496 2010-08-19 09:28:59Z des $ */
-
-/*-
- * Copyright (c) 1999 James Howard and Dag-Erling Coïdan Smørgrav
- * Copyright (c) 2008-2009 Gabor Kovesdan <gabor@FreeBSD.org>
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#ifndef __ANDROID__
-#include <bzlib.h>
-#endif
-#include <limits.h>
-#include <regex.h>
-#include <stdbool.h>
-#include <stdio.h>
-#ifndef __ANDROID__
-#include <zlib.h>
-#endif
-
-#ifdef WITHOUT_NLS
-#define getstr(n) errstr[n]
-#else
-#include <nl_types.h>
-
-extern nl_catd catalog;
-#define getstr(n) catgets(catalog, 1, n, errstr[n])
-#endif
-
-extern const char *errstr[];
-
-#define VERSION "2.5.1-FreeBSD"
-
-#define GREP_FIXED 0
-#define GREP_BASIC 1
-#define GREP_EXTENDED 2
-
-#define BINFILE_BIN 0
-#define BINFILE_SKIP 1
-#define BINFILE_TEXT 2
-
-#define FILE_STDIO 0
-#define FILE_GZIP 1
-#define FILE_BZIP 2
-
-#define DIR_READ 0
-#define DIR_SKIP 1
-#define DIR_RECURSE 2
-
-#define DEV_READ 0
-#define DEV_SKIP 1
-
-#define LINK_READ 0
-#define LINK_EXPLICIT 1
-#define LINK_SKIP 2
-
-#define EXCL_PAT 0
-#define INCL_PAT 1
-
-#define MAX_LINE_MATCHES 32
-
-struct file {
- int fd;
- bool binary;
-};
-
-struct str {
- off_t off;
- size_t len;
- char *dat;
- char *file;
- int line_no;
-};
-
-struct epat {
- char *pat;
- int mode;
-};
-
-typedef struct {
- size_t len;
- unsigned char *pattern;
- int qsBc[UCHAR_MAX + 1];
- /* flags */
- bool bol;
- bool eol;
- bool reversed;
- bool word;
-} fastgrep_t;
-
-/* Flags passed to regcomp() and regexec() */
-extern int cflags, eflags;
-
-/* Command line flags */
-extern bool Eflag, Fflag, Gflag, Hflag, Lflag,
- bflag, cflag, hflag, iflag, lflag, mflag, nflag, oflag,
- qflag, sflag, vflag, wflag, xflag;
-extern bool dexclude, dinclude, fexclude, finclude, lbflag, nullflag, nulldataflag;
-extern unsigned char line_sep;
-extern unsigned long long Aflag, Bflag, mcount;
-extern char *label;
-extern const char *color;
-extern int binbehave, devbehave, dirbehave, filebehave, grepbehave, linkbehave;
-
-extern bool notfound;
-extern int tail;
-extern unsigned int dpatterns, fpatterns, patterns;
-extern char **pattern;
-extern struct epat *dpattern, *fpattern;
-extern regex_t *er_pattern, *r_pattern;
-extern fastgrep_t *fg_pattern;
-
-/* For regex errors */
-#define RE_ERROR_BUF 512
-extern char re_error[RE_ERROR_BUF + 1]; /* Seems big enough */
-
-/* util.c */
-bool file_matching(const char *fname);
-int procfile(const char *fn);
-int grep_tree(char **argv);
-void *grep_malloc(size_t size);
-void *grep_calloc(size_t nmemb, size_t size);
-void *grep_realloc(void *ptr, size_t size);
-char *grep_strdup(const char *str);
-void printline(struct str *line, int sep, regmatch_t *matches, int m);
-
-/* queue.c */
-void enqueue(struct str *x);
-void printqueue(void);
-void clearqueue(void);
-
-/* file.c */
-void grep_close(struct file *f);
-struct file *grep_open(const char *path);
-char *grep_fgetln(struct file *f, size_t *len);
-
-/* fastgrep.c */
-int fastcomp(fastgrep_t *, const char *);
-void fgrepcomp(fastgrep_t *, const char *);
-int grep_search(fastgrep_t *, const unsigned char *, size_t, regmatch_t *);
diff --git a/toolbox/upstream-netbsd/usr.bin/grep/queue.c b/toolbox/upstream-netbsd/usr.bin/grep/queue.c
deleted file mode 100644
index e3c6be1..0000000
--- a/toolbox/upstream-netbsd/usr.bin/grep/queue.c
+++ /dev/null
@@ -1,116 +0,0 @@
-/* $NetBSD: queue.c,v 1.5 2011/08/31 16:24:57 plunky Exp $ */
-/* $FreeBSD: head/usr.bin/grep/queue.c 211496 2010-08-19 09:28:59Z des $ */
-/*-
- * Copyright (c) 1999 James Howard and Dag-Erling Coïdan Smørgrav
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-/*
- * A really poor man's queue. It does only what it has to and gets out of
- * Dodge. It is used in place of <sys/queue.h> to get a better performance.
- */
-
-#if HAVE_NBTOOL_CONFIG_H
-#include "nbtool_config.h"
-#endif
-
-#include <sys/cdefs.h>
-__RCSID("$NetBSD: queue.c,v 1.5 2011/08/31 16:24:57 plunky Exp $");
-
-#include <sys/param.h>
-#include <sys/queue.h>
-
-#include <stdlib.h>
-#include <string.h>
-
-#include "grep.h"
-
-struct qentry {
- STAILQ_ENTRY(qentry) list;
- struct str data;
-};
-
-static STAILQ_HEAD(, qentry) queue = STAILQ_HEAD_INITIALIZER(queue);
-static unsigned long long count;
-
-static struct qentry *dequeue(void);
-
-void
-enqueue(struct str *x)
-{
- struct qentry *item;
-
- item = grep_malloc(sizeof(struct qentry));
- item->data.dat = grep_malloc(sizeof(char) * x->len);
- item->data.len = x->len;
- item->data.line_no = x->line_no;
- item->data.off = x->off;
- memcpy(item->data.dat, x->dat, x->len);
- item->data.file = x->file;
-
- STAILQ_INSERT_TAIL(&queue, item, list);
-
- if (++count > Bflag) {
- item = dequeue();
- free(item->data.dat);
- free(item);
- }
-}
-
-static struct qentry *
-dequeue(void)
-{
- struct qentry *item;
-
- item = STAILQ_FIRST(&queue);
- if (item == NULL)
- return (NULL);
-
- STAILQ_REMOVE_HEAD(&queue, list);
- --count;
- return (item);
-}
-
-void
-printqueue(void)
-{
- struct qentry *item;
-
- while ((item = dequeue()) != NULL) {
- printline(&item->data, '-', NULL, 0);
- free(item->data.dat);
- free(item);
- }
-}
-
-void
-clearqueue(void)
-{
- struct qentry *item;
-
- while ((item = dequeue()) != NULL) {
- free(item->data.dat);
- free(item);
- }
-}
diff --git a/toolbox/upstream-netbsd/usr.bin/grep/util.c b/toolbox/upstream-netbsd/usr.bin/grep/util.c
deleted file mode 100644
index ecd948d..0000000
--- a/toolbox/upstream-netbsd/usr.bin/grep/util.c
+++ /dev/null
@@ -1,499 +0,0 @@
-/* $NetBSD: util.c,v 1.17 2013/01/21 03:24:43 msaitoh Exp $ */
-/* $FreeBSD: head/usr.bin/grep/util.c 211496 2010-08-19 09:28:59Z des $ */
-/* $OpenBSD: util.c,v 1.39 2010/07/02 22:18:03 tedu Exp $ */
-
-/*-
- * Copyright (c) 1999 James Howard and Dag-Erling Coïdan Smørgrav
- * Copyright (C) 2008-2010 Gabor Kovesdan <gabor@FreeBSD.org>
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#if HAVE_NBTOOL_CONFIG_H
-#include "nbtool_config.h"
-#endif
-
-#include <sys/cdefs.h>
-__RCSID("$NetBSD: util.c,v 1.17 2013/01/21 03:24:43 msaitoh Exp $");
-
-#include <sys/stat.h>
-#include <sys/types.h>
-
-#include <ctype.h>
-#include <err.h>
-#include <errno.h>
-#include <fnmatch.h>
-#include <fts.h>
-#include <libgen.h>
-#include <stdbool.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include <wchar.h>
-#include <wctype.h>
-
-#include "grep.h"
-
-static bool first, first_global = true;
-static unsigned long long since_printed;
-
-static int procline(struct str *l, int);
-
-bool
-file_matching(const char *fname)
-{
- char *fname_base, *fname_copy;
- unsigned int i;
- bool ret;
-
- ret = finclude ? false : true;
- fname_copy = grep_strdup(fname);
- fname_base = basename(fname_copy);
-
- for (i = 0; i < fpatterns; ++i) {
- if (fnmatch(fpattern[i].pat, fname, 0) == 0 ||
- fnmatch(fpattern[i].pat, fname_base, 0) == 0) {
- if (fpattern[i].mode == EXCL_PAT) {
- free(fname_copy);
- return (false);
- } else
- ret = true;
- }
- }
- free(fname_copy);
- return (ret);
-}
-
-static inline bool
-dir_matching(const char *dname)
-{
- unsigned int i;
- bool ret;
-
- ret = dinclude ? false : true;
-
- for (i = 0; i < dpatterns; ++i) {
- if (dname != NULL &&
- fnmatch(dname, dpattern[i].pat, 0) == 0) {
- if (dpattern[i].mode == EXCL_PAT)
- return (false);
- else
- ret = true;
- }
- }
- return (ret);
-}
-
-/*
- * Processes a directory when a recursive search is performed with
- * the -R option. Each appropriate file is passed to procfile().
- */
-int
-grep_tree(char **argv)
-{
- FTS *fts;
- FTSENT *p;
- char *d, *dir = NULL;
- int c, fts_flags;
- bool ok;
-
- c = fts_flags = 0;
-
- switch(linkbehave) {
- case LINK_EXPLICIT:
- fts_flags = FTS_COMFOLLOW;
- break;
- case LINK_SKIP:
- fts_flags = FTS_PHYSICAL;
- break;
- default:
- fts_flags = FTS_LOGICAL;
-
- }
-
- fts_flags |= FTS_NOSTAT | FTS_NOCHDIR;
-
- if (!(fts = fts_open(argv, fts_flags, NULL)))
- err(2, "fts_open");
- while ((p = fts_read(fts)) != NULL) {
- switch (p->fts_info) {
- case FTS_DNR:
- /* FALLTHROUGH */
- case FTS_ERR:
- errx(2, "%s: %s", p->fts_path, strerror(p->fts_errno));
- break;
- case FTS_D:
- /* FALLTHROUGH */
- case FTS_DP:
- break;
- case FTS_DC:
- /* Print a warning for recursive directory loop */
- warnx("warning: %s: recursive directory loop",
- p->fts_path);
- break;
- default:
- /* Check for file exclusion/inclusion */
- ok = true;
- if (dexclude || dinclude) {
- if ((d = strrchr(p->fts_path, '/')) != NULL) {
- dir = grep_malloc(sizeof(char) *
- (d - p->fts_path + 1));
- memcpy(dir, p->fts_path,
- d - p->fts_path);
- dir[d - p->fts_path] = '\0';
- }
- ok = dir_matching(dir);
- free(dir);
- dir = NULL;
- }
- if (fexclude || finclude)
- ok &= file_matching(p->fts_path);
-
- if (ok)
- c += procfile(p->fts_path);
- break;
- }
- }
-
- fts_close(fts);
- return (c);
-}
-
-/*
- * Opens a file and processes it. Each file is processed line-by-line
- * passing the lines to procline().
- */
-int
-procfile(const char *fn)
-{
- struct file *f;
- struct stat sb;
- struct str ln;
- mode_t s;
- int c, t;
-
- if (mflag && (mcount <= 0))
- return (0);
-
- if (strcmp(fn, "-") == 0) {
- fn = label != NULL ? label : getstr(1);
- f = grep_open(NULL);
- } else {
- if (!stat(fn, &sb)) {
- /* Check if we need to process the file */
- s = sb.st_mode & S_IFMT;
- if (s == S_IFDIR && dirbehave == DIR_SKIP)
- return (0);
- if ((s == S_IFIFO || s == S_IFCHR || s == S_IFBLK
- || s == S_IFSOCK) && devbehave == DEV_SKIP)
- return (0);
- }
- f = grep_open(fn);
- }
- if (f == NULL) {
- if (!sflag)
- warn("%s", fn);
- if (errno == ENOENT)
- notfound = true;
- return (0);
- }
-
- ln.file = grep_malloc(strlen(fn) + 1);
- strcpy(ln.file, fn);
- ln.line_no = 0;
- ln.len = 0;
- tail = 0;
- ln.off = -1;
-
- for (first = true, c = 0; c == 0 || !(lflag || qflag); ) {
- ln.off += ln.len + 1;
- if ((ln.dat = grep_fgetln(f, &ln.len)) == NULL || ln.len == 0)
- break;
- if (ln.len > 0 && ln.dat[ln.len - 1] == line_sep)
- --ln.len;
- ln.line_no++;
-
- /* Return if we need to skip a binary file */
- if (f->binary && binbehave == BINFILE_SKIP) {
- grep_close(f);
- free(ln.file);
- free(f);
- return (0);
- }
- /* Process the file line-by-line */
- t = procline(&ln, f->binary);
- c += t;
-
- /* Count the matches if we have a match limit */
- if (mflag) {
- mcount -= t;
- if (mcount <= 0)
- break;
- }
- }
- if (Bflag > 0)
- clearqueue();
- grep_close(f);
-
- if (cflag) {
- if (!hflag)
- printf("%s:", ln.file);
- printf("%u%c", c, line_sep);
- }
- if (lflag && !qflag && c != 0)
- printf("%s%c", fn, line_sep);
- if (Lflag && !qflag && c == 0)
- printf("%s%c", fn, line_sep);
- if (c && !cflag && !lflag && !Lflag &&
- binbehave == BINFILE_BIN && f->binary && !qflag)
- printf(getstr(8), fn);
-
- free(ln.file);
- free(f);
- return (c);
-}
-
-#define iswword(x) (iswalnum((x)) || (x) == L'_')
-
-/*
- * Processes a line comparing it with the specified patterns. Each pattern
- * is looped to be compared along with the full string, saving each and every
- * match, which is necessary to colorize the output and to count the
- * matches. The matching lines are passed to printline() to display the
- * appropriate output.
- */
-static int
-procline(struct str *l, int nottext)
-{
- regmatch_t matches[MAX_LINE_MATCHES];
- regmatch_t pmatch;
- size_t st = 0;
- unsigned int i;
- int c = 0, m = 0, r = 0;
-
- /* Loop to process the whole line */
- while (st <= l->len) {
- pmatch.rm_so = st;
- pmatch.rm_eo = l->len;
-
- /* Loop to compare with all the patterns */
- for (i = 0; i < patterns; i++) {
-/*
- * XXX: grep_search() is a workaround for speed up and should be
- * removed in the future. See fastgrep.c.
- */
- if (fg_pattern[i].pattern) {
- r = grep_search(&fg_pattern[i],
- (unsigned char *)l->dat,
- l->len, &pmatch);
- r = (r == 0) ? 0 : REG_NOMATCH;
- st = pmatch.rm_eo;
- } else {
- r = regexec(&r_pattern[i], l->dat, 1,
- &pmatch, eflags);
- r = (r == 0) ? 0 : REG_NOMATCH;
- st = pmatch.rm_eo;
- }
- if (r == REG_NOMATCH)
- continue;
- /* Check for full match */
- if (xflag &&
- (pmatch.rm_so != 0 ||
- (size_t)pmatch.rm_eo != l->len))
- continue;
- /* Check for whole word match */
- if (fg_pattern[i].word && pmatch.rm_so != 0) {
- wchar_t wbegin, wend;
-
- wbegin = wend = L' ';
- if (pmatch.rm_so != 0 &&
- sscanf(&l->dat[pmatch.rm_so - 1],
- "%lc", &wbegin) != 1)
- continue;
- if ((size_t)pmatch.rm_eo != l->len &&
- sscanf(&l->dat[pmatch.rm_eo],
- "%lc", &wend) != 1)
- continue;
- if (iswword(wbegin) || iswword(wend))
- continue;
- }
- c = 1;
- if (m < MAX_LINE_MATCHES)
- matches[m++] = pmatch;
- /* matches - skip further patterns */
- if ((color != NULL && !oflag) || qflag || lflag)
- break;
- }
-
- if (vflag) {
- c = !c;
- break;
- }
- /* One pass if we are not recording matches */
- if ((color != NULL && !oflag) || qflag || lflag)
- break;
-
- if (st == (size_t)pmatch.rm_so)
- break; /* No matches */
- }
-
- if (c && binbehave == BINFILE_BIN && nottext)
- return (c); /* Binary file */
-
- /* Dealing with the context */
- if ((tail || c) && !cflag && !qflag && !lflag && !Lflag) {
- if (c) {
- if ((Aflag || Bflag) && !first_global &&
- (first || since_printed > Bflag))
- printf("--\n");
- tail = Aflag;
- if (Bflag > 0)
- printqueue();
- printline(l, ':', matches, m);
- } else {
- printline(l, '-', matches, m);
- tail--;
- }
- first = false;
- first_global = false;
- since_printed = 0;
- } else {
- if (Bflag)
- enqueue(l);
- since_printed++;
- }
- return (c);
-}
-
-/*
- * Safe malloc() for internal use.
- */
-void *
-grep_malloc(size_t size)
-{
- void *ptr;
-
- if ((ptr = malloc(size)) == NULL)
- err(2, "malloc");
- return (ptr);
-}
-
-/*
- * Safe calloc() for internal use.
- */
-void *
-grep_calloc(size_t nmemb, size_t size)
-{
- void *ptr;
-
- if ((ptr = calloc(nmemb, size)) == NULL)
- err(2, "calloc");
- return (ptr);
-}
-
-/*
- * Safe realloc() for internal use.
- */
-void *
-grep_realloc(void *ptr, size_t size)
-{
-
- if ((ptr = realloc(ptr, size)) == NULL)
- err(2, "realloc");
- return (ptr);
-}
-
-/*
- * Safe strdup() for internal use.
- */
-char *
-grep_strdup(const char *str)
-{
- char *ret;
-
- if ((ret = strdup(str)) == NULL)
- err(2, "strdup");
- return (ret);
-}
-
-/*
- * Prints a matching line according to the command line options.
- */
-void
-printline(struct str *line, int sep, regmatch_t *matches, int m)
-{
- size_t a = 0;
- int i, n = 0;
-
- if (!hflag) {
- if (nullflag == 0)
- fputs(line->file, stdout);
- else {
- printf("%s", line->file);
- putchar(0);
- }
- ++n;
- }
- if (nflag) {
- if (n > 0)
- putchar(sep);
- printf("%d", line->line_no);
- ++n;
- }
- if (bflag) {
- if (n > 0)
- putchar(sep);
- printf("%lld", (long long)line->off);
- ++n;
- }
- if (n)
- putchar(sep);
- /* --color and -o */
- if ((oflag || color) && m > 0) {
- for (i = 0; i < m; i++) {
- if (!oflag)
- fwrite(line->dat + a, matches[i].rm_so - a, 1,
- stdout);
- if (color)
- fprintf(stdout, "\33[%sm\33[K", color);
-
- fwrite(line->dat + matches[i].rm_so,
- matches[i].rm_eo - matches[i].rm_so, 1,
- stdout);
- if (color)
- fprintf(stdout, "\33[m\33[K");
- a = matches[i].rm_eo;
- if (oflag)
- putchar('\n');
- }
- if (!oflag) {
- if (line->len - a > 0)
- fwrite(line->dat + a, line->len - a, 1, stdout);
- putchar(line_sep);
- }
- } else {
- fwrite(line->dat, line->len, 1, stdout);
- putchar(line_sep);
- }
-}