summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorGravatar Nikias Bassen2013-09-17 11:30:01 +0200
committerGravatar Nikias Bassen2013-09-17 11:30:01 +0200
commitf4758e8b15cd30fe3f7f18de42e2ea20bc5696f0 (patch)
tree671e85e639b689b0b888a0f51c7dd5e15d408930 /src
parent10939f3ad5755d1117f20df2b97c0cbbd83bbcbe (diff)
downloadusbmuxd-f4758e8b15cd30fe3f7f18de42e2ea20bc5696f0.tar.gz
usbmuxd-f4758e8b15cd30fe3f7f18de42e2ea20bc5696f0.tar.bz2
remove libusbmuxd sources and adapt source tree to use autotools
libusbmuxd has been split off and is now managed in a separate repository. By the time of this commit, the repository is: git clone http://git.sukimashita.com/libusbmuxd.git
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am14
-rw-r--r--src/client.c616
-rw-r--r--src/client.h47
-rw-r--r--src/device.c781
-rw-r--r--src/device.h52
-rw-r--r--src/log.c95
-rw-r--r--src/log.h44
-rw-r--r--src/main.c618
-rw-r--r--src/usb-linux.c598
-rw-r--r--src/usb.h66
-rw-r--r--src/utils.c120
-rw-r--r--src/utils.h68
12 files changed, 3119 insertions, 0 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
new file mode 100644
index 0000000..d2fcfef
--- /dev/null
+++ b/src/Makefile.am
@@ -0,0 +1,14 @@
1AM_CFLAGS = $(GLOBAL_CFLAGS) -I$(top_srcdir)/src $(libplist_CFLAGS) $(libusb_CFLAGS)
2AM_LDFLAGS = $(libplist_LIBS) $(libusb_LIBS)
3
4bin_PROGRAMS = usbmuxd
5
6usbmuxd_SOURCES = client.c client.h \
7 device.c device.h \
8 log.c log.h \
9 usb-linux.c usb.h \
10 utils.c utils.h \
11 main.c
12usbmuxd_CFLAGS = $(AM_CFLAGS)
13usbmuxd_LDFLAGS = $(AM_LDFLAGS)
14
diff --git a/src/client.c b/src/client.c
new file mode 100644
index 0000000..ac1045a
--- /dev/null
+++ b/src/client.c
@@ -0,0 +1,616 @@
1/*
2 usbmuxd - iPhone/iPod Touch USB multiplex server daemon
3
4Copyright (C) 2009 Hector Martin "marcan" <hector@marcansoft.com>
5Copyright (C) 2009 Nikias Bassen <nikias@gmx.li>
6
7This program is free software; you can redistribute it and/or modify
8it under the terms of the GNU General Public License as published by
9the Free Software Foundation, either version 2 or version 3.
10
11This program is distributed in the hope that it will be useful,
12but WITHOUT ANY WARRANTY; without even the implied warranty of
13MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14GNU General Public License for more details.
15
16You should have received a copy of the GNU General Public License
17along with this program; if not, write to the Free Software
18Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19
20*/
21
22#ifdef HAVE_CONFIG_H
23#include <config.h>
24#endif
25
26#include <stdlib.h>
27#include <string.h>
28#include <errno.h>
29#include <unistd.h>
30#include <sys/types.h>
31#include <sys/socket.h>
32#include <sys/un.h>
33#include <arpa/inet.h>
34
35#ifdef HAVE_PLIST
36#include <plist/plist.h>
37#endif
38
39#include "log.h"
40#include "usb.h"
41#include "client.h"
42#include "device.h"
43
44#ifdef HAVE_PLIST
45#define CMD_BUF_SIZE 1024
46#else
47#define CMD_BUF_SIZE 256
48#endif
49#define REPLY_BUF_SIZE 1024
50
51enum client_state {
52 CLIENT_COMMAND, // waiting for command
53 CLIENT_LISTEN, // listening for devices
54 CLIENT_CONNECTING1, // issued connection request
55 CLIENT_CONNECTING2, // connection established, but waiting for response message to get sent
56 CLIENT_CONNECTED, // connected
57 CLIENT_DEAD
58};
59
60struct mux_client {
61 int fd;
62 unsigned char *ob_buf;
63 uint32_t ob_size;
64 uint32_t ob_capacity;
65 unsigned char *ib_buf;
66 uint32_t ib_size;
67 uint32_t ib_capacity;
68 short events, devents;
69 uint32_t connect_tag;
70 int connect_device;
71 enum client_state state;
72 uint32_t proto_version;
73};
74
75static struct collection client_list;
76
77int client_read(struct mux_client *client, void *buffer, uint32_t len)
78{
79 usbmuxd_log(LL_SPEW, "client_read fd %d buf %p len %d", client->fd, buffer, len);
80 if(client->state != CLIENT_CONNECTED) {
81 usbmuxd_log(LL_ERROR, "Attempted to read from client %d not in CONNECTED state", client->fd);
82 return -1;
83 }
84 return recv(client->fd, buffer, len, 0);
85}
86
87int client_write(struct mux_client *client, void *buffer, uint32_t len)
88{
89 usbmuxd_log(LL_SPEW, "client_write fd %d buf %p len %d", client->fd, buffer, len);
90 if(client->state != CLIENT_CONNECTED) {
91 usbmuxd_log(LL_ERROR, "Attempted to write to client %d not in CONNECTED state", client->fd);
92 return -1;
93 }
94 return send(client->fd, buffer, len, 0);
95}
96
97int client_set_events(struct mux_client *client, short events)
98{
99 if((client->state != CLIENT_CONNECTED) && (client->state != CLIENT_CONNECTING2)) {
100 usbmuxd_log(LL_ERROR, "client_set_events to client %d not in CONNECTED state", client->fd);
101 return -1;
102 }
103 client->devents = events;
104 if(client->state == CLIENT_CONNECTED)
105 client->events = events;
106 return 0;
107}
108
109int client_accept(int listenfd)
110{
111 struct sockaddr_un addr;
112 int cfd;
113 socklen_t len = sizeof(struct sockaddr_un);
114 cfd = accept(listenfd, (struct sockaddr *)&addr, &len);
115 if (cfd < 0) {
116 usbmuxd_log(LL_ERROR, "accept() failed (%s)", strerror(errno));
117 return cfd;
118 }
119
120 struct mux_client *client;
121 client = malloc(sizeof(struct mux_client));
122 memset(client, 0, sizeof(struct mux_client));
123
124 client->fd = cfd;
125 client->ob_buf = malloc(REPLY_BUF_SIZE);
126 client->ob_size = 0;
127 client->ob_capacity = REPLY_BUF_SIZE;
128 client->ib_buf = malloc(CMD_BUF_SIZE);
129 client->ib_size = 0;
130 client->ib_capacity = CMD_BUF_SIZE;
131 client->state = CLIENT_COMMAND;
132 client->events = POLLIN;
133
134 collection_add(&client_list, client);
135
136 usbmuxd_log(LL_INFO, "New client on fd %d", client->fd);
137 return client->fd;
138}
139
140void client_close(struct mux_client *client)
141{
142 usbmuxd_log(LL_INFO, "Disconnecting client fd %d", client->fd);
143 if(client->state == CLIENT_CONNECTING1 || client->state == CLIENT_CONNECTING2) {
144 usbmuxd_log(LL_INFO, "Client died mid-connect, aborting device %d connection", client->connect_device);
145 client->state = CLIENT_DEAD;
146 device_abort_connect(client->connect_device, client);
147 }
148 close(client->fd);
149 if(client->ob_buf)
150 free(client->ob_buf);
151 if(client->ib_buf)
152 free(client->ib_buf);
153 collection_remove(&client_list, client);
154 free(client);
155}
156
157void client_get_fds(struct fdlist *list)
158{
159 FOREACH(struct mux_client *client, &client_list) {
160 fdlist_add(list, FD_CLIENT, client->fd, client->events);
161 } ENDFOREACH
162}
163
164static int send_pkt(struct mux_client *client, uint32_t tag, enum usbmuxd_msgtype msg, void *payload, int payload_length)
165{
166 struct usbmuxd_header hdr;
167 hdr.version = client->proto_version;
168 hdr.length = sizeof(hdr) + payload_length;
169 hdr.message = msg;
170 hdr.tag = tag;
171 usbmuxd_log(LL_DEBUG, "send_pkt fd %d tag %d msg %d payload_length %d", client->fd, tag, msg, payload_length);
172 if((client->ob_capacity - client->ob_size) < hdr.length) {
173 usbmuxd_log(LL_ERROR, "Client %d output buffer full (%d bytes) while sending message %d (%d bytes)", client->fd, client->ob_capacity, hdr.message, hdr.length);
174 client_close(client);
175 return -1;
176 }
177 memcpy(client->ob_buf + client->ob_size, &hdr, sizeof(hdr));
178 if(payload && payload_length)
179 memcpy(client->ob_buf + client->ob_size + sizeof(hdr), payload, payload_length);
180 client->ob_size += hdr.length;
181 client->events |= POLLOUT;
182 return hdr.length;
183}
184
185static int send_result(struct mux_client *client, uint32_t tag, uint32_t result)
186{
187 int res = -1;
188#ifdef HAVE_PLIST
189 if (client->proto_version == 1) {
190 /* XML plist packet */
191 char *xml = NULL;
192 uint32_t xmlsize = 0;
193 plist_t dict = plist_new_dict();
194 plist_dict_insert_item(dict, "MessageType", plist_new_string("Result"));
195 plist_dict_insert_item(dict, "Number", plist_new_uint(result));
196 plist_to_xml(dict, &xml, &xmlsize);
197 plist_free(dict);
198 if (xml) {
199 res = send_pkt(client, tag, MESSAGE_PLIST, xml, xmlsize);
200 free(xml);
201 } else {
202 usbmuxd_log(LL_ERROR, "%s: Could not convert plist to xml", __func__);
203 }
204 } else
205#endif
206 {
207 /* binary packet */
208 res = send_pkt(client, tag, MESSAGE_RESULT, &result, sizeof(uint32_t));
209 }
210 return res;
211}
212
213int client_notify_connect(struct mux_client *client, enum usbmuxd_result result)
214{
215 usbmuxd_log(LL_SPEW, "client_notify_connect fd %d result %d", client->fd, result);
216 if(client->state == CLIENT_DEAD)
217 return -1;
218 if(client->state != CLIENT_CONNECTING1) {
219 usbmuxd_log(LL_ERROR, "client_notify_connect when client %d is not in CONNECTING1 state", client->fd);
220 return -1;
221 }
222 if(send_result(client, client->connect_tag, result) < 0)
223 return -1;
224 if(result == RESULT_OK) {
225 client->state = CLIENT_CONNECTING2;
226 client->events = POLLOUT; // wait for the result packet to go through
227 // no longer need this
228 free(client->ib_buf);
229 client->ib_buf = NULL;
230 } else {
231 client->state = CLIENT_COMMAND;
232 }
233 return 0;
234}
235
236static int notify_device_add(struct mux_client *client, struct device_info *dev)
237{
238 int res = -1;
239#ifdef HAVE_PLIST
240 if (client->proto_version == 1) {
241 /* XML plist packet */
242 char *xml = NULL;
243 uint32_t xmlsize = 0;
244 plist_t dict = plist_new_dict();
245 plist_dict_insert_item(dict, "MessageType", plist_new_string("Attached"));
246 plist_t props = plist_new_dict();
247 // TODO: get current usb speed
248 plist_dict_insert_item(props, "ConnectionSpeed", plist_new_uint(480000000));
249 plist_dict_insert_item(props, "ConnectionType", plist_new_string("USB"));
250 plist_dict_insert_item(props, "DeviceID", plist_new_uint(dev->id));
251 plist_dict_insert_item(props, "LocationID", plist_new_uint(dev->location));
252 plist_dict_insert_item(props, "ProductID", plist_new_uint(dev->pid));
253 plist_dict_insert_item(props, "SerialNumber", plist_new_string(dev->serial));
254 plist_dict_insert_item(dict, "Properties", props);
255 plist_to_xml(dict, &xml, &xmlsize);
256 plist_free(dict);
257 if (xml) {
258 res = send_pkt(client, 0, MESSAGE_PLIST, xml, xmlsize);
259 free(xml);
260 } else {
261 usbmuxd_log(LL_ERROR, "%s: Could not convert plist to xml", __func__);
262 }
263 } else
264#endif
265 {
266 /* binary packet */
267 struct usbmuxd_device_record dmsg;
268 memset(&dmsg, 0, sizeof(dmsg));
269 dmsg.device_id = dev->id;
270 strncpy(dmsg.serial_number, dev->serial, 256);
271 dmsg.serial_number[255] = 0;
272 dmsg.location = dev->location;
273 dmsg.product_id = dev->pid;
274 res = send_pkt(client, 0, MESSAGE_DEVICE_ADD, &dmsg, sizeof(dmsg));
275 }
276 return res;
277}
278
279static int notify_device_remove(struct mux_client *client, uint32_t device_id)
280{
281 int res = -1;
282#ifdef HAVE_PLIST
283 if (client->proto_version == 1) {
284 /* XML plist packet */
285 char *xml = NULL;
286 uint32_t xmlsize = 0;
287 plist_t dict = plist_new_dict();
288 plist_dict_insert_item(dict, "MessageType", plist_new_string("Detached"));
289 plist_dict_insert_item(dict, "DeviceID", plist_new_uint(device_id));
290 plist_to_xml(dict, &xml, &xmlsize);
291 plist_free(dict);
292 if (xml) {
293 res = send_pkt(client, 0, MESSAGE_PLIST, xml, xmlsize);
294 free(xml);
295 } else {
296 usbmuxd_log(LL_ERROR, "%s: Could not convert plist to xml", __func__);
297 }
298 } else
299#endif
300 {
301 /* binary packet */
302 res = send_pkt(client, 0, MESSAGE_DEVICE_REMOVE, &device_id, sizeof(uint32_t));
303 }
304 return res;
305}
306
307static int start_listen(struct mux_client *client)
308{
309 struct device_info *devs;
310 struct device_info *dev;
311 int count, i;
312
313 client->state = CLIENT_LISTEN;
314 count = device_get_count();
315 if(!count)
316 return 0;
317 devs = malloc(sizeof(struct device_info) * count);
318 count = device_get_list(devs);
319
320 // going to need a larger buffer for many devices
321 int needed_buffer = count * (sizeof(struct usbmuxd_device_record) + sizeof(struct usbmuxd_header)) + REPLY_BUF_SIZE;
322 if(client->ob_capacity < needed_buffer) {
323 usbmuxd_log(LL_DEBUG, "Enlarging client %d reply buffer %d -> %d to make space for device notifications", client->fd, client->ob_capacity, needed_buffer);
324 client->ob_buf = realloc(client->ob_buf, needed_buffer);
325 client->ob_capacity = needed_buffer;
326 }
327 dev = devs;
328 for(i=0; i<count; i++) {
329 if(notify_device_add(client, dev++) < 0) {
330 free(devs);
331 return -1;
332 }
333 }
334 free(devs);
335 return count;
336}
337
338static int client_command(struct mux_client *client, struct usbmuxd_header *hdr)
339{
340 int res;
341 usbmuxd_log(LL_DEBUG, "Client command in fd %d len %d ver %d msg %d tag %d", client->fd, hdr->length, hdr->version, hdr->message, hdr->tag);
342
343 if(client->state != CLIENT_COMMAND) {
344 usbmuxd_log(LL_ERROR, "Client %d command received in the wrong state", client->fd);
345 if(send_result(client, hdr->tag, RESULT_BADCOMMAND) < 0)
346 return -1;
347 client_close(client);
348 return -1;
349 }
350
351 struct usbmuxd_connect_request *ch;
352#ifdef HAVE_PLIST
353 char *payload;
354 uint32_t payload_size;
355#endif
356
357 switch(hdr->message) {
358#ifdef HAVE_PLIST
359 case MESSAGE_PLIST:
360 client->proto_version = 1;
361 payload = (char*)(hdr) + sizeof(struct usbmuxd_header);
362 payload_size = hdr->length - sizeof(struct usbmuxd_header);
363 plist_t dict = NULL;
364 plist_from_xml(payload, payload_size, &dict);
365 if (!dict) {
366 usbmuxd_log(LL_ERROR, "Could not parse plist from payload!");
367 return -1;
368 } else {
369 char *message = NULL;
370 plist_t node = plist_dict_get_item(dict, "MessageType");
371 plist_get_string_val(node, &message);
372 if (!message) {
373 usbmuxd_log(LL_ERROR, "Could not extract MessageType from plist!");
374 plist_free(dict);
375 return -1;
376 }
377 if (!strcmp(message, "Listen")) {
378 free(message);
379 plist_free(dict);
380 if (send_result(client, hdr->tag, 0) < 0)
381 return -1;
382 usbmuxd_log(LL_DEBUG, "Client %d now LISTENING", client->fd);
383 return start_listen(client);
384 } else if (!strcmp(message, "Connect")) {
385 uint64_t val;
386 uint16_t portnum = 0;
387 uint32_t device_id = 0;
388 free(message);
389 // get device id
390 node = plist_dict_get_item(dict, "DeviceID");
391 if (!node) {
392 usbmuxd_log(LL_ERROR, "Received connect request without device_id!");
393 plist_free(dict);
394 if (send_result(client, hdr->tag, RESULT_BADDEV) < 0)
395 return -1;
396 return 0;
397 }
398 val = 0;
399 plist_get_uint_val(node, &val);
400 device_id = (uint32_t)val;
401
402 // get port number
403 node = plist_dict_get_item(dict, "PortNumber");
404 if (!node) {
405 usbmuxd_log(LL_ERROR, "Received connect request without port number!");
406 plist_free(dict);
407 if (send_result(client, hdr->tag, RESULT_BADCOMMAND) < 0)
408 return -1;
409 return 0;
410 }
411 val = 0;
412 plist_get_uint_val(node, &val);
413 portnum = (uint16_t)val;
414
415 usbmuxd_log(LL_DEBUG, "Client %d connection request to device %d port %d", client->fd, device_id, ntohs(portnum));
416 res = device_start_connect(device_id, ntohs(portnum), client);
417 if(res < 0) {
418 if (send_result(client, hdr->tag, -res) < 0)
419 return -1;
420 } else {
421 client->connect_tag = hdr->tag;
422 client->connect_device = device_id;
423 client->state = CLIENT_CONNECTING1;
424 }
425 return 0;
426 } else {
427 usbmuxd_log(LL_ERROR, "Unexpected command '%s' received!", message);
428 free(message);
429 plist_free(dict);
430 if (send_result(client, hdr->tag, RESULT_BADCOMMAND) < 0)
431 return -1;
432 return 0;
433 }
434 }
435 // should not be reached?!
436 return -1;
437#endif
438 case MESSAGE_LISTEN:
439 if(send_result(client, hdr->tag, 0) < 0)
440 return -1;
441 usbmuxd_log(LL_DEBUG, "Client %d now LISTENING", client->fd);
442 return start_listen(client);
443 case MESSAGE_CONNECT:
444 ch = (void*)hdr;
445 usbmuxd_log(LL_DEBUG, "Client %d connection request to device %d port %d", client->fd, ch->device_id, ntohs(ch->port));
446 res = device_start_connect(ch->device_id, ntohs(ch->port), client);
447 if(res < 0) {
448 if(send_result(client, hdr->tag, -res) < 0)
449 return -1;
450 } else {
451 client->connect_tag = hdr->tag;
452 client->connect_device = ch->device_id;
453 client->state = CLIENT_CONNECTING1;
454 }
455 return 0;
456 default:
457 usbmuxd_log(LL_ERROR, "Client %d invalid command %d", client->fd, hdr->message);
458 if(send_result(client, hdr->tag, RESULT_BADCOMMAND) < 0)
459 return -1;
460 return 0;
461 }
462 return -1;
463}
464
465static void process_send(struct mux_client *client)
466{
467 int res;
468 if(!client->ob_size) {
469 usbmuxd_log(LL_WARNING, "Client %d OUT process but nothing to send?", client->fd);
470 client->events &= ~POLLOUT;
471 return;
472 }
473 res = send(client->fd, client->ob_buf, client->ob_size, 0);
474 if(res <= 0) {
475 usbmuxd_log(LL_ERROR, "Send to client fd %d failed: %d %s", client->fd, res, strerror(errno));
476 client_close(client);
477 return;
478 }
479 if(res == client->ob_size) {
480 client->ob_size = 0;
481 client->events &= ~POLLOUT;
482 if(client->state == CLIENT_CONNECTING2) {
483 usbmuxd_log(LL_DEBUG, "Client %d switching to CONNECTED state", client->fd);
484 client->state = CLIENT_CONNECTED;
485 client->events = client->devents;
486 // no longer need this
487 free(client->ob_buf);
488 client->ob_buf = NULL;
489 }
490 } else {
491 client->ob_size -= res;
492 memmove(client->ob_buf, client->ob_buf + res, client->ob_size);
493 }
494}
495static void process_recv(struct mux_client *client)
496{
497 int res;
498 int did_read = 0;
499 if(client->ib_size < sizeof(struct usbmuxd_header)) {
500 res = recv(client->fd, client->ib_buf + client->ib_size, sizeof(struct usbmuxd_header) - client->ib_size, 0);
501 if(res <= 0) {
502 if(res < 0)
503 usbmuxd_log(LL_ERROR, "Receive from client fd %d failed: %s", client->fd, strerror(errno));
504 else
505 usbmuxd_log(LL_INFO, "Client %d connection closed", client->fd);
506 client_close(client);
507 return;
508 }
509 client->ib_size += res;
510 if(client->ib_size < sizeof(struct usbmuxd_header))
511 return;
512 did_read = 1;
513 }
514 struct usbmuxd_header *hdr = (void*)client->ib_buf;
515#ifdef HAVE_PLIST
516 if((hdr->version != 0) && (hdr->version != 1)) {
517 usbmuxd_log(LL_INFO, "Client %d version mismatch: expected 0 or 1, got %d", client->fd, hdr->version);
518#else
519 if(hdr->version != USBMUXD_PROTOCOL_VERSION) {
520 usbmuxd_log(LL_INFO, "Client %d version mismatch: expected %d, got %d", client->fd, USBMUXD_PROTOCOL_VERSION, hdr->version);
521#endif
522 client_close(client);
523 return;
524 }
525 if(hdr->length > client->ib_capacity) {
526 usbmuxd_log(LL_INFO, "Client %d message is too long (%d bytes)", client->fd, hdr->length);
527 client_close(client);
528 return;
529 }
530 if(hdr->length < sizeof(struct usbmuxd_header)) {
531 usbmuxd_log(LL_ERROR, "Client %d message is too short (%d bytes)", client->fd, hdr->length);
532 client_close(client);
533 return;
534 }
535 if(client->ib_size < hdr->length) {
536 if(did_read)
537 return; //maybe we would block, so defer to next loop
538 res = recv(client->fd, client->ib_buf + client->ib_size, hdr->length - client->ib_size, 0);
539 if(res < 0) {
540 usbmuxd_log(LL_ERROR, "Receive from client fd %d failed: %s", client->fd, strerror(errno));
541 client_close(client);
542 return;
543 } else if(res == 0) {
544 usbmuxd_log(LL_INFO, "Client %d connection closed", client->fd);
545 client_close(client);
546 return;
547 }
548 client->ib_size += res;
549 if(client->ib_size < hdr->length)
550 return;
551 }
552 client_command(client, hdr);
553 client->ib_size = 0;
554}
555
556void client_process(int fd, short events)
557{
558 struct mux_client *client = NULL;
559 FOREACH(struct mux_client *lc, &client_list) {
560 if(lc->fd == fd) {
561 client = lc;
562 break;
563 }
564 } ENDFOREACH
565
566 if(!client) {
567 usbmuxd_log(LL_INFO, "client_process: fd %d not found in client list", fd);
568 return;
569 }
570
571 if(client->state == CLIENT_CONNECTED) {
572 usbmuxd_log(LL_SPEW, "client_process in CONNECTED state");
573 device_client_process(client->connect_device, client, events);
574 } else {
575 if(events & POLLIN) {
576 process_recv(client);
577 } else if(events & POLLOUT) { //not both in case client died as part of process_recv
578 process_send(client);
579 }
580 }
581
582}
583
584void client_device_add(struct device_info *dev)
585{
586 usbmuxd_log(LL_DEBUG, "client_device_add: id %d, location 0x%x, serial %s", dev->id, dev->location, dev->serial);
587 FOREACH(struct mux_client *client, &client_list) {
588 if(client->state == CLIENT_LISTEN)
589 notify_device_add(client, dev);
590 } ENDFOREACH
591}
592void client_device_remove(int device_id)
593{
594 uint32_t id = device_id;
595 usbmuxd_log(LL_DEBUG, "client_device_remove: id %d", device_id);
596 FOREACH(struct mux_client *client, &client_list) {
597 if(client->state == CLIENT_LISTEN)
598 notify_device_remove(client, id);
599 } ENDFOREACH
600}
601
602
603void client_init(void)
604{
605 usbmuxd_log(LL_DEBUG, "client_init");
606 collection_init(&client_list);
607}
608
609void client_shutdown(void)
610{
611 usbmuxd_log(LL_DEBUG, "client_shutdown");
612 FOREACH(struct mux_client *client, &client_list) {
613 client_close(client);
614 } ENDFOREACH
615 collection_free(&client_list);
616}
diff --git a/src/client.h b/src/client.h
new file mode 100644
index 0000000..60d8348
--- /dev/null
+++ b/src/client.h
@@ -0,0 +1,47 @@
1/*
2 usbmuxd - iPhone/iPod Touch USB multiplex server daemon
3
4Copyright (C) 2009 Hector Martin "marcan" <hector@marcansoft.com>
5Copyright (C) 2009 Nikias Bassen <nikias@gmx.li>
6
7This program is free software; you can redistribute it and/or modify
8it under the terms of the GNU General Public License as published by
9the Free Software Foundation, either version 2 or version 3.
10
11This program is distributed in the hope that it will be useful,
12but WITHOUT ANY WARRANTY; without even the implied warranty of
13MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14GNU General Public License for more details.
15
16You should have received a copy of the GNU General Public License
17along with this program; if not, write to the Free Software
18Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19
20*/
21
22#ifndef __CLIENT_H__
23#define __CLIENT_H__
24
25#include <stdint.h>
26#include "usbmuxd-proto.h"
27
28struct device_info;
29struct mux_client;
30
31int client_read(struct mux_client *client, void *buffer, uint32_t len);
32int client_write(struct mux_client *client, void *buffer, uint32_t len);
33int client_set_events(struct mux_client *client, short events);
34void client_close(struct mux_client *client);
35int client_notify_connect(struct mux_client *client, enum usbmuxd_result result);
36
37void client_device_add(struct device_info *dev);
38void client_device_remove(int device_id);
39
40int client_accept(int fd);
41void client_get_fds(struct fdlist *list);
42void client_process(int fd, short events);
43
44void client_init(void);
45void client_shutdown(void);
46
47#endif
diff --git a/src/device.c b/src/device.c
new file mode 100644
index 0000000..8c786a7
--- /dev/null
+++ b/src/device.c
@@ -0,0 +1,781 @@
1/*
2 usbmuxd - iPhone/iPod Touch USB multiplex server daemon
3
4Copyright (C) 2009 Hector Martin "marcan" <hector@marcansoft.com>
5
6This program is free software; you can redistribute it and/or modify
7it under the terms of the GNU General Public License as published by
8the Free Software Foundation, either version 2 or version 3.
9
10This program is distributed in the hope that it will be useful,
11but WITHOUT ANY WARRANTY; without even the implied warranty of
12MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13GNU General Public License for more details.
14
15You should have received a copy of the GNU General Public License
16along with this program; if not, write to the Free Software
17Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18
19*/
20
21#define _BSD_SOURCE
22
23#ifdef HAVE_CONFIG_H
24#include <config.h>
25#endif
26
27#include <sys/time.h>
28#include <netinet/in.h>
29#include <netinet/tcp.h>
30#include <stdlib.h>
31#include <string.h>
32#include <stdint.h>
33#include <inttypes.h>
34#include "device.h"
35#include "client.h"
36#include "usb.h"
37#include "log.h"
38
39int next_device_id;
40
41#define DEV_MRU 65536
42
43#define CONN_INBUF_SIZE 262144
44#define CONN_OUTBUF_SIZE 65536
45
46#define ACK_TIMEOUT 30
47
48enum mux_protocol {
49 MUX_PROTO_VERSION = 0,
50 MUX_PROTO_TCP = IPPROTO_TCP,
51};
52
53enum mux_dev_state {
54 MUXDEV_INIT, // sent version packet
55 MUXDEV_ACTIVE, // received version packet, active
56 MUXDEV_DEAD // dead
57};
58
59enum mux_conn_state {
60 CONN_CONNECTING, // SYN
61 CONN_CONNECTED, // SYN/SYNACK/ACK -> active
62 CONN_REFUSED, // RST received during SYN
63 CONN_DYING, // RST received
64 CONN_DEAD // being freed; used to prevent infinite recursion between client<->device freeing
65};
66
67struct mux_header
68{
69 uint32_t protocol;
70 uint32_t length;
71};
72
73struct version_header
74{
75 uint32_t major;
76 uint32_t minor;
77 uint32_t padding;
78};
79
80struct mux_device;
81
82#define CONN_ACK_PENDING 1
83
84struct mux_connection
85{
86 struct mux_device *dev;
87 struct mux_client *client;
88 enum mux_conn_state state;
89 uint16_t sport, dport;
90 uint32_t tx_seq, tx_ack, tx_acked, tx_win;
91 uint32_t rx_seq, rx_recvd, rx_ack, rx_win;
92 uint32_t max_payload;
93 uint32_t sendable;
94 int flags;
95 unsigned char *ib_buf;
96 uint32_t ib_size;
97 uint32_t ib_capacity;
98 unsigned char *ob_buf;
99 uint32_t ob_capacity;
100 short events;
101 uint64_t last_ack_time;
102};
103
104struct mux_device
105{
106 struct usb_device *usbdev;
107 int id;
108 enum mux_dev_state state;
109 struct collection connections;
110 uint16_t next_sport;
111 unsigned char *pktbuf;
112 uint32_t pktlen;
113};
114
115static struct collection device_list;
116
117uint64_t mstime64(void)
118{
119 struct timeval tv;
120 gettimeofday(&tv, NULL);
121 return tv.tv_sec * 1000 + tv.tv_usec / 1000;
122}
123
124static int get_next_device_id(void)
125{
126 while(1) {
127 int ok = 1;
128 FOREACH(struct mux_device *dev, &device_list) {
129 if(dev->id == next_device_id) {
130 next_device_id++;
131 ok = 0;
132 break;
133 }
134 } ENDFOREACH
135 if(ok)
136 return next_device_id++;
137 }
138}
139
140static int send_packet(struct mux_device *dev, enum mux_protocol proto, void *header, const void *data, int length)
141{
142 unsigned char *buffer;
143 int hdrlen;
144 int res;
145
146 switch(proto) {
147 case MUX_PROTO_VERSION:
148 hdrlen = sizeof(struct version_header);
149 break;
150 case MUX_PROTO_TCP:
151 hdrlen = sizeof(struct tcphdr);
152 break;
153 default:
154 usbmuxd_log(LL_ERROR, "Invalid protocol %d for outgoing packet (dev %d hdr %p data %p len %d)", proto, dev->id, header, data, length);
155 return -1;
156 }
157 usbmuxd_log(LL_SPEW, "send_packet(%d, 0x%x, %p, %p, %d)", dev->id, proto, header, data, length);
158
159 int total = sizeof(struct mux_header) + hdrlen + length;
160
161 if(total > USB_MTU) {
162 usbmuxd_log(LL_ERROR, "Tried to send packet larger than USB MTU (hdr %d data %d total %d) to device %d", hdrlen, length, total, dev->id);
163 return -1;
164 }
165
166 buffer = malloc(total);
167 struct mux_header *mhdr = (struct mux_header *)buffer;
168 mhdr->protocol = htonl(proto);
169 mhdr->length = htonl(total);
170 memcpy(buffer + sizeof(struct mux_header), header, hdrlen);
171 if(data && length)
172 memcpy(buffer + sizeof(struct mux_header) + hdrlen, data, length);
173
174 if((res = usb_send(dev->usbdev, buffer, total)) < 0) {
175 usbmuxd_log(LL_ERROR, "usb_send failed while sending packet (len %d) to device %d: %d", total, dev->id, res);
176 free(buffer);
177 return res;
178 }
179 return total;
180}
181
182static uint16_t find_sport(struct mux_device *dev)
183{
184 if(collection_count(&dev->connections) >= 65535)
185 return 0; //insanity
186
187 while(1) {
188 int ok = 1;
189 FOREACH(struct mux_connection *conn, &dev->connections) {
190 if(dev->next_sport == conn->sport) {
191 dev->next_sport++;
192 ok = 0;
193 break;
194 }
195 } ENDFOREACH
196 if(ok)
197 return dev->next_sport++;
198 }
199}
200
201static int send_anon_rst(struct mux_device *dev, uint16_t sport, uint16_t dport, uint32_t ack)
202{
203 struct tcphdr th;
204 memset(&th, 0, sizeof(th));
205 th.th_sport = htons(sport);
206 th.th_dport = htons(dport);
207 th.th_ack = htonl(ack);
208 th.th_flags = TH_RST;
209 th.th_off = sizeof(th) / 4;
210
211 usbmuxd_log(LL_DEBUG, "[OUT] dev=%d sport=%d dport=%d flags=0x%x", dev->id, sport, dport, th.th_flags);
212
213 int res = send_packet(dev, MUX_PROTO_TCP, &th, NULL, 0);
214 return res;
215}
216
217static int send_tcp(struct mux_connection *conn, uint8_t flags, const unsigned char *data, int length)
218{
219 struct tcphdr th;
220 memset(&th, 0, sizeof(th));
221 th.th_sport = htons(conn->sport);
222 th.th_dport = htons(conn->dport);
223 th.th_seq = htonl(conn->tx_seq);
224 th.th_ack = htonl(conn->tx_ack);
225 th.th_flags = flags;
226 th.th_off = sizeof(th) / 4;
227 th.th_win = htons(conn->tx_win >> 8);
228
229 usbmuxd_log(LL_DEBUG, "[OUT] dev=%d sport=%d dport=%d seq=%d ack=%d flags=0x%x window=%d[%d] len=%d",
230 conn->dev->id, conn->sport, conn->dport, conn->tx_seq, conn->tx_ack, flags, conn->tx_win, conn->tx_win >> 8, length);
231
232 int res = send_packet(conn->dev, MUX_PROTO_TCP, &th, data, length);
233 if(res >= 0) {
234 conn->tx_acked = conn->tx_ack;
235 conn->last_ack_time = mstime64();
236 conn->flags &= ~CONN_ACK_PENDING;
237 }
238 return res;
239}
240
241static void connection_teardown(struct mux_connection *conn)
242{
243 int res;
244 if(conn->state == CONN_DEAD)
245 return;
246 usbmuxd_log(LL_DEBUG, "connection_teardown dev %d sport %d dport %d", conn->dev->id, conn->sport, conn->dport);
247 if(conn->dev->state != MUXDEV_DEAD && conn->state != CONN_DYING && conn->state != CONN_REFUSED) {
248 res = send_tcp(conn, TH_RST, NULL, 0);
249 if(res < 0)
250 usbmuxd_log(LL_ERROR, "Error sending TCP RST to device %d (%d->%d)", conn->dev->id, conn->sport, conn->dport);
251 }
252 if(conn->client) {
253 if(conn->state == CONN_REFUSED || conn->state == CONN_CONNECTING) {
254 client_notify_connect(conn->client, RESULT_CONNREFUSED);
255 } else {
256 conn->state = CONN_DEAD;
257 client_close(conn->client);
258 }
259 }
260 if(conn->ib_buf)
261 free(conn->ib_buf);
262 if(conn->ob_buf)
263 free(conn->ob_buf);
264 collection_remove(&conn->dev->connections, conn);
265 free(conn);
266}
267
268int device_start_connect(int device_id, uint16_t dport, struct mux_client *client)
269{
270 struct mux_device *dev = NULL;
271 FOREACH(struct mux_device *cdev, &device_list) {
272 if(cdev->id == device_id) {
273 dev = cdev;
274 break;
275 }
276 } ENDFOREACH
277 if(!dev) {
278 usbmuxd_log(LL_WARNING, "Attempted to connect to nonexistent device %d", device_id);
279 return -RESULT_BADDEV;
280 }
281
282 uint16_t sport = find_sport(dev);
283 if(!sport) {
284 usbmuxd_log(LL_WARNING, "Unable to allocate port for device %d", device_id);
285 return -RESULT_BADDEV;
286 }
287
288 struct mux_connection *conn;
289 conn = malloc(sizeof(struct mux_connection));
290 memset(conn, 0, sizeof(struct mux_connection));
291
292 conn->dev = dev;
293 conn->client = client;
294 conn->state = CONN_CONNECTING;
295 conn->sport = sport;
296 conn->dport = dport;
297 conn->tx_seq = 0;
298 conn->tx_ack = 0;
299 conn->tx_acked = 0;
300 conn->tx_win = 131072;
301 conn->rx_recvd = 0;
302 conn->flags = 0;
303 conn->max_payload = USB_MTU - sizeof(struct mux_header) - sizeof(struct tcphdr);
304
305 conn->ob_buf = malloc(CONN_OUTBUF_SIZE);
306 conn->ob_capacity = CONN_OUTBUF_SIZE;
307 conn->ib_buf = malloc(CONN_INBUF_SIZE);
308 conn->ib_capacity = CONN_INBUF_SIZE;
309 conn->ib_size = 0;
310
311 int res;
312
313 res = send_tcp(conn, TH_SYN, NULL, 0);
314 if(res < 0) {
315 usbmuxd_log(LL_ERROR, "Error sending TCP SYN to device %d (%d->%d)", dev->id, sport, dport);
316 free(conn);
317 return -RESULT_CONNREFUSED; //bleh
318 }
319 collection_add(&dev->connections, conn);
320 return 0;
321}
322
323static void update_connection(struct mux_connection *conn)
324{
325 uint32_t sent = conn->tx_seq - conn->rx_ack;
326
327 if(conn->rx_win > sent)
328 conn->sendable = conn->rx_win - sent;
329 else
330 conn->sendable = 0;
331
332 if(conn->sendable > conn->ob_capacity)
333 conn->sendable = conn->ob_capacity;
334 if(conn->sendable > conn->max_payload)
335 conn->sendable = conn->max_payload;
336
337 if(conn->sendable > 0)
338 conn->events |= POLLIN;
339 else
340 conn->events &= ~POLLIN;
341
342 if(conn->ib_size)
343 conn->events |= POLLOUT;
344 else
345 conn->events &= ~POLLOUT;
346
347 if(conn->tx_acked != conn->tx_ack)
348 conn->flags |= CONN_ACK_PENDING;
349 else
350 conn->flags &= ~CONN_ACK_PENDING;
351
352 usbmuxd_log(LL_SPEW, "update_connection: sendable %d, events %d, flags %d", conn->sendable, conn->events, conn->flags);
353 client_set_events(conn->client, conn->events);
354}
355
356void device_client_process(int device_id, struct mux_client *client, short events)
357{
358 struct mux_connection *conn = NULL;
359 FOREACH(struct mux_device *dev, &device_list) {
360 if(dev->id == device_id) {
361 FOREACH(struct mux_connection *lconn, &dev->connections) {
362 if(lconn->client == client) {
363 conn = lconn;
364 break;
365 }
366 } ENDFOREACH
367 break;
368 }
369 } ENDFOREACH
370
371 if(!conn) {
372 usbmuxd_log(LL_WARNING, "Could not find connection for device %d client %p", device_id, client);
373 return;
374 }
375 usbmuxd_log(LL_SPEW, "device_client_process (%d)", events);
376
377 int res;
378 int size;
379 if(events & POLLOUT) {
380 size = client_write(conn->client, conn->ib_buf, conn->ib_size);
381 if(size <= 0) {
382 usbmuxd_log(LL_DEBUG, "error writing to client (%d)", size);
383 connection_teardown(conn);
384 return;
385 }
386 conn->tx_ack += size;
387 if(size == conn->ib_size) {
388 conn->ib_size = 0;
389 } else {
390 conn->ib_size -= size;
391 memmove(conn->ib_buf, conn->ib_buf + size, conn->ib_size);
392 }
393 }
394 if(events & POLLIN) {
395 size = client_read(conn->client, conn->ob_buf, conn->sendable);
396 if(size <= 0) {
397 usbmuxd_log(LL_DEBUG, "error reading from client (%d)", size);
398 connection_teardown(conn);
399 return;
400 }
401 res = send_tcp(conn, TH_ACK, conn->ob_buf, size);
402 if(res < 0) {
403 connection_teardown(conn);
404 return;
405 }
406 conn->tx_seq += size;
407 }
408
409 update_connection(conn);
410}
411
412static void connection_device_input(struct mux_connection *conn, unsigned char *payload, uint32_t payload_length)
413{
414 if((conn->ib_size + payload_length) > conn->ib_capacity) {
415 usbmuxd_log(LL_ERROR, "Input buffer overflow on device %d connection %d->%d (space=%d, payload=%d)", conn->dev->id, conn->sport, conn->dport, conn->ib_capacity-conn->ib_size, payload_length);
416 connection_teardown(conn);
417 return;
418 }
419 memcpy(conn->ib_buf + conn->ib_size, payload, payload_length);
420 conn->ib_size += payload_length;
421 conn->rx_recvd += payload_length;
422 update_connection(conn);
423}
424
425void device_abort_connect(int device_id, struct mux_client *client)
426{
427 FOREACH(struct mux_device *dev, &device_list) {
428 if(dev->id == device_id) {
429 FOREACH(struct mux_connection *conn, &dev->connections) {
430 if(conn->client == client) {
431 connection_teardown(conn);
432 return;
433 }
434 } ENDFOREACH
435 usbmuxd_log(LL_WARNING, "Attempted to abort for nonexistent connection for device %d", device_id);
436 return;
437 }
438 } ENDFOREACH
439 usbmuxd_log(LL_WARNING, "Attempted to abort connection for nonexistent device %d", device_id);
440}
441
442static void device_version_input(struct mux_device *dev, struct version_header *vh)
443{
444 if(dev->state != MUXDEV_INIT) {
445 usbmuxd_log(LL_WARNING, "Version packet from already initialized device %d", dev->id);
446 return;
447 }
448 vh->major = ntohl(vh->major);
449 vh->minor = ntohl(vh->minor);
450 if(vh->major != 1 || vh->minor != 0) {
451 usbmuxd_log(LL_ERROR, "Device %d has unknown version %d.%d\n", dev->id, vh->major, vh->minor);
452 collection_remove(&device_list, dev);
453 free(dev);
454 return;
455 }
456 usbmuxd_log(LL_NOTICE, "Connected to v%d.%d device %d on location 0x%x with serial number %s", vh->major, vh->minor, dev->id, usb_get_location(dev->usbdev), usb_get_serial(dev->usbdev));
457 dev->state = MUXDEV_ACTIVE;
458 collection_init(&dev->connections);
459 struct device_info info;
460 info.id = dev->id;
461 info.location = usb_get_location(dev->usbdev);
462 info.serial = usb_get_serial(dev->usbdev);
463 info.pid = usb_get_pid(dev->usbdev);
464 client_device_add(&info);
465}
466
467static void device_tcp_input(struct mux_device *dev, struct tcphdr *th, unsigned char *payload, uint32_t payload_length)
468{
469 uint16_t sport = ntohs(th->th_dport);
470 uint16_t dport = ntohs(th->th_sport);
471 struct mux_connection *conn = NULL;
472
473 usbmuxd_log(LL_DEBUG, "[IN] dev=%d sport=%d dport=%d seq=%d ack=%d flags=0x%x window=%d[%d] len=%d",
474 dev->id, dport, sport, ntohl(th->th_seq), ntohl(th->th_ack), th->th_flags, ntohs(th->th_win) << 8, ntohs(th->th_win), payload_length);
475
476 if(dev->state != MUXDEV_ACTIVE) {
477 usbmuxd_log(LL_ERROR, "Received TCP packet from device %d but the device isn't active yet, discarding\n", dev->id);
478 return;
479 }
480
481 FOREACH(struct mux_connection *lconn, &dev->connections) {
482 if(lconn->sport == sport && lconn->dport == dport) {
483 conn = lconn;
484 break;
485 }
486 } ENDFOREACH
487
488 if(!conn) {
489 usbmuxd_log(LL_INFO, "No connection for device %d incoming packet %d->%d", dev->id, dport, sport);
490 if(!(th->th_flags & TH_RST)) {
491 if(send_anon_rst(dev, sport, dport, ntohl(th->th_seq)) < 0)
492 usbmuxd_log(LL_ERROR, "Error sending TCP RST to device %d (%d->%d)", conn->dev->id, sport, dport);
493 }
494 return;
495 }
496
497 conn->rx_seq = ntohl(th->th_seq);
498 conn->rx_ack = ntohl(th->th_ack);
499 conn->rx_win = ntohs(th->th_win) << 8;
500
501 if(th->th_flags & TH_RST) {
502 char *buf = malloc(payload_length+1);
503 memcpy(buf, payload, payload_length);
504 if(payload_length && (buf[payload_length-1] == '\n'))
505 buf[payload_length-1] = 0;
506 buf[payload_length] = 0;
507 usbmuxd_log(LL_DEBUG, "RST reason: %s", buf);
508 free(buf);
509 }
510
511 if(conn->state == CONN_CONNECTING) {
512 if(th->th_flags != (TH_SYN|TH_ACK)) {
513 if(th->th_flags & TH_RST)
514 conn->state = CONN_REFUSED;
515 usbmuxd_log(LL_INFO, "Connection refused by device %d (%d->%d)", dev->id, sport, dport);
516 connection_teardown(conn); //this also sends the notification to the client
517 } else {
518 conn->tx_seq++;
519 conn->tx_ack++;
520 conn->rx_recvd = conn->rx_seq;
521 if(send_tcp(conn, TH_ACK, NULL, 0) < 0) {
522 usbmuxd_log(LL_ERROR, "Error sending TCP ACK to device %d (%d->%d)", dev->id, sport, dport);
523 connection_teardown(conn);
524 return;
525 }
526 conn->state = CONN_CONNECTED;
527 if(client_notify_connect(conn->client, RESULT_OK) < 0) {
528 conn->client = NULL;
529 connection_teardown(conn);
530 }
531 update_connection(conn);
532 }
533 } else if(conn->state == CONN_CONNECTED) {
534 if(th->th_flags != TH_ACK) {
535 usbmuxd_log(LL_INFO, "Connection reset by device %d (%d->%d)", dev->id, sport, dport);
536 if(th->th_flags & TH_RST)
537 conn->state = CONN_DYING;
538 connection_teardown(conn);
539 } else {
540 connection_device_input(conn, payload, payload_length);
541 }
542 }
543}
544
545void device_data_input(struct usb_device *usbdev, unsigned char *buffer, uint32_t length)
546{
547 struct mux_device *dev = NULL;
548 FOREACH(struct mux_device *tdev, &device_list) {
549 if(tdev->usbdev == usbdev) {
550 dev = tdev;
551 break;
552 }
553 } ENDFOREACH
554 if(!dev) {
555 usbmuxd_log(LL_WARNING, "Cannot find device entry for RX input from USB device %p on location 0x%x", usbdev, usb_get_location(usbdev));
556 return;
557 }
558
559 if(!length)
560 return;
561
562 // sanity check (should never happen with current USB implementation)
563 if((length > USB_MRU) || (length > DEV_MRU)) {
564 usbmuxd_log(LL_ERROR, "Too much data received from USB (%d), file a bug", length);
565 return;
566 }
567
568 usbmuxd_log(LL_SPEW, "Mux data input for device %p: %p len %d", dev, buffer, length);
569
570 // handle broken up transfers
571 if(dev->pktlen) {
572 if((length + dev->pktlen) > DEV_MRU) {
573 usbmuxd_log(LL_ERROR, "Incoming split packet is too large (%d so far), dropping!", length + dev->pktlen);
574 dev->pktlen = 0;
575 return;
576 }
577 memcpy(dev->pktbuf + dev->pktlen, buffer, length);
578 struct mux_header *mhdr = (struct mux_header *)dev->pktbuf;
579 if((length < USB_MRU) || (ntohl(mhdr->length) == (length + dev->pktlen))) {
580 buffer = dev->pktbuf;
581 length += dev->pktlen;
582 dev->pktlen = 0;
583 usbmuxd_log(LL_SPEW, "Gathered mux data from buffer (total size: %d)", length);
584 } else {
585 dev->pktlen += length;
586 usbmuxd_log(LL_SPEW, "Appended mux data to buffer (total size: %d)", dev->pktlen);
587 return;
588 }
589 } else {
590 struct mux_header *mhdr = (struct mux_header *)buffer;
591 if((length == USB_MRU) && (length < ntohl(mhdr->length))) {
592 memcpy(dev->pktbuf, buffer, length);
593 dev->pktlen = length;
594 usbmuxd_log(LL_SPEW, "Copied mux data to buffer (size: %d)", dev->pktlen);
595 return;
596 }
597 }
598
599 struct mux_header *mhdr = (struct mux_header *)buffer;
600
601 if(ntohl(mhdr->length) != length) {
602 usbmuxd_log(LL_ERROR, "Incoming packet size mismatch (dev %d, expected %d, got %d)", dev->id, ntohl(mhdr->length), length);
603 return;
604 }
605
606 struct tcphdr *th;
607 unsigned char *payload;
608 uint32_t payload_length;
609
610 switch(ntohl(mhdr->protocol)) {
611 case MUX_PROTO_VERSION:
612 if(length < (sizeof(struct mux_header) + sizeof(struct version_header))) {
613 usbmuxd_log(LL_ERROR, "Incoming version packet is too small (%d)", length);
614 return;
615 }
616 device_version_input(dev, (struct version_header *)(mhdr+1));
617 break;
618 case MUX_PROTO_TCP:
619 if(length < (sizeof(struct mux_header) + sizeof(struct tcphdr))) {
620 usbmuxd_log(LL_ERROR, "Incoming TCP packet is too small (%d)", length);
621 return;
622 }
623 th = (struct tcphdr *)(mhdr+1);
624 payload = (unsigned char *)(th+1);
625 payload_length = length - sizeof(struct tcphdr) - sizeof(struct mux_header);
626 device_tcp_input(dev, (struct tcphdr *)(mhdr+1), payload, payload_length);
627 break;
628 default:
629 usbmuxd_log(LL_ERROR, "Incoming packet for device %d has unknown protocol 0x%x)", dev->id, ntohl(mhdr->protocol));
630 break;
631 }
632
633}
634
635int device_add(struct usb_device *usbdev)
636{
637 int res;
638 int id = get_next_device_id();
639 struct mux_device *dev;
640 usbmuxd_log(LL_NOTICE, "Connecting to new device on location 0x%x as ID %d", usb_get_location(usbdev), id);
641 dev = malloc(sizeof(struct mux_device));
642 dev->id = id;
643 dev->usbdev = usbdev;
644 dev->state = MUXDEV_INIT;
645 dev->next_sport = 1;
646 dev->pktbuf = malloc(DEV_MRU);
647 dev->pktlen = 0;
648 struct version_header vh;
649 vh.major = htonl(1);
650 vh.minor = htonl(0);
651 vh.padding = 0;
652 if((res = send_packet(dev, MUX_PROTO_VERSION, &vh, NULL, 0)) < 0) {
653 usbmuxd_log(LL_ERROR, "Error sending version request packet to device %d", id);
654 free(dev);
655 return res;
656 }
657 collection_add(&device_list, dev);
658 return 0;
659}
660
661void device_remove(struct usb_device *usbdev)
662{
663 FOREACH(struct mux_device *dev, &device_list) {
664 if(dev->usbdev == usbdev) {
665 usbmuxd_log(LL_NOTICE, "Removed device %d on location 0x%x", dev->id, usb_get_location(usbdev));
666 if(dev->state == MUXDEV_ACTIVE) {
667 dev->state = MUXDEV_DEAD;
668 FOREACH(struct mux_connection *conn, &dev->connections) {
669 connection_teardown(conn);
670 } ENDFOREACH
671 client_device_remove(dev->id);
672 collection_free(&dev->connections);
673 }
674 collection_remove(&device_list, dev);
675 free(dev->pktbuf);
676 free(dev);
677 return;
678 }
679 } ENDFOREACH
680 usbmuxd_log(LL_WARNING, "Cannot find device entry while removing USB device %p on location 0x%x", usbdev, usb_get_location(usbdev));
681}
682
683int device_get_count(void)
684{
685 int count = 0;
686 FOREACH(struct mux_device *dev, &device_list) {
687 if(dev->state == MUXDEV_ACTIVE)
688 count++;
689 } ENDFOREACH
690 return count;
691}
692
693int device_get_list(struct device_info *p)
694{
695 int count = 0;
696 FOREACH(struct mux_device *dev, &device_list) {
697 if(dev->state == MUXDEV_ACTIVE) {
698 p->id = dev->id;
699 p->serial = usb_get_serial(dev->usbdev);
700 p->location = usb_get_location(dev->usbdev);
701 p->pid = usb_get_pid(dev->usbdev);
702 count++;
703 p++;
704 }
705 } ENDFOREACH
706 return count;
707}
708
709int device_get_timeout(void)
710{
711 uint64_t oldest = (uint64_t)-1;
712 FOREACH(struct mux_device *dev, &device_list) {
713 if(dev->state == MUXDEV_ACTIVE) {
714 FOREACH(struct mux_connection *conn, &dev->connections) {
715 if((conn->state == CONN_CONNECTED) && (conn->flags & CONN_ACK_PENDING) && conn->last_ack_time < oldest)
716 oldest = conn->last_ack_time;
717 } ENDFOREACH
718 }
719 } ENDFOREACH
720 uint64_t ct = mstime64();
721 if(oldest == -1)
722 return 100000; //meh
723 if((ct - oldest) > ACK_TIMEOUT)
724 return 0;
725 return ACK_TIMEOUT - (ct - oldest);
726}
727
728void device_check_timeouts(void)
729{
730 uint64_t ct = mstime64();
731 FOREACH(struct mux_device *dev, &device_list) {
732 if(dev->state == MUXDEV_ACTIVE) {
733 FOREACH(struct mux_connection *conn, &dev->connections) {
734 if((conn->state == CONN_CONNECTED) &&
735 (conn->flags & CONN_ACK_PENDING) &&
736 (ct - conn->last_ack_time) > ACK_TIMEOUT) {
737 usbmuxd_log(LL_DEBUG, "Sending ACK due to expired timeout (%" PRIu64 " -> %" PRIu64 ")", conn->last_ack_time, ct);
738 if(send_tcp(conn, TH_ACK, NULL, 0) < 0) {
739 usbmuxd_log(LL_ERROR, "Error sending TCP ACK to device %d (%d->%d)", dev->id, conn->sport, conn->dport);
740 connection_teardown(conn);
741 }
742 }
743 } ENDFOREACH
744 }
745 } ENDFOREACH
746}
747
748void device_init(void)
749{
750 usbmuxd_log(LL_DEBUG, "device_init");
751 collection_init(&device_list);
752 next_device_id = 1;
753}
754
755void device_kill_connections(void)
756{
757 usbmuxd_log(LL_DEBUG, "device_kill_connections");
758 FOREACH(struct mux_device *dev, &device_list) {
759 if(dev->state != MUXDEV_INIT) {
760 FOREACH(struct mux_connection *conn, &dev->connections) {
761 connection_teardown(conn);
762 } ENDFOREACH
763 }
764 } ENDFOREACH
765 // give USB a while to send the final connection RSTs and the like
766 usb_process_timeout(100);
767}
768
769void device_shutdown(void)
770{
771 usbmuxd_log(LL_DEBUG, "device_shutdown");
772 FOREACH(struct mux_device *dev, &device_list) {
773 FOREACH(struct mux_connection *conn, &dev->connections) {
774 connection_teardown(conn);
775 } ENDFOREACH
776 collection_free(&dev->connections);
777 collection_remove(&device_list, dev);
778 free(dev);
779 } ENDFOREACH
780 collection_free(&device_list);
781}
diff --git a/src/device.h b/src/device.h
new file mode 100644
index 0000000..ea77069
--- /dev/null
+++ b/src/device.h
@@ -0,0 +1,52 @@
1/*
2 usbmuxd - iPhone/iPod Touch USB multiplex server daemon
3
4Copyright (C) 2009 Hector Martin "marcan" <hector@marcansoft.com>
5
6This program is free software; you can redistribute it and/or modify
7it under the terms of the GNU General Public License as published by
8the Free Software Foundation, either version 2 or version 3.
9
10This program is distributed in the hope that it will be useful,
11but WITHOUT ANY WARRANTY; without even the implied warranty of
12MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13GNU General Public License for more details.
14
15You should have received a copy of the GNU General Public License
16along with this program; if not, write to the Free Software
17Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18
19*/
20
21#ifndef __DEVICE_H__
22#define __DEVICE_H__
23
24#include "usb.h"
25#include "client.h"
26
27struct device_info {
28 int id;
29 const char *serial;
30 uint32_t location;
31 uint16_t pid;
32};
33
34void device_data_input(struct usb_device *dev, unsigned char *buf, uint32_t length);
35
36int device_add(struct usb_device *dev);
37void device_remove(struct usb_device *dev);
38
39int device_start_connect(int device_id, uint16_t port, struct mux_client *client);
40void device_client_process(int device_id, struct mux_client *client, short events);
41void device_abort_connect(int device_id, struct mux_client *client);
42
43int device_get_count(void);
44int device_get_list(struct device_info *p);
45
46int device_get_timeout(void);
47void device_check_timeouts(void);
48
49void device_init(void);
50void device_kill_connections(void);
51void device_shutdown(void);
52#endif
diff --git a/src/log.c b/src/log.c
new file mode 100644
index 0000000..1973257
--- /dev/null
+++ b/src/log.c
@@ -0,0 +1,95 @@
1/*
2 usbmuxd - iPhone/iPod Touch USB multiplex server daemon
3
4Copyright (C) 2009 Hector Martin "marcan" <hector@marcansoft.com>
5Copyright (C) 2009 Nikias Bassen <nikias@gmx.li>
6
7This program is free software; you can redistribute it and/or modify
8it under the terms of the GNU General Public License as published by
9the Free Software Foundation, either version 2 or version 3.
10
11This program is distributed in the hope that it will be useful,
12but WITHOUT ANY WARRANTY; without even the implied warranty of
13MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14GNU General Public License for more details.
15
16You should have received a copy of the GNU General Public License
17along with this program; if not, write to the Free Software
18Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19
20*/
21
22#ifdef HAVE_CONFIG_H
23#include <config.h>
24#endif
25
26#include <stdio.h>
27#include <stdlib.h>
28#include <string.h>
29#include <stdarg.h>
30#include <time.h>
31#include <sys/time.h>
32#include <syslog.h>
33
34#include "log.h"
35
36int log_level = LL_WARNING;
37
38int log_syslog = 0;
39
40void log_enable_syslog()
41{
42 if (!log_syslog) {
43 openlog("usbmuxd", LOG_PID, 0);
44 log_syslog = 1;
45 }
46}
47
48void log_disable_syslog()
49{
50 if (log_syslog) {
51 closelog();
52 }
53}
54
55static int level_to_syslog_level(int level)
56{
57 int result = level + LOG_CRIT;
58 if (result > LOG_DEBUG) {
59 result = LOG_DEBUG;
60 }
61 return result;
62}
63
64void usbmuxd_log(enum loglevel level, const char *fmt, ...)
65{
66 va_list ap;
67 char *fs;
68 struct timeval ts;
69 struct tm *tp;
70
71 if(level > log_level)
72 return;
73
74 gettimeofday(&ts, NULL);
75 tp = localtime(&ts.tv_sec);
76
77 fs = malloc(20 + strlen(fmt));
78
79 if(log_syslog) {
80 sprintf(fs, "[%d] %s\n", level, fmt);
81 } else {
82 strftime(fs, 10, "[%H:%M:%S", tp);
83 sprintf(fs+9, ".%03d][%d] %s\n", (int)(ts.tv_usec / 1000), level, fmt);
84 }
85
86 va_start(ap, fmt);
87 if (log_syslog) {
88 vsyslog(level_to_syslog_level(level), fs, ap);
89 } else {
90 vfprintf(stderr, fs, ap);
91 }
92 va_end(ap);
93
94 free(fs);
95}
diff --git a/src/log.h b/src/log.h
new file mode 100644
index 0000000..eeefa41
--- /dev/null
+++ b/src/log.h
@@ -0,0 +1,44 @@
1/*
2 usbmuxd - iPhone/iPod Touch USB multiplex server daemon
3
4Copyright (C) 2009 Hector Martin "marcan" <hector@marcansoft.com>
5Copyright (C) 2009 Nikias Bassen <nikias@gmx.li>
6
7This program is free software; you can redistribute it and/or modify
8it under the terms of the GNU General Public License as published by
9the Free Software Foundation, either version 2 or version 3.
10
11This program is distributed in the hope that it will be useful,
12but WITHOUT ANY WARRANTY; without even the implied warranty of
13MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14GNU General Public License for more details.
15
16You should have received a copy of the GNU General Public License
17along with this program; if not, write to the Free Software
18Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19
20*/
21
22#ifndef __LOG_H__
23#define __LOG_H__
24
25enum loglevel {
26 LL_FATAL = 0,
27 LL_ERROR,
28 LL_WARNING,
29 LL_NOTICE,
30 LL_INFO,
31 LL_DEBUG,
32 LL_SPEW,
33 LL_FLOOD,
34};
35
36extern int log_level;
37
38void log_enable_syslog();
39void log_disable_syslog();
40
41void usbmuxd_log(enum loglevel level, const char *fmt, ...) __attribute__ ((format (printf, 2, 3)));
42
43
44#endif
diff --git a/src/main.c b/src/main.c
new file mode 100644
index 0000000..de5e5cc
--- /dev/null
+++ b/src/main.c
@@ -0,0 +1,618 @@
1/*
2 usbmuxd - iPhone/iPod Touch USB multiplex server daemon
3
4Copyright (C) 2009 Hector Martin "marcan" <hector@marcansoft.com>
5Copyright (C) 2009 Nikias Bassen <nikias@gmx.li>
6Copyright (C) 2009 Paul Sladen <libiphone@paul.sladen.org>
7
8This program is free software; you can redistribute it and/or modify
9it under the terms of the GNU General Public License as published by
10the Free Software Foundation, either version 2 or version 3.
11
12This program is distributed in the hope that it will be useful,
13but WITHOUT ANY WARRANTY; without even the implied warranty of
14MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15GNU General Public License for more details.
16
17You should have received a copy of the GNU General Public License
18along with this program; if not, write to the Free Software
19Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20
21*/
22
23#define _BSD_SOURCE
24#define _GNU_SOURCE
25
26#ifdef HAVE_CONFIG_H
27#include <config.h>
28#endif
29
30#include <stdio.h>
31#include <errno.h>
32#include <string.h>
33#include <stdlib.h>
34#include <signal.h>
35#include <unistd.h>
36#include <sys/socket.h>
37#include <sys/un.h>
38#include <sys/stat.h>
39#include <sys/types.h>
40#include <fcntl.h>
41#include <getopt.h>
42#include <pwd.h>
43#include <grp.h>
44
45#include "log.h"
46#include "usb.h"
47#include "device.h"
48#include "client.h"
49
50static const char *socket_path = "/var/run/usbmuxd";
51static const char *lockfile = "/var/run/usbmuxd.pid";
52
53int should_exit;
54int should_discover;
55
56static int verbose = 0;
57static int foreground = 0;
58static int drop_privileges = 0;
59static const char *drop_user = NULL;
60static int opt_udev = 0;
61static int opt_exit = 0;
62static int exit_signal = 0;
63static int daemon_pipe;
64
65static int report_to_parent = 0;
66
67int create_socket(void) {
68 struct sockaddr_un bind_addr;
69 int listenfd;
70
71 if(unlink(socket_path) == -1 && errno != ENOENT) {
72 usbmuxd_log(LL_FATAL, "unlink(%s) failed: %s", socket_path, strerror(errno));
73 return -1;
74 }
75
76 listenfd = socket(AF_UNIX, SOCK_STREAM, 0);
77 if (listenfd == -1) {
78 usbmuxd_log(LL_FATAL, "socket() failed: %s", strerror(errno));
79 return -1;
80 }
81
82 bzero(&bind_addr, sizeof(bind_addr));
83 bind_addr.sun_family = AF_UNIX;
84 strcpy(bind_addr.sun_path, socket_path);
85 if (bind(listenfd, (struct sockaddr*)&bind_addr, sizeof(bind_addr)) != 0) {
86 usbmuxd_log(LL_FATAL, "bind() failed: %s", strerror(errno));
87 return -1;
88 }
89
90 // Start listening
91 if (listen(listenfd, 5) != 0) {
92 usbmuxd_log(LL_FATAL, "listen() failed: %s", strerror(errno));
93 return -1;
94 }
95
96 chmod(socket_path, 0666);
97
98 return listenfd;
99}
100
101void handle_signal(int sig)
102{
103 if (sig != SIGUSR1 && sig != SIGUSR2) {
104 usbmuxd_log(LL_NOTICE,"Caught signal %d, exiting", sig);
105 should_exit = 1;
106 } else {
107 if(opt_udev) {
108 if (sig == SIGUSR1) {
109 usbmuxd_log(LL_INFO, "Caught SIGUSR1, checking if we can terminate (no more devices attached)...");
110 if (device_get_count() > 0) {
111 // we can't quit, there are still devices attached.
112 usbmuxd_log(LL_NOTICE, "Refusing to terminate, there are still devices attached. Kill me with signal 15 (TERM) to force quit.");
113 } else {
114 // it's safe to quit
115 should_exit = 1;
116 }
117 } else if (sig == SIGUSR2) {
118 usbmuxd_log(LL_INFO, "Caught SIGUSR2, scheduling device discovery");
119 should_discover = 1;
120 }
121 } else {
122 usbmuxd_log(LL_INFO, "Caught SIGUSR1/2 but we weren't started in --udev mode, ignoring");
123 }
124 }
125}
126
127void set_signal_handlers(void)
128{
129 struct sigaction sa;
130 sigset_t set;
131
132 // Mask all signals we handle. They will be unmasked by ppoll().
133 sigemptyset(&set);
134 sigaddset(&set, SIGINT);
135 sigaddset(&set, SIGQUIT);
136 sigaddset(&set, SIGTERM);
137 sigaddset(&set, SIGUSR1);
138 sigaddset(&set, SIGUSR2);
139 sigprocmask(SIG_SETMASK, &set, NULL);
140
141 memset(&sa, 0, sizeof(struct sigaction));
142 sa.sa_handler = handle_signal;
143 sigaction(SIGINT, &sa, NULL);
144 sigaction(SIGQUIT, &sa, NULL);
145 sigaction(SIGTERM, &sa, NULL);
146 sigaction(SIGUSR1, &sa, NULL);
147 sigaction(SIGUSR2, &sa, NULL);
148}
149
150#if defined(__FreeBSD__) || defined(__APPLE__)
151static int ppoll(struct pollfd *fds, nfds_t nfds, const struct timespec *timeout, const sigset_t *sigmask)
152{
153 int ready;
154 sigset_t origmask;
155 int to = timeout->tv_sec*1000 + timeout->tv_nsec/1000000;
156
157 sigprocmask(SIG_SETMASK, sigmask, &origmask);
158 ready = poll(fds, nfds, to);
159 sigprocmask(SIG_SETMASK, &origmask, NULL);
160
161 return ready;
162}
163#endif
164
165int main_loop(int listenfd)
166{
167 int to, cnt, i, dto;
168 struct fdlist pollfds;
169 struct timespec tspec;
170
171 sigset_t empty_sigset;
172 sigemptyset(&empty_sigset); // unmask all signals
173
174 fdlist_create(&pollfds);
175 while(!should_exit) {
176 usbmuxd_log(LL_FLOOD, "main_loop iteration");
177 to = usb_get_timeout();
178 usbmuxd_log(LL_FLOOD, "USB timeout is %d ms", to);
179 dto = device_get_timeout();
180 usbmuxd_log(LL_FLOOD, "Device timeout is %d ms", to);
181 if(dto < to)
182 to = dto;
183
184 fdlist_reset(&pollfds);
185 fdlist_add(&pollfds, FD_LISTEN, listenfd, POLLIN);
186 usb_get_fds(&pollfds);
187 client_get_fds(&pollfds);
188 usbmuxd_log(LL_FLOOD, "fd count is %d", pollfds.count);
189
190 tspec.tv_sec = to / 1000;
191 tspec.tv_nsec = (to % 1000) * 1000000;
192 cnt = ppoll(pollfds.fds, pollfds.count, &tspec, &empty_sigset);
193 usbmuxd_log(LL_FLOOD, "poll() returned %d", cnt);
194 if(cnt == -1) {
195 if(errno == EINTR) {
196 if(should_exit) {
197 usbmuxd_log(LL_INFO, "Event processing interrupted");
198 break;
199 }
200 if(should_discover) {
201 should_discover = 0;
202 usbmuxd_log(LL_INFO, "Device discovery triggered by udev");
203 usb_discover();
204 }
205 }
206 } else if(cnt == 0) {
207 if(usb_process() < 0) {
208 usbmuxd_log(LL_FATAL, "usb_process() failed");
209 fdlist_free(&pollfds);
210 return -1;
211 }
212 device_check_timeouts();
213 } else {
214 int done_usb = 0;
215 for(i=0; i<pollfds.count; i++) {
216 if(pollfds.fds[i].revents) {
217 if(!done_usb && pollfds.owners[i] == FD_USB) {
218 if(usb_process() < 0) {
219 usbmuxd_log(LL_FATAL, "usb_process() failed");
220 fdlist_free(&pollfds);
221 return -1;
222 }
223 done_usb = 1;
224 }
225 if(pollfds.owners[i] == FD_LISTEN) {
226 if(client_accept(listenfd) < 0) {
227 usbmuxd_log(LL_FATAL, "client_accept() failed");
228 fdlist_free(&pollfds);
229 return -1;
230 }
231 }
232 if(pollfds.owners[i] == FD_CLIENT) {
233 client_process(pollfds.fds[i].fd, pollfds.fds[i].revents);
234 }
235 }
236 }
237 }
238 }
239 fdlist_free(&pollfds);
240 return 0;
241}
242
243/**
244 * make this program run detached from the current console
245 */
246static int daemonize(void)
247{
248 pid_t pid;
249 pid_t sid;
250 int pfd[2];
251 int res;
252
253 // already a daemon
254 if (getppid() == 1)
255 return 0;
256
257 if((res = pipe(pfd)) < 0) {
258 usbmuxd_log(LL_FATAL, "pipe() failed.");
259 return res;
260 }
261
262 pid = fork();
263 if (pid < 0) {
264 usbmuxd_log(LL_FATAL, "fork() failed.");
265 return pid;
266 }
267
268 if (pid > 0) {
269 // exit parent process
270 int status;
271 close(pfd[1]);
272
273 if((res = read(pfd[0],&status,sizeof(int))) != sizeof(int)) {
274 fprintf(stderr, "usbmuxd: ERROR: Failed to get init status from child, check syslog for messages.\n");
275 exit(1);
276 }
277 if(status != 0)
278 fprintf(stderr, "usbmuxd: ERROR: Child process exited with error %d, check syslog for messages.\n", status);
279 exit(status);
280 }
281 // At this point we are executing as the child process
282 // but we need to do one more fork
283
284 daemon_pipe = pfd[1];
285 close(pfd[0]);
286 report_to_parent = 1;
287
288 // Change the file mode mask
289 umask(0);
290
291 // Create a new SID for the child process
292 sid = setsid();
293 if (sid < 0) {
294 usbmuxd_log(LL_FATAL, "setsid() failed.");
295 return -1;
296 }
297
298 pid = fork();
299 if (pid < 0) {
300 usbmuxd_log(LL_FATAL, "fork() failed (second).");
301 return pid;
302 }
303
304 if (pid > 0) {
305 // exit parent process
306 close(daemon_pipe);
307 exit(0);
308 }
309
310 // Change the current working directory.
311 if ((chdir("/")) < 0) {
312 usbmuxd_log(LL_FATAL, "chdir() failed");
313 return -2;
314 }
315 // Redirect standard files to /dev/null
316 if (!freopen("/dev/null", "r", stdin)) {
317 usbmuxd_log(LL_FATAL, "Redirection of stdin failed.");
318 return -3;
319 }
320 if (!freopen("/dev/null", "w", stdout)) {
321 usbmuxd_log(LL_FATAL, "Redirection of stdout failed.");
322 return -3;
323 }
324
325 return 0;
326}
327
328static int notify_parent(int status)
329{
330 int res;
331
332 report_to_parent = 0;
333 if ((res = write(daemon_pipe, &status, sizeof(int))) != sizeof(int)) {
334 usbmuxd_log(LL_FATAL, "Could not notify parent!");
335 if(res >= 0)
336 return -2;
337 else
338 return res;
339 }
340 close(daemon_pipe);
341 if (!freopen("/dev/null", "w", stderr)) {
342 usbmuxd_log(LL_FATAL, "Redirection of stderr failed.");
343 return -1;
344 }
345 return 0;
346}
347
348static void usage()
349{
350 printf("usage: usbmuxd [options]\n");
351 printf("\t-h|--help Print this message.\n");
352 printf("\t-v|--verbose Be verbose (use twice or more to increase).\n");
353 printf("\t-f|--foreground Do not daemonize (implies one -v).\n");
354 printf("\t-U|--user USER Change to this user after startup (needs usb privileges).\n");
355 printf("\t-u|--udev Run in udev operation mode.\n");
356 printf("\t-x|--exit Tell a running instance to exit if there are no devices\n");
357 printf("\t connected (must be in udev mode).\n");
358 printf("\t-X|--force-exit Tell a running instance to exit, even if there are still\n");
359 printf("\t devices connected (always works).\n");
360 printf("\n");
361}
362
363static void parse_opts(int argc, char **argv)
364{
365 static struct option longopts[] = {
366 {"help", 0, NULL, 'h'},
367 {"foreground", 0, NULL, 'f'},
368 {"verbose", 0, NULL, 'v'},
369 {"user", 2, NULL, 'U'},
370 {"udev", 0, NULL, 'u'},
371 {"exit", 0, NULL, 'x'},
372 {"force-exit", 0, NULL, 'X'},
373 {NULL, 0, NULL, 0}
374 };
375 int c;
376
377 while (1) {
378 c = getopt_long(argc, argv, "hfvuU:xX", longopts, (int *) 0);
379 if (c == -1) {
380 break;
381 }
382
383 switch (c) {
384 case 'h':
385 usage();
386 exit(0);
387 case 'f':
388 foreground = 1;
389 break;
390 case 'v':
391 ++verbose;
392 break;
393 case 'U':
394 drop_privileges = 1;
395 drop_user = optarg;
396 break;
397 case 'u':
398 opt_udev = 1;
399 break;
400 case 'x':
401 opt_exit = 1;
402 exit_signal = SIGUSR1;
403 break;
404 case 'X':
405 opt_exit = 1;
406 exit_signal = SIGTERM;
407 break;
408 default:
409 usage();
410 exit(2);
411 }
412 }
413}
414
415int main(int argc, char *argv[])
416{
417 int listenfd;
418 int res = 0;
419 int lfd;
420 struct flock lock;
421 char pids[10];
422
423 parse_opts(argc, argv);
424
425 argc -= optind;
426 argv += optind;
427
428 if (!foreground) {
429 verbose += LL_WARNING;
430 log_enable_syslog();
431 } else {
432 verbose += LL_NOTICE;
433 }
434
435 /* set log level to specified verbosity */
436 log_level = verbose;
437
438 usbmuxd_log(LL_NOTICE, "usbmuxd v%s starting up", PACKAGE_VERSION);
439 should_exit = 0;
440 should_discover = 0;
441
442 set_signal_handlers();
443 signal(SIGPIPE, SIG_IGN);
444
445 res = lfd = open(lockfile, O_WRONLY|O_CREAT, 0644);
446 if(res == -1) {
447 usbmuxd_log(LL_FATAL, "Could not open lockfile");
448 goto terminate;
449 }
450 lock.l_type = F_WRLCK;
451 lock.l_whence = SEEK_SET;
452 lock.l_start = 0;
453 lock.l_len = 0;
454 fcntl(lfd, F_GETLK, &lock);
455 close(lfd);
456 if (lock.l_type != F_UNLCK) {
457 if (opt_exit) {
458 if (lock.l_pid && !kill(lock.l_pid, 0)) {
459 usbmuxd_log(LL_NOTICE, "Sending signal %d to instance with pid %d", exit_signal, lock.l_pid);
460 res = 0;
461 if (kill(lock.l_pid, exit_signal) < 0) {
462 usbmuxd_log(LL_FATAL, "Could not deliver signal %d to pid %d", exit_signal, lock.l_pid);
463 res = -1;
464 }
465 goto terminate;
466 } else {
467 usbmuxd_log(LL_ERROR, "Could not determine pid of the other running instance!");
468 res = -1;
469 goto terminate;
470 }
471 } else {
472 if (!opt_udev) {
473 usbmuxd_log(LL_ERROR, "Another instance is already running (pid %d). exiting.", lock.l_pid);
474 res = -1;
475 } else {
476 usbmuxd_log(LL_NOTICE, "Another instance is already running (pid %d). Telling it to check for devices.", lock.l_pid);
477 if (lock.l_pid && !kill(lock.l_pid, 0)) {
478 usbmuxd_log(LL_NOTICE, "Sending signal SIGUSR2 to instance with pid %d", lock.l_pid);
479 res = 0;
480 if (kill(lock.l_pid, SIGUSR2) < 0) {
481 usbmuxd_log(LL_FATAL, "Could not deliver SIGUSR2 to pid %d", lock.l_pid);
482 res = -1;
483 }
484 } else {
485 usbmuxd_log(LL_ERROR, "Could not determine pid of the other running instance!");
486 res = -1;
487 }
488 }
489 goto terminate;
490 }
491 }
492 unlink(lockfile);
493
494 if (opt_exit) {
495 usbmuxd_log(LL_NOTICE, "No running instance found, none killed. exiting.");
496 goto terminate;
497 }
498
499 if (!foreground) {
500 if ((res = daemonize()) < 0) {
501 fprintf(stderr, "usbmuxd: FATAL: Could not daemonize!\n");
502 usbmuxd_log(LL_FATAL, "Could not daemonize!");
503 goto terminate;
504 }
505 }
506
507 // now open the lockfile and place the lock
508 res = lfd = open(lockfile, O_WRONLY|O_CREAT|O_TRUNC|O_EXCL, 0644);
509 if(res < 0) {
510 usbmuxd_log(LL_FATAL, "Could not open lockfile");
511 goto terminate;
512 }
513 lock.l_type = F_WRLCK;
514 lock.l_whence = SEEK_SET;
515 lock.l_start = 0;
516 lock.l_len = 0;
517 if ((res = fcntl(lfd, F_SETLK, &lock)) < 0) {
518 usbmuxd_log(LL_FATAL, "Lockfile locking failed!");
519 goto terminate;
520 }
521 sprintf(pids, "%d", getpid());
522 if ((res = write(lfd, pids, strlen(pids))) != strlen(pids)) {
523 usbmuxd_log(LL_FATAL, "Could not write pidfile!");
524 if(res >= 0)
525 res = -2;
526 goto terminate;
527 }
528
529 usbmuxd_log(LL_INFO, "Creating socket");
530 res = listenfd = create_socket();
531 if(listenfd < 0)
532 goto terminate;
533
534 // drop elevated privileges
535 if (drop_privileges && (getuid() == 0 || geteuid() == 0)) {
536 struct passwd *pw;
537 if (!drop_user) {
538 usbmuxd_log(LL_FATAL, "No user to drop privileges to?");
539 res = -1;
540 goto terminate;
541 }
542 pw = getpwnam(drop_user);
543 if (!pw) {
544 usbmuxd_log(LL_FATAL, "Dropping privileges failed, check if user '%s' exists!", drop_user);
545 res = -1;
546 goto terminate;
547 }
548 if (pw->pw_uid == 0) {
549 usbmuxd_log(LL_INFO, "Not dropping privileges to root");
550 } else {
551 if ((res = initgroups(drop_user, pw->pw_gid)) < 0) {
552 usbmuxd_log(LL_FATAL, "Failed to drop privileges (cannot set supplementary groups)");
553 goto terminate;
554 }
555 if ((res = setgid(pw->pw_gid)) < 0) {
556 usbmuxd_log(LL_FATAL, "Failed to drop privileges (cannot set group ID to %d)", pw->pw_gid);
557 goto terminate;
558 }
559 if ((res = setuid(pw->pw_uid)) < 0) {
560 usbmuxd_log(LL_FATAL, "Failed to drop privileges (cannot set user ID to %d)", pw->pw_uid);
561 goto terminate;
562 }
563
564 // security check
565 if (setuid(0) != -1) {
566 usbmuxd_log(LL_FATAL, "Failed to drop privileges properly!");
567 res = -1;
568 goto terminate;
569 }
570 if (getuid() != pw->pw_uid || getgid() != pw->pw_gid) {
571 usbmuxd_log(LL_FATAL, "Failed to drop privileges properly!");
572 res = -1;
573 goto terminate;
574 }
575 usbmuxd_log(LL_NOTICE, "Successfully dropped privileges to '%s'", drop_user);
576 }
577 }
578
579 client_init();
580 device_init();
581 usbmuxd_log(LL_INFO, "Initializing USB");
582 if((res = usb_init()) < 0)
583 goto terminate;
584
585 usbmuxd_log(LL_INFO, "%d device%s detected", res, (res==1)?"":"s");
586
587 usbmuxd_log(LL_NOTICE, "Initialization complete");
588
589 if (report_to_parent)
590 if((res = notify_parent(0)) < 0)
591 goto terminate;
592
593 if(opt_udev)
594 usb_autodiscover(0); // discovery triggered by udev
595
596 res = main_loop(listenfd);
597 if(res < 0)
598 usbmuxd_log(LL_FATAL, "main_loop failed");
599
600 usbmuxd_log(LL_NOTICE, "usbmuxd shutting down");
601 device_kill_connections();
602 usb_shutdown();
603 device_shutdown();
604 client_shutdown();
605 usbmuxd_log(LL_NOTICE, "Shutdown complete");
606
607terminate:
608 log_disable_syslog();
609
610 if (res < 0)
611 res = -res;
612 else
613 res = 0;
614 if (report_to_parent)
615 notify_parent(res);
616
617 return res;
618}
diff --git a/src/usb-linux.c b/src/usb-linux.c
new file mode 100644
index 0000000..334d967
--- /dev/null
+++ b/src/usb-linux.c
@@ -0,0 +1,598 @@
1/*
2 usbmuxd - iPhone/iPod Touch USB multiplex server daemon
3
4Copyright (C) 2009 Hector Martin "marcan" <hector@marcansoft.com>
5Copyright (C) 2009 Nikias Bassen <nikias@gmx.li>
6Copyright (C) 2009 Martin Szulecki <opensuse@sukimashita.com>
7
8This program is free software; you can redistribute it and/or modify
9it under the terms of the GNU General Public License as published by
10the Free Software Foundation, either version 2 or version 3.
11
12This program is distributed in the hope that it will be useful,
13but WITHOUT ANY WARRANTY; without even the implied warranty of
14MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15GNU General Public License for more details.
16
17You should have received a copy of the GNU General Public License
18along with this program; if not, write to the Free Software
19Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20
21*/
22
23#ifdef HAVE_CONFIG_H
24#include <config.h>
25#endif
26
27#include <stdio.h>
28#include <stdlib.h>
29#include <stdint.h>
30#include <string.h>
31
32#include <libusb.h>
33
34#include "usb.h"
35#include "log.h"
36#include "device.h"
37
38// interval for device connection/disconnection polling, in milliseconds
39// we need this because there is currently no asynchronous device discovery mechanism in libusb
40#define DEVICE_POLL_TIME 1000
41
42struct usb_device {
43 libusb_device_handle *dev;
44 uint8_t bus, address;
45 uint16_t vid, pid;
46 char serial[256];
47 int alive;
48 uint8_t interface, ep_in, ep_out;
49 struct libusb_transfer *rx_xfer;
50 struct collection tx_xfers;
51 int wMaxPacketSize;
52};
53
54static struct collection device_list;
55
56static struct timeval next_dev_poll_time;
57
58static int devlist_failures;
59static int device_polling;
60
61static void usb_disconnect(struct usb_device *dev)
62{
63 if(!dev->dev) {
64 return;
65 }
66
67 // kill the rx xfer and tx xfers and try to make sure the callbacks get called before we free the device
68 if(dev->rx_xfer) {
69 usbmuxd_log(LL_DEBUG, "usb_disconnect: cancelling RX xfer");
70 libusb_cancel_transfer(dev->rx_xfer);
71 }
72 FOREACH(struct libusb_transfer *xfer, &dev->tx_xfers) {
73 usbmuxd_log(LL_DEBUG, "usb_disconnect: cancelling TX xfer %p", xfer);
74 libusb_cancel_transfer(xfer);
75 } ENDFOREACH
76
77 while(dev->rx_xfer || collection_count(&dev->tx_xfers)) {
78 struct timeval tv;
79 int res;
80
81 tv.tv_sec = 0;
82 tv.tv_usec = 1000;
83 if((res = libusb_handle_events_timeout(NULL, &tv)) < 0) {
84 usbmuxd_log(LL_ERROR, "libusb_handle_events_timeout for usb_disconnect failed: %d", res);
85 break;
86 }
87 }
88 collection_free(&dev->tx_xfers);
89 libusb_release_interface(dev->dev, dev->interface);
90 libusb_close(dev->dev);
91 dev->dev = NULL;
92 collection_remove(&device_list, dev);
93 free(dev);
94}
95
96static void tx_callback(struct libusb_transfer *xfer)
97{
98 struct usb_device *dev = xfer->user_data;
99 usbmuxd_log(LL_SPEW, "TX callback dev %d-%d len %d -> %d status %d", dev->bus, dev->address, xfer->length, xfer->actual_length, xfer->status);
100 if(xfer->status != LIBUSB_TRANSFER_COMPLETED) {
101 switch(xfer->status) {
102 case LIBUSB_TRANSFER_COMPLETED: //shut up compiler
103 case LIBUSB_TRANSFER_ERROR:
104 // funny, this happens when we disconnect the device while waiting for a transfer, sometimes
105 usbmuxd_log(LL_INFO, "Device %d-%d TX aborted due to error or disconnect", dev->bus, dev->address);
106 break;
107 case LIBUSB_TRANSFER_TIMED_OUT:
108 usbmuxd_log(LL_ERROR, "TX transfer timed out for device %d-%d", dev->bus, dev->address);
109 break;
110 case LIBUSB_TRANSFER_CANCELLED:
111 usbmuxd_log(LL_DEBUG, "Device %d-%d TX transfer cancelled", dev->bus, dev->address);
112 break;
113 case LIBUSB_TRANSFER_STALL:
114 usbmuxd_log(LL_ERROR, "TX transfer stalled for device %d-%d", dev->bus, dev->address);
115 break;
116 case LIBUSB_TRANSFER_NO_DEVICE:
117 // other times, this happens, and also even when we abort the transfer after device removal
118 usbmuxd_log(LL_INFO, "Device %d-%d TX aborted due to disconnect", dev->bus, dev->address);
119 break;
120 case LIBUSB_TRANSFER_OVERFLOW:
121 usbmuxd_log(LL_ERROR, "TX transfer overflow for device %d-%d", dev->bus, dev->address);
122 break;
123 // and nothing happens (this never gets called) if the device is freed after a disconnect! (bad)
124 }
125 // we can't usb_disconnect here due to a deadlock, so instead mark it as dead and reap it after processing events
126 // we'll do device_remove there too
127 dev->alive = 0;
128 }
129 if(xfer->buffer)
130 free(xfer->buffer);
131 collection_remove(&dev->tx_xfers, xfer);
132 libusb_free_transfer(xfer);
133}
134
135int usb_send(struct usb_device *dev, const unsigned char *buf, int length)
136{
137 int res;
138 struct libusb_transfer *xfer = libusb_alloc_transfer(0);
139 libusb_fill_bulk_transfer(xfer, dev->dev, dev->ep_out, (void*)buf, length, tx_callback, dev, 0);
140 if((res = libusb_submit_transfer(xfer)) < 0) {
141 usbmuxd_log(LL_ERROR, "Failed to submit TX transfer %p len %d to device %d-%d: %d", buf, length, dev->bus, dev->address, res);
142 libusb_free_transfer(xfer);
143 return res;
144 }
145 collection_add(&dev->tx_xfers, xfer);
146 if (length % dev->wMaxPacketSize == 0) {
147 usbmuxd_log(LL_DEBUG, "Send ZLP");
148 // Send Zero Length Packet
149 xfer = libusb_alloc_transfer(0);
150 void *buffer = malloc(1);
151 libusb_fill_bulk_transfer(xfer, dev->dev, dev->ep_out, buffer, 0, tx_callback, dev, 0);
152 if((res = libusb_submit_transfer(xfer)) < 0) {
153 usbmuxd_log(LL_ERROR, "Failed to submit TX ZLP transfer to device %d-%d: %d", dev->bus, dev->address, res);
154 libusb_free_transfer(xfer);
155 return res;
156 }
157 collection_add(&dev->tx_xfers, xfer);
158 }
159 return 0;
160}
161
162static void rx_callback(struct libusb_transfer *xfer)
163{
164 struct usb_device *dev = xfer->user_data;
165 usbmuxd_log(LL_SPEW, "RX callback dev %d-%d len %d status %d", dev->bus, dev->address, xfer->actual_length, xfer->status);
166 if(xfer->status == LIBUSB_TRANSFER_COMPLETED) {
167 device_data_input(dev, xfer->buffer, xfer->actual_length);
168 libusb_submit_transfer(xfer);
169 } else {
170 switch(xfer->status) {
171 case LIBUSB_TRANSFER_COMPLETED: //shut up compiler
172 case LIBUSB_TRANSFER_ERROR:
173 // funny, this happens when we disconnect the device while waiting for a transfer, sometimes
174 usbmuxd_log(LL_INFO, "Device %d-%d RX aborted due to error or disconnect", dev->bus, dev->address);
175 break;
176 case LIBUSB_TRANSFER_TIMED_OUT:
177 usbmuxd_log(LL_ERROR, "RX transfer timed out for device %d-%d", dev->bus, dev->address);
178 break;
179 case LIBUSB_TRANSFER_CANCELLED:
180 usbmuxd_log(LL_DEBUG, "Device %d-%d RX transfer cancelled", dev->bus, dev->address);
181 break;
182 case LIBUSB_TRANSFER_STALL:
183 usbmuxd_log(LL_ERROR, "RX transfer stalled for device %d-%d", dev->bus, dev->address);
184 break;
185 case LIBUSB_TRANSFER_NO_DEVICE:
186 // other times, this happens, and also even when we abort the transfer after device removal
187 usbmuxd_log(LL_INFO, "Device %d-%d RX aborted due to disconnect", dev->bus, dev->address);
188 break;
189 case LIBUSB_TRANSFER_OVERFLOW:
190 usbmuxd_log(LL_ERROR, "RX transfer overflow for device %d-%d", dev->bus, dev->address);
191 break;
192 // and nothing happens (this never gets called) if the device is freed after a disconnect! (bad)
193 }
194 free(xfer->buffer);
195 dev->rx_xfer = NULL;
196 libusb_free_transfer(xfer);
197 // we can't usb_disconnect here due to a deadlock, so instead mark it as dead and reap it after processing events
198 // we'll do device_remove there too
199 dev->alive = 0;
200 }
201}
202
203static int start_rx(struct usb_device *dev)
204{
205 int res;
206 void *buf;
207 dev->rx_xfer = libusb_alloc_transfer(0);
208 buf = malloc(USB_MRU);
209 libusb_fill_bulk_transfer(dev->rx_xfer, dev->dev, dev->ep_in, buf, USB_MRU, rx_callback, dev, 0);
210 if((res = libusb_submit_transfer(dev->rx_xfer)) != 0) {
211 usbmuxd_log(LL_ERROR, "Failed to submit RX transfer to device %d-%d: %d", dev->bus, dev->address, res);
212 libusb_free_transfer(dev->rx_xfer);
213 dev->rx_xfer = NULL;
214 return res;
215 }
216 return 0;
217}
218
219int usb_discover(void)
220{
221 int cnt, i, j, res;
222 int valid_count = 0;
223 libusb_device **devs;
224
225 cnt = libusb_get_device_list(NULL, &devs);
226 if(cnt < 0) {
227 usbmuxd_log(LL_WARNING, "Could not get device list: %d", cnt);
228 devlist_failures++;
229 // sometimes libusb fails getting the device list if you've just removed something
230 if(devlist_failures > 5) {
231 usbmuxd_log(LL_FATAL, "Too many errors getting device list\n");
232 return cnt;
233 } else {
234 gettimeofday(&next_dev_poll_time, NULL);
235 next_dev_poll_time.tv_usec += DEVICE_POLL_TIME * 1000;
236 next_dev_poll_time.tv_sec += next_dev_poll_time.tv_usec / 1000000;
237 next_dev_poll_time.tv_usec = next_dev_poll_time.tv_usec % 1000000;
238 return 0;
239 }
240 }
241 devlist_failures = 0;
242
243 usbmuxd_log(LL_SPEW, "usb_discover: scanning %d devices", cnt);
244
245 FOREACH(struct usb_device *usbdev, &device_list) {
246 usbdev->alive = 0;
247 } ENDFOREACH
248
249 for(i=0; i<cnt; i++) {
250 // the following are non-blocking operations on the device list
251 libusb_device *dev = devs[i];
252 uint8_t bus = libusb_get_bus_number(dev);
253 uint8_t address = libusb_get_device_address(dev);
254 struct libusb_device_descriptor devdesc;
255 int found = 0;
256 FOREACH(struct usb_device *usbdev, &device_list) {
257 if(usbdev->bus == bus && usbdev->address == address) {
258 valid_count++;
259 usbdev->alive = 1;
260 found = 1;
261 break;
262 }
263 } ENDFOREACH
264 if(found)
265 continue; //device already found
266 if((res = libusb_get_device_descriptor(dev, &devdesc)) != 0) {
267 usbmuxd_log(LL_WARNING, "Could not get device descriptor for device %d-%d: %d", bus, address, res);
268 continue;
269 }
270 if(devdesc.idVendor != VID_APPLE)
271 continue;
272 if((devdesc.idProduct < PID_RANGE_LOW) ||
273 (devdesc.idProduct > PID_RANGE_MAX))
274 continue;
275 libusb_device_handle *handle;
276 usbmuxd_log(LL_INFO, "Found new device with v/p %04x:%04x at %d-%d", devdesc.idVendor, devdesc.idProduct, bus, address);
277 // potentially blocking operations follow; they will only run when new devices are detected, which is acceptable
278 if((res = libusb_open(dev, &handle)) != 0) {
279 usbmuxd_log(LL_WARNING, "Could not open device %d-%d: %d", bus, address, res);
280 continue;
281 }
282 int current_config = 0;
283 if((res = libusb_get_configuration(handle, &current_config)) != 0) {
284 usbmuxd_log(LL_WARNING, "Could not get configuration for device %d-%d: %d", bus, address, res);
285 libusb_close(handle);
286 continue;
287 }
288 if (current_config != devdesc.bNumConfigurations) {
289 struct libusb_config_descriptor *config;
290 if((res = libusb_get_active_config_descriptor(dev, &config)) != 0) {
291 usbmuxd_log(LL_NOTICE, "Could not get old configuration descriptor for device %d-%d: %d", bus, address, res);
292 } else {
293 for(j=0; j<config->bNumInterfaces; j++) {
294 const struct libusb_interface_descriptor *intf = &config->interface[j].altsetting[0];
295 if((res = libusb_kernel_driver_active(handle, intf->bInterfaceNumber)) < 0) {
296 usbmuxd_log(LL_NOTICE, "Could not check kernel ownership of interface %d for device %d-%d: %d", intf->bInterfaceNumber, bus, address, res);
297 continue;
298 }
299 if(res == 1) {
300 usbmuxd_log(LL_INFO, "Detaching kernel driver for device %d-%d, interface %d", bus, address, intf->bInterfaceNumber);
301 if((res = libusb_detach_kernel_driver(handle, intf->bInterfaceNumber)) < 0) {
302 usbmuxd_log(LL_WARNING, "Could not detach kernel driver (%d), configuration change will probably fail!", res);
303 continue;
304 }
305 }
306 }
307 libusb_free_config_descriptor(config);
308 }
309 if((res = libusb_set_configuration(handle, devdesc.bNumConfigurations)) != 0) {
310 usbmuxd_log(LL_WARNING, "Could not set configuration %d for device %d-%d: %d", devdesc.bNumConfigurations, bus, address, res);
311 libusb_close(handle);
312 continue;
313 }
314 }
315
316 struct libusb_config_descriptor *config;
317 if((res = libusb_get_active_config_descriptor(dev, &config)) != 0) {
318 usbmuxd_log(LL_WARNING, "Could not get configuration descriptor for device %d-%d: %d", bus, address, res);
319 libusb_close(handle);
320 continue;
321 }
322
323 struct usb_device *usbdev;
324 usbdev = malloc(sizeof(struct usb_device));
325 memset(usbdev, 0, sizeof(*usbdev));
326
327 for(j=0; j<config->bNumInterfaces; j++) {
328 const struct libusb_interface_descriptor *intf = &config->interface[j].altsetting[0];
329 if(intf->bInterfaceClass != INTERFACE_CLASS ||
330 intf->bInterfaceSubClass != INTERFACE_SUBCLASS ||
331 intf->bInterfaceProtocol != INTERFACE_PROTOCOL)
332 continue;
333 if(intf->bNumEndpoints != 2) {
334 usbmuxd_log(LL_WARNING, "Endpoint count mismatch for interface %d of device %d-%d", intf->bInterfaceNumber, bus, address);
335 continue;
336 }
337 if((intf->endpoint[0].bEndpointAddress & 0x80) == LIBUSB_ENDPOINT_OUT &&
338 (intf->endpoint[1].bEndpointAddress & 0x80) == LIBUSB_ENDPOINT_IN) {
339 usbdev->interface = intf->bInterfaceNumber;
340 usbdev->ep_out = intf->endpoint[0].bEndpointAddress;
341 usbdev->ep_in = intf->endpoint[1].bEndpointAddress;
342 usbmuxd_log(LL_INFO, "Found interface %d with endpoints %02x/%02x for device %d-%d", usbdev->interface, usbdev->ep_out, usbdev->ep_in, bus, address);
343 break;
344 } else if((intf->endpoint[1].bEndpointAddress & 0x80) == LIBUSB_ENDPOINT_OUT &&
345 (intf->endpoint[0].bEndpointAddress & 0x80) == LIBUSB_ENDPOINT_IN) {
346 usbdev->interface = intf->bInterfaceNumber;
347 usbdev->ep_out = intf->endpoint[1].bEndpointAddress;
348 usbdev->ep_in = intf->endpoint[0].bEndpointAddress;
349 usbmuxd_log(LL_INFO, "Found interface %d with swapped endpoints %02x/%02x for device %d-%d", usbdev->interface, usbdev->ep_out, usbdev->ep_in, bus, address);
350 break;
351 } else {
352 usbmuxd_log(LL_WARNING, "Endpoint type mismatch for interface %d of device %d-%d", intf->bInterfaceNumber, bus, address);
353 }
354 }
355
356 if(j == config->bNumInterfaces) {
357 usbmuxd_log(LL_WARNING, "Could not find a suitable USB interface for device %d-%d", bus, address);
358 libusb_free_config_descriptor(config);
359 libusb_close(handle);
360 free(usbdev);
361 continue;
362 }
363
364 libusb_free_config_descriptor(config);
365
366 if((res = libusb_claim_interface(handle, usbdev->interface)) != 0) {
367 usbmuxd_log(LL_WARNING, "Could not claim interface %d for device %d-%d: %d", usbdev->interface, bus, address, res);
368 libusb_close(handle);
369 free(usbdev);
370 continue;
371 }
372
373 if((res = libusb_get_string_descriptor_ascii(handle, devdesc.iSerialNumber, (uint8_t *)usbdev->serial, 256)) <= 0) {
374 usbmuxd_log(LL_WARNING, "Could not get serial number for device %d-%d: %d", bus, address, res);
375 libusb_release_interface(handle, usbdev->interface);
376 libusb_close(handle);
377 free(usbdev);
378 continue;
379 }
380 usbdev->serial[res] = 0;
381 usbdev->bus = bus;
382 usbdev->address = address;
383 usbdev->vid = devdesc.idVendor;
384 usbdev->pid = devdesc.idProduct;
385 usbdev->dev = handle;
386 usbdev->alive = 1;
387 usbdev->wMaxPacketSize = libusb_get_max_packet_size(dev, usbdev->ep_out);
388 if (usbdev->wMaxPacketSize <= 0) {
389 usbmuxd_log(LL_ERROR, "Could not determine wMaxPacketSize for device %d-%d, setting to 64", usbdev->bus, usbdev->address);
390 usbdev->wMaxPacketSize = 64;
391 } else {
392 usbmuxd_log(LL_INFO, "Using wMaxPacketSize=%d for device %d-%d", usbdev->wMaxPacketSize, usbdev->bus, usbdev->address);
393 }
394
395 collection_init(&usbdev->tx_xfers);
396
397 collection_add(&device_list, usbdev);
398
399 if(device_add(usbdev) < 0) {
400 usb_disconnect(usbdev);
401 continue;
402 }
403 if(start_rx(usbdev) < 0) {
404 device_remove(usbdev);
405 usb_disconnect(usbdev);
406 continue;
407 }
408 valid_count++;
409 }
410 FOREACH(struct usb_device *usbdev, &device_list) {
411 if(!usbdev->alive) {
412 device_remove(usbdev);
413 usb_disconnect(usbdev);
414 }
415 } ENDFOREACH
416
417 libusb_free_device_list(devs, 1);
418
419 gettimeofday(&next_dev_poll_time, NULL);
420 next_dev_poll_time.tv_usec += DEVICE_POLL_TIME * 1000;
421 next_dev_poll_time.tv_sec += next_dev_poll_time.tv_usec / 1000000;
422 next_dev_poll_time.tv_usec = next_dev_poll_time.tv_usec % 1000000;
423
424 return valid_count;
425}
426
427const char *usb_get_serial(struct usb_device *dev)
428{
429 if(!dev->dev) {
430 return NULL;
431 }
432 return dev->serial;
433}
434
435uint32_t usb_get_location(struct usb_device *dev)
436{
437 if(!dev->dev) {
438 return 0;
439 }
440 return (dev->bus << 16) | dev->address;
441}
442
443uint16_t usb_get_pid(struct usb_device *dev)
444{
445 if(!dev->dev) {
446 return 0;
447 }
448 return dev->pid;
449}
450
451void usb_get_fds(struct fdlist *list)
452{
453 const struct libusb_pollfd **usbfds;
454 const struct libusb_pollfd **p;
455 usbfds = libusb_get_pollfds(NULL);
456 if(!usbfds) {
457 usbmuxd_log(LL_ERROR, "libusb_get_pollfds failed");
458 return;
459 }
460 p = usbfds;
461 while(*p) {
462 fdlist_add(list, FD_USB, (*p)->fd, (*p)->events);
463 p++;
464 }
465 free(usbfds);
466}
467
468void usb_autodiscover(int enable)
469{
470 usbmuxd_log(LL_DEBUG, "usb polling enable: %d", enable);
471 device_polling = enable;
472}
473
474static int dev_poll_remain_ms(void)
475{
476 int msecs;
477 struct timeval tv;
478 if(!device_polling)
479 return 100000; // devices will never be polled if this is > 0
480 gettimeofday(&tv, NULL);
481 msecs = (next_dev_poll_time.tv_sec - tv.tv_sec) * 1000;
482 msecs += (next_dev_poll_time.tv_usec - tv.tv_usec) / 1000;
483 if(msecs < 0)
484 return 0;
485 return msecs;
486}
487
488int usb_get_timeout(void)
489{
490 struct timeval tv;
491 int msec;
492 int res;
493 int pollrem;
494 pollrem = dev_poll_remain_ms();
495 res = libusb_get_next_timeout(NULL, &tv);
496 if(res == 0)
497 return pollrem;
498 if(res < 0) {
499 usbmuxd_log(LL_ERROR, "libusb_get_next_timeout failed: %d", res);
500 return pollrem;
501 }
502 msec = tv.tv_sec * 1000;
503 msec += tv.tv_usec / 1000;
504 if(msec > pollrem)
505 return pollrem;
506 return msec;
507}
508
509int usb_process(void)
510{
511 int res;
512 struct timeval tv;
513 tv.tv_sec = tv.tv_usec = 0;
514 res = libusb_handle_events_timeout(NULL, &tv);
515 if(res < 0) {
516 usbmuxd_log(LL_ERROR, "libusb_handle_events_timeout failed: %d", res);
517 return res;
518 }
519 // reap devices marked dead due to an RX error
520 FOREACH(struct usb_device *usbdev, &device_list) {
521 if(!usbdev->alive) {
522 device_remove(usbdev);
523 usb_disconnect(usbdev);
524 }
525 } ENDFOREACH
526
527 if(dev_poll_remain_ms() <= 0) {
528 res = usb_discover();
529 if(res < 0) {
530 usbmuxd_log(LL_ERROR, "usb_discover failed: %d", res);
531 return res;
532 }
533 }
534 return 0;
535}
536
537int usb_process_timeout(int msec)
538{
539 int res;
540 struct timeval tleft, tcur, tfin;
541 gettimeofday(&tcur, NULL);
542 tfin.tv_sec = tcur.tv_sec + (msec / 1000);
543 tfin.tv_usec = tcur.tv_usec + (msec % 1000) * 1000;
544 tfin.tv_sec += tfin.tv_usec / 1000000;
545 tfin.tv_usec %= 1000000;
546 while((tfin.tv_sec > tcur.tv_sec) || ((tfin.tv_sec == tcur.tv_sec) && (tfin.tv_usec > tcur.tv_usec))) {
547 tleft.tv_sec = tfin.tv_sec - tcur.tv_sec;
548 tleft.tv_usec = tfin.tv_usec - tcur.tv_usec;
549 if(tleft.tv_usec < 0) {
550 tleft.tv_usec += 1000000;
551 tleft.tv_sec -= 1;
552 }
553 res = libusb_handle_events_timeout(NULL, &tleft);
554 if(res < 0) {
555 usbmuxd_log(LL_ERROR, "libusb_handle_events_timeout failed: %d", res);
556 return res;
557 }
558 // reap devices marked dead due to an RX error
559 FOREACH(struct usb_device *usbdev, &device_list) {
560 if(!usbdev->alive) {
561 device_remove(usbdev);
562 usb_disconnect(usbdev);
563 }
564 } ENDFOREACH
565 gettimeofday(&tcur, NULL);
566 }
567 return 0;
568}
569
570int usb_init(void)
571{
572 int res;
573 usbmuxd_log(LL_DEBUG, "usb_init for linux / libusb 1.0");
574
575 devlist_failures = 0;
576 device_polling = 1;
577 res = libusb_init(NULL);
578 //libusb_set_debug(NULL, 3);
579 if(res != 0) {
580 usbmuxd_log(LL_FATAL, "libusb_init failed: %d", res);
581 return -1;
582 }
583
584 collection_init(&device_list);
585
586 return usb_discover();
587}
588
589void usb_shutdown(void)
590{
591 usbmuxd_log(LL_DEBUG, "usb_shutdown");
592 FOREACH(struct usb_device *usbdev, &device_list) {
593 device_remove(usbdev);
594 usb_disconnect(usbdev);
595 } ENDFOREACH
596 collection_free(&device_list);
597 libusb_exit(NULL);
598}
diff --git a/src/usb.h b/src/usb.h
new file mode 100644
index 0000000..6fe7ee6
--- /dev/null
+++ b/src/usb.h
@@ -0,0 +1,66 @@
1/*
2 usbmuxd - iPhone/iPod Touch USB multiplex server daemon
3
4Copyright (C) 2009 Hector Martin "marcan" <hector@marcansoft.com>
5Copyright (C) 2009 Nikias Bassen <nikias@gmx.li>
6Copyright (C) 2009 Martin Szulecki <opensuse@sukimashita.com>
7
8This program is free software; you can redistribute it and/or modify
9it under the terms of the GNU General Public License as published by
10the Free Software Foundation, either version 2 or version 3.
11
12This program is distributed in the hope that it will be useful,
13but WITHOUT ANY WARRANTY; without even the implied warranty of
14MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15GNU General Public License for more details.
16
17You should have received a copy of the GNU General Public License
18along with this program; if not, write to the Free Software
19Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20
21*/
22
23#ifndef __USB_H__
24#define __USB_H__
25
26#include <stdint.h>
27#include "utils.h"
28
29#define INTERFACE_CLASS 255
30#define INTERFACE_SUBCLASS 254
31#define INTERFACE_PROTOCOL 2
32
33// libusb fragments packets larger than this (usbfs limitation)
34// on input, this creates race conditions and other issues
35#define USB_MRU 16384
36
37// max transmission packet size
38// libusb fragments these too, but doesn't send ZLPs so we're safe
39// but we need to send a ZLP ourselves at the end (see usb-linux.c)
40// we're using 3 * 16384 to optimize for the fragmentation
41// this results in three URBs per full transfer, 32 USB packets each
42// if there are ZLP issues this should make them show up easily too
43#define USB_MTU (3 * 16384)
44
45#define USB_PACKET_SIZE 512
46
47#define VID_APPLE 0x5ac
48#define PID_RANGE_LOW 0x1290
49#define PID_RANGE_MAX 0x12af
50
51struct usb_device;
52
53int usb_init(void);
54void usb_shutdown(void);
55const char *usb_get_serial(struct usb_device *dev);
56uint32_t usb_get_location(struct usb_device *dev);
57uint16_t usb_get_pid(struct usb_device *dev);
58void usb_get_fds(struct fdlist *list);
59int usb_get_timeout(void);
60int usb_send(struct usb_device *dev, const unsigned char *buf, int length);
61int usb_discover(void);
62void usb_autodiscover(int enable);
63int usb_process(void);
64int usb_process_timeout(int msec);
65
66#endif
diff --git a/src/utils.c b/src/utils.c
new file mode 100644
index 0000000..ceef535
--- /dev/null
+++ b/src/utils.c
@@ -0,0 +1,120 @@
1/*
2 usbmuxd - iPhone/iPod Touch USB multiplex server daemon
3
4Copyright (C) 2009 Hector Martin "marcan" <hector@marcansoft.com>
5Copyright (C) 2009 Nikias Bassen <nikias@gmx.li>
6
7This library is free software; you can redistribute it and/or modify
8it under the terms of the GNU Lesser General Public License as
9published by the Free Software Foundation, either version 2.1 of the
10License, or (at your option) any later version.
11
12This library is distributed in the hope that it will be useful,
13but WITHOUT ANY WARRANTY; without even the implied warranty of
14MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15GNU General Public License for more details.
16
17You should have received a copy of the GNU Lesser General Public
18License along with this program; if not, write to the Free Software
19Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20
21*/
22
23#ifdef HAVE_CONFIG_H
24#include <config.h>
25#endif
26
27#include <stdlib.h>
28#include <string.h>
29#include <stdio.h>
30#include "utils.h"
31
32#include "log.h"
33#define util_error(...) usbmuxd_log(LL_ERROR, __VA_ARGS__)
34
35void fdlist_create(struct fdlist *list)
36{
37 list->count = 0;
38 list->capacity = 4;
39 list->owners = malloc(sizeof(*list->owners) * list->capacity);
40 list->fds = malloc(sizeof(*list->fds) * list->capacity);
41}
42void fdlist_add(struct fdlist *list, enum fdowner owner, int fd, short events)
43{
44 if(list->count == list->capacity) {
45 list->capacity *= 2;
46 list->owners = realloc(list->owners, sizeof(*list->owners) * list->capacity);
47 list->fds = realloc(list->fds, sizeof(*list->fds) * list->capacity);
48 }
49 list->owners[list->count] = owner;
50 list->fds[list->count].fd = fd;
51 list->fds[list->count].events = events;
52 list->fds[list->count].revents = 0;
53 list->count++;
54}
55
56void fdlist_free(struct fdlist *list)
57{
58 list->count = 0;
59 list->capacity = 0;
60 free(list->owners);
61 list->owners = NULL;
62 free(list->fds);
63 list->fds = NULL;
64}
65
66void fdlist_reset(struct fdlist *list)
67{
68 list->count = 0;
69}
70
71void collection_init(struct collection *col)
72{
73 col->list = malloc(sizeof(void *));
74 memset(col->list, 0, sizeof(void *));
75 col->capacity = 1;
76}
77
78void collection_free(struct collection *col)
79{
80 free(col->list);
81 col->list = NULL;
82 col->capacity = 0;
83}
84
85void collection_add(struct collection *col, void *element)
86{
87 int i;
88 for(i=0; i<col->capacity; i++) {
89 if(!col->list[i]) {
90 col->list[i] = element;
91 return;
92 }
93 }
94 col->list = realloc(col->list, sizeof(void*) * col->capacity * 2);
95 memset(&col->list[col->capacity], 0, sizeof(void *) * col->capacity);
96 col->list[col->capacity] = element;
97 col->capacity *= 2;
98}
99
100void collection_remove(struct collection *col, void *element)
101{
102 int i;
103 for(i=0; i<col->capacity; i++) {
104 if(col->list[i] == element) {
105 col->list[i] = NULL;
106 return;
107 }
108 }
109 util_error("collection_remove: element %p not present in collection %p (cap %d)", element, col, col->capacity);
110}
111
112int collection_count(struct collection *col)
113{
114 int i, cnt = 0;
115 for(i=0; i<col->capacity; i++) {
116 if(col->list[i])
117 cnt++;
118 }
119 return cnt;
120}
diff --git a/src/utils.h b/src/utils.h
new file mode 100644
index 0000000..5aa2915
--- /dev/null
+++ b/src/utils.h
@@ -0,0 +1,68 @@
1/*
2 usbmuxd - iPhone/iPod Touch USB multiplex server daemon
3
4Copyright (C) 2009 Hector Martin "marcan" <hector@marcansoft.com>
5Copyright (C) 2009 Nikias Bassen <nikias@gmx.li>
6
7This library is free software; you can redistribute it and/or modify
8it under the terms of the GNU Lesser General Public License as
9published by the Free Software Foundation, either version 2.1 of the
10License, or (at your option) any later version.
11
12This library is distributed in the hope that it will be useful,
13but WITHOUT ANY WARRANTY; without even the implied warranty of
14MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15GNU General Public License for more details.
16
17You should have received a copy of the GNU Lesser General Public
18License along with this program; if not, write to the Free Software
19Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20
21*/
22
23#ifndef __UTILS_H__
24#define __UTILS_H__
25
26#include <poll.h>
27
28enum fdowner {
29 FD_LISTEN,
30 FD_CLIENT,
31 FD_USB
32};
33
34struct fdlist {
35 int count;
36 int capacity;
37 enum fdowner *owners;
38 struct pollfd *fds;
39};
40
41void fdlist_create(struct fdlist *list);
42void fdlist_add(struct fdlist *list, enum fdowner owner, int fd, short events);
43void fdlist_free(struct fdlist *list);
44void fdlist_reset(struct fdlist *list);
45
46struct collection {
47 void **list;
48 int capacity;
49};
50
51void collection_init(struct collection *col);
52void collection_add(struct collection *col, void *element);
53void collection_remove(struct collection *col, void *element);
54int collection_count(struct collection *col);
55void collection_free(struct collection *col);
56
57#define FOREACH(var, col) \
58 do { \
59 int _iter; \
60 for(_iter=0; _iter<(col)->capacity; _iter++) { \
61 if(!(col)->list[_iter]) continue; \
62 var = (col)->list[_iter];
63
64#define ENDFOREACH \
65 } \
66 } while(0);
67
68#endif