2 * Copyright (c) 2019 Samsung Electronics Co., Ltd.
4 * Licensed under the Apache License, Version 2.0 (the License);
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
22 #include <gio/gunixfdlist.h>
23 #include <linux/limits.h>
25 #include <sys/types.h>
35 #define BUFF_SIZE 4096
36 #define FIFO_BASE_DIR "/run/dumpsys/priv/fifo"
37 #define METHOD_DUMP "Dump"
38 #define SERVICE_NAME_MAX_LEN 255
40 static int out_fd = -1;
41 static GVariant *dump_args;
43 static void* copy_data(void *arg)
45 int source_fd = ((int*)arg)[0];
46 int dest_fd = ((int*)arg)[1];
48 ssize_t read_count = 0;
49 while (read_count >= 0) {
51 read_count = read(source_fd, buff, sizeof(buff));
55 if (read_count == -1) {
56 if (errno == EAGAIN) {
61 printf("read error: %m\n");
64 ssize_t bytes_to_write = read_count;
65 while (bytes_to_write > 0) {
66 ssize_t write_count = write(dest_fd, buff, read_count);
67 if (write_count == -1) {
68 printf("write error: %m\n");
71 bytes_to_write -= write_count;
78 static bool make_fifo(int *write_fd, int *read_fd)
81 char *fifo_path = NULL;
83 if (asprintf(&fifo_path, "%s/dumpsys_XXXXXX", FIFO_BASE_DIR) == -1) {
84 printf("asprintf error: %m\n");
87 uid_t ruid, euid, suid;
88 if (getresuid(&ruid, &euid, &suid) == -1) {
89 printf("getresuid error: %m\n");
93 if (seteuid(suid) == -1) {
94 printf("setuid error: %m\n");
98 if (mktemp(fifo_path) == NULL) {
99 printf("mktemp error: %m\n");
103 if (mkfifo(fifo_path, 0600) == -1) {
104 printf("mkfifo error: %m\n");
109 * We must open for reading before open for writing, because of "No such device or address" error.
110 * Read must have O_NONBLOCK flag, otherwise open will block us until
111 * someone open fifo for writing.
113 *read_fd = open(fifo_path, O_RDONLY | O_NONBLOCK);
114 if (*read_fd == -1) {
115 printf("open fifo for reading error: %m\n");
119 *write_fd = open(fifo_path, O_WRONLY | O_NONBLOCK);
120 if (*write_fd == -1) {
121 printf("open fifo for writing error: %m\n");
126 if (unlink(fifo_path) == -1) {
127 printf("unlink '%s' error: %m\n", fifo_path);
133 int old_flags = fcntl(*read_fd, F_GETFL);
134 if (old_flags == -1) {
135 printf("fcntl() GETFL error: %m\n");
141 fcntl(*read_fd, F_SETFL, old_flags & ~O_NONBLOCK);
145 if (seteuid(euid) == -1) {
146 printf("setuid error: %m\n");
155 static void call_dump(const gchar *name, const gchar *application_name, GDBusConnection *connection)
157 GError *error = NULL;
159 GDBusMessage *method_call_message = g_dbus_message_new_method_call(name,
164 g_dbus_message_set_body(method_call_message, dump_args);
166 GUnixFDList *fd_list = g_unix_fd_list_new();
168 int write_fd, read_fd;
169 if (!make_fifo(&write_fd, &read_fd)) {
173 pthread_t copy_thread;
175 fd_pair2[0] = read_fd;
176 fd_pair2[1] = out_fd;
177 if (pthread_create(©_thread, NULL, copy_data, &fd_pair2[0]) != 0) {
178 printf("pthread_create error: %m\n");
181 g_unix_fd_list_append(fd_list, write_fd, &error);
184 dprintf(STDERR_FILENO, "g_unix_fd_list_append() error: %s\n", error->message);
188 g_dbus_message_set_unix_fd_list(method_call_message, fd_list);
190 GDBusMessage *repl = g_dbus_connection_send_message_with_reply_sync(connection,
192 G_DBUS_SEND_MESSAGE_FLAGS_NONE,
198 if (repl != NULL && g_dbus_message_get_message_type(repl) == G_DBUS_MESSAGE_TYPE_ERROR)
199 g_dbus_message_to_gerror(repl, &error);
202 printf("Send message error: %s\n", error ? error->message : "(unspecified)");
206 g_object_unref(fd_list);
207 g_object_unref(method_call_message);
208 g_object_unref(repl);
210 if (pthread_join(copy_thread, NULL) != 0)
211 printf("pthread_join error: %m\n");
216 static void call_dump_for_list_of_services(GDBusConnection *connection, GList* list_of_services)
218 GList *element = g_list_first(list_of_services);
220 while (element != NULL) {
221 char *name = (gchar *)element->data;
222 char service_name[SERVICE_NAME_MAX_LEN];
224 snprintf(service_name, sizeof(service_name), "%s.%s", DUMPSYS_SERVICE_NAME_PREFIX, name);
225 call_dump(service_name, name, connection);
227 element = g_list_next(element);
232 static GDBusConnection* make_connection(GBusType bus_type)
234 GError *error = NULL;
235 GDBusConnection *connection = NULL;
237 connection = g_bus_get_sync(bus_type, NULL, &error);
238 if (connection == NULL || error != NULL)
239 printf("make_connection error: %s\n", error->message);
244 static void process_bus(GBusType bus_type, GList *list_of_services)
246 GDBusConnection *connection = make_connection(bus_type);
247 if (connection == NULL) {
248 printf("can't connect\n");
252 call_dump_for_list_of_services(connection, list_of_services);
254 g_object_unref(connection);
257 static void process_each_bus(GList *list_of_services)
259 uid_t ruid, euid, suid;
260 if (getresuid(&ruid, &euid, &suid) == -1) {
261 printf("getresuid error: %m\n");
265 process_bus(G_BUS_TYPE_SYSTEM, list_of_services);
268 static void set_dump_args(int start, int argc, char *argv[])
270 GVariantBuilder builder;
271 g_variant_builder_init(&builder, G_VARIANT_TYPE("(as)"));
272 g_variant_builder_open(&builder, G_VARIANT_TYPE("as"));
274 for (int i = start; i < argc; i++)
275 g_variant_builder_add(&builder, "s", argv[i]);
277 g_variant_builder_close(&builder);
278 dump_args = g_variant_builder_end(&builder);
281 static void print_help(char **argv)
284 printf("\t%s [-o filename] <service_name> -- [service arguments]\n\n", argv[0]);
287 int main(int argc, char *argv[])
290 char *filename = NULL;
291 out_fd = STDOUT_FILENO;
293 while ((opt = getopt(argc, argv, "o:")) != -1) {
301 if (optind >= argc) {
306 if (filename != NULL) {
307 out_fd = open(filename, O_WRONLY | O_CREAT, 0600);
309 printf("Can not open file '%s': %m\n", filename);
314 GList *list_of_services = NULL;
319 list_of_services = g_list_append(list_of_services, strdup(argv[i]));
321 set_dump_args(i+1, argc, argv);
323 process_each_bus(list_of_services);
325 g_list_free_full(list_of_services, free);