summaryrefslogtreecommitdiffstats
path: root/src/notification_proxy.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/notification_proxy.c')
-rw-r--r--src/notification_proxy.c258
1 files changed, 112 insertions, 146 deletions
diff --git a/src/notification_proxy.c b/src/notification_proxy.c
index 80a82c4..c7e4660 100644
--- a/src/notification_proxy.c
+++ b/src/notification_proxy.c
@@ -8,25 +8,37 @@
8 * modify it under the terms of the GNU Lesser General Public 8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either 9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version. 10 * version 2.1 of the License, or (at your option) any later version.
11 * 11 *
12 * This library is distributed in the hope that it will be useful, 12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details. 15 * Lesser General Public License for more details.
16 * 16 *
17 * You should have received a copy of the GNU Lesser General Public 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 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 19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 */ 20 */
21 21
22#ifdef HAVE_CONFIG_H
23#include <config.h>
24#endif
22#include <string.h> 25#include <string.h>
23#include <stdlib.h> 26#include <stdlib.h>
27
28#ifndef _MSC_VER
24#include <unistd.h> 29#include <unistd.h>
30#endif
31
25#include <plist/plist.h> 32#include <plist/plist.h>
26 33
27#include "notification_proxy.h" 34#include "notification_proxy.h"
28#include "property_list_service.h" 35#include "property_list_service.h"
29#include "debug.h" 36#include "common/debug.h"
37
38#ifdef _WIN32
39#include <windows.h>
40#define sleep(x) Sleep(x*1000)
41#endif
30 42
31struct np_thread { 43struct np_thread {
32 np_client_t client; 44 np_client_t client;
@@ -41,19 +53,19 @@ struct np_thread {
41 */ 53 */
42static void np_lock(np_client_t client) 54static void np_lock(np_client_t client)
43{ 55{
44 debug_info("NP: Locked"); 56 debug_info("Locked");
45 g_mutex_lock(client->mutex); 57 mutex_lock(&client->mutex);
46} 58}
47 59
48/** 60/**
49 * Unlocks a notification_proxy client, used for thread safety. 61 * Unlocks a notification_proxy client, used for thread safety.
50 * 62 *
51 * @param client notification_proxy client to unlock 63 * @param client notification_proxy client to unlock
52 */ 64 */
53static void np_unlock(np_client_t client) 65static void np_unlock(np_client_t client)
54{ 66{
55 debug_info("NP: Unlocked"); 67 debug_info("Unlocked");
56 g_mutex_unlock(client->mutex); 68 mutex_unlock(&client->mutex);
57} 69}
58 70
59/** 71/**
@@ -82,78 +94,85 @@ static np_error_t np_error(property_list_service_error_t err)
82 return NP_E_UNKNOWN_ERROR; 94 return NP_E_UNKNOWN_ERROR;
83} 95}
84 96
85/** 97np_error_t np_client_new(idevice_t device, lockdownd_service_descriptor_t service, np_client_t *client)
86 * Connects to the notification_proxy on the specified device.
87 *
88 * @param device The device to connect to.
89 * @param port Destination port (usually given by lockdownd_start_service).
90 * @param client Pointer that will be set to a newly allocated np_client_t
91 * upon successful return.
92 *
93 * @return NP_E_SUCCESS on success, NP_E_INVALID_ARG when device is NULL,
94 * or NP_E_CONN_FAILED when the connection to the device could not be
95 * established.
96 */
97np_error_t np_client_new(idevice_t device, uint16_t port, np_client_t *client)
98{ 98{
99 /* makes sure thread environment is available */
100 if (!g_thread_supported())
101 g_thread_init(NULL);
102
103 if (!device)
104 return NP_E_INVALID_ARG;
105
106 property_list_service_client_t plistclient = NULL; 99 property_list_service_client_t plistclient = NULL;
107 if (property_list_service_client_new(device, port, &plistclient) != PROPERTY_LIST_SERVICE_E_SUCCESS) { 100 np_error_t err = np_error(property_list_service_client_new(device, service, &plistclient));
108 return NP_E_CONN_FAILED; 101 if (err != NP_E_SUCCESS) {
102 return err;
109 } 103 }
110 104
111 np_client_t client_loc = (np_client_t) malloc(sizeof(struct np_client_private)); 105 np_client_t client_loc = (np_client_t) malloc(sizeof(struct np_client_private));
112 client_loc->parent = plistclient; 106 client_loc->parent = plistclient;
113 107
114 client_loc->mutex = g_mutex_new(); 108 mutex_init(&client_loc->mutex);
115 109 client_loc->notifier = THREAD_T_NULL;
116 client_loc->notifier = NULL;
117 110
118 *client = client_loc; 111 *client = client_loc;
119 return NP_E_SUCCESS; 112 return NP_E_SUCCESS;
120} 113}
121 114
122/** 115np_error_t np_client_start_service(idevice_t device, np_client_t* client, const char* label)
123 * Disconnects a notification_proxy client from the device and frees up the 116{
124 * notification_proxy client data. 117 int32_t err = NP_E_UNKNOWN_ERROR;
125 * 118 service_client_factory_start_service(device, NP_SERVICE_NAME, (void**)client, label, SERVICE_CONSTRUCTOR(np_client_new), &err);
126 * @param client The notification_proxy client to disconnect and free. 119 return err;
127 * 120}
128 * @return NP_E_SUCCESS on success, or NP_E_INVALID_ARG when client is NULL. 121
129 */
130np_error_t np_client_free(np_client_t client) 122np_error_t np_client_free(np_client_t client)
131{ 123{
124 plist_t dict;
125 property_list_service_client_t parent;
126
132 if (!client) 127 if (!client)
133 return NP_E_INVALID_ARG; 128 return NP_E_INVALID_ARG;
134 129
135 property_list_service_client_free(client->parent); 130 dict = plist_new_dict();
131 plist_dict_set_item(dict,"Command", plist_new_string("Shutdown"));
132 property_list_service_send_xml_plist(client->parent, dict);
133 plist_free(dict);
134
135 parent = client->parent;
136 /* notifies the client->notifier thread that it should terminate */
136 client->parent = NULL; 137 client->parent = NULL;
138
137 if (client->notifier) { 139 if (client->notifier) {
138 debug_info("joining np callback"); 140 debug_info("joining np callback");
139 g_thread_join(client->notifier); 141 thread_join(client->notifier);
140 } 142 thread_free(client->notifier);
141 if (client->mutex) { 143 client->notifier = THREAD_T_NULL;
142 g_mutex_free(client->mutex); 144 } else {
145 dict = NULL;
146 property_list_service_receive_plist(parent, &dict);
147 if (dict) {
148#ifndef STRIP_DEBUG_CODE
149 char *cmd_value = NULL;
150 plist_t cmd_value_node = plist_dict_get_item(dict, "Command");
151 if (plist_get_node_type(cmd_value_node) == PLIST_STRING) {
152 plist_get_string_val(cmd_value_node, &cmd_value);
153 }
154 if (cmd_value && !strcmp(cmd_value, "ProxyDeath")) {
155 // this is the expected answer
156 } else {
157 debug_info("Did not get ProxyDeath but:");
158 debug_plist(dict);
159 }
160 if (cmd_value) {
161 free(cmd_value);
162 }
163#endif
164 plist_free(dict);
165 }
143 } 166 }
167
168 property_list_service_client_free(parent);
169
170 mutex_destroy(&client->mutex);
144 free(client); 171 free(client);
145 172
146 return NP_E_SUCCESS; 173 return NP_E_SUCCESS;
147} 174}
148 175
149/**
150 * Sends a notification to the device's notification_proxy.
151 *
152 * @param client The client to send to
153 * @param notification The notification message to send
154 *
155 * @return NP_E_SUCCESS on success, or an error returned by np_plist_send
156 */
157np_error_t np_post_notification(np_client_t client, const char *notification) 176np_error_t np_post_notification(np_client_t client, const char *notification)
158{ 177{
159 if (!client || !notification) { 178 if (!client || !notification) {
@@ -162,66 +181,24 @@ np_error_t np_post_notification(np_client_t client, const char *notification)
162 np_lock(client); 181 np_lock(client);
163 182
164 plist_t dict = plist_new_dict(); 183 plist_t dict = plist_new_dict();
165 plist_dict_insert_item(dict,"Command", plist_new_string("PostNotification")); 184 plist_dict_set_item(dict,"Command", plist_new_string("PostNotification"));
166 plist_dict_insert_item(dict,"Name", plist_new_string(notification)); 185 plist_dict_set_item(dict,"Name", plist_new_string(notification));
167 186
168 np_error_t res = np_error(property_list_service_send_xml_plist(client->parent, dict)); 187 np_error_t res = np_error(property_list_service_send_xml_plist(client->parent, dict));
169 plist_free(dict); 188 plist_free(dict);
170 189
171 dict = plist_new_dict();
172 plist_dict_insert_item(dict,"Command", plist_new_string("Shutdown"));
173
174 res = np_error(property_list_service_send_xml_plist(client->parent, dict));
175 plist_free(dict);
176
177 if (res != NP_E_SUCCESS) { 190 if (res != NP_E_SUCCESS) {
178 debug_info("Error sending XML plist to device!"); 191 debug_info("Error sending XML plist to device!");
179 } 192 }
180
181 // try to read an answer, we just ignore errors here
182 dict = NULL;
183 property_list_service_receive_plist(client->parent, &dict);
184 if (dict) {
185#ifndef STRIP_DEBUG_CODE
186 char *cmd_value = NULL;
187 plist_t cmd_value_node = plist_dict_get_item(dict, "Command");
188 if (plist_get_node_type(cmd_value_node) == PLIST_STRING) {
189 plist_get_string_val(cmd_value_node, &cmd_value);
190 }
191
192 if (cmd_value && !strcmp(cmd_value, "ProxyDeath")) {
193 // this is the expected answer
194 } else {
195 debug_plist(dict);
196 }
197 g_free(cmd_value);
198#endif
199 plist_free(dict);
200 }
201
202 np_unlock(client); 193 np_unlock(client);
203 return res; 194 return res;
204} 195}
205 196
206/** 197static np_error_t internal_np_observe_notification(np_client_t client, const char *notification)
207 * Tells the device to send a notification on the specified event.
208 *
209 * @param client The client to send to
210 * @param notification The notifications that should be observed.
211 *
212 * @return NP_E_SUCCESS on success, NP_E_INVALID_ARG when client or
213 * notification are NULL, or an error returned by np_plist_send.
214 */
215np_error_t np_observe_notification( np_client_t client, const char *notification )
216{ 198{
217 if (!client || !notification) {
218 return NP_E_INVALID_ARG;
219 }
220 np_lock(client);
221
222 plist_t dict = plist_new_dict(); 199 plist_t dict = plist_new_dict();
223 plist_dict_insert_item(dict,"Command", plist_new_string("ObserveNotification")); 200 plist_dict_set_item(dict,"Command", plist_new_string("ObserveNotification"));
224 plist_dict_insert_item(dict,"Name", plist_new_string(notification)); 201 plist_dict_set_item(dict,"Name", plist_new_string(notification));
225 202
226 np_error_t res = np_error(property_list_service_send_xml_plist(client->parent, dict)); 203 np_error_t res = np_error(property_list_service_send_xml_plist(client->parent, dict));
227 if (res != NP_E_SUCCESS) { 204 if (res != NP_E_SUCCESS) {
@@ -229,21 +206,20 @@ np_error_t np_observe_notification( np_client_t client, const char *notification
229 } 206 }
230 plist_free(dict); 207 plist_free(dict);
231 208
209 return res;
210}
211
212np_error_t np_observe_notification( np_client_t client, const char *notification )
213{
214 if (!client || !notification) {
215 return NP_E_INVALID_ARG;
216 }
217 np_lock(client);
218 np_error_t res = internal_np_observe_notification(client, notification);
232 np_unlock(client); 219 np_unlock(client);
233 return res; 220 return res;
234} 221}
235 222
236/**
237 * Tells the device to send a notification on specified events.
238 *
239 * @param client The client to send to
240 * @param notification_spec Specification of the notifications that should be
241 * observed. This is expected to be an array of const char* that MUST have a
242 * terminating NULL entry.
243 *
244 * @return NP_E_SUCCESS on success, NP_E_INVALID_ARG when client is null,
245 * or an error returned by np_observe_notification.
246 */
247np_error_t np_observe_notifications(np_client_t client, const char **notification_spec) 223np_error_t np_observe_notifications(np_client_t client, const char **notification_spec)
248{ 224{
249 int i = 0; 225 int i = 0;
@@ -258,13 +234,15 @@ np_error_t np_observe_notifications(np_client_t client, const char **notificatio
258 return NP_E_INVALID_ARG; 234 return NP_E_INVALID_ARG;
259 } 235 }
260 236
237 np_lock(client);
261 while (notifications[i]) { 238 while (notifications[i]) {
262 res = np_observe_notification(client, notifications[i]); 239 res = internal_np_observe_notification(client, notifications[i]);
263 if (res != NP_E_SUCCESS) { 240 if (res != NP_E_SUCCESS) {
264 break; 241 break;
265 } 242 }
266 i++; 243 i++;
267 } 244 }
245 np_unlock(client);
268 246
269 return res; 247 return res;
270} 248}
@@ -277,7 +255,7 @@ np_error_t np_observe_notifications(np_client_t client, const char **notificatio
277 * with the notification that has been received. 255 * with the notification that has been received.
278 * 256 *
279 * @return 0 if a notification has been received or nothing has been received, 257 * @return 0 if a notification has been received or nothing has been received,
280 * or a negative value if an error occured. 258 * or a negative value if an error occurred.
281 * 259 *
282 * @note You probably want to check out np_set_notify_callback 260 * @note You probably want to check out np_set_notify_callback
283 * @see np_set_notify_callback 261 * @see np_set_notify_callback
@@ -292,11 +270,15 @@ static int np_get_notification(np_client_t client, char **notification)
292 270
293 np_lock(client); 271 np_lock(client);
294 272
295 property_list_service_receive_plist_with_timeout(client->parent, &dict, 500); 273 property_list_service_error_t perr = property_list_service_receive_plist_with_timeout(client->parent, &dict, 500);
296 if (!dict) { 274 if (perr == PROPERTY_LIST_SERVICE_E_RECEIVE_TIMEOUT) {
297 debug_info("NotificationProxy: no notification received!"); 275 debug_info("NotificationProxy: no notification received!");
298 res = 0; 276 res = 0;
299 } else { 277 } else if (perr != PROPERTY_LIST_SERVICE_E_SUCCESS) {
278 debug_info("NotificationProxy: error %d occurred!", perr);
279 res = perr;
280 }
281 if (dict) {
300 char *cmd_value = NULL; 282 char *cmd_value = NULL;
301 plist_t cmd_value_node = plist_dict_get_item(dict, "Command"); 283 plist_t cmd_value_node = plist_dict_get_item(dict, "Command");
302 284
@@ -315,11 +297,11 @@ static int np_get_notification(np_client_t client, char **notification)
315 res = -2; 297 res = -2;
316 if (name_value_node && name_value) { 298 if (name_value_node && name_value) {
317 *notification = name_value; 299 *notification = name_value;
318 debug_info("got notification %s\n", __func__, name_value); 300 debug_info("got notification %s", __func__, name_value);
319 res = 0; 301 res = 0;
320 } 302 }
321 } else if (cmd_value && !strcmp(cmd_value, "ProxyDeath")) { 303 } else if (cmd_value && !strcmp(cmd_value, "ProxyDeath")) {
322 debug_info("ERROR: NotificationProxy died!"); 304 debug_info("NotificationProxy died!");
323 res = -1; 305 res = -1;
324 } else if (cmd_value) { 306 } else if (cmd_value) {
325 debug_info("unknown NotificationProxy command '%s' received!", cmd_value); 307 debug_info("unknown NotificationProxy command '%s' received!", cmd_value);
@@ -342,7 +324,7 @@ static int np_get_notification(np_client_t client, char **notification)
342/** 324/**
343 * Internally used thread function. 325 * Internally used thread function.
344 */ 326 */
345gpointer np_notifier( gpointer arg ) 327void* np_notifier( void* arg )
346{ 328{
347 char *notification = NULL; 329 char *notification = NULL;
348 struct np_thread *npt = (struct np_thread*)arg; 330 struct np_thread *npt = (struct np_thread*)arg;
@@ -351,7 +333,10 @@ gpointer np_notifier( gpointer arg )
351 333
352 debug_info("starting callback."); 334 debug_info("starting callback.");
353 while (npt->client->parent) { 335 while (npt->client->parent) {
354 np_get_notification(npt->client, &notification); 336 if (np_get_notification(npt->client, &notification) < 0) {
337 npt->cbfunc("", npt->user_data);
338 break;
339 }
355 if (notification) { 340 if (notification) {
356 npt->cbfunc(notification, npt->user_data); 341 npt->cbfunc(notification, npt->user_data);
357 free(notification); 342 free(notification);
@@ -366,25 +351,6 @@ gpointer np_notifier( gpointer arg )
366 return NULL; 351 return NULL;
367} 352}
368 353
369/**
370 * This function allows an application to define a callback function that will
371 * be called when a notification has been received.
372 * It will start a thread that polls for notifications and calls the callback
373 * function if a notification has been received.
374 *
375 * @param client the NP client
376 * @param notify_cb pointer to a callback function or NULL to de-register a
377 * previously set callback function.
378 * @param user_data Pointer that will be passed to the callback function as
379 * user data. If notify_cb is NULL, this parameter is ignored.
380 *
381 * @note Only one callback function can be registered at the same time;
382 * any previously set callback function will be removed automatically.
383 *
384 * @return NP_E_SUCCESS when the callback was successfully registered,
385 * NP_E_INVALID_ARG when client is NULL, or NP_E_UNKNOWN_ERROR when
386 * the callback thread could no be created.
387 */
388np_error_t np_set_notify_callback( np_client_t client, np_notify_cb_t notify_cb, void *user_data ) 354np_error_t np_set_notify_callback( np_client_t client, np_notify_cb_t notify_cb, void *user_data )
389{ 355{
390 if (!client) 356 if (!client)
@@ -394,11 +360,12 @@ np_error_t np_set_notify_callback( np_client_t client, np_notify_cb_t notify_cb,
394 360
395 np_lock(client); 361 np_lock(client);
396 if (client->notifier) { 362 if (client->notifier) {
397 debug_info("callback already set, removing\n"); 363 debug_info("callback already set, removing");
398 property_list_service_client_t parent = client->parent; 364 property_list_service_client_t parent = client->parent;
399 client->parent = NULL; 365 client->parent = NULL;
400 g_thread_join(client->notifier); 366 thread_join(client->notifier);
401 client->notifier = NULL; 367 thread_free(client->notifier);
368 client->notifier = THREAD_T_NULL;
402 client->parent = parent; 369 client->parent = parent;
403 } 370 }
404 371
@@ -409,8 +376,7 @@ np_error_t np_set_notify_callback( np_client_t client, np_notify_cb_t notify_cb,
409 npt->cbfunc = notify_cb; 376 npt->cbfunc = notify_cb;
410 npt->user_data = user_data; 377 npt->user_data = user_data;
411 378
412 client->notifier = g_thread_create(np_notifier, npt, TRUE, NULL); 379 if (thread_new(&client->notifier, np_notifier, npt) == 0) {
413 if (client->notifier) {
414 res = NP_E_SUCCESS; 380 res = NP_E_SUCCESS;
415 } 381 }
416 } 382 }