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.c253
1 files changed, 107 insertions, 146 deletions
diff --git a/src/notification_proxy.c b/src/notification_proxy.c
index 80a82c4..60b2e03 100644
--- a/src/notification_proxy.c
+++ b/src/notification_proxy.c
@@ -8,17 +8,20 @@
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>
24#include <unistd.h> 27#include <unistd.h>
@@ -26,7 +29,11 @@
26 29
27#include "notification_proxy.h" 30#include "notification_proxy.h"
28#include "property_list_service.h" 31#include "property_list_service.h"
29#include "debug.h" 32#include "common/debug.h"
33
34#ifdef WIN32
35#define sleep(x) Sleep(x*1000)
36#endif
30 37
31struct np_thread { 38struct np_thread {
32 np_client_t client; 39 np_client_t client;
@@ -41,19 +48,19 @@ struct np_thread {
41 */ 48 */
42static void np_lock(np_client_t client) 49static void np_lock(np_client_t client)
43{ 50{
44 debug_info("NP: Locked"); 51 debug_info("Locked");
45 g_mutex_lock(client->mutex); 52 mutex_lock(&client->mutex);
46} 53}
47 54
48/** 55/**
49 * Unlocks a notification_proxy client, used for thread safety. 56 * Unlocks a notification_proxy client, used for thread safety.
50 * 57 *
51 * @param client notification_proxy client to unlock 58 * @param client notification_proxy client to unlock
52 */ 59 */
53static void np_unlock(np_client_t client) 60static void np_unlock(np_client_t client)
54{ 61{
55 debug_info("NP: Unlocked"); 62 debug_info("Unlocked");
56 g_mutex_unlock(client->mutex); 63 mutex_unlock(&client->mutex);
57} 64}
58 65
59/** 66/**
@@ -82,78 +89,85 @@ static np_error_t np_error(property_list_service_error_t err)
82 return NP_E_UNKNOWN_ERROR; 89 return NP_E_UNKNOWN_ERROR;
83} 90}
84 91
85/** 92np_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{ 93{
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; 94 property_list_service_client_t plistclient = NULL;
107 if (property_list_service_client_new(device, port, &plistclient) != PROPERTY_LIST_SERVICE_E_SUCCESS) { 95 np_error_t err = np_error(property_list_service_client_new(device, service, &plistclient));
108 return NP_E_CONN_FAILED; 96 if (err != NP_E_SUCCESS) {
97 return err;
109 } 98 }
110 99
111 np_client_t client_loc = (np_client_t) malloc(sizeof(struct np_client_private)); 100 np_client_t client_loc = (np_client_t) malloc(sizeof(struct np_client_private));
112 client_loc->parent = plistclient; 101 client_loc->parent = plistclient;
113 102
114 client_loc->mutex = g_mutex_new(); 103 mutex_init(&client_loc->mutex);
115 104 client_loc->notifier = THREAD_T_NULL;
116 client_loc->notifier = NULL;
117 105
118 *client = client_loc; 106 *client = client_loc;
119 return NP_E_SUCCESS; 107 return NP_E_SUCCESS;
120} 108}
121 109
122/** 110np_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 111{
124 * notification_proxy client data. 112 np_error_t err = NP_E_UNKNOWN_ERROR;
125 * 113 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. 114 return err;
127 * 115}
128 * @return NP_E_SUCCESS on success, or NP_E_INVALID_ARG when client is NULL. 116
129 */
130np_error_t np_client_free(np_client_t client) 117np_error_t np_client_free(np_client_t client)
131{ 118{
119 plist_t dict;
120 property_list_service_client_t parent;
121
132 if (!client) 122 if (!client)
133 return NP_E_INVALID_ARG; 123 return NP_E_INVALID_ARG;
134 124
135 property_list_service_client_free(client->parent); 125 dict = plist_new_dict();
126 plist_dict_set_item(dict,"Command", plist_new_string("Shutdown"));
127 property_list_service_send_xml_plist(client->parent, dict);
128 plist_free(dict);
129
130 parent = client->parent;
131 /* notifies the client->notifier thread that it should terminate */
136 client->parent = NULL; 132 client->parent = NULL;
133
137 if (client->notifier) { 134 if (client->notifier) {
138 debug_info("joining np callback"); 135 debug_info("joining np callback");
139 g_thread_join(client->notifier); 136 thread_join(client->notifier);
140 } 137 thread_free(client->notifier);
141 if (client->mutex) { 138 client->notifier = THREAD_T_NULL;
142 g_mutex_free(client->mutex); 139 } else {
140 dict = NULL;
141 property_list_service_receive_plist(parent, &dict);
142 if (dict) {
143#ifndef STRIP_DEBUG_CODE
144 char *cmd_value = NULL;
145 plist_t cmd_value_node = plist_dict_get_item(dict, "Command");
146 if (plist_get_node_type(cmd_value_node) == PLIST_STRING) {
147 plist_get_string_val(cmd_value_node, &cmd_value);
148 }
149 if (cmd_value && !strcmp(cmd_value, "ProxyDeath")) {
150 // this is the expected answer
151 } else {
152 debug_info("Did not get ProxyDeath but:");
153 debug_plist(dict);
154 }
155 if (cmd_value) {
156 free(cmd_value);
157 }
158#endif
159 plist_free(dict);
160 }
143 } 161 }
162
163 property_list_service_client_free(parent);
164
165 mutex_destroy(&client->mutex);
144 free(client); 166 free(client);
145 167
146 return NP_E_SUCCESS; 168 return NP_E_SUCCESS;
147} 169}
148 170
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) 171np_error_t np_post_notification(np_client_t client, const char *notification)
158{ 172{
159 if (!client || !notification) { 173 if (!client || !notification) {
@@ -162,66 +176,24 @@ np_error_t np_post_notification(np_client_t client, const char *notification)
162 np_lock(client); 176 np_lock(client);
163 177
164 plist_t dict = plist_new_dict(); 178 plist_t dict = plist_new_dict();
165 plist_dict_insert_item(dict,"Command", plist_new_string("PostNotification")); 179 plist_dict_set_item(dict,"Command", plist_new_string("PostNotification"));
166 plist_dict_insert_item(dict,"Name", plist_new_string(notification)); 180 plist_dict_set_item(dict,"Name", plist_new_string(notification));
167 181
168 np_error_t res = np_error(property_list_service_send_xml_plist(client->parent, dict)); 182 np_error_t res = np_error(property_list_service_send_xml_plist(client->parent, dict));
169 plist_free(dict); 183 plist_free(dict);
170 184
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) { 185 if (res != NP_E_SUCCESS) {
178 debug_info("Error sending XML plist to device!"); 186 debug_info("Error sending XML plist to device!");
179 } 187 }
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); 188 np_unlock(client);
203 return res; 189 return res;
204} 190}
205 191
206/** 192static 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{ 193{
217 if (!client || !notification) {
218 return NP_E_INVALID_ARG;
219 }
220 np_lock(client);
221
222 plist_t dict = plist_new_dict(); 194 plist_t dict = plist_new_dict();
223 plist_dict_insert_item(dict,"Command", plist_new_string("ObserveNotification")); 195 plist_dict_set_item(dict,"Command", plist_new_string("ObserveNotification"));
224 plist_dict_insert_item(dict,"Name", plist_new_string(notification)); 196 plist_dict_set_item(dict,"Name", plist_new_string(notification));
225 197
226 np_error_t res = np_error(property_list_service_send_xml_plist(client->parent, dict)); 198 np_error_t res = np_error(property_list_service_send_xml_plist(client->parent, dict));
227 if (res != NP_E_SUCCESS) { 199 if (res != NP_E_SUCCESS) {
@@ -229,21 +201,20 @@ np_error_t np_observe_notification( np_client_t client, const char *notification
229 } 201 }
230 plist_free(dict); 202 plist_free(dict);
231 203
204 return res;
205}
206
207np_error_t np_observe_notification( np_client_t client, const char *notification )
208{
209 if (!client || !notification) {
210 return NP_E_INVALID_ARG;
211 }
212 np_lock(client);
213 np_error_t res = internal_np_observe_notification(client, notification);
232 np_unlock(client); 214 np_unlock(client);
233 return res; 215 return res;
234} 216}
235 217
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) 218np_error_t np_observe_notifications(np_client_t client, const char **notification_spec)
248{ 219{
249 int i = 0; 220 int i = 0;
@@ -258,13 +229,15 @@ np_error_t np_observe_notifications(np_client_t client, const char **notificatio
258 return NP_E_INVALID_ARG; 229 return NP_E_INVALID_ARG;
259 } 230 }
260 231
232 np_lock(client);
261 while (notifications[i]) { 233 while (notifications[i]) {
262 res = np_observe_notification(client, notifications[i]); 234 res = internal_np_observe_notification(client, notifications[i]);
263 if (res != NP_E_SUCCESS) { 235 if (res != NP_E_SUCCESS) {
264 break; 236 break;
265 } 237 }
266 i++; 238 i++;
267 } 239 }
240 np_unlock(client);
268 241
269 return res; 242 return res;
270} 243}
@@ -277,7 +250,7 @@ np_error_t np_observe_notifications(np_client_t client, const char **notificatio
277 * with the notification that has been received. 250 * with the notification that has been received.
278 * 251 *
279 * @return 0 if a notification has been received or nothing has been received, 252 * @return 0 if a notification has been received or nothing has been received,
280 * or a negative value if an error occured. 253 * or a negative value if an error occurred.
281 * 254 *
282 * @note You probably want to check out np_set_notify_callback 255 * @note You probably want to check out np_set_notify_callback
283 * @see np_set_notify_callback 256 * @see np_set_notify_callback
@@ -292,11 +265,15 @@ static int np_get_notification(np_client_t client, char **notification)
292 265
293 np_lock(client); 266 np_lock(client);
294 267
295 property_list_service_receive_plist_with_timeout(client->parent, &dict, 500); 268 property_list_service_error_t perr = property_list_service_receive_plist_with_timeout(client->parent, &dict, 500);
296 if (!dict) { 269 if (perr == PROPERTY_LIST_SERVICE_E_RECEIVE_TIMEOUT) {
297 debug_info("NotificationProxy: no notification received!"); 270 debug_info("NotificationProxy: no notification received!");
298 res = 0; 271 res = 0;
299 } else { 272 } else if (perr != PROPERTY_LIST_SERVICE_E_SUCCESS) {
273 debug_info("NotificationProxy: error %d occurred!", perr);
274 res = perr;
275 }
276 if (dict) {
300 char *cmd_value = NULL; 277 char *cmd_value = NULL;
301 plist_t cmd_value_node = plist_dict_get_item(dict, "Command"); 278 plist_t cmd_value_node = plist_dict_get_item(dict, "Command");
302 279
@@ -315,11 +292,11 @@ static int np_get_notification(np_client_t client, char **notification)
315 res = -2; 292 res = -2;
316 if (name_value_node && name_value) { 293 if (name_value_node && name_value) {
317 *notification = name_value; 294 *notification = name_value;
318 debug_info("got notification %s\n", __func__, name_value); 295 debug_info("got notification %s", __func__, name_value);
319 res = 0; 296 res = 0;
320 } 297 }
321 } else if (cmd_value && !strcmp(cmd_value, "ProxyDeath")) { 298 } else if (cmd_value && !strcmp(cmd_value, "ProxyDeath")) {
322 debug_info("ERROR: NotificationProxy died!"); 299 debug_info("NotificationProxy died!");
323 res = -1; 300 res = -1;
324 } else if (cmd_value) { 301 } else if (cmd_value) {
325 debug_info("unknown NotificationProxy command '%s' received!", cmd_value); 302 debug_info("unknown NotificationProxy command '%s' received!", cmd_value);
@@ -342,7 +319,7 @@ static int np_get_notification(np_client_t client, char **notification)
342/** 319/**
343 * Internally used thread function. 320 * Internally used thread function.
344 */ 321 */
345gpointer np_notifier( gpointer arg ) 322void* np_notifier( void* arg )
346{ 323{
347 char *notification = NULL; 324 char *notification = NULL;
348 struct np_thread *npt = (struct np_thread*)arg; 325 struct np_thread *npt = (struct np_thread*)arg;
@@ -351,7 +328,10 @@ gpointer np_notifier( gpointer arg )
351 328
352 debug_info("starting callback."); 329 debug_info("starting callback.");
353 while (npt->client->parent) { 330 while (npt->client->parent) {
354 np_get_notification(npt->client, &notification); 331 if (np_get_notification(npt->client, &notification) < 0) {
332 npt->cbfunc("", npt->user_data);
333 break;
334 }
355 if (notification) { 335 if (notification) {
356 npt->cbfunc(notification, npt->user_data); 336 npt->cbfunc(notification, npt->user_data);
357 free(notification); 337 free(notification);
@@ -366,25 +346,6 @@ gpointer np_notifier( gpointer arg )
366 return NULL; 346 return NULL;
367} 347}
368 348
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 ) 349np_error_t np_set_notify_callback( np_client_t client, np_notify_cb_t notify_cb, void *user_data )
389{ 350{
390 if (!client) 351 if (!client)
@@ -394,11 +355,12 @@ np_error_t np_set_notify_callback( np_client_t client, np_notify_cb_t notify_cb,
394 355
395 np_lock(client); 356 np_lock(client);
396 if (client->notifier) { 357 if (client->notifier) {
397 debug_info("callback already set, removing\n"); 358 debug_info("callback already set, removing");
398 property_list_service_client_t parent = client->parent; 359 property_list_service_client_t parent = client->parent;
399 client->parent = NULL; 360 client->parent = NULL;
400 g_thread_join(client->notifier); 361 thread_join(client->notifier);
401 client->notifier = NULL; 362 thread_free(client->notifier);
363 client->notifier = THREAD_T_NULL;
402 client->parent = parent; 364 client->parent = parent;
403 } 365 }
404 366
@@ -409,8 +371,7 @@ np_error_t np_set_notify_callback( np_client_t client, np_notify_cb_t notify_cb,
409 npt->cbfunc = notify_cb; 371 npt->cbfunc = notify_cb;
410 npt->user_data = user_data; 372 npt->user_data = user_data;
411 373
412 client->notifier = g_thread_create(np_notifier, npt, TRUE, NULL); 374 if (thread_new(&client->notifier, np_notifier, npt) == 0) {
413 if (client->notifier) {
414 res = NP_E_SUCCESS; 375 res = NP_E_SUCCESS;
415 } 376 }
416 } 377 }