summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGravatar Nikias Bassen2025-06-12 07:30:16 +0200
committerGravatar Nikias Bassen2025-06-12 07:30:16 +0200
commiteeb320dc055716e0ebeeb8845c9ca8c4e2451124 (patch)
tree0030be86adaafbdd5aaefa2e114e72ca8f6f747d
parentc65b19d8edfcc82230205a76da95d3a4a8a282ec (diff)
downloadlibimobiledevice-eeb320dc055716e0ebeeb8845c9ca8c4e2451124.tar.gz
libimobiledevice-eeb320dc055716e0ebeeb8845c9ca8c4e2451124.tar.bz2
Add os_trace_relay service implementation
-rw-r--r--include/Makefile.am1
-rw-r--r--include/libimobiledevice/ostrace.h198
-rw-r--r--src/Makefile.am1
-rw-r--r--src/ostrace.c458
-rw-r--r--src/ostrace.h37
5 files changed, 695 insertions, 0 deletions
diff --git a/include/Makefile.am b/include/Makefile.am
index 2abaf49..59e54df 100644
--- a/include/Makefile.am
+++ b/include/Makefile.am
@@ -23,6 +23,7 @@ nobase_include_HEADERS = \
23 libimobiledevice/diagnostics_relay.h \ 23 libimobiledevice/diagnostics_relay.h \
24 libimobiledevice/debugserver.h \ 24 libimobiledevice/debugserver.h \
25 libimobiledevice/syslog_relay.h \ 25 libimobiledevice/syslog_relay.h \
26 libimobiledevice/ostrace.h \
26 libimobiledevice/mobileactivation.h \ 27 libimobiledevice/mobileactivation.h \
27 libimobiledevice/preboard.h \ 28 libimobiledevice/preboard.h \
28 libimobiledevice/companion_proxy.h \ 29 libimobiledevice/companion_proxy.h \
diff --git a/include/libimobiledevice/ostrace.h b/include/libimobiledevice/ostrace.h
new file mode 100644
index 0000000..fb5a88f
--- /dev/null
+++ b/include/libimobiledevice/ostrace.h
@@ -0,0 +1,198 @@
1/**
2 * @file libimobiledevice/ostrace.h
3 * @brief System log and tracing capabilities.
4 * \internal
5 *
6 * Copyright (c) 2020-2025 Nikias Bassen, All Rights Reserved.
7 *
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
12 *
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21 */
22
23#ifndef OSTRACE_H
24#define OSTRACE_H
25
26#ifdef __cplusplus
27extern "C" {
28#endif
29
30#include <libimobiledevice/libimobiledevice.h>
31#include <libimobiledevice/lockdown.h>
32
33/** Service identifier passed to lockdownd_start_service() to start the os trace relay service */
34#define OSTRACE_SERVICE_NAME "com.apple.os_trace_relay"
35
36/** Error Codes */
37typedef enum {
38 OSTRACE_E_SUCCESS = 0,
39 OSTRACE_E_INVALID_ARG = -1,
40 OSTRACE_E_MUX_ERROR = -2,
41 OSTRACE_E_SSL_ERROR = -3,
42 OSTRACE_E_NOT_ENOUGH_DATA = -4,
43 OSTRACE_E_TIMEOUT = -5,
44 OSTRACE_E_PLIST_ERROR = -6,
45 OSTRACE_E_REQUEST_FAILED = -7,
46 OSTRACE_E_UNKNOWN_ERROR = -256
47} ostrace_error_t;
48
49typedef struct ostrace_client_private ostrace_client_private; /**< \private */
50typedef ostrace_client_private *ostrace_client_t; /**< The client handle. */
51
52#pragma pack(push,1)
53struct ostrace_packet_header_t {
54 uint8_t marker;
55 uint32_t type;
56 uint32_t header_size; // 0x81
57 uint32_t pid;
58 uint64_t procid; // == pid
59 unsigned char procuuid[16]; // procuuid
60 uint16_t procpath_len; // path to process
61 uint64_t aid; // activity id, usually 0
62 uint64_t paid; // (parent?) activity id, usually 0
63 uint64_t time_sec; // tv.tv_sec 64 bit
64 uint32_t time_usec; // tv.usec 32 bit
65 uint8_t unk06;
66 uint8_t level; // Notice=0, Info=0x01, Debug=0x02, Error=0x10, Fault=0x11
67 uint8_t unk07;
68 uint8_t unk08;
69 uint8_t unk09;
70 uint8_t unk10;
71 uint8_t unk11;
72 uint8_t unk12;
73 uint64_t timestamp; // ?
74 uint32_t thread_id;
75 uint32_t unk13; // 0
76 unsigned char imageuuid[16]; // framework/dylib uuid
77 uint16_t imagepath_len; // framework/dylib
78 uint32_t message_len; // actual log message
79 uint32_t offset; // offset for like timestamp or sth
80 uint16_t subsystem_len; // "subsystem"
81 uint16_t unk14;
82 uint16_t category_len; // "category"
83 uint16_t unk15;
84 uint32_t unk16; // 0
85};
86#pragma pack(pop)
87
88/** Receives unparsed ostrace data from the ostrace service */
89typedef void (*ostrace_activity_cb_t)(const unsigned char* buf, unsigned int len, void *user_data);
90
91/** Receives archive data from the ostrace service */
92typedef int (*ostrace_archive_write_cb_t)(const unsigned char* buf, unsigned int len, void *user_data);
93
94/* Interface */
95
96/**
97 * Connects to the os_trace_relay service on the specified device.
98 *
99 * @param device The device to connect to.
100 * @param service The service descriptor returned by lockdownd_start_service.
101 * @param client Pointer that will point to a newly allocated
102 * ostrace_client_t upon successful return. Must be freed using
103 * ostrace_client_free() after use.
104 *
105 * @return OSTRACE_E_SUCCESS on success, OSTRACE_E_INVALID_ARG when
106 * client is NULL, or an OSTRACE_E_* error code otherwise.
107 */
108LIBIMOBILEDEVICE_API ostrace_error_t ostrace_client_new(idevice_t device, lockdownd_service_descriptor_t service, ostrace_client_t * client);
109
110/**
111 * Starts a new os_trace_relay service on the specified device and connects to it.
112 *
113 * @param device The device to connect to.
114 * @param client Pointer that will point to a newly allocated
115 * ostrace_client_t upon successful return. Must be freed using
116 * ostrace_client_free() after use.
117 * @param label The label to use for communication. Usually the program name.
118 * Pass NULL to disable sending the label in requests to lockdownd.
119 *
120 * @return OSTRACE_E_SUCCESS on success, or an OSTRACE_E_* error code otherwise.
121 */
122LIBIMOBILEDEVICE_API ostrace_error_t ostrace_client_start_service(idevice_t device, ostrace_client_t * client, const char* label);
123
124/**
125 * Disconnects a ostrace client from the device and frees up the
126 * ostrace client data.
127 *
128 * @param client The ostrace client to disconnect and free.
129 *
130 * @return OSTRACE_E_SUCCESS on success, OSTRACE_E_INVALID_ARG when
131 * client is NULL, or an OSTRACE_E_* error code otherwise.
132 */
133LIBIMOBILEDEVICE_API ostrace_error_t ostrace_client_free(ostrace_client_t client);
134
135/**
136 * Starts capturing OS trace activity data of the device using a callback.
137 *
138 * Use ostrace_stop_activity() to stop receiving the ostrace.
139 *
140 * @param client The ostrace client to use
141 * @param options Options dictionary to pass to StartActivity request.
142 * Valid options are MessageFilter (PLIST_INT, default 65535),
143 * Pid (PLIST_INT, default -1), and StreamFlags (PLIST_INT, default 60)
144 * @param callback Callback to receive data from ostrace.
145 * @param user_data Custom pointer passed to the callback function.
146 * @param user_data_free_func Function pointer that will be called when the
147 * activity is stopped to release user_data. Can be NULL for none.
148 *
149 * @return OSTRACE_E_SUCCESS on success,
150 * OSTRACE_E_INVALID_ARG when one or more parameters are
151 * invalid or OSTRACE_E_UNKNOWN_ERROR when an unspecified
152 * error occurs or an ostrace activity has already been started.
153 */
154LIBIMOBILEDEVICE_API ostrace_error_t ostrace_start_activity(ostrace_client_t client, plist_t options, ostrace_activity_cb_t callback, void* user_data);
155
156/**
157 * Stops the ostrace activity.
158 *
159 * Use ostrace_start_activity() to start receiving OS trace data.
160 *
161 * @param client The ostrace client to use
162 *
163 * @return OSTRACE_E_SUCCESS on success,
164 * OSTRACE_E_INVALID_ARG when one or more parameters are
165 * invalid or OSTRACE_E_UNKNOWN_ERROR when an unspecified
166 * error occurs or an ostrace activity has already been started.
167 */
168LIBIMOBILEDEVICE_API ostrace_error_t ostrace_stop_activity(ostrace_client_t client);
169
170/**
171 * Returns a dictionary with all currently running processes on the device.
172 *
173 * @param client The ostrace client to use
174 * @param list Pointer that will receive an allocated PLIST_DICT structure with the process data
175 *
176 * @return OSTRACE_E_SUCCESS on success, or an OSTRACE_E_* error code otherwise
177 */
178LIBIMOBILEDEVICE_API ostrace_error_t ostrace_get_pid_list(ostrace_client_t client, plist_t* list);
179
180/**
181 * Creates a syslog archive.
182 *
183 * @note The device will close the connection once the transfer is complete. The client
184 * is not usable after that anymore and must be disposed with ostrace_client_free.
185 *
186 * @param client The ostrace client to use
187 * @param options A dictionary with options for the request.
188 * Valid parameters are StartTime (PLIST_UINT), SizeLimit (PLIST_UINT), and AgeLimit (PLIST_UINT).
189 *
190 * @return OSTRACE_E_SUCCESS on success, or an OSTRACE_E_* error code otherwise
191 */
192LIBIMOBILEDEVICE_API ostrace_error_t ostrace_create_archive(ostrace_client_t client, plist_t options, ostrace_archive_write_cb_t callback, void* user_data);
193
194#ifdef __cplusplus
195}
196#endif
197
198#endif
diff --git a/src/Makefile.am b/src/Makefile.am
index 58cf07c..1ee9be8 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -58,6 +58,7 @@ libimobiledevice_1_0_la_SOURCES = \
58 companion_proxy.c companion_proxy.h \ 58 companion_proxy.c companion_proxy.h \
59 reverse_proxy.c reverse_proxy.h \ 59 reverse_proxy.c reverse_proxy.h \
60 syslog_relay.c syslog_relay.h \ 60 syslog_relay.c syslog_relay.h \
61 ostrace.c ostrace.h \
61 bt_packet_logger.c bt_packet_logger.h 62 bt_packet_logger.c bt_packet_logger.h
62 63
63if WIN32 64if WIN32
diff --git a/src/ostrace.c b/src/ostrace.c
new file mode 100644
index 0000000..0fdd147
--- /dev/null
+++ b/src/ostrace.c
@@ -0,0 +1,458 @@
1/*
2 * ostrace.c
3 * com.apple.os_trace_relay service implementation.
4 *
5 * Copyright (c) 2020-2025 Nikias Bassen, All Rights Reserved.
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 */
21
22#ifdef HAVE_CONFIG_H
23#include <config.h>
24#endif
25#include <string.h>
26#include <stdlib.h>
27
28#include <plist/plist.h>
29
30#include "ostrace.h"
31#include "lockdown.h"
32#include "common/debug.h"
33#include "endianness.h"
34
35struct ostrace_worker_thread {
36 ostrace_client_t client;
37 ostrace_activity_cb_t cbfunc;
38 void *user_data;
39};
40
41/**
42 * Convert a service_error_t value to a ostrace_error_t value.
43 * Used internally to get correct error codes.
44 *
45 * @param err An service_error_t error code
46 *
47 * @return A matching ostrace_error_t error code,
48 * OSTRACE_E_UNKNOWN_ERROR otherwise.
49 */
50static ostrace_error_t ostrace_error(service_error_t err)
51{
52 switch (err) {
53 case SERVICE_E_SUCCESS:
54 return OSTRACE_E_SUCCESS;
55 case SERVICE_E_INVALID_ARG:
56 return OSTRACE_E_INVALID_ARG;
57 case SERVICE_E_MUX_ERROR:
58 return OSTRACE_E_MUX_ERROR;
59 case SERVICE_E_SSL_ERROR:
60 return OSTRACE_E_SSL_ERROR;
61 case SERVICE_E_NOT_ENOUGH_DATA:
62 return OSTRACE_E_NOT_ENOUGH_DATA;
63 case SERVICE_E_TIMEOUT:
64 return OSTRACE_E_TIMEOUT;
65 default:
66 break;
67 }
68 return OSTRACE_E_UNKNOWN_ERROR;
69}
70
71ostrace_error_t ostrace_client_new(idevice_t device, lockdownd_service_descriptor_t service, ostrace_client_t * client)
72{
73 *client = NULL;
74
75 if (!device || !service || service->port == 0 || !client || *client) {
76 debug_info("Incorrect parameter passed to ostrace_client_new.");
77 return OSTRACE_E_INVALID_ARG;
78 }
79
80 debug_info("Creating ostrace_client, port = %d.", service->port);
81
82 service_client_t parent = NULL;
83 ostrace_error_t ret = ostrace_error(service_client_new(device, service, &parent));
84 if (ret != OSTRACE_E_SUCCESS) {
85 debug_info("Creating base service client failed. Error: %i", ret);
86 return ret;
87 }
88
89 ostrace_client_t client_loc = (ostrace_client_t) malloc(sizeof(struct ostrace_client_private));
90 client_loc->parent = parent;
91 client_loc->worker = THREAD_T_NULL;
92
93 *client = client_loc;
94
95 debug_info("ostrace_client successfully created.");
96 return 0;
97}
98
99ostrace_error_t ostrace_client_start_service(idevice_t device, ostrace_client_t * client, const char* label)
100{
101 ostrace_error_t err = OSTRACE_E_UNKNOWN_ERROR;
102 service_client_factory_start_service(device, OSTRACE_SERVICE_NAME, (void**)client, label, SERVICE_CONSTRUCTOR(ostrace_client_new), &err);
103 return err;
104}
105
106ostrace_error_t ostrace_client_free(ostrace_client_t client)
107{
108 if (!client)
109 return OSTRACE_E_INVALID_ARG;
110 ostrace_stop_activity(client);
111 ostrace_error_t err = ostrace_error(service_client_free(client->parent));
112 free(client);
113
114 return err;
115}
116
117/*
118ostrace_error_t ostrace_receive_with_timeout(ostrace_client_t client, char* data, uint32_t size, uint32_t *received, unsigned int timeout)
119{
120 ostrace_error_t res = OSTRACE_E_UNKNOWN_ERROR;
121 int bytes = 0;
122
123 if (!client || !data || (size == 0)) {
124 return OSTRACE_E_INVALID_ARG;
125 }
126
127 res = ostrace_error(service_receive_with_timeout(client->parent, data, size, (uint32_t*)&bytes, timeout));
128 if (res != OSTRACE_E_SUCCESS && res != OSTRACE_E_TIMEOUT && res != OSTRACE_E_NOT_ENOUGH_DATA) {
129 debug_info("Could not read data, error %d", res);
130 }
131 if (received) {
132 *received = (uint32_t)bytes;
133 }
134
135 return res;
136}*/
137
138static ostrace_error_t ostrace_send_plist(ostrace_client_t client, plist_t plist)
139{
140 ostrace_error_t res = OSTRACE_E_UNKNOWN_ERROR;
141 uint32_t blen = 0;
142 char* bin = NULL;
143 uint32_t sent = 0;
144 uint32_t swapped_len = 0;
145
146 if (!client || !plist) {
147 return OSTRACE_E_INVALID_ARG;
148 }
149
150 plist_to_bin(plist, &bin, &blen);
151 swapped_len = htobe32(blen);
152
153 res = ostrace_error(service_send(client->parent, (char*)&swapped_len, 4, &sent));
154 if (res == OSTRACE_E_SUCCESS) {
155 res = ostrace_error(service_send(client->parent, bin, blen, &sent));
156 }
157 free(bin);
158 return res;
159}
160
161static ostrace_error_t ostrace_receive_plist(ostrace_client_t client, plist_t *plist)
162{
163 ostrace_error_t res = OSTRACE_E_UNKNOWN_ERROR;
164 uint8_t msgtype = 0;
165 uint32_t received = 0;
166 res = ostrace_error(service_receive(client->parent, (char*)&msgtype, 1, &received));
167 if (res != OSTRACE_E_SUCCESS) {
168 debug_info("Failed to read message type from service");
169 return res;
170 }
171 uint32_t rlen = 0;
172 res = ostrace_error(service_receive(client->parent, (char*)&rlen, 4, &received));
173 if (res != OSTRACE_E_SUCCESS) {
174 debug_info("Failed to read message size from service");
175 return res;
176 }
177
178 if (msgtype == 1) {
179 rlen = be32toh(rlen);
180 } else if (msgtype == 2) {
181 rlen = le32toh(rlen);
182 } else {
183 debug_info("Unexpected message type %d", msgtype);
184 return OSTRACE_E_UNKNOWN_ERROR;
185 }
186 debug_info("got length %d", rlen);
187
188 char* buf = (char*)malloc(rlen);
189 res = ostrace_error(service_receive(client->parent, buf, rlen, &received));
190 if (res != OSTRACE_E_SUCCESS) {
191 return res;
192 }
193
194 plist_t reply = NULL;
195 plist_err_t perr = plist_from_memory(buf, received, &reply, NULL);
196 free(buf);
197 if (perr != PLIST_ERR_SUCCESS) {
198 return OSTRACE_E_UNKNOWN_ERROR;
199 }
200 *plist = reply;
201 return OSTRACE_E_SUCCESS;
202}
203
204static ostrace_error_t _ostrace_check_result(plist_t reply)
205{
206 ostrace_error_t res = OSTRACE_E_REQUEST_FAILED;
207 if (!reply) {
208 return res;
209 }
210 plist_t p_status = plist_dict_get_item(reply, "Status");
211 if (!p_status) {
212 return res;
213 }
214 const char* status = plist_get_string_ptr(p_status, NULL);
215 if (!status) {
216 return res;
217 }
218 if (!strcmp(status, "RequestSuccessful")) {
219 res = OSTRACE_E_SUCCESS;
220 }
221 return res;
222}
223
224void *ostrace_worker(void *arg)
225{
226 ostrace_error_t res = OSTRACE_E_UNKNOWN_ERROR;
227 struct ostrace_worker_thread *oswt = (struct ostrace_worker_thread*)arg;
228
229 if (!oswt)
230 return NULL;
231
232 uint8_t msgtype = 0;
233 uint32_t received = 0;
234
235 debug_info("Running");
236
237 while (oswt->client->parent) {
238 res = ostrace_error(service_receive(oswt->client->parent, (char*)&msgtype, 1, &received));
239 if (res == OSTRACE_E_TIMEOUT) {
240 debug_info("Nothing received, retrying\n");
241 continue;
242 }
243 if (res != OSTRACE_E_SUCCESS) {
244 debug_info("Failed to read message type from service");
245 break;
246 }
247 uint32_t rlen = 0;
248 res = ostrace_error(service_receive(oswt->client->parent, (char*)&rlen, 4, &received));
249 if (res != OSTRACE_E_SUCCESS) {
250 debug_info("Failed to read message size from service");
251 break;
252 }
253
254 if (msgtype == 1) {
255 rlen = be32toh(rlen);
256 } else if (msgtype == 2) {
257 rlen = le32toh(rlen);
258 } else {
259 debug_info("Unexpected message type %d", msgtype);
260 break;
261 }
262
263 debug_info("got length %d", rlen);
264
265 unsigned char* buf = (unsigned char*)malloc(rlen);
266 res = ostrace_error(service_receive(oswt->client->parent, (char*)buf, rlen, &received));
267 if (res != OSTRACE_E_SUCCESS) {
268 debug_info("Failed to receive %d bytes, error %d", rlen, res);
269 break;
270 }
271 if (received < rlen) {
272 debug_info("Failed to receive all data, got %d/%d", received, rlen);
273 break;
274 }
275 oswt->cbfunc(buf, received, oswt->user_data);
276 }
277
278 if (oswt) {
279 free(oswt);
280 }
281
282 debug_info("Exiting");
283
284 return NULL;
285}
286
287ostrace_error_t ostrace_start_activity(ostrace_client_t client, plist_t options, ostrace_activity_cb_t callback, void* user_data)
288{
289 if (!client || !callback)
290 return OSTRACE_E_INVALID_ARG;
291
292 ostrace_error_t res = OSTRACE_E_UNKNOWN_ERROR;
293
294 if (client->worker) {
295 debug_info("Another ostrace activity thread appears to be running already.");
296 return res;
297 }
298
299 plist_t dict = plist_new_dict();
300 plist_dict_set_item(dict, "Pid", plist_new_uint(0x0FFFFFFFF));
301 plist_dict_set_item(dict, "MessageFilter", plist_new_uint(0xFFFF));
302 plist_dict_set_item(dict, "StreamFlags", plist_new_uint(0x3C));
303 if (options) {
304 plist_dict_merge(&dict, options);
305 }
306 plist_dict_set_item(dict, "Request", plist_new_string("StartActivity"));
307
308 res = ostrace_send_plist(client, dict);
309 plist_free(dict);
310 if (res != OSTRACE_E_SUCCESS) {
311 return res;
312 }
313
314 dict = NULL;
315 res = ostrace_receive_plist(client, &dict);
316 if (res != OSTRACE_E_SUCCESS) {
317 return res;
318 }
319 res = _ostrace_check_result(dict);
320 if (res != OSTRACE_E_SUCCESS) {
321 return res;
322 }
323
324 /* start worker thread */
325 struct ostrace_worker_thread *oswt = (struct ostrace_worker_thread*)malloc(sizeof(struct ostrace_worker_thread));
326 if (oswt) {
327 oswt->client = client;
328 oswt->cbfunc = callback;
329 oswt->user_data = user_data;
330
331 if (thread_new(&client->worker, ostrace_worker, oswt) == 0) {
332 res = OSTRACE_E_SUCCESS;
333 }
334 }
335
336 return res;
337}
338
339ostrace_error_t ostrace_stop_activity(ostrace_client_t client)
340{
341 if (client->worker) {
342 /* notify thread to finish */
343 service_client_t parent = client->parent;
344 client->parent = NULL;
345 /* join thread to make it exit */
346 thread_join(client->worker);
347 thread_free(client->worker);
348 client->worker = THREAD_T_NULL;
349 client->parent = parent;
350 }
351
352 return OSTRACE_E_SUCCESS;
353}
354
355ostrace_error_t ostrace_get_pid_list(ostrace_client_t client, plist_t* list)
356{
357 ostrace_error_t res = OSTRACE_E_UNKNOWN_ERROR;
358 plist_t dict = plist_new_dict();
359 plist_dict_set_item(dict, "Request", plist_new_string("PidList"));
360
361 if (!client || !list) {
362 return OSTRACE_E_INVALID_ARG;
363 }
364
365 res = ostrace_send_plist(client, dict);
366 plist_free(dict);
367 if (res != OSTRACE_E_SUCCESS) {
368 return res;
369 }
370
371 plist_t reply = NULL;
372 res = ostrace_receive_plist(client, &reply);
373 if (res != OSTRACE_E_SUCCESS) {
374 return res;
375 }
376 res = _ostrace_check_result(reply);
377 if (res != OSTRACE_E_SUCCESS) {
378 return res;
379 }
380
381 plist_t payload = plist_dict_get_item(reply, "Payload");
382 if (!payload) {
383 return OSTRACE_E_REQUEST_FAILED;
384 }
385 *list = plist_copy(payload);
386 plist_free(reply);
387
388 return OSTRACE_E_SUCCESS;
389}
390
391ostrace_error_t ostrace_create_archive(ostrace_client_t client, plist_t options, ostrace_archive_write_cb_t callback, void* user_data)
392{
393 ostrace_error_t res = OSTRACE_E_UNKNOWN_ERROR;
394 if (!client || !callback) {
395 return OSTRACE_E_INVALID_ARG;
396 }
397 plist_t dict = plist_new_dict();
398 if (options) {
399 plist_dict_merge(&dict, options);
400 }
401 plist_dict_set_item(dict, "Request", plist_new_string("CreateArchive"));
402
403 res = ostrace_send_plist(client, dict);
404 plist_free(dict);
405 if (res != OSTRACE_E_SUCCESS) {
406 return res;
407 }
408
409 plist_t reply = NULL;
410 res = ostrace_receive_plist(client, &reply);
411 if (res != OSTRACE_E_SUCCESS) {
412 return res;
413 }
414
415 res = _ostrace_check_result(reply);
416 if (res != OSTRACE_E_SUCCESS) {
417 return res;
418 }
419
420 debug_info("Receiving archive...\n");
421 while (1) {
422 uint8_t msgtype = 0;
423 uint32_t received = 0;
424 res = ostrace_error(service_receive(client->parent, (char*)&msgtype, 1, &received));
425 if (res != OSTRACE_E_SUCCESS) {
426 debug_info("Could not read message type from service: %d", res);
427 break;
428 }
429 if (msgtype != 3) {
430 debug_info("Unexpected packet type %d", msgtype);
431 return OSTRACE_E_REQUEST_FAILED;
432 }
433 uint32_t rlen = 0;
434 res = ostrace_error(service_receive(client->parent, (char*)&rlen, 4, &received));
435 if (res != OSTRACE_E_SUCCESS) {
436 debug_info("Failed to read message size from service");
437 break;
438 }
439
440 rlen = le32toh(rlen);
441 debug_info("got length %d", rlen);
442
443 unsigned char* buf = (unsigned char*)malloc(rlen);
444 res = ostrace_error(service_receive(client->parent, (char*)buf, rlen, &received));
445 if (res != OSTRACE_E_SUCCESS) {
446 debug_info("Could not read data from service: %d", res);
447 break;
448 }
449 if (callback(buf, received, user_data) < 0) {
450 debug_info("Aborted through callback");
451 return OSTRACE_E_REQUEST_FAILED;
452 }
453 }
454 debug_info("Done.\n");
455
456 return OSTRACE_E_SUCCESS;
457}
458
diff --git a/src/ostrace.h b/src/ostrace.h
new file mode 100644
index 0000000..dcc3e8d
--- /dev/null
+++ b/src/ostrace.h
@@ -0,0 +1,37 @@
1/*
2 * ostrace.h
3 * com.apple.os_trace_relay service header file.
4 *
5 * Copyright (c) 2020-2025 Nikias Bassen, All Rights Reserved.
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 */
21
22#ifndef _OSTRACE_H
23#define _OSTRACE_H
24
25#include "idevice.h"
26#include "libimobiledevice/ostrace.h"
27#include "service.h"
28#include <libimobiledevice-glue/thread.h>
29
30struct ostrace_client_private {
31 service_client_t parent;
32 THREAD_T worker;
33};
34
35void *ostrace_worker(void *arg);
36
37#endif