summaryrefslogtreecommitdiffstats
path: root/src/syslog_relay.c
diff options
context:
space:
mode:
authorGravatar Martin Szulecki2013-05-23 14:14:48 +0200
committerGravatar Martin Szulecki2013-05-23 14:14:48 +0200
commit06d2399461182e0c0c113fb473902e204117641e (patch)
treea246259839027fbf1cf5064a1a6c8c132bdce70e /src/syslog_relay.c
parentc68121961226dcce52ad38daced6ad848555849e (diff)
downloadlibimobiledevice-06d2399461182e0c0c113fb473902e204117641e.tar.gz
libimobiledevice-06d2399461182e0c0c113fb473902e204117641e.tar.bz2
Add protocol implementation for syslog_relay service
Diffstat (limited to 'src/syslog_relay.c')
-rw-r--r--src/syslog_relay.c304
1 files changed, 304 insertions, 0 deletions
diff --git a/src/syslog_relay.c b/src/syslog_relay.c
new file mode 100644
index 0000000..807305b
--- /dev/null
+++ b/src/syslog_relay.c
@@ -0,0 +1,304 @@
1/*
2 * syslog_relay.c
3 * com.apple.syslog_relay service implementation.
4 *
5 * Copyright (c) 2013 Martin Szulecki 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 "syslog_relay.h"
29#include "lockdown.h"
30#include "common/debug.h"
31
32struct syslog_relay_worker_thread {
33 syslog_relay_client_t client;
34 syslog_relay_receive_cb_t cbfunc;
35 void *user_data;
36};
37
38/**
39 * Convert a service_error_t value to a syslog_relay_error_t value.
40 * Used internally to get correct error codes.
41 *
42 * @param err An service_error_t error code
43 *
44 * @return A matching syslog_relay_error_t error code,
45 * SYSLOG_RELAY_E_UNKNOWN_ERROR otherwise.
46 */
47static syslog_relay_error_t syslog_relay_error(service_error_t err)
48{
49 switch (err) {
50 case SERVICE_E_SUCCESS:
51 return SYSLOG_RELAY_E_SUCCESS;
52 case SERVICE_E_INVALID_ARG:
53 return SYSLOG_RELAY_E_INVALID_ARG;
54 case SERVICE_E_MUX_ERROR:
55 return SYSLOG_RELAY_E_MUX_ERROR;
56 case SERVICE_E_SSL_ERROR:
57 return SYSLOG_RELAY_E_SSL_ERROR;
58 default:
59 break;
60 }
61 return SYSLOG_RELAY_E_UNKNOWN_ERROR;
62}
63
64/**
65 * Connects to the syslog_relay service on the specified device.
66 *
67 * @param device The device to connect to.
68 * @param service The service descriptor returned by lockdownd_start_service.
69 * @param client Pointer that will point to a newly allocated
70 * syslog_relay_client_t upon successful return. Must be freed using
71 * syslog_relay_client_free() after use.
72 *
73 * @return SYSLOG_RELAY_E_SUCCESS on success, SYSLOG_RELAY_E_INVALID_ARG when
74 * client is NULL, or an SYSLOG_RELAY_E_* error code otherwise.
75 */
76syslog_relay_error_t syslog_relay_client_new(idevice_t device, lockdownd_service_descriptor_t service, syslog_relay_client_t * client)
77{
78 *client = NULL;
79
80 if (!device || !service || service->port == 0 || !client || *client) {
81 debug_info("Incorrect parameter passed to syslog_relay_client_new.");
82 return SYSLOG_RELAY_E_INVALID_ARG;
83 }
84
85 debug_info("Creating syslog_relay_client, port = %d.", service->port);
86
87 service_client_t parent = NULL;
88 syslog_relay_error_t ret = syslog_relay_error(service_client_new(device, service, &parent));
89 if (ret != SYSLOG_RELAY_E_SUCCESS) {
90 debug_info("Creating base service client failed. Error: %i", ret);
91 return ret;
92 }
93
94 syslog_relay_client_t client_loc = (syslog_relay_client_t) malloc(sizeof(struct syslog_relay_client_private));
95 client_loc->parent = parent;
96 client_loc->worker = (thread_t)NULL;
97
98 *client = client_loc;
99
100 debug_info("syslog_relay_client successfully created.");
101 return 0;
102}
103
104/**
105 * Starts a new syslog_relay service on the specified device and connects to it.
106 *
107 * @param device The device to connect to.
108 * @param client Pointer that will point to a newly allocated
109 * syslog_relay_client_t upon successful return. Must be freed using
110 * syslog_relay_client_free() after use.
111 * @param label The label to use for communication. Usually the program name.
112 * Pass NULL to disable sending the label in requests to lockdownd.
113 *
114 * @return SYSLOG_RELAY_E_SUCCESS on success, or an SYSLOG_RELAY_E_* error
115 * code otherwise.
116 */
117syslog_relay_error_t syslog_relay_client_start_service(idevice_t device, syslog_relay_client_t * client, const char* label)
118{
119 syslog_relay_error_t err = SYSLOG_RELAY_E_UNKNOWN_ERROR;
120 service_client_factory_start_service(device, SYSLOG_RELAY_SERVICE_NAME, (void**)client, label, SERVICE_CONSTRUCTOR(syslog_relay_client_new), &err);
121 return err;
122}
123
124/**
125 * Disconnects a syslog_relay client from the device and frees up the
126 * syslog_relay client data.
127 *
128 * @param client The syslog_relay client to disconnect and free.
129 *
130 * @return SYSLOG_RELAY_E_SUCCESS on success, SYSLOG_RELAY_E_INVALID_ARG when
131 * client is NULL, or an SYSLOG_RELAY_E_* error code otherwise.
132 */
133syslog_relay_error_t syslog_relay_client_free(syslog_relay_client_t client)
134{
135 if (!client)
136 return SYSLOG_RELAY_E_INVALID_ARG;
137
138 syslog_relay_error_t err = syslog_relay_error(service_client_free(client->parent));
139 client->parent = NULL;
140 if (client->worker) {
141 debug_info("Joining syslog capture callback worker thread");
142 thread_join(client->worker);
143 }
144 free(client);
145
146 return err;
147}
148
149/**
150 * Sends data to the service.
151 *
152 * @param client The syslog_relay client
153 * @param data Data to send
154 * @param size Size of the data to send
155 * @param sent Number of bytes sent (can be NULL to ignore)
156 *
157 * @return SYSLOG_RELAY_E_SUCCESS on success,
158 * SYSLOG_RELAY_E_INVALID_ARG when client or plist is NULL
159 */
160syslog_relay_error_t syslog_relay_send(syslog_relay_client_t client, const char* data, uint32_t size, uint32_t *sent)
161{
162 syslog_relay_error_t res = SYSLOG_RELAY_E_UNKNOWN_ERROR;
163 int bytes = 0;
164
165 if (!client || !data || (size == 0)) {
166 return SYSLOG_RELAY_E_INVALID_ARG;
167 }
168
169 res = syslog_relay_error(service_send(client->parent, data, size, (uint32_t*)&bytes));
170 if (bytes <= 0) {
171 debug_info("ERROR: sending to device failed.");
172 }
173 if (sent) {
174 *sent = (uint32_t)bytes;
175 }
176
177 return res;
178}
179
180/**
181 * Receives data from the service.
182 *
183 * @param client The syslog_relay client
184 * @param data Buffer that will be filled with the data received
185 * @param size Number of bytes to receive
186 * @param received Number of bytes received (can be NULL to ignore)
187 * @param timeout Maximum time in milliseconds to wait for data.
188 *
189 * @return SYSLOG_RELAY_E_SUCCESS on success,
190 * SYSLOG_RELAY_E_INVALID_ARG when client or plist is NULL
191 */
192syslog_relay_error_t syslog_relay_receive(syslog_relay_client_t client, char* data, uint32_t size, uint32_t *received)
193{
194 return syslog_relay_receive_with_timeout(client, data, size, received, 1000);
195}
196
197/**
198 * Receives data using the given syslog_relay client with specified timeout.
199 *
200 * @param client The syslog_relay client to use for receiving
201 * @param data Buffer that will be filled with the data received
202 * @param size Number of bytes to receive
203 * @param received Number of bytes received (can be NULL to ignore)
204 * @param timeout Maximum time in milliseconds to wait for data.
205 *
206 * @return SYSLOG_RELAY_E_SUCCESS on success,
207 * SYSLOG_RELAY_E_INVALID_ARG when one or more parameters are
208 * invalid, SYSLOG_RELAY_E_MUX_ERROR when a communication error
209 * occurs, or SYSLOG_RELAY_E_UNKNOWN_ERROR when an unspecified
210 * error occurs.
211 */
212syslog_relay_error_t syslog_relay_receive_with_timeout(syslog_relay_client_t client, char* data, uint32_t size, uint32_t *received, unsigned int timeout)
213{
214 syslog_relay_error_t res = SYSLOG_RELAY_E_UNKNOWN_ERROR;
215 int bytes = 0;
216
217 if (!client || !data || (size == 0)) {
218 return SYSLOG_RELAY_E_INVALID_ARG;
219 }
220
221 res = syslog_relay_error(service_receive_with_timeout(client->parent, data, size, (uint32_t*)&bytes, timeout));
222 if (bytes <= 0) {
223 debug_info("Could not read data, error %d", res);
224 }
225 if (received) {
226 *received = (uint32_t)bytes;
227 }
228
229 return res;
230}
231
232void *syslog_relay_worker(void *arg)
233{
234 service_error_t ret = SERVICE_E_UNKNOWN_ERROR;
235 struct syslog_relay_worker_thread *srwt = (struct syslog_relay_worker_thread*)arg;
236
237 if (!srwt)
238 return NULL;
239
240 debug_info("Running");
241
242 while (srwt->client->parent) {
243 char c;
244 uint32_t bytes = 0;
245 ret = syslog_relay_receive_with_timeout(srwt->client, &c, 1, &bytes, 0);
246 if (ret < 0 || (bytes != 1)) {
247 debug_info("Connection to syslog relay interrupted");
248 break;
249 }
250 if(c != 0) {
251 srwt->cbfunc(c, srwt->user_data);
252 }
253 }
254
255 if (srwt) {
256 free(srwt);
257 }
258
259 debug_info("Exiting");
260
261 return NULL;
262}
263
264syslog_relay_error_t syslog_relay_start_capture(syslog_relay_client_t client, syslog_relay_receive_cb_t callback, void* user_data)
265{
266 if (!client || !callback)
267 return SYSLOG_RELAY_E_INVALID_ARG;
268
269 syslog_relay_error_t res = SYSLOG_RELAY_E_UNKNOWN_ERROR;
270
271 if (client->worker) {
272 debug_info("Another syslog capture thread appears to be running already.");
273 return res;
274 }
275
276 /* start worker thread */
277 struct syslog_relay_worker_thread *srwt = (struct syslog_relay_worker_thread*)malloc(sizeof(struct syslog_relay_worker_thread));
278 if (srwt) {
279 srwt->client = client;
280 srwt->cbfunc = callback;
281 srwt->user_data = user_data;
282
283 if (thread_create(&client->worker, syslog_relay_worker, srwt) == 0) {
284 res = SYSLOG_RELAY_E_SUCCESS;
285 }
286 }
287
288 return res;
289}
290
291syslog_relay_error_t syslog_relay_stop_capture(syslog_relay_client_t client)
292{
293 if (client->worker) {
294 /* notify thread to finish */
295 service_client_t parent = client->parent;
296 client->parent = NULL;
297 /* join thread to make it exit */
298 thread_join(client->worker);
299 client->worker = (thread_t)NULL;
300 client->parent = parent;
301 }
302
303 return SYSLOG_RELAY_E_SUCCESS;
304} \ No newline at end of file