Merge "Clean up the partition name mapping in fastboot."
diff --git a/adb/adb.cpp b/adb/adb.cpp
index 39e71e5..0181daa 100644
--- a/adb/adb.cpp
+++ b/adb/adb.cpp
@@ -31,6 +31,8 @@
#include <time.h>
#include <chrono>
+#include <condition_variable>
+#include <mutex>
#include <string>
#include <thread>
#include <vector>
@@ -48,6 +50,7 @@
#include "adb_io.h"
#include "adb_listeners.h"
#include "adb_utils.h"
+#include "sysdeps/chrono.h"
#include "transport.h"
#if !ADB_HOST
@@ -313,19 +316,15 @@
if (type == "bootloader") {
D("setting connection_state to kCsBootloader");
t->SetConnectionState(kCsBootloader);
- update_transports();
} else if (type == "device") {
D("setting connection_state to kCsDevice");
t->SetConnectionState(kCsDevice);
- update_transports();
} else if (type == "recovery") {
D("setting connection_state to kCsRecovery");
t->SetConnectionState(kCsRecovery);
- update_transports();
} else if (type == "sideload") {
D("setting connection_state to kCsSideload");
t->SetConnectionState(kCsSideload);
- update_transports();
} else {
D("setting connection_state to kCsHost");
t->SetConnectionState(kCsHost);
@@ -353,6 +352,8 @@
send_auth_request(t);
}
#endif
+
+ update_transports();
}
void handle_packet(apacket *p, atransport *t)
@@ -1229,4 +1230,50 @@
return ret - 1;
return -1;
}
+
+static auto& init_mutex = *new std::mutex();
+static auto& init_cv = *new std::condition_variable();
+static bool device_scan_complete = false;
+static bool transports_ready = false;
+
+void update_transport_status() {
+ bool result = iterate_transports([](const atransport* t) {
+ if (t->type == kTransportUsb && t->online != 1) {
+ return false;
+ }
+ return true;
+ });
+
+ D("update_transport_status: transports_ready = %s", result ? "true" : "false");
+
+ bool ready;
+
+ {
+ std::lock_guard<std::mutex> lock(init_mutex);
+ transports_ready = result;
+ ready = transports_ready && device_scan_complete;
+ }
+
+ if (ready) {
+ D("update_transport_status: notifying");
+ init_cv.notify_all();
+ }
+}
+
+void adb_notify_device_scan_complete() {
+ D("device scan complete");
+
+ {
+ std::lock_guard<std::mutex> lock(init_mutex);
+ device_scan_complete = true;
+ }
+
+ update_transport_status();
+}
+
+void adb_wait_for_device_initialization() {
+ std::unique_lock<std::mutex> lock(init_mutex);
+ init_cv.wait_for(lock, 3s, []() { return device_scan_complete && transports_ready; });
+}
+
#endif // ADB_HOST
diff --git a/adb/adb.h b/adb/adb.h
index e3675d8..d6b2b81 100644
--- a/adb/adb.h
+++ b/adb/adb.h
@@ -228,4 +228,18 @@
void parse_banner(const std::string&, atransport* t);
+// On startup, the adb server needs to wait until all of the connected devices are ready.
+// To do this, we need to know when the scan has identified all of the potential new transports, and
+// when each transport becomes ready.
+// TODO: Do this for mDNS as well, instead of just USB?
+
+// We've found all of the transports we potentially care about.
+void adb_notify_device_scan_complete();
+
+// One or more transports have changed status, check to see if we're ready.
+void update_transport_status();
+
+// Wait until device scan has completed and every transport is ready, or a timeout elapses.
+void adb_wait_for_device_initialization();
+
#endif
diff --git a/adb/adb_client.cpp b/adb/adb_client.cpp
index b656887..4f3ff25 100644
--- a/adb/adb_client.cpp
+++ b/adb/adb_client.cpp
@@ -28,12 +28,15 @@
#include <sys/stat.h>
#include <sys/types.h>
+#include <condition_variable>
+#include <mutex>
#include <string>
#include <thread>
#include <vector>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
+#include <android-base/thread_annotations.h>
#include <cutils/sockets.h>
#include "adb_io.h"
@@ -177,9 +180,8 @@
} else {
fprintf(stderr, "* daemon started successfully\n");
}
- // Give the server some time to start properly and detect devices.
- std::this_thread::sleep_for(3s);
- // fall through to _adb_connect
+ // The server will wait until it detects all of its connected devices before acking.
+ // Fall through to _adb_connect.
} else {
// If a server is already running, check its version matches.
int version = ADB_SERVER_VERSION - 1;
diff --git a/adb/client/main.cpp b/adb/client/main.cpp
index 606203c..fe5099c 100644
--- a/adb/client/main.cpp
+++ b/adb/client/main.cpp
@@ -156,33 +156,38 @@
}
#endif
- // Inform our parent that we are up and running.
+ // Wait for the USB scan to complete before notifying the parent that we're up.
+ // We need to perform this in a thread, because we would otherwise block the event loop.
+ std::thread notify_thread([ack_reply_fd]() {
+ adb_wait_for_device_initialization();
- // Any error output written to stderr now goes to adb.log. We could
- // keep around a copy of the stderr fd and use that to write any errors
- // encountered by the following code, but that is probably overkill.
+ // Any error output written to stderr now goes to adb.log. We could
+ // keep around a copy of the stderr fd and use that to write any errors
+ // encountered by the following code, but that is probably overkill.
#if defined(_WIN32)
- const HANDLE ack_reply_handle = cast_int_to_handle(ack_reply_fd);
- const CHAR ack[] = "OK\n";
- const DWORD bytes_to_write = arraysize(ack) - 1;
- DWORD written = 0;
- if (!WriteFile(ack_reply_handle, ack, bytes_to_write, &written, NULL)) {
- fatal("adb: cannot write ACK to handle 0x%p: %s", ack_reply_handle,
- android::base::SystemErrorCodeToString(GetLastError()).c_str());
- }
- if (written != bytes_to_write) {
- fatal("adb: cannot write %lu bytes of ACK: only wrote %lu bytes",
- bytes_to_write, written);
- }
- CloseHandle(ack_reply_handle);
+ const HANDLE ack_reply_handle = cast_int_to_handle(ack_reply_fd);
+ const CHAR ack[] = "OK\n";
+ const DWORD bytes_to_write = arraysize(ack) - 1;
+ DWORD written = 0;
+ if (!WriteFile(ack_reply_handle, ack, bytes_to_write, &written, NULL)) {
+ fatal("adb: cannot write ACK to handle 0x%p: %s", ack_reply_handle,
+ android::base::SystemErrorCodeToString(GetLastError()).c_str());
+ }
+ if (written != bytes_to_write) {
+ fatal("adb: cannot write %lu bytes of ACK: only wrote %lu bytes", bytes_to_write,
+ written);
+ }
+ CloseHandle(ack_reply_handle);
#else
- // TODO(danalbert): Can't use SendOkay because we're sending "OK\n", not
- // "OKAY".
- if (!android::base::WriteStringToFd("OK\n", ack_reply_fd)) {
- fatal_errno("error writing ACK to fd %d", ack_reply_fd);
- }
- unix_close(ack_reply_fd);
+ // TODO(danalbert): Can't use SendOkay because we're sending "OK\n", not
+ // "OKAY".
+ if (!android::base::WriteStringToFd("OK\n", ack_reply_fd)) {
+ fatal_errno("error writing ACK to fd %d", ack_reply_fd);
+ }
+ unix_close(ack_reply_fd);
#endif
+ });
+ notify_thread.detach();
}
D("Event loop starting");
diff --git a/adb/client/usb_libusb.cpp b/adb/client/usb_libusb.cpp
index d39884a..20610ee 100644
--- a/adb/client/usb_libusb.cpp
+++ b/adb/client/usb_libusb.cpp
@@ -352,6 +352,8 @@
}
libusb_free_device_list(list, 1);
+ adb_notify_device_scan_complete();
+
std::this_thread::sleep_for(500ms);
}
}
diff --git a/adb/client/usb_osx.cpp b/adb/client/usb_osx.cpp
index e4a543b..e366754 100644
--- a/adb/client/usb_osx.cpp
+++ b/adb/client/usb_osx.cpp
@@ -38,6 +38,7 @@
#include <android-base/logging.h>
#include <android-base/stringprintf.h>
+#include <android-base/thread_annotations.h>
#include "adb.h"
#include "transport.h"
@@ -429,7 +430,7 @@
VLOG(USB) << "RunLoopThread done";
}
-static void usb_cleanup() {
+static void usb_cleanup() NO_THREAD_SAFETY_ANALYSIS {
VLOG(USB) << "usb_cleanup";
// Wait until usb operations in RunLoopThread finish, and prevent further operations.
operate_device_lock.lock();
diff --git a/adb/fdevent.cpp b/adb/fdevent.cpp
index 72c9eef..b28de4b 100644
--- a/adb/fdevent.cpp
+++ b/adb/fdevent.cpp
@@ -26,15 +26,19 @@
#include <unistd.h>
#include <atomic>
+#include <functional>
#include <list>
+#include <mutex>
#include <unordered_map>
#include <vector>
#include <android-base/logging.h>
#include <android-base/stringprintf.h>
+#include <android-base/thread_annotations.h>
#include "adb_io.h"
#include "adb_trace.h"
+#include "adb_unique_fd.h"
#include "adb_utils.h"
#if !ADB_HOST
@@ -75,6 +79,10 @@
static bool main_thread_valid;
static unsigned long main_thread_id;
+static auto& run_queue_notify_fd = *new unique_fd();
+static auto& run_queue_mutex = *new std::mutex();
+static auto& run_queue GUARDED_BY(run_queue_mutex) = *new std::vector<std::function<void()>>();
+
void check_main_thread() {
if (main_thread_valid) {
CHECK_EQ(main_thread_id, adb_thread_id());
@@ -112,8 +120,7 @@
return android::base::StringPrintf("(fdevent %d %s)", fde->fd, state.c_str());
}
-fdevent *fdevent_create(int fd, fd_func func, void *arg)
-{
+fdevent* fdevent_create(int fd, fd_func func, void* arg) {
check_main_thread();
fdevent *fde = (fdevent*) malloc(sizeof(fdevent));
if(fde == 0) return 0;
@@ -122,8 +129,7 @@
return fde;
}
-void fdevent_destroy(fdevent *fde)
-{
+void fdevent_destroy(fdevent* fde) {
check_main_thread();
if(fde == 0) return;
if(!(fde->state & FDE_CREATED)) {
@@ -278,8 +284,7 @@
}
}
-static void fdevent_call_fdfunc(fdevent* fde)
-{
+static void fdevent_call_fdfunc(fdevent* fde) {
unsigned events = fde->events;
fde->events = 0;
CHECK(fde->state & FDE_PENDING);
@@ -292,10 +297,7 @@
#include <sys/ioctl.h>
-static void fdevent_subproc_event_func(int fd, unsigned ev,
- void* /* userdata */)
-{
-
+static void fdevent_subproc_event_func(int fd, unsigned ev, void* /* userdata */) {
D("subproc handling on fd = %d, ev = %x", fd, ev);
CHECK_GE(fd, 0);
@@ -342,8 +344,7 @@
}
}
-void fdevent_subproc_setup()
-{
+static void fdevent_subproc_setup() {
int s[2];
if(adb_socketpair(s)) {
@@ -358,12 +359,63 @@
}
#endif // !ADB_HOST
-void fdevent_loop()
-{
+static void fdevent_run_flush() REQUIRES(run_queue_mutex) {
+ for (auto& f : run_queue) {
+ f();
+ }
+ run_queue.clear();
+}
+
+static void fdevent_run_func(int fd, unsigned ev, void* /* userdata */) {
+ CHECK_GE(fd, 0);
+ CHECK(ev & FDE_READ);
+
+ char buf[1024];
+
+ // Empty the fd.
+ if (adb_read(fd, buf, sizeof(buf)) == -1) {
+ PLOG(FATAL) << "failed to empty run queue notify fd";
+ }
+
+ std::lock_guard<std::mutex> lock(run_queue_mutex);
+ fdevent_run_flush();
+}
+
+static void fdevent_run_setup() {
+ std::lock_guard<std::mutex> lock(run_queue_mutex);
+ CHECK(run_queue_notify_fd.get() == -1);
+ int s[2];
+ if (adb_socketpair(s) != 0) {
+ PLOG(FATAL) << "failed to create run queue notify socketpair";
+ }
+
+ run_queue_notify_fd.reset(s[0]);
+ fdevent* fde = fdevent_create(s[1], fdevent_run_func, nullptr);
+ CHECK(fde != nullptr);
+ fdevent_add(fde, FDE_READ);
+
+ fdevent_run_flush();
+}
+
+void fdevent_run_on_main_thread(std::function<void()> fn) {
+ std::lock_guard<std::mutex> lock(run_queue_mutex);
+ run_queue.push_back(std::move(fn));
+
+ // run_queue_notify_fd could still be -1 if we're called before fdevent has finished setting up.
+ // In that case, rely on the setup code to flush the queue without a notification being needed.
+ if (run_queue_notify_fd != -1) {
+ if (adb_write(run_queue_notify_fd.get(), "", 1) != 1) {
+ PLOG(FATAL) << "failed to write to run queue notify fd";
+ }
+ }
+}
+
+void fdevent_loop() {
set_main_thread();
#if !ADB_HOST
fdevent_subproc_setup();
#endif // !ADB_HOST
+ fdevent_run_setup();
while (true) {
if (terminate_loop) {
@@ -393,6 +445,11 @@
void fdevent_reset() {
g_poll_node_map.clear();
g_pending_list.clear();
+
+ std::lock_guard<std::mutex> lock(run_queue_mutex);
+ run_queue_notify_fd.reset();
+ run_queue.clear();
+
main_thread_valid = false;
terminate_loop = false;
}
diff --git a/adb/fdevent.h b/adb/fdevent.h
index e32845a..896400a 100644
--- a/adb/fdevent.h
+++ b/adb/fdevent.h
@@ -20,6 +20,8 @@
#include <stddef.h>
#include <stdint.h> /* for int64_t */
+#include <functional>
+
/* events that may be observed */
#define FDE_READ 0x0001
#define FDE_WRITE 0x0002
@@ -78,6 +80,9 @@
void check_main_thread();
+// Queue an operation to run on the main thread.
+void fdevent_run_on_main_thread(std::function<void()> fn);
+
// The following functions are used only for tests.
void fdevent_terminate_loop();
size_t fdevent_installed_count();
diff --git a/adb/fdevent_test.cpp b/adb/fdevent_test.cpp
index bdb973a..86e0209 100644
--- a/adb/fdevent_test.cpp
+++ b/adb/fdevent_test.cpp
@@ -173,3 +173,24 @@
std::thread thread(InvalidFdThreadFunc);
thread.join();
}
+
+TEST_F(FdeventTest, run_on_main_thread) {
+ std::vector<int> vec;
+
+ PrepareThread();
+ std::thread thread(fdevent_loop);
+
+ for (int i = 0; i < 100; ++i) {
+ fdevent_run_on_main_thread([i, &vec]() {
+ check_main_thread();
+ vec.push_back(i);
+ });
+ }
+
+ TerminateThread(thread);
+
+ ASSERT_EQ(100u, vec.size());
+ for (int i = 0; i < 100; ++i) {
+ ASSERT_EQ(i, vec[i]);
+ }
+}
diff --git a/adb/fdevent_test.h b/adb/fdevent_test.h
index f4215ae..5ca49ac 100644
--- a/adb/fdevent_test.h
+++ b/adb/fdevent_test.h
@@ -53,11 +53,11 @@
size_t GetAdditionalLocalSocketCount() {
#if ADB_HOST
- // dummy socket installed in PrepareThread()
- return 1;
-#else
- // dummy socket and one more socket installed in fdevent_subproc_setup()
+ // dummy socket installed in PrepareThread() + fdevent_run_on_main_thread socket
return 2;
+#else
+ // dummy socket + fdevent_run_on_main_thread + fdevent_subproc_setup() sockets
+ return 3;
#endif
}
diff --git a/adb/transport.cpp b/adb/transport.cpp
index cc8c162..20de642 100644
--- a/adb/transport.cpp
+++ b/adb/transport.cpp
@@ -400,8 +400,27 @@
return &tracker->socket;
}
+// Check if all of the USB transports are connected.
+bool iterate_transports(std::function<bool(const atransport*)> fn) {
+ std::lock_guard<std::mutex> lock(transport_lock);
+ for (const auto& t : transport_list) {
+ if (!fn(t)) {
+ return false;
+ }
+ }
+ for (const auto& t : pending_list) {
+ if (!fn(t)) {
+ return false;
+ }
+ }
+ return true;
+}
+
// Call this function each time the transport list has changed.
void update_transports() {
+ update_transport_status();
+
+ // Notify `adb track-devices` clients.
std::string transports = list_transports(false);
device_tracker* tracker = device_tracker_list;
diff --git a/adb/transport.h b/adb/transport.h
index 8c15d66..e129355 100644
--- a/adb/transport.h
+++ b/adb/transport.h
@@ -198,6 +198,10 @@
void kick_transport(atransport* t);
void update_transports(void);
+// Iterates across all of the current and pending transports.
+// Stops iteration and returns false if fn returns false, otherwise returns true.
+bool iterate_transports(std::function<bool(const atransport*)> fn);
+
void init_transport_registration(void);
void init_mdns_transport_discovery(void);
std::string list_transports(bool long_listing);
diff --git a/adb/transport_mdns.cpp b/adb/transport_mdns.cpp
index e49b1c6..3603f09 100644
--- a/adb/transport_mdns.cpp
+++ b/adb/transport_mdns.cpp
@@ -24,6 +24,8 @@
#include <arpa/inet.h>
#endif
+#include <thread>
+
#include <android-base/stringprintf.h>
#include <dns_sd.h>
@@ -262,19 +264,22 @@
}
}
-void init_mdns_transport_discovery(void) {
- DNSServiceErrorType errorCode =
- DNSServiceBrowse(&service_ref, 0, 0, kADBServiceType, nullptr,
- register_mdns_transport, nullptr);
+void init_mdns_transport_discovery_thread(void) {
+ DNSServiceErrorType errorCode = DNSServiceBrowse(&service_ref, 0, 0, kADBServiceType, nullptr,
+ register_mdns_transport, nullptr);
if (errorCode != kDNSServiceErr_NoError) {
D("Got %d initiating mDNS browse.", errorCode);
return;
}
- fdevent_install(&service_ref_fde,
- adb_DNSServiceRefSockFD(service_ref),
- pump_service_ref,
- &service_ref);
- fdevent_set(&service_ref_fde, FDE_READ);
+ fdevent_run_on_main_thread([]() {
+ fdevent_install(&service_ref_fde, adb_DNSServiceRefSockFD(service_ref), pump_service_ref,
+ &service_ref);
+ fdevent_set(&service_ref_fde, FDE_READ);
+ });
+}
+
+void init_mdns_transport_discovery(void) {
+ std::thread(init_mdns_transport_discovery_thread).detach();
}
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index 9efe985..5d5ac9b 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -26,8 +26,6 @@
* SUCH DAMAGE.
*/
-#define _LARGEFILE64_SOURCE
-
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
diff --git a/init/README.md b/init/README.md
index 9fc8d47..72b6c6b 100644
--- a/init/README.md
+++ b/init/README.md
@@ -356,9 +356,9 @@
Init halts executing commands until the forked process exits.
`exec_start <service>`
-> Start service a given service and halt processing of additional init commands
- until it returns. It functions similarly to the `exec` command, but uses an
- existing service definition instead of providing them as arguments.
+> Start a given service and halt the processing of additional init commands
+ until it returns. The command functions similarly to the `exec` command,
+ but uses an existing service definition in place of the exec argument vector.
`export <name> <value>`
> Set the environment variable _name_ equal to _value_ in the
diff --git a/libcutils/fs_config.cpp b/libcutils/fs_config.cpp
index a2dd677..2b3443f 100644
--- a/libcutils/fs_config.cpp
+++ b/libcutils/fs_config.cpp
@@ -92,6 +92,9 @@
{ 00755, AID_ROOT, AID_ROOT, 0, 0 },
// clang-format on
};
+#ifndef __ANDROID_VNDK__
+auto __for_testing_only__android_dirs = android_dirs;
+#endif
// Rules for files.
// These rules are applied based on "first match", so they
@@ -238,6 +241,9 @@
{ 00644, AID_ROOT, AID_ROOT, 0, 0 },
// clang-format on
};
+#ifndef __ANDROID_VNDK__
+auto __for_testing_only__android_files = android_files;
+#endif
static size_t strip(const char* path, size_t len, const char suffix[]) {
if (len < strlen(suffix)) return len;
diff --git a/libcutils/tests/Android.bp b/libcutils/tests/Android.bp
index a0b1d7b..7884190 100644
--- a/libcutils/tests/Android.bp
+++ b/libcutils/tests/Android.bp
@@ -27,7 +27,8 @@
"test_str_parms.cpp",
"android_get_control_socket_test.cpp",
"android_get_control_file_test.cpp",
- "multiuser_test.cpp"
+ "multiuser_test.cpp",
+ "fs_config.cpp",
],
},
diff --git a/libcutils/tests/fs_config.cpp b/libcutils/tests/fs_config.cpp
new file mode 100644
index 0000000..3917a0b
--- /dev/null
+++ b/libcutils/tests/fs_config.cpp
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2017 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 <string>
+
+#include <gtest/gtest.h>
+
+#include <android-base/strings.h>
+
+#include <private/android_filesystem_config.h>
+
+extern const struct fs_path_config* __for_testing_only__android_dirs;
+extern const struct fs_path_config* __for_testing_only__android_files;
+
+static void check_one(const struct fs_path_config* paths, const std::string& prefix,
+ const std::string& alternate) {
+ for (size_t idx = 0; paths[idx].prefix; ++idx) {
+ std::string path(paths[idx].prefix);
+ if (android::base::StartsWith(path, prefix.c_str())) {
+ path = alternate + path.substr(prefix.length());
+ size_t second;
+ for (second = 0; paths[second].prefix; ++second) {
+ if (path == paths[second].prefix) break;
+ }
+ if (!paths[second].prefix) {
+ // guaranteed to fail expectations, trigger test failure with
+ // a message that reports the violation as an inequality.
+ EXPECT_STREQ((prefix + path.substr(alternate.length())).c_str(), path.c_str());
+ }
+ }
+ }
+}
+
+static void check_two(const struct fs_path_config* paths, const std::string& prefix) {
+ ASSERT_FALSE(paths == nullptr);
+ std::string alternate = "system/" + prefix;
+ check_one(paths, prefix, alternate);
+ check_one(paths, alternate, prefix);
+}
+
+TEST(fs_config, vendor_dirs_alias) {
+ check_two(__for_testing_only__android_dirs, "vendor/");
+}
+
+TEST(fs_config, vendor_files_alias) {
+ check_two(__for_testing_only__android_files, "vendor/");
+}
+
+TEST(fs_config, oem_dirs_alias) {
+ check_two(__for_testing_only__android_dirs, "oem/");
+}
+
+TEST(fs_config, oem_files_alias) {
+ check_two(__for_testing_only__android_files, "oem/");
+}
+
+TEST(fs_config, odm_dirs_alias) {
+ check_two(__for_testing_only__android_dirs, "odm/");
+}
+
+TEST(fs_config, odm_files_alias) {
+ check_two(__for_testing_only__android_files, "odm/");
+}
diff --git a/libmemunreachable/.clang-format b/libmemunreachable/.clang-format
new file mode 120000
index 0000000..fd0645f
--- /dev/null
+++ b/libmemunreachable/.clang-format
@@ -0,0 +1 @@
+../.clang-format-2
\ No newline at end of file
diff --git a/libmemunreachable/Android.bp b/libmemunreachable/Android.bp
index 2867f64..cdac76b 100644
--- a/libmemunreachable/Android.bp
+++ b/libmemunreachable/Android.bp
@@ -9,8 +9,16 @@
clang: true,
shared_libs: [
"libbase",
- "liblog",
],
+
+ target: {
+ android: {
+ static_libs: ["libasync_safe"],
+ },
+ host: {
+ shared_libs: ["liblog"],
+ }
+ },
}
cc_library_shared {
diff --git a/libmemunreachable/HeapWalker.cpp b/libmemunreachable/HeapWalker.cpp
index 62366f2..c365ae5 100644
--- a/libmemunreachable/HeapWalker.cpp
+++ b/libmemunreachable/HeapWalker.cpp
@@ -42,11 +42,9 @@
} else {
Range overlap = inserted.first->first;
if (overlap != range) {
- ALOGE("range %p-%p overlaps with existing range %p-%p",
- reinterpret_cast<void*>(begin),
- reinterpret_cast<void*>(end),
- reinterpret_cast<void*>(overlap.begin),
- reinterpret_cast<void*>(overlap.end));
+ MEM_ALOGE("range %p-%p overlaps with existing range %p-%p", reinterpret_cast<void*>(begin),
+ reinterpret_cast<void*>(end), reinterpret_cast<void*>(overlap.begin),
+ reinterpret_cast<void*>(overlap.end));
}
return false;
}
@@ -154,7 +152,7 @@
void* ret = mmap(page, page_size, PROT_READ, MAP_ANONYMOUS|MAP_PRIVATE|MAP_FIXED, -1, 0);
if (ret == MAP_FAILED) {
- ALOGE("failed to map page at %p: %s", page, strerror(errno));
+ MEM_ALOGE("failed to map page at %p: %s", page, strerror(errno));
return false;
}
@@ -167,7 +165,7 @@
handler.reset();
return;
}
- ALOGW("failed to read page at %p, signal %d", si->si_addr, signal);
+ MEM_ALOGW("failed to read page at %p, signal %d", si->si_addr, signal);
if (!MapOverPage(si->si_addr)) {
handler.reset();
}
diff --git a/libmemunreachable/LeakPipe.cpp b/libmemunreachable/LeakPipe.cpp
index 080f8a7..78117e2 100644
--- a/libmemunreachable/LeakPipe.cpp
+++ b/libmemunreachable/LeakPipe.cpp
@@ -44,11 +44,11 @@
int ret = sendmsg(sock, &hdr, 0);
if (ret < 0) {
- ALOGE("failed to send fd: %s", strerror(errno));
+ MEM_ALOGE("failed to send fd: %s", strerror(errno));
return false;
}
if (ret == 0) {
- ALOGE("eof when sending fd");
+ MEM_ALOGE("eof when sending fd");
return false;
}
@@ -71,17 +71,17 @@
int ret = recvmsg(sock, &hdr, 0);
if (ret < 0) {
- ALOGE("failed to receive fd: %s", strerror(errno));
+ MEM_ALOGE("failed to receive fd: %s", strerror(errno));
return -1;
}
if (ret == 0) {
- ALOGE("eof when receiving fd");
+ MEM_ALOGE("eof when receiving fd");
return -1;
}
struct cmsghdr* cmsg = CMSG_FIRSTHDR(&hdr);
if (cmsg == NULL || cmsg->cmsg_level != SOL_SOCKET || cmsg->cmsg_type != SCM_RIGHTS) {
- ALOGE("missing fd while receiving fd");
+ MEM_ALOGE("missing fd while receiving fd");
return -1;
}
diff --git a/libmemunreachable/LeakPipe.h b/libmemunreachable/LeakPipe.h
index 3f4e0b7..3ea2d8f 100644
--- a/libmemunreachable/LeakPipe.h
+++ b/libmemunreachable/LeakPipe.h
@@ -36,7 +36,7 @@
LeakPipe() {
int ret = socketpair(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC, 0, sv_);
if (ret < 0) {
- LOG_ALWAYS_FATAL("failed to create socketpair: %s", strerror(errno));
+ MEM_LOG_ALWAYS_FATAL("failed to create socketpair: %s", strerror(errno));
}
}
@@ -105,10 +105,10 @@
bool Send(const T& value) {
ssize_t ret = TEMP_FAILURE_RETRY(write(fd_, &value, sizeof(T)));
if (ret < 0) {
- ALOGE("failed to send value: %s", strerror(errno));
+ MEM_ALOGE("failed to send value: %s", strerror(errno));
return false;
} else if (static_cast<size_t>(ret) != sizeof(T)) {
- ALOGE("eof while writing value");
+ MEM_ALOGE("eof while writing value");
return false;
}
@@ -124,10 +124,10 @@
ssize_t ret = TEMP_FAILURE_RETRY(write(fd_, vector.data(), size));
if (ret < 0) {
- ALOGE("failed to send vector: %s", strerror(errno));
+ MEM_ALOGE("failed to send vector: %s", strerror(errno));
return false;
} else if (static_cast<size_t>(ret) != size) {
- ALOGE("eof while writing vector");
+ MEM_ALOGE("eof while writing vector");
return false;
}
@@ -143,10 +143,10 @@
bool Receive(T* value) {
ssize_t ret = TEMP_FAILURE_RETRY(read(fd_, reinterpret_cast<void*>(value), sizeof(T)));
if (ret < 0) {
- ALOGE("failed to receive value: %s", strerror(errno));
+ MEM_ALOGE("failed to receive value: %s", strerror(errno));
return false;
} else if (static_cast<size_t>(ret) != sizeof(T)) {
- ALOGE("eof while receiving value");
+ MEM_ALOGE("eof while receiving value");
return false;
}
@@ -166,10 +166,10 @@
while (size > 0) {
ssize_t ret = TEMP_FAILURE_RETRY(read(fd_, ptr, size));
if (ret < 0) {
- ALOGE("failed to send vector: %s", strerror(errno));
+ MEM_ALOGE("failed to send vector: %s", strerror(errno));
return false;
} else if (ret == 0) {
- ALOGE("eof while reading vector");
+ MEM_ALOGE("eof while reading vector");
return false;
}
size -= ret;
diff --git a/libmemunreachable/MemUnreachable.cpp b/libmemunreachable/MemUnreachable.cpp
index ac19a66..e7c0beb 100644
--- a/libmemunreachable/MemUnreachable.cpp
+++ b/libmemunreachable/MemUnreachable.cpp
@@ -78,51 +78,49 @@
bool MemUnreachable::CollectAllocations(const allocator::vector<ThreadInfo>& threads,
const allocator::vector<Mapping>& mappings) {
- ALOGI("searching process %d for allocations", pid_);
+ MEM_ALOGI("searching process %d for allocations", pid_);
allocator::vector<Mapping> heap_mappings{mappings};
allocator::vector<Mapping> anon_mappings{mappings};
allocator::vector<Mapping> globals_mappings{mappings};
allocator::vector<Mapping> stack_mappings{mappings};
- if (!ClassifyMappings(mappings, heap_mappings, anon_mappings,
- globals_mappings, stack_mappings)) {
+ if (!ClassifyMappings(mappings, heap_mappings, anon_mappings, globals_mappings, stack_mappings)) {
return false;
}
for (auto it = heap_mappings.begin(); it != heap_mappings.end(); it++) {
- ALOGV("Heap mapping %" PRIxPTR "-%" PRIxPTR " %s", it->begin, it->end, it->name);
- HeapIterate(*it, [&](uintptr_t base, size_t size) {
- heap_walker_.Allocation(base, base + size);
- });
+ MEM_ALOGV("Heap mapping %" PRIxPTR "-%" PRIxPTR " %s", it->begin, it->end, it->name);
+ HeapIterate(*it,
+ [&](uintptr_t base, size_t size) { heap_walker_.Allocation(base, base + size); });
}
for (auto it = anon_mappings.begin(); it != anon_mappings.end(); it++) {
- ALOGV("Anon mapping %" PRIxPTR "-%" PRIxPTR " %s", it->begin, it->end, it->name);
+ MEM_ALOGV("Anon mapping %" PRIxPTR "-%" PRIxPTR " %s", it->begin, it->end, it->name);
heap_walker_.Allocation(it->begin, it->end);
}
for (auto it = globals_mappings.begin(); it != globals_mappings.end(); it++) {
- ALOGV("Globals mapping %" PRIxPTR "-%" PRIxPTR " %s", it->begin, it->end, it->name);
+ MEM_ALOGV("Globals mapping %" PRIxPTR "-%" PRIxPTR " %s", it->begin, it->end, it->name);
heap_walker_.Root(it->begin, it->end);
}
for (auto thread_it = threads.begin(); thread_it != threads.end(); thread_it++) {
for (auto it = stack_mappings.begin(); it != stack_mappings.end(); it++) {
if (thread_it->stack.first >= it->begin && thread_it->stack.first <= it->end) {
- ALOGV("Stack %" PRIxPTR "-%" PRIxPTR " %s", thread_it->stack.first, it->end, it->name);
+ MEM_ALOGV("Stack %" PRIxPTR "-%" PRIxPTR " %s", thread_it->stack.first, it->end, it->name);
heap_walker_.Root(thread_it->stack.first, it->end);
}
}
heap_walker_.Root(thread_it->regs);
}
- ALOGI("searching done");
+ MEM_ALOGI("searching done");
return true;
}
bool MemUnreachable::GetUnreachableMemory(allocator::vector<Leak>& leaks,
size_t limit, size_t* num_leaks, size_t* leak_bytes) {
- ALOGI("sweeping process %d for unreachable memory", pid_);
+ MEM_ALOGI("sweeping process %d for unreachable memory", pid_);
leaks.clear();
if (!heap_walker_.DetectLeaks()) {
@@ -133,9 +131,9 @@
allocator::vector<Range> leaked1{allocator_};
heap_walker_.Leaked(leaked1, 0, num_leaks, leak_bytes);
- ALOGI("sweeping done");
+ MEM_ALOGI("sweeping done");
- ALOGI("folding related leaks");
+ MEM_ALOGI("folding related leaks");
LeakFolding folding(allocator_, heap_walker_);
if (!folding.FoldLeaks()) {
@@ -188,7 +186,7 @@
std::min(leak->size, Leak::contents_length));
}
- ALOGI("folding done");
+ MEM_ALOGI("folding done");
std::sort(leaks.begin(), leaks.end(), [](const Leak& a, const Leak& b) {
return a.total_size > b.total_size;
@@ -276,7 +274,7 @@
/////////////////////////////////////////////
// Collection thread
/////////////////////////////////////////////
- ALOGI("collecting thread info for process %d...", parent_pid);
+ MEM_ALOGI("collecting thread info for process %d...", parent_pid);
ThreadCapture thread_capture(parent_pid, heap);
allocator::vector<ThreadInfo> thread_info(heap);
@@ -351,7 +349,7 @@
} else {
// Nothing left to do in the collection thread, return immediately,
// releasing all the captured threads.
- ALOGI("collection thread done");
+ MEM_ALOGI("collection thread done");
return 0;
}
}};
@@ -397,10 +395,10 @@
return false;
}
- ALOGI("unreachable memory detection done");
- ALOGE("%zu bytes in %zu allocation%s unreachable out of %zu bytes in %zu allocation%s",
- info.leak_bytes, info.num_leaks, plural(info.num_leaks),
- info.allocation_bytes, info.num_allocations, plural(info.num_allocations));
+ MEM_ALOGI("unreachable memory detection done");
+ MEM_ALOGE("%zu bytes in %zu allocation%s unreachable out of %zu bytes in %zu allocation%s",
+ info.leak_bytes, info.num_leaks, plural(info.num_leaks), info.allocation_bytes,
+ info.num_allocations, plural(info.num_allocations));
return true;
}
@@ -517,7 +515,7 @@
}
for (auto it = info.leaks.begin(); it != info.leaks.end(); it++) {
- ALOGE("%s", it->ToString(log_contents).c_str());
+ MEM_ALOGE("%s", it->ToString(log_contents).c_str());
}
return true;
}
diff --git a/libmemunreachable/PtracerThread.cpp b/libmemunreachable/PtracerThread.cpp
index 4e3c41e..73b0493 100644
--- a/libmemunreachable/PtracerThread.cpp
+++ b/libmemunreachable/PtracerThread.cpp
@@ -70,7 +70,7 @@
child_pid_(0) {
stack_ = std::make_unique<Stack>(PTHREAD_STACK_MIN);
if (stack_->top() == nullptr) {
- LOG_ALWAYS_FATAL("failed to mmap child stack: %s", strerror(errno));
+ MEM_LOG_ALWAYS_FATAL("failed to mmap child stack: %s", strerror(errno));
}
func_ = std::function<int()>{[&, func]() -> int {
@@ -102,7 +102,7 @@
CLONE_VM|CLONE_FS|CLONE_FILES/*|CLONE_UNTRACED*/,
reinterpret_cast<void*>(&func_));
if (child_pid_ < 0) {
- ALOGE("failed to clone child: %s", strerror(errno));
+ MEM_ALOGE("failed to clone child: %s", strerror(errno));
return false;
}
@@ -120,7 +120,7 @@
int status;
int ret = TEMP_FAILURE_RETRY(waitpid(child_pid_, &status, __WALL));
if (ret < 0) {
- ALOGE("waitpid %d failed: %s", child_pid_, strerror(errno));
+ MEM_ALOGE("waitpid %d failed: %s", child_pid_, strerror(errno));
return -1;
}
@@ -131,7 +131,7 @@
} else if (WIFSIGNALED(status)) {
return -WTERMSIG(status);
} else {
- ALOGE("unexpected status %x", status);
+ MEM_ALOGE("unexpected status %x", status);
return -1;
}
}
diff --git a/libmemunreachable/ScopedPipe.h b/libmemunreachable/ScopedPipe.h
index 9beef9a..7f44953 100644
--- a/libmemunreachable/ScopedPipe.h
+++ b/libmemunreachable/ScopedPipe.h
@@ -26,7 +26,7 @@
ScopedPipe() : pipefd_{-1, -1} {
int ret = pipe2(pipefd_, O_CLOEXEC);
if (ret < 0) {
- LOG_ALWAYS_FATAL("failed to open pipe");
+ MEM_LOG_ALWAYS_FATAL("failed to open pipe");
}
}
~ScopedPipe() {
diff --git a/libmemunreachable/ScopedSignalHandler.h b/libmemunreachable/ScopedSignalHandler.h
index 1fd9d4d..ada2ae4 100644
--- a/libmemunreachable/ScopedSignalHandler.h
+++ b/libmemunreachable/ScopedSignalHandler.h
@@ -37,22 +37,18 @@
template <class F>
void install(int signal, F&& f) {
- LOG_ALWAYS_FATAL_IF(signal_ != -1, "ScopedSignalHandler already installed");
+ MEM_LOG_ALWAYS_FATAL_IF(signal_ != -1, "ScopedSignalHandler already installed");
handler_ = SignalFn(std::allocator_arg, allocator_,
- [=](int signal, siginfo_t* si, void* uctx) {
- f(*this, signal, si, uctx);
- });
+ [=](int signal, siginfo_t* si, void* uctx) { f(*this, signal, si, uctx); });
- struct sigaction act{};
- act.sa_sigaction = [](int signal, siginfo_t* si, void* uctx) {
- handler_(signal, si, uctx);
- };
+ struct sigaction act {};
+ act.sa_sigaction = [](int signal, siginfo_t* si, void* uctx) { handler_(signal, si, uctx); };
act.sa_flags = SA_SIGINFO;
int ret = sigaction(signal, &act, &old_act_);
if (ret < 0) {
- LOG_ALWAYS_FATAL("failed to install segfault handler: %s", strerror(errno));
+ MEM_LOG_ALWAYS_FATAL("failed to install segfault handler: %s", strerror(errno));
}
signal_ = signal;
@@ -62,7 +58,7 @@
if (signal_ != -1) {
int ret = sigaction(signal_, &old_act_, NULL);
if (ret < 0) {
- ALOGE("failed to uninstall segfault handler");
+ MEM_ALOGE("failed to uninstall segfault handler");
}
handler_ = SignalFn{};
signal_ = -1;
diff --git a/libmemunreachable/ThreadCapture.cpp b/libmemunreachable/ThreadCapture.cpp
index 9155c29..3891f2d 100644
--- a/libmemunreachable/ThreadCapture.cpp
+++ b/libmemunreachable/ThreadCapture.cpp
@@ -110,7 +110,7 @@
android::base::unique_fd fd(open(path, O_CLOEXEC | O_DIRECTORY | O_RDONLY));
if (fd == -1) {
- ALOGE("failed to open %s: %s", path, strerror(errno));
+ MEM_ALOGE("failed to open %s: %s", path, strerror(errno));
return false;
}
@@ -126,7 +126,7 @@
do {
nread = syscall(SYS_getdents64, fd.get(), dirent_buf, sizeof(dirent_buf));
if (nread < 0) {
- ALOGE("failed to get directory entries from %s: %s", path, strerror(errno));
+ MEM_ALOGE("failed to get directory entries from %s: %s", path, strerror(errno));
return false;
} else if (nread > 0) {
ssize_t off = 0;
@@ -177,8 +177,7 @@
void ThreadCaptureImpl::PtraceDetach(pid_t tid, unsigned int signal) {
void* sig_ptr = reinterpret_cast<void*>(static_cast<uintptr_t>(signal));
if (ptrace(PTRACE_DETACH, tid, NULL, sig_ptr) < 0 && errno != ESRCH) {
- ALOGE("failed to detach from thread %d of process %d: %s", tid, pid_,
- strerror(errno));
+ MEM_ALOGE("failed to detach from thread %d of process %d: %s", tid, pid_, strerror(errno));
}
}
@@ -187,8 +186,7 @@
int ThreadCaptureImpl::PtraceAttach(pid_t tid) {
int ret = ptrace(PTRACE_SEIZE, tid, NULL, NULL);
if (ret < 0) {
- ALOGE("failed to attach to thread %d of process %d: %s", tid, pid_,
- strerror(errno));
+ MEM_ALOGE("failed to attach to thread %d of process %d: %s", tid, pid_, strerror(errno));
return -1;
}
@@ -200,8 +198,7 @@
if (errno == ESRCH) {
return 0;
} else {
- ALOGE("failed to interrupt thread %d of process %d: %s", tid, pid_,
- strerror(errno));
+ MEM_ALOGE("failed to interrupt thread %d of process %d: %s", tid, pid_, strerror(errno));
PtraceDetach(tid, 0);
return -1;
}
@@ -219,8 +216,7 @@
iovec.iov_len = sizeof(regs);
if (ptrace(PTRACE_GETREGSET, tid, reinterpret_cast<void*>(NT_PRSTATUS), &iovec)) {
- ALOGE("ptrace getregset for thread %d of process %d failed: %s",
- tid, pid_, strerror(errno));
+ MEM_ALOGE("ptrace getregset for thread %d of process %d failed: %s", tid, pid_, strerror(errno));
return false;
}
@@ -258,15 +254,13 @@
int status = 0;
if (TEMP_FAILURE_RETRY(waitpid(tid, &status, __WALL)) < 0) {
- ALOGE("failed to wait for pause of thread %d of process %d: %s", tid, pid_,
- strerror(errno));
+ MEM_ALOGE("failed to wait for pause of thread %d of process %d: %s", tid, pid_, strerror(errno));
PtraceDetach(tid, 0);
return -1;
}
if (!WIFSTOPPED(status)) {
- ALOGE("thread %d of process %d was not paused after waitpid, killed?",
- tid, pid_);
+ MEM_ALOGE("thread %d of process %d was not paused after waitpid, killed?", tid, pid_);
return 0;
}
@@ -285,8 +279,8 @@
// normal ptrace interrupt stop
break;
default:
- ALOGE("unexpected signal %d with PTRACE_EVENT_STOP for thread %d of process %d",
- signal, tid, pid_);
+ MEM_ALOGE("unexpected signal %d with PTRACE_EVENT_STOP for thread %d of process %d", signal,
+ tid, pid_);
return -1;
}
} else {
diff --git a/libmemunreachable/log.h b/libmemunreachable/log.h
index cdfbfd9..1725c53 100644
--- a/libmemunreachable/log.h
+++ b/libmemunreachable/log.h
@@ -19,6 +19,32 @@
#define LOG_TAG "libmemunreachable"
+#if defined(__ANDROID__)
+
+#include <async_safe/log.h>
+
+#define MEM_ALOGE(...) async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG, ##__VA_ARGS__)
+#define MEM_ALOGW(...) async_safe_format_log(ANDROID_LOG_WARN, LOG_TAG, ##__VA_ARGS__)
+#define MEM_ALOGI(...) async_safe_format_log(ANDROID_LOG_INFO, LOG_TAG, ##__VA_ARGS__)
+#define MEM_ALOGV(...) async_safe_format_log(ANDROID_LOG_VERBOSE, LOG_TAG, ##__VA_ARGS__)
+
+#define MEM_LOG_ALWAYS_FATAL(...) async_safe_fatal(__VA_ARGS__)
+
+#define MEM_LOG_ALWAYS_FATAL_IF(cond, ...) \
+ ((__predict_false(cond)) ? async_safe_fatal(__VA_ARGS__) : (void)0)
+
+#else
+
#include <log/log.h>
+#define MEM_ALOGW ALOGW
+#define MEM_ALOGE ALOGE
+#define MEM_ALOGV ALOGV
+#define MEM_ALOGI ALOGI
+
+#define MEM_LOG_ALWAYS_FATAL LOG_ALWAYS_FATAL
+#define MEM_LOG_ALWAYS_FATAL_IF LOG_ALWAYS_FATAL_IF
+
+#endif
+
#endif // LIBMEMUNREACHABLE_LOG_H_
diff --git a/toolbox/Android.mk b/toolbox/Android.mk
index aa755ed..d6ead1a 100644
--- a/toolbox/Android.mk
+++ b/toolbox/Android.mk
@@ -95,13 +95,3 @@
LOCAL_MODULE := grep
LOCAL_POST_INSTALL_CMD := $(hide) $(foreach t,egrep fgrep,ln -sf grep $(TARGET_OUT)/bin/$(t);)
include $(BUILD_EXECUTABLE)
-
-
-# We build gzip separately, so it can provide gunzip and zcat too.
-include $(CLEAR_VARS)
-LOCAL_MODULE := gzip
-LOCAL_SRC_FILES := gzip.c
-LOCAL_CFLAGS += -Wall -Werror
-LOCAL_SHARED_LIBRARIES += libz
-LOCAL_POST_INSTALL_CMD := $(hide) $(foreach t,gunzip zcat,ln -sf gzip $(TARGET_OUT)/bin/$(t);)
-include $(BUILD_EXECUTABLE)
diff --git a/toolbox/gzip.c b/toolbox/gzip.c
deleted file mode 100644
index 62c4518..0000000
--- a/toolbox/gzip.c
+++ /dev/null
@@ -1,261 +0,0 @@
-/* gzip.c - gzip/gunzip/zcat tools for gzip data
- *
- * Copyright 2017 The Android Open Source Project
- *
- * GZIP RFC: http://www.ietf.org/rfc/rfc1952.txt
-
-TODO: port to toybox.
-
-*/
-
-#define _GNU_SOURCE
-
-#include <errno.h>
-#include <error.h>
-#include <fcntl.h>
-#include <getopt.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/stat.h>
-
-#include <zlib.h>
-
-// toybox-style flags/globals.
-#define FLAG_c 1
-#define FLAG_d 2
-#define FLAG_f 4
-#define FLAG_k 8
-static struct {
- int optflags;
-} toys;
-static struct {
- int level;
-} TT;
-
-static void xstat(const char *path, struct stat *sb)
-{
- if (stat(path, sb)) error(1, errno, "stat %s", path);
-}
-
-static void fix_time(const char *path, struct stat *sb)
-{
- struct timespec times[] = { sb->st_atim, sb->st_mtim };
-
- if (utimensat(AT_FDCWD, path, times, 0)) error(1, errno, "utimes");
-}
-
-static FILE *xfdopen(const char *name, int flags, mode_t open_mode,
- const char *mode)
-{
- FILE *fp;
- int fd;
-
- if (!strcmp(name, "-")) fd = dup((*mode == 'r') ? 0 : 1);
- else fd = open(name, flags, open_mode);
-
- if (fd == -1) error(1, errno, "open %s (%s)", name, mode);
- fp = fdopen(fd, mode);
- if (fp == NULL) error(1, errno, "fopen %s (%s)", name, mode);
- return fp;
-}
-
-static gzFile xgzopen(const char *name, int flags, mode_t open_mode,
- const char *mode)
-{
- gzFile f;
- int fd;
-
- if (!strcmp(name, "-")) fd = dup((*mode == 'r') ? 0 : 1);
- else fd = open(name, flags, open_mode);
-
- if (fd == -1) error(1, errno, "open %s (%s)", name, mode);
- f = gzdopen(fd, mode);
- if (f == NULL) error(1, errno, "gzdopen %s (%s)", name, mode);
- return f;
-}
-
-static void gzfatal(gzFile f, char *what)
-{
- int err;
- const char *msg = gzerror(f, &err);
-
- error(1, (err == Z_ERRNO) ? errno : 0, "%s: %s", what, msg);
-}
-
-static void gunzip(char *arg)
-{
- struct stat sb;
- char buf[BUFSIZ];
- int len, both_files;
- char *in_name, *out_name;
- gzFile in;
- FILE *out;
-
- // "gunzip x.gz" will decompress "x.gz" to "x".
- len = strlen(arg);
- if (len > 3 && !strcmp(arg+len-3, ".gz")) {
- in_name = strdup(arg);
- out_name = strdup(arg);
- out_name[len-3] = '\0';
- } else if (!strcmp(arg, "-")) {
- // "-" means stdin; assume output to stdout.
- // TODO: require -f to read compressed data from tty?
- in_name = strdup("-");
- out_name = strdup("-");
- } else error(1, 0, "unknown suffix");
-
- if (toys.optflags&FLAG_c) {
- free(out_name);
- out_name = strdup("-");
- }
-
- both_files = strcmp(in_name, "-") && strcmp(out_name, "-");
- if (both_files) xstat(in_name, &sb);
-
- in = xgzopen(in_name, O_RDONLY, 0, "r");
- out = xfdopen(out_name, O_CREAT|O_WRONLY|((toys.optflags&FLAG_f)?0:O_EXCL),
- both_files?sb.st_mode:0666, "w");
-
- while ((len = gzread(in, buf, sizeof(buf))) > 0) {
- if (fwrite(buf, 1, len, out) != (size_t) len) error(1, errno, "fwrite");
- }
- if (len < 0) gzfatal(in, "gzread");
- if (fclose(out)) error(1, errno, "fclose");
- if (gzclose(in) != Z_OK) error(1, 0, "gzclose");
-
- if (both_files) fix_time(out_name, &sb);
- if (!(toys.optflags&(FLAG_c|FLAG_k))) unlink(in_name);
- free(in_name);
- free(out_name);
-}
-
-static void gzip(char *in_name)
-{
- char buf[BUFSIZ];
- size_t len;
- char *out_name;
- FILE *in;
- gzFile out;
- struct stat sb;
- int both_files;
-
- if (toys.optflags&FLAG_c) {
- out_name = strdup("-");
- } else {
- if (asprintf(&out_name, "%s.gz", in_name) == -1) {
- error(1, errno, "asprintf");
- }
- }
-
- both_files = strcmp(in_name, "-") && strcmp(out_name, "-");
- if (both_files) xstat(in_name, &sb);
-
- snprintf(buf, sizeof(buf), "w%d", TT.level);
- in = xfdopen(in_name, O_RDONLY, 0, "r");
- out = xgzopen(out_name, O_CREAT|O_WRONLY|((toys.optflags&FLAG_f)?0:O_EXCL),
- both_files?sb.st_mode:0, buf);
-
- while ((len = fread(buf, 1, sizeof(buf), in)) > 0) {
- if (gzwrite(out, buf, len) != (int) len) gzfatal(out, "gzwrite");
- }
- if (ferror(in)) error(1, errno, "fread");
- if (fclose(in)) error(1, errno, "fclose");
- if (gzclose(out) != Z_OK) error(1, 0, "gzclose");
-
- if (both_files) fix_time(out_name, &sb);
- if (!(toys.optflags&(FLAG_c|FLAG_k))) unlink(in_name);
- free(out_name);
-}
-
-static void do_file(char *arg)
-{
- if (toys.optflags&FLAG_d) gunzip(arg);
- else gzip(arg);
-}
-
-static void usage()
-{
- char *cmd = basename(getprogname());
-
- printf("usage: %s [-c] [-d] [-f] [-#] [FILE...]\n", cmd);
- printf("\n");
- if (!strcmp(cmd, "zcat")) {
- printf("Decompress files to stdout. Like `gzip -dc`.\n");
- printf("\n");
- printf("-c\tOutput to stdout\n");
- printf("-f\tForce: allow read from tty\n");
- } else if (!strcmp(cmd, "gunzip")) {
- printf("Decompress files. With no files, decompresses stdin to stdout.\n");
- printf("On success, the input files are removed and replaced by new\n");
- printf("files without the .gz suffix.\n");
- printf("\n");
- printf("-c\tOutput to stdout\n");
- printf("-f\tForce: allow read from tty\n");
- printf("-k\tKeep input files (don't remove)\n");
- } else { // gzip
- printf("Compress files. With no files, compresses stdin to stdout.\n");
- printf("On success, the input files are removed and replaced by new\n");
- printf("files with the .gz suffix.\n");
- printf("\n");
- printf("-c\tOutput to stdout\n");
- printf("-d\tDecompress (act as gunzip)\n");
- printf("-f\tForce: allow overwrite of output file\n");
- printf("-k\tKeep input files (don't remove)\n");
- printf("-#\tCompression level 1-9 (1:fastest, 6:default, 9:best)\n");
- }
- printf("\n");
-}
-
-int main(int argc, char *argv[])
-{
- char *cmd = basename(argv[0]);
- int opt_ch;
-
- toys.optflags = 0;
- TT.level = 6;
-
- if (!strcmp(cmd, "gunzip")) {
- // gunzip == gzip -d
- toys.optflags = FLAG_d;
- } else if (!strcmp(cmd, "zcat")) {
- // zcat == gzip -dc
- toys.optflags = (FLAG_c|FLAG_d);
- }
-
- while ((opt_ch = getopt(argc, argv, "cdfhk123456789")) != -1) {
- switch (opt_ch) {
- case 'c': toys.optflags |= FLAG_c; break;
- case 'd': toys.optflags |= FLAG_d; break;
- case 'f': toys.optflags |= FLAG_f; break;
- case 'k': toys.optflags |= FLAG_k; break;
-
- case '1':
- case '2':
- case '3':
- case '4':
- case '5':
- case '6':
- case '7':
- case '8':
- case '9':
- TT.level = opt_ch - '0';
- break;
-
- default:
- usage();
- return 1;
- }
- }
-
- if (optind == argc) {
- // With no arguments, we go from stdin to stdout.
- toys.optflags |= FLAG_c;
- do_file("-");
- return 0;
- }
-
- // Otherwise process each file in turn.
- while (optind < argc) do_file(argv[optind++]);
- return 0;
-}