diff options
Diffstat (limited to 'src/notification_proxy.c')
-rw-r--r-- | src/notification_proxy.c | 253 |
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 | ||
31 | struct np_thread { | 38 | struct np_thread { |
32 | np_client_t client; | 39 | np_client_t client; |
@@ -41,19 +48,19 @@ struct np_thread { | |||
41 | */ | 48 | */ |
42 | static void np_lock(np_client_t client) | 49 | static 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 | */ |
53 | static void np_unlock(np_client_t client) | 60 | static 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 | /** | 92 | np_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 | */ | ||
97 | np_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 | /** | 110 | np_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 | */ | ||
130 | np_error_t np_client_free(np_client_t client) | 117 | np_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 | */ | ||
157 | np_error_t np_post_notification(np_client_t client, const char *notification) | 171 | np_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 | /** | 192 | static 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 | */ | ||
215 | np_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 | |||
207 | np_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 | */ | ||
247 | np_error_t np_observe_notifications(np_client_t client, const char **notification_spec) | 218 | np_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 | */ |
345 | gpointer np_notifier( gpointer arg ) | 322 | void* 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, ¬ification); | 331 | if (np_get_notification(npt->client, ¬ification) < 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 | */ | ||
388 | np_error_t np_set_notify_callback( np_client_t client, np_notify_cb_t notify_cb, void *user_data ) | 349 | np_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 | } |