summaryrefslogtreecommitdiffstats
path: root/usbmuxd
diff options
context:
space:
mode:
Diffstat (limited to 'usbmuxd')
-rw-r--r--usbmuxd/CMakeLists.txt9
-rw-r--r--usbmuxd/client.c430
-rw-r--r--usbmuxd/client.h92
-rw-r--r--usbmuxd/device.c751
-rw-r--r--usbmuxd/device.h52
-rw-r--r--usbmuxd/log.c94
-rw-r--r--usbmuxd/log.h43
-rw-r--r--usbmuxd/main.c548
-rw-r--r--usbmuxd/usb-linux.c503
-rw-r--r--usbmuxd/usb.h67
-rw-r--r--usbmuxd/utils.c110
-rw-r--r--usbmuxd/utils.h65
12 files changed, 2764 insertions, 0 deletions
diff --git a/usbmuxd/CMakeLists.txt b/usbmuxd/CMakeLists.txt
new file mode 100644
index 0000000..b982dc0
--- /dev/null
+++ b/usbmuxd/CMakeLists.txt
@@ -0,0 +1,9 @@
1find_package(USB REQUIRED)
2include_directories(${USB_INCLUDE_DIRS})
3set(LIBS ${LIBS} ${USB_LIBRARIES})
4
5add_definitions(-Wall -O2 -g)
6add_executable(usbmuxd main.c usb-linux.c log.c utils.c device.c client.c)
7target_link_libraries(usbmuxd ${LIBS})
8
9install(TARGETS usbmuxd RUNTIME DESTINATION sbin) \ No newline at end of file
diff --git a/usbmuxd/client.c b/usbmuxd/client.c
new file mode 100644
index 0000000..7a3160f
--- /dev/null
+++ b/usbmuxd/client.c
@@ -0,0 +1,430 @@
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18
19*/
20
21#ifdef HAVE_CONFIG_H
22#include <config.h>
23#endif
24
25#include <stdlib.h>
26#include <errno.h>
27#include <unistd.h>
28#include <sys/types.h>
29#include <sys/socket.h>
30#include <sys/un.h>
31#include <arpa/inet.h>
32
33#include "log.h"
34#include "usb.h"
35#include "client.h"
36#include "device.h"
37
38#define CMD_BUF_SIZE 256
39#define REPLY_BUF_SIZE 1024
40
41enum client_state {
42 CLIENT_COMMAND, // waiting for command
43 CLIENT_LISTEN, // listening for devices
44 CLIENT_CONNECTING1, // issued connection request
45 CLIENT_CONNECTING2, // connection established, but waiting for response message to get sent
46 CLIENT_CONNECTED, // connected
47 CLIENT_DEAD
48};
49
50struct mux_client {
51 int fd;
52 unsigned char *ob_buf;
53 int ob_size;
54 int ob_capacity;
55 unsigned char *ib_buf;
56 int ib_size;
57 int ib_capacity;
58 short events, devents;
59 uint32_t connect_tag;
60 int connect_device;
61 enum client_state state;
62};
63
64static struct collection client_list;
65
66int client_read(struct mux_client *client, void *buffer, int len)
67{
68 usbmuxd_log(LL_SPEW, "client_read fd %d buf %p len %d", client->fd, buffer, len);
69 if(client->state != CLIENT_CONNECTED) {
70 usbmuxd_log(LL_ERROR, "Attempted to read from client %d not in CONNECTED state", client->fd);
71 return -1;
72 }
73 return recv(client->fd, buffer, len, 0);
74}
75
76int client_write(struct mux_client *client, void *buffer, int len)
77{
78 usbmuxd_log(LL_SPEW, "client_write fd %d buf %p len %d", client->fd, buffer, len);
79 if(client->state != CLIENT_CONNECTED) {
80 usbmuxd_log(LL_ERROR, "Attempted to write to client %d not in CONNECTED state", client->fd);
81 return -1;
82 }
83 return send(client->fd, buffer, len, 0);
84}
85
86int client_set_events(struct mux_client *client, short events)
87{
88 if((client->state != CLIENT_CONNECTED) && (client->state != CLIENT_CONNECTING2)) {
89 usbmuxd_log(LL_ERROR, "client_set_events to client %d not in CONNECTED state", client->fd);
90 return -1;
91 }
92 client->devents = events;
93 if(client->state == CLIENT_CONNECTED)
94 client->events = events;
95 return 0;
96}
97
98int client_accept(int listenfd)
99{
100 struct sockaddr_un addr;
101 int cfd;
102 socklen_t len = sizeof(struct sockaddr_un);
103 cfd = accept(listenfd, (struct sockaddr *)&addr, &len);
104 if (cfd < 0) {
105 usbmuxd_log(LL_ERROR, "accept() failed (%s)", strerror(errno));
106 return cfd;
107 }
108
109 struct mux_client *client;
110 client = malloc(sizeof(struct mux_client));
111 memset(client, 0, sizeof(struct mux_client));
112
113 client->fd = cfd;
114 client->ob_buf = malloc(REPLY_BUF_SIZE);
115 client->ob_size = 0;
116 client->ob_capacity = REPLY_BUF_SIZE;
117 client->ib_buf = malloc(CMD_BUF_SIZE);
118 client->ib_size = 0;
119 client->ib_capacity = CMD_BUF_SIZE;
120 client->state = CLIENT_COMMAND;
121 client->events = POLLIN;
122
123 collection_add(&client_list, client);
124
125 usbmuxd_log(LL_INFO, "New client on fd %d", client->fd);
126 return client->fd;
127}
128
129void client_close(struct mux_client *client)
130{
131 usbmuxd_log(LL_INFO, "Disconnecting client fd %d", client->fd);
132 if(client->state == CLIENT_CONNECTING1 || client->state == CLIENT_CONNECTING2) {
133 usbmuxd_log(LL_INFO, "Client died mid-connect, aborting device %d connection", client->connect_device);
134 client->state = CLIENT_DEAD;
135 device_abort_connect(client->connect_device, client);
136 }
137 close(client->fd);
138 if(client->ob_buf)
139 free(client->ob_buf);
140 if(client->ib_buf)
141 free(client->ib_buf);
142 collection_remove(&client_list, client);
143 free(client);
144}
145
146void client_get_fds(struct fdlist *list)
147{
148 FOREACH(struct mux_client *client, &client_list) {
149 fdlist_add(list, FD_CLIENT, client->fd, client->events);
150 } ENDFOREACH
151}
152
153static int send_pkt(struct mux_client *client, uint32_t tag, enum client_msgtype msg, void *payload, int payload_length)
154{
155 struct client_header hdr;
156 hdr.version = CLIENT_PROTOCOL_VERSION;
157 hdr.length = sizeof(hdr) + payload_length;
158 hdr.message = msg;
159 hdr.tag = tag;
160 usbmuxd_log(LL_DEBUG, "send_pkt fd %d tag %d msg %d payload_length %d", client->fd, tag, msg, payload_length);
161 if((client->ob_capacity - client->ob_size) < hdr.length) {
162 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);
163 client_close(client);
164 return -1;
165 }
166 memcpy(client->ob_buf + client->ob_size, &hdr, sizeof(hdr));
167 if(payload && payload_length)
168 memcpy(client->ob_buf + client->ob_size + sizeof(hdr), payload, payload_length);
169 client->ob_size += hdr.length;
170 client->events |= POLLOUT;
171 return hdr.length;
172}
173
174static int send_result(struct mux_client *client, uint32_t tag, uint32_t result)
175{
176 return send_pkt(client, tag, MESSAGE_RESULT, &result, sizeof(uint32_t));
177}
178
179int client_notify_connect(struct mux_client *client, enum client_result result)
180{
181 usbmuxd_log(LL_SPEW, "client_notify_connect fd %d result %d", client->fd, result);
182 if(client->state == CLIENT_DEAD)
183 return -1;
184 if(client->state != CLIENT_CONNECTING1) {
185 usbmuxd_log(LL_ERROR, "client_notify_connect when client %d is not in CONNECTING1 state", client->fd);
186 return -1;
187 }
188 if(send_result(client, client->connect_tag, result) < 0)
189 return -1;
190 if(result == RESULT_OK) {
191 client->state = CLIENT_CONNECTING2;
192 client->events = POLLOUT; // wait for the result packet to go through
193 // no longer need this
194 free(client->ib_buf);
195 client->ib_buf = NULL;
196 } else {
197 client->state = CLIENT_COMMAND;
198 }
199 return 0;
200}
201
202static int notify_device(struct mux_client *client, struct device_info *dev)
203{
204 struct client_msg_dev dmsg;
205 memset(&dmsg, 0, sizeof(dmsg));
206 dmsg.device_id = dev->id;
207 strncpy(dmsg.device_serial, dev->serial, 256);
208 dmsg.device_serial[255] = 0;
209 dmsg.location = dev->location;
210 dmsg.device_pid = dev->pid;
211 return send_pkt(client, 0, MESSAGE_DEVICE_ADD, &dmsg, sizeof(dmsg));
212}
213
214static int start_listen(struct mux_client *client)
215{
216 struct device_info *devs;
217 struct device_info *dev;
218 int count, i;
219
220 client->state = CLIENT_LISTEN;
221 count = device_get_count();
222 if(!count)
223 return 0;
224 devs = malloc(sizeof(struct device_info) * count);
225 count = device_get_list(devs);
226
227 // going to need a larger buffer for many devices
228 int needed_buffer = count * (sizeof(struct client_msg_dev) + sizeof(struct client_header)) + REPLY_BUF_SIZE;
229 if(client->ob_capacity < needed_buffer) {
230 usbmuxd_log(LL_DEBUG, "Enlarging client %d reply buffer %d -> %d to make space for device notifications", client->fd, client->ob_capacity, needed_buffer);
231 client->ob_buf = realloc(client->ob_buf, needed_buffer);
232 client->ob_capacity = needed_buffer;
233 }
234 dev = devs;
235 for(i=0; i<count; i++) {
236 if(notify_device(client, dev++) < 0) {
237 free(devs);
238 return -1;
239 }
240 }
241 free(devs);
242 return count;
243}
244
245static int client_command(struct mux_client *client, struct client_header *hdr, const char *payload)
246{
247 int res;
248 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);
249
250 if(client->state != CLIENT_COMMAND) {
251 usbmuxd_log(LL_ERROR, "Client %d command received in the wrong state", client->fd);
252 if(send_result(client, hdr->tag, RESULT_BADCOMMAND) < 0)
253 return -1;
254 client_close(client);
255 return -1;
256 }
257
258 struct client_msg_connect *ch;
259 switch(hdr->message) {
260 case MESSAGE_LISTEN:
261 if(send_result(client, hdr->tag, 0) < 0)
262 return -1;
263 usbmuxd_log(LL_DEBUG, "Client %d now LISTENING", client->fd);
264 return start_listen(client);
265 case MESSAGE_CONNECT:
266 ch = (void*)payload;
267 usbmuxd_log(LL_DEBUG, "Client %d connection request to device %d port %d", client->fd, ch->device_id, ntohs(ch->port));
268 res = device_start_connect(ch->device_id, ntohs(ch->port), client);
269 if(res < 0) {
270 if(send_result(client, hdr->tag, -res) < 0)
271 return -1;
272 } else {
273 client->connect_tag = hdr->tag;
274 client->connect_device = ch->device_id;
275 client->state = CLIENT_CONNECTING1;
276 }
277 return 0;
278 default:
279 usbmuxd_log(LL_ERROR, "Client %d invalid command %d", client->fd, hdr->message);
280 if(send_result(client, hdr->tag, RESULT_BADCOMMAND) < 0)
281 return -1;
282 return 0;
283 }
284 return -1;
285}
286
287static void process_send(struct mux_client *client)
288{
289 int res;
290 if(!client->ob_size) {
291 usbmuxd_log(LL_WARNING, "Client %d OUT process but nothing to send?", client->fd);
292 client->events &= ~POLLOUT;
293 return;
294 }
295 res = send(client->fd, client->ob_buf, client->ob_size, 0);
296 if(res <= 0) {
297 usbmuxd_log(LL_ERROR, "Send to client fd %d failed: %d %s", client->fd, res, strerror(errno));
298 client_close(client);
299 return;
300 }
301 if(res == client->ob_size) {
302 client->ob_size = 0;
303 client->events &= ~POLLOUT;
304 if(client->state == CLIENT_CONNECTING2) {
305 usbmuxd_log(LL_DEBUG, "Client %d switching to CONNECTED state", client->fd);
306 client->state = CLIENT_CONNECTED;
307 client->events = client->devents;
308 // no longer need this
309 free(client->ob_buf);
310 client->ob_buf = NULL;
311 }
312 } else {
313 client->ob_size -= res;
314 memmove(client->ob_buf, client->ob_buf + res, client->ob_size);
315 }
316}
317static void process_recv(struct mux_client *client)
318{
319 int res;
320 int did_read = 0;
321 if(client->ib_size < sizeof(struct client_header)) {
322 res = recv(client->fd, client->ib_buf + client->ib_size, sizeof(struct client_header) - client->ib_size, 0);
323 if(res <= 0) {
324 if(res < 0)
325 usbmuxd_log(LL_ERROR, "Receive from client fd %d failed: %s", client->fd, strerror(errno));
326 else
327 usbmuxd_log(LL_INFO, "Client %d connection closed", client->fd);
328 client_close(client);
329 return;
330 }
331 client->ib_size += res;
332 if(client->ib_size < sizeof(struct client_header))
333 return;
334 did_read = 1;
335 }
336 struct client_header *hdr = (void*)client->ib_buf;
337 if(hdr->version != CLIENT_PROTOCOL_VERSION) {
338 usbmuxd_log(LL_INFO, "Client %d version mismatch: expected %d, got %d", client->fd, CLIENT_PROTOCOL_VERSION, hdr->version);
339 client_close(client);
340 }
341 if(hdr->length > client->ib_capacity) {
342 usbmuxd_log(LL_INFO, "Client %d message is too long (%d bytes)", client->fd, hdr->length);
343 client_close(client);
344 }
345 if(hdr->length < sizeof(struct client_header)) {
346 usbmuxd_log(LL_ERROR, "Client %d message is too short (%d bytes)", client->fd, hdr->length);
347 client_close(client);
348 }
349 if(client->ib_size < hdr->length) {
350 if(did_read)
351 return; //maybe we would block, so defer to next loop
352 res = recv(client->fd, client->ib_buf + client->ib_size, hdr->length - client->ib_size, 0);
353 if(res < 0) {
354 usbmuxd_log(LL_ERROR, "Receive from client fd %d failed: %s", client->fd, strerror(errno));
355 client_close(client);
356 return;
357 } else if(res == 0) {
358 usbmuxd_log(LL_INFO, "Client %d connection closed", client->fd);
359 client_close(client);
360 return;
361 }
362 client->ib_size += res;
363 if(client->ib_size < hdr->length)
364 return;
365 }
366 client_command(client, hdr, (char *)(hdr+1));
367 client->ib_size = 0;
368}
369
370void client_process(int fd, short events)
371{
372 struct mux_client *client = NULL;
373 FOREACH(struct mux_client *lc, &client_list) {
374 if(lc->fd == fd) {
375 client = lc;
376 break;
377 }
378 } ENDFOREACH
379
380 if(!client) {
381 usbmuxd_log(LL_ERROR, "client_process: fd %d not found in client list", fd);
382 return;
383 }
384
385 if(client->state == CLIENT_CONNECTED) {
386 usbmuxd_log(LL_SPEW, "client_process in CONNECTED state");
387 device_client_process(client->connect_device, client, events);
388 } else {
389 if(events & POLLIN) {
390 process_recv(client);
391 } else if(events & POLLOUT) { //not both in case client died as part of process_recv
392 process_send(client);
393 }
394 }
395
396}
397
398void client_device_add(struct device_info *dev)
399{
400 usbmuxd_log(LL_DEBUG, "client_device_add: id %d, location 0x%x, serial %s", dev->id, dev->location, dev->serial);
401 FOREACH(struct mux_client *client, &client_list) {
402 if(client->state == CLIENT_LISTEN)
403 notify_device(client, dev);
404 } ENDFOREACH
405}
406void client_device_remove(int device_id)
407{
408 uint32_t id = device_id;
409 usbmuxd_log(LL_DEBUG, "client_device_remove: id %d", device_id);
410 FOREACH(struct mux_client *client, &client_list) {
411 if(client->state == CLIENT_LISTEN)
412 send_pkt(client, 0, MESSAGE_DEVICE_REMOVE, &id, sizeof(uint32_t));
413 } ENDFOREACH
414}
415
416
417void client_init(void)
418{
419 usbmuxd_log(LL_DEBUG, "client_init");
420 collection_init(&client_list);
421}
422
423void client_shutdown(void)
424{
425 usbmuxd_log(LL_DEBUG, "client_shutdown");
426 FOREACH(struct mux_client *client, &client_list) {
427 client_close(client);
428 } ENDFOREACH
429 collection_free(&client_list);
430}
diff --git a/usbmuxd/client.h b/usbmuxd/client.h
new file mode 100644
index 0000000..0cda676
--- /dev/null
+++ b/usbmuxd/client.h
@@ -0,0 +1,92 @@
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18
19*/
20
21#ifndef __CLIENT_H__
22#define __CLIENT_H__
23
24#include <stdint.h>
25
26struct device_info;
27struct mux_client;
28
29enum client_result {
30 RESULT_OK = 0,
31 RESULT_BADCOMMAND = 1,
32 RESULT_BADDEV = 2,
33 RESULT_CONNREFUSED = 3,
34 // ???
35 // ???
36 RESULT_BADVERSION = 6,
37};
38
39enum client_msgtype {
40 MESSAGE_RESULT = 1,
41 MESSAGE_CONNECT = 2,
42 MESSAGE_LISTEN = 3,
43 MESSAGE_DEVICE_ADD = 4,
44 MESSAGE_DEVICE_REMOVE = 5,
45 //???
46 //???
47 //MESSAGE_PLIST = 8,
48};
49
50#define CLIENT_PROTOCOL_VERSION 0
51
52struct client_header {
53 uint32_t length;
54 uint32_t version;
55 uint32_t message;
56 uint32_t tag;
57};
58
59struct client_msg_result {
60 uint32_t result;
61};
62
63struct client_msg_connect {
64 uint32_t device_id;
65 uint16_t port;
66};
67
68struct client_msg_dev {
69 uint32_t device_id;
70 uint16_t device_pid;
71 char device_serial[256];
72 uint16_t padding;
73 uint32_t location;
74};
75
76int client_read(struct mux_client *client, void *buffer, int len);
77int client_write(struct mux_client *client, void *buffer, int len);
78int client_set_events(struct mux_client *client, short events);
79void client_close(struct mux_client *client);
80int client_notify_connect(struct mux_client *client, enum client_result result);
81
82void client_device_add(struct device_info *dev);
83void client_device_remove(int device_id);
84
85int client_accept(int fd);
86void client_get_fds(struct fdlist *list);
87void client_process(int fd, short events);
88
89void client_init(void);
90void client_shutdown(void);
91
92#endif
diff --git a/usbmuxd/device.c b/usbmuxd/device.c
new file mode 100644
index 0000000..3a5883c
--- /dev/null
+++ b/usbmuxd/device.c
@@ -0,0 +1,751 @@
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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_PKTBUF_SIZE 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 int max_payload;
93 int sendable;
94 int flags;
95 unsigned char *ib_buf;
96 int ib_size;
97 int ib_capacity;
98 unsigned char *ob_buf;
99 int 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 int 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 conn->sendable = conn->rx_win - (conn->tx_seq - conn->rx_ack);
326
327 if(conn->sendable > conn->ob_capacity)
328 conn->sendable = conn->ob_capacity;
329 if(conn->sendable > conn->max_payload)
330 conn->sendable = conn->max_payload;
331
332 if(conn->sendable > 0)
333 conn->events |= POLLIN;
334 else
335 conn->events &= ~POLLIN;
336
337 if(conn->ib_size)
338 conn->events |= POLLOUT;
339 else
340 conn->events &= ~POLLOUT;
341
342 if(conn->tx_acked != conn->tx_ack)
343 conn->flags |= CONN_ACK_PENDING;
344 else
345 conn->flags &= ~CONN_ACK_PENDING;
346
347 usbmuxd_log(LL_SPEW, "update_connection: sendable %d, events %d, flags %d", conn->sendable, conn->events, conn->flags);
348 client_set_events(conn->client, conn->events);
349}
350
351void device_client_process(int device_id, struct mux_client *client, short events)
352{
353 struct mux_connection *conn = NULL;
354 FOREACH(struct mux_device *dev, &device_list) {
355 if(dev->id == device_id) {
356 FOREACH(struct mux_connection *lconn, &dev->connections) {
357 if(lconn->client == client) {
358 conn = lconn;
359 break;
360 }
361 } ENDFOREACH
362 break;
363 }
364 } ENDFOREACH
365
366 if(!conn) {
367 usbmuxd_log(LL_WARNING, "Could not find connection for device %d client %p", device_id, client);
368 return;
369 }
370 usbmuxd_log(LL_SPEW, "device_client_process (%d)", events);
371
372 int res;
373 int size;
374 if(events & POLLOUT) {
375 size = client_write(conn->client, conn->ib_buf, conn->ib_size);
376 if(size <= 0) {
377 usbmuxd_log(LL_DEBUG, "error writing to client (%d)", size);
378 connection_teardown(conn);
379 return;
380 }
381 conn->tx_ack += size;
382 if(size == conn->ib_size) {
383 conn->ib_size = 0;
384 } else {
385 conn->ib_size -= size;
386 memmove(conn->ib_buf, conn->ib_buf + size, conn->ib_size);
387 }
388 }
389 if(events & POLLIN) {
390 size = client_read(conn->client, conn->ob_buf, conn->sendable);
391 if(size <= 0) {
392 usbmuxd_log(LL_DEBUG, "error reading from client (%d)", size);
393 connection_teardown(conn);
394 return;
395 }
396 res = send_tcp(conn, TH_ACK, conn->ob_buf, size);
397 if(res < 0) {
398 connection_teardown(conn);
399 return;
400 }
401 conn->tx_seq += size;
402 }
403
404 update_connection(conn);
405}
406
407static void connection_device_input(struct mux_connection *conn, unsigned char *payload, int payload_length)
408{
409 if((conn->ib_size + payload_length) > conn->ib_capacity) {
410 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);
411 connection_teardown(conn);
412 return;
413 }
414 memcpy(conn->ib_buf + conn->ib_size, payload, payload_length);
415 conn->ib_size += payload_length;
416 conn->rx_recvd += payload_length;
417 update_connection(conn);
418}
419
420void device_abort_connect(int device_id, struct mux_client *client)
421{
422 FOREACH(struct mux_device *dev, &device_list) {
423 if(dev->id == device_id) {
424 FOREACH(struct mux_connection *conn, &dev->connections) {
425 if(conn->client == client) {
426 connection_teardown(conn);
427 return;
428 }
429 } ENDFOREACH
430 usbmuxd_log(LL_WARNING, "Attempted to abort for nonexistent connection for device %d", device_id);
431 return;
432 }
433 } ENDFOREACH
434 usbmuxd_log(LL_WARNING, "Attempted to abort connection for nonexistent device %d", device_id);
435}
436
437static void device_version_input(struct mux_device *dev, struct version_header *vh)
438{
439 if(dev->state != MUXDEV_INIT) {
440 usbmuxd_log(LL_WARNING, "Version packet from already initialized device %d", dev->id);
441 return;
442 }
443 vh->major = ntohl(vh->major);
444 vh->minor = ntohl(vh->minor);
445 if(vh->major != 1 || vh->minor != 0) {
446 usbmuxd_log(LL_ERROR, "Device %d has unknown version %d.%d\n", dev->id, vh->major, vh->minor);
447 collection_remove(&device_list, dev);
448 free(dev);
449 return;
450 }
451 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));
452 dev->state = MUXDEV_ACTIVE;
453 collection_init(&dev->connections);
454 struct device_info info;
455 info.id = dev->id;
456 info.location = usb_get_location(dev->usbdev);
457 info.serial = usb_get_serial(dev->usbdev);
458 info.pid = usb_get_pid(dev->usbdev);
459 client_device_add(&info);
460}
461
462static void device_tcp_input(struct mux_device *dev, struct tcphdr *th, unsigned char *payload, int payload_length)
463{
464 usbmuxd_log(LL_DEBUG, "[IN] dev=%d sport=%d dport=%d seq=%d ack=%d flags=0x%x window=%d[%d] len=%d",
465 dev->id, ntohs(th->th_sport), ntohs(th->th_dport), ntohl(th->th_seq), ntohl(th->th_ack), th->th_flags, ntohs(th->th_win) << 8, ntohs(th->th_win), payload_length);
466
467 uint16_t sport = ntohs(th->th_dport);
468 uint16_t dport = ntohs(th->th_sport);
469 struct mux_connection *conn = NULL;
470 FOREACH(struct mux_connection *lconn, &dev->connections) {
471 if(lconn->sport == sport && lconn->dport == dport) {
472 conn = lconn;
473 break;
474 }
475 } ENDFOREACH
476
477 if(!conn) {
478 usbmuxd_log(LL_WARNING, "No connection for device %d incoming packet %d->%d", dev->id, dport, sport);
479 if(!(th->th_flags & TH_RST)) {
480 if(send_anon_rst(dev, sport, dport, ntohl(th->th_seq)) < 0)
481 usbmuxd_log(LL_ERROR, "Error sending TCP RST to device %d (%d->%d)", conn->dev->id, sport, dport);
482 }
483 return;
484 }
485
486 conn->rx_seq = ntohl(th->th_seq);
487 conn->rx_ack = ntohl(th->th_ack);
488 conn->rx_win = ntohs(th->th_win) << 8;
489
490 if(th->th_flags & TH_RST) {
491 char *buf = malloc(payload_length+1);
492 memcpy(buf, payload, payload_length);
493 if(payload_length && (buf[payload_length-1] == '\n'))
494 buf[payload_length-1] = 0;
495 buf[payload_length] = 0;
496 usbmuxd_log(LL_DEBUG, "RST reason: %s", buf);
497 free(buf);
498 }
499
500 if(conn->state == CONN_CONNECTING) {
501 if(th->th_flags != (TH_SYN|TH_ACK)) {
502 if(th->th_flags & TH_RST)
503 conn->state = CONN_REFUSED;
504 usbmuxd_log(LL_INFO, "Connection refused by device %d (%d->%d)", dev->id, sport, dport);
505 connection_teardown(conn); //this also sends the notification to the client
506 } else {
507 conn->tx_seq++;
508 conn->tx_ack++;
509 conn->rx_recvd = conn->rx_seq;
510 if(send_tcp(conn, TH_ACK, NULL, 0) < 0) {
511 usbmuxd_log(LL_ERROR, "Error sending TCP ACK to device %d (%d->%d)", dev->id, sport, dport);
512 connection_teardown(conn);
513 return;
514 }
515 conn->state = CONN_CONNECTED;
516 if(client_notify_connect(conn->client, RESULT_OK) < 0) {
517 conn->client = NULL;
518 connection_teardown(conn);
519 }
520 update_connection(conn);
521 }
522 } else if(conn->state == CONN_CONNECTED) {
523 if(th->th_flags != TH_ACK) {
524 usbmuxd_log(LL_INFO, "Connection reset by device %d (%d->%d)", dev->id, sport, dport);
525 if(th->th_flags & TH_RST)
526 conn->state = CONN_DYING;
527 connection_teardown(conn);
528 } else {
529 connection_device_input(conn, payload, payload_length);
530 }
531 }
532}
533
534void device_data_input(struct usb_device *usbdev, unsigned char *buffer, int length)
535{
536 struct mux_device *dev = NULL;
537 FOREACH(struct mux_device *tdev, &device_list) {
538 if(tdev->usbdev == usbdev) {
539 dev = tdev;
540 break;
541 }
542 } ENDFOREACH
543 if(!dev) {
544 usbmuxd_log(LL_WARNING, "Cannot find device entry for RX input from USB device %p on location 0x%x", usbdev, usb_get_location(usbdev));
545 return;
546 }
547
548 if(!length)
549 return;
550
551 usbmuxd_log(LL_SPEW, "Mux data input for device %p: %p len %d", dev, buffer, length);
552
553 // handle broken up transfers
554 if(dev->pktlen) {
555 memcpy(dev->pktbuf + dev->pktlen, buffer, length);
556 struct mux_header *mhdr = (struct mux_header *)dev->pktbuf;
557 if((length < USB_MRU) || (ntohl(mhdr->length) == length)) {
558 buffer = dev->pktbuf;
559 length += dev->pktlen;
560 dev->pktlen = 0;
561 usbmuxd_log(LL_SPEW, "Gathered mux data from buffer (total size: %d)", length);
562 } else {
563 dev->pktlen += length;
564 usbmuxd_log(LL_SPEW, "Appended mux data to buffer (total size: %d)", dev->pktlen);
565 return;
566 }
567 } else {
568 struct mux_header *mhdr = (struct mux_header *)buffer;
569 if((length == USB_MRU) && (length < ntohl(mhdr->length))) {
570 memcpy(dev->pktbuf, buffer, length);
571 dev->pktlen = length;
572 usbmuxd_log(LL_SPEW, "Copied mux data to buffer (size: %d)", dev->pktlen);
573 return;
574 }
575 }
576
577 struct mux_header *mhdr = (struct mux_header *)buffer;
578
579 if(ntohl(mhdr->length) != length) {
580 usbmuxd_log(LL_ERROR, "Incoming packet size mismatch (dev %d, expected %d, got %d)", dev->id, ntohl(mhdr->length), length);
581 return;
582 }
583
584 struct tcphdr *th;
585 unsigned char *payload;
586 int payload_length;
587
588 switch(ntohl(mhdr->protocol)) {
589 case MUX_PROTO_VERSION:
590 device_version_input(dev, (struct version_header *)(mhdr+1));
591 break;
592 case MUX_PROTO_TCP:
593 th = (struct tcphdr *)(mhdr+1);
594 payload = (unsigned char *)(th+1);
595 payload_length = length - sizeof(struct tcphdr) - sizeof(struct mux_header);
596 device_tcp_input(dev, (struct tcphdr *)(mhdr+1), payload, payload_length);
597 break;
598 default:
599 usbmuxd_log(LL_ERROR, "Incoming packet for device %d has unknown protocol 0x%x)", dev->id, ntohl(mhdr->protocol));
600 break;
601 }
602
603}
604
605int device_add(struct usb_device *usbdev)
606{
607 int res;
608 int id = get_next_device_id();
609 struct mux_device *dev;
610 usbmuxd_log(LL_NOTICE, "Connecting to new device on location 0x%x as ID %d", usb_get_location(usbdev), id);
611 dev = malloc(sizeof(struct mux_device));
612 dev->id = id;
613 dev->usbdev = usbdev;
614 dev->state = MUXDEV_INIT;
615 dev->next_sport = 1;
616 dev->pktbuf = malloc(DEV_PKTBUF_SIZE);
617 dev->pktlen = 0;
618 struct version_header vh;
619 vh.major = htonl(1);
620 vh.minor = htonl(0);
621 vh.padding = 0;
622 if((res = send_packet(dev, MUX_PROTO_VERSION, &vh, NULL, 0)) < 0) {
623 usbmuxd_log(LL_ERROR, "Error sending version request packet to device %d\n", id);
624 free(dev);
625 return res;
626 }
627 collection_add(&device_list, dev);
628 return 0;
629}
630
631void device_remove(struct usb_device *usbdev)
632{
633 FOREACH(struct mux_device *dev, &device_list) {
634 if(dev->usbdev == usbdev) {
635 usbmuxd_log(LL_NOTICE, "Removed device %d on location 0x%x", dev->id, usb_get_location(usbdev));
636 if(dev->state == MUXDEV_ACTIVE) {
637 dev->state = MUXDEV_DEAD;
638 FOREACH(struct mux_connection *conn, &dev->connections) {
639 connection_teardown(conn);
640 } ENDFOREACH
641 client_device_remove(dev->id);
642 collection_free(&dev->connections);
643 }
644 collection_remove(&device_list, dev);
645 free(dev->pktbuf);
646 free(dev);
647 return;
648 }
649 } ENDFOREACH
650 usbmuxd_log(LL_WARNING, "Cannot find device entry while removing USB device %p on location 0x%x", usbdev, usb_get_location(usbdev));
651}
652
653int device_get_count(void)
654{
655 int count = 0;
656 FOREACH(struct mux_device *dev, &device_list) {
657 if(dev->state == MUXDEV_ACTIVE)
658 count++;
659 } ENDFOREACH
660 return count;
661}
662
663int device_get_list(struct device_info *p)
664{
665 int count = 0;
666 FOREACH(struct mux_device *dev, &device_list) {
667 if(dev->state == MUXDEV_ACTIVE) {
668 p->id = dev->id;
669 p->serial = usb_get_serial(dev->usbdev);
670 p->location = usb_get_location(dev->usbdev);
671 p->pid = usb_get_pid(dev->usbdev);
672 count++;
673 p++;
674 }
675 } ENDFOREACH
676 return count;
677}
678
679int device_get_timeout(void)
680{
681 uint64_t oldest = (uint64_t)-1;
682 FOREACH(struct mux_device *dev, &device_list) {
683 if(dev->state == MUXDEV_ACTIVE) {
684 FOREACH(struct mux_connection *conn, &dev->connections) {
685 if((conn->state == CONN_CONNECTED) && (conn->flags & CONN_ACK_PENDING) && conn->last_ack_time < oldest)
686 oldest = conn->last_ack_time;
687 } ENDFOREACH
688 }
689 } ENDFOREACH
690 uint64_t ct = mstime64();
691 if(oldest == -1)
692 return 100000; //meh
693 if((ct - oldest) > ACK_TIMEOUT)
694 return 0;
695 return ACK_TIMEOUT - (ct - oldest);
696}
697
698void device_check_timeouts(void)
699{
700 uint64_t ct = mstime64();
701 FOREACH(struct mux_device *dev, &device_list) {
702 if(dev->state == MUXDEV_ACTIVE) {
703 FOREACH(struct mux_connection *conn, &dev->connections) {
704 if((conn->state == CONN_CONNECTED) &&
705 (conn->flags & CONN_ACK_PENDING) &&
706 (ct - conn->last_ack_time) > ACK_TIMEOUT) {
707 usbmuxd_log(LL_DEBUG, "Sending ACK due to expired timeout (%" PRIu64 " -> %" PRIu64 ")", conn->last_ack_time, ct);
708 if(send_tcp(conn, TH_ACK, NULL, 0) < 0) {
709 usbmuxd_log(LL_ERROR, "Error sending TCP ACK to device %d (%d->%d)", dev->id, conn->sport, conn->dport);
710 connection_teardown(conn);
711 }
712 }
713 } ENDFOREACH
714 }
715 } ENDFOREACH
716}
717
718void device_init(void)
719{
720 usbmuxd_log(LL_DEBUG, "device_init");
721 collection_init(&device_list);
722 next_device_id = 1;
723}
724
725void device_kill_connections(void)
726{
727 usbmuxd_log(LL_DEBUG, "device_kill_connections");
728 FOREACH(struct mux_device *dev, &device_list) {
729 if(dev->state != MUXDEV_INIT) {
730 FOREACH(struct mux_connection *conn, &dev->connections) {
731 connection_teardown(conn);
732 } ENDFOREACH
733 }
734 } ENDFOREACH
735 // give USB a while to send the final connection RSTs and the like
736 usb_process_timeout(100);
737}
738
739void device_shutdown(void)
740{
741 usbmuxd_log(LL_DEBUG, "device_shutdown");
742 FOREACH(struct mux_device *dev, &device_list) {
743 FOREACH(struct mux_connection *conn, &dev->connections) {
744 connection_teardown(conn);
745 } ENDFOREACH
746 collection_free(&dev->connections);
747 collection_remove(&device_list, dev);
748 free(dev);
749 } ENDFOREACH
750 collection_free(&device_list);
751}
diff --git a/usbmuxd/device.h b/usbmuxd/device.h
new file mode 100644
index 0000000..ce6c50b
--- /dev/null
+++ b/usbmuxd/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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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, int 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/usbmuxd/log.c b/usbmuxd/log.c
new file mode 100644
index 0000000..2ccb3cc
--- /dev/null
+++ b/usbmuxd/log.c
@@ -0,0 +1,94 @@
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18
19*/
20
21#ifdef HAVE_CONFIG_H
22#include <config.h>
23#endif
24
25#include <stdio.h>
26#include <stdlib.h>
27#include <string.h>
28#include <stdarg.h>
29#include <time.h>
30#include <sys/time.h>
31#include <syslog.h>
32
33#include "log.h"
34
35int log_level = LL_WARNING;
36
37int log_syslog = 0;
38
39void log_enable_syslog()
40{
41 if (!log_syslog) {
42 openlog("usbmuxd", LOG_PID, 0);
43 log_syslog = 1;
44 }
45}
46
47void log_disable_syslog()
48{
49 if (log_syslog) {
50 closelog();
51 }
52}
53
54static int level_to_syslog_level(int level)
55{
56 int result = level + LOG_CRIT;
57 if (result > LOG_DEBUG) {
58 result = LOG_DEBUG;
59 }
60 return result;
61}
62
63void usbmuxd_log(enum loglevel level, const char *fmt, ...)
64{
65 va_list ap;
66 char *fs;
67 struct timeval ts;
68 struct tm *tp;
69
70 gettimeofday(&ts, NULL);
71 tp = localtime(&ts.tv_sec);
72
73 if(level > log_level)
74 return;
75
76 fs = malloc(20 + strlen(fmt));
77
78 if(log_syslog) {
79 sprintf(fs, "[%d] %s\n", level, fmt);
80 } else {
81 strftime(fs, 10, "[%H:%M:%S", tp);
82 sprintf(fs+9, ".%03d][%d] %s\n", (int)(ts.tv_usec / 1000), level, fmt);
83 }
84
85 va_start(ap, fmt);
86 if (log_syslog) {
87 vsyslog(level_to_syslog_level(level), fs, ap);
88 } else {
89 vfprintf(stderr, fs, ap);
90 }
91 va_end(ap);
92
93 free(fs);
94}
diff --git a/usbmuxd/log.h b/usbmuxd/log.h
new file mode 100644
index 0000000..4a2ac2e
--- /dev/null
+++ b/usbmuxd/log.h
@@ -0,0 +1,43 @@
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18
19*/
20
21#ifndef __LOG_H__
22#define __LOG_H__
23
24enum loglevel {
25 LL_FATAL = 0,
26 LL_ERROR,
27 LL_WARNING,
28 LL_NOTICE,
29 LL_INFO,
30 LL_DEBUG,
31 LL_SPEW,
32 LL_FLOOD,
33};
34
35extern int log_level;
36
37void log_enable_syslog();
38void log_disable_syslog();
39
40void usbmuxd_log(enum loglevel level, const char *fmt, ...) __attribute__ ((format (printf, 2, 3)));
41
42
43#endif
diff --git a/usbmuxd/main.c b/usbmuxd/main.c
new file mode 100644
index 0000000..dde99c2
--- /dev/null
+++ b/usbmuxd/main.c
@@ -0,0 +1,548 @@
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18
19*/
20
21#define _BSD_SOURCE
22
23#ifdef HAVE_CONFIG_H
24#include <config.h>
25#endif
26
27#include <stdio.h>
28#include <errno.h>
29#include <string.h>
30#include <stdlib.h>
31#include <signal.h>
32#include <unistd.h>
33#include <sys/socket.h>
34#include <sys/un.h>
35#include <sys/stat.h>
36#include <sys/types.h>
37#include <fcntl.h>
38#include <getopt.h>
39#include <pwd.h>
40#include <grp.h>
41
42#include "log.h"
43#include "usb.h"
44#include "device.h"
45#include "client.h"
46
47static const char *socket_path = "/var/run/usbmuxd";
48static const char *lockfile = "/var/run/usbmuxd.pid";
49
50int should_exit;
51
52static int verbose = 0;
53static int foreground = 0;
54static int drop_privileges = 0;
55static const char *drop_user = "usbmux";
56static int opt_udev = 0;
57static int opt_exit = 0;
58static int exit_signal = 0;
59static int daemon_pipe;
60
61static int report_to_parent = 0;
62
63int create_socket(void) {
64 struct sockaddr_un bind_addr;
65 int listenfd;
66
67 if(unlink(socket_path) == -1 && errno != ENOENT) {
68 usbmuxd_log(LL_FATAL, "unlink(%s) failed: %s", socket_path, strerror(errno));
69 return -1;
70 }
71
72 listenfd = socket(AF_UNIX, SOCK_STREAM, 0);
73 if (listenfd == -1) {
74 usbmuxd_log(LL_FATAL, "socket() failed: %s", strerror(errno));
75 return -1;
76 }
77
78 bzero(&bind_addr, sizeof(bind_addr));
79 bind_addr.sun_family = AF_UNIX;
80 strcpy(bind_addr.sun_path, socket_path);
81 if (bind(listenfd, (struct sockaddr*)&bind_addr, sizeof(bind_addr)) != 0) {
82 usbmuxd_log(LL_FATAL, "bind() failed: %s", strerror(errno));
83 return -1;
84 }
85
86 // Start listening
87 if (listen(listenfd, 5) != 0) {
88 usbmuxd_log(LL_FATAL, "listen() failed: %s", strerror(errno));
89 return -1;
90 }
91
92 chmod(socket_path, 0666);
93
94 return listenfd;
95}
96
97void handle_signal(int sig)
98{
99 if (sig != SIGUSR1) {
100 usbmuxd_log(LL_NOTICE,"Caught signal %d, exiting", sig);
101 should_exit = 1;
102 } else {
103 if(opt_udev) {
104 usbmuxd_log(LL_INFO, "Caught SIGUSR1, checking if we can terminate (no more devices attached)...");
105 if (device_get_count() > 0) {
106 // we can't quit, there are still devices attached.
107 usbmuxd_log(LL_NOTICE, "Refusing to terminate, there are still devices attached. Kill me with signal 15 (TERM) to force quit.");
108 } else {
109 // it's safe to quit
110 should_exit = 1;
111 }
112 } else {
113 usbmuxd_log(LL_INFO, "Caught SIGUSR1 but we weren't started in --udev mode, ignoring");
114 }
115 }
116}
117
118void set_signal_handlers(void)
119{
120 struct sigaction sa;
121 memset(&sa, 0, sizeof(struct sigaction));
122 sa.sa_handler = handle_signal;
123 sigaction(SIGINT, &sa, NULL);
124 sigaction(SIGQUIT, &sa, NULL);
125 sigaction(SIGTERM, &sa, NULL);
126 sigaction(SIGUSR1, &sa, NULL);
127}
128
129int main_loop(int listenfd)
130{
131 int to, cnt, i, dto;
132 struct fdlist pollfds;
133
134 while(!should_exit) {
135 usbmuxd_log(LL_FLOOD, "main_loop iteration");
136 to = usb_get_timeout();
137 usbmuxd_log(LL_FLOOD, "USB timeout is %d ms", to);
138 dto = device_get_timeout();
139 usbmuxd_log(LL_FLOOD, "Device timeout is %d ms", to);
140 if(dto < to)
141 to = dto;
142
143 fdlist_create(&pollfds);
144 fdlist_add(&pollfds, FD_LISTEN, listenfd, POLLIN);
145 usb_get_fds(&pollfds);
146 client_get_fds(&pollfds);
147 usbmuxd_log(LL_FLOOD, "fd count is %d", pollfds.count);
148
149 cnt = poll(pollfds.fds, pollfds.count, to);
150 usbmuxd_log(LL_FLOOD, "poll() returned %d", cnt);
151
152 if(cnt == -1) {
153 if(errno == EINTR && should_exit) {
154 usbmuxd_log(LL_INFO, "event processing interrupted");
155 fdlist_free(&pollfds);
156 return 0;
157 }
158 } else if(cnt == 0) {
159 if(usb_process() < 0) {
160 usbmuxd_log(LL_FATAL, "usb_process() failed");
161 fdlist_free(&pollfds);
162 return -1;
163 }
164 device_check_timeouts();
165 } else {
166 int done_usb = 0;
167 for(i=0; i<pollfds.count; i++) {
168 if(pollfds.fds[i].revents) {
169 if(!done_usb && pollfds.owners[i] == FD_USB) {
170 if(usb_process() < 0) {
171 usbmuxd_log(LL_FATAL, "usb_process() failed");
172 fdlist_free(&pollfds);
173 return -1;
174 }
175 done_usb = 1;
176 }
177 if(pollfds.owners[i] == FD_LISTEN) {
178 if(client_accept(listenfd) < 0) {
179 usbmuxd_log(LL_FATAL, "client_accept() failed");
180 fdlist_free(&pollfds);
181 return -1;
182 }
183 }
184 if(pollfds.owners[i] == FD_CLIENT) {
185 client_process(pollfds.fds[i].fd, pollfds.fds[i].revents);
186 }
187 }
188 }
189 }
190 fdlist_free(&pollfds);
191 }
192 return 0;
193}
194
195/**
196 * make this program run detached from the current console
197 */
198static int daemonize(void)
199{
200 pid_t pid;
201 pid_t sid;
202 int pfd[2];
203 int res;
204
205 // already a daemon
206 if (getppid() == 1)
207 return 0;
208
209 if((res = pipe(pfd)) < 0) {
210 usbmuxd_log(LL_FATAL, "pipe() failed.");
211 return res;
212 }
213
214 pid = fork();
215 if (pid < 0) {
216 usbmuxd_log(LL_FATAL, "fork() failed.");
217 return pid;
218 }
219
220 if (pid > 0) {
221 // exit parent process
222 int status;
223 close(pfd[1]);
224
225 if((res = read(pfd[0],&status,sizeof(int))) != sizeof(int)) {
226 fprintf(stderr, "usbmuxd: ERROR: Failed to get init status from child, check syslog for messages.\n");
227 exit(1);
228 }
229 if(status != 0)
230 fprintf(stderr, "usbmuxd: ERROR: Child process exited with error %d, check syslog for messages.\n", status);
231 exit(status);
232 }
233 // At this point we are executing as the child process
234 // but we need to do one more fork
235
236 daemon_pipe = pfd[1];
237 close(pfd[0]);
238 report_to_parent = 1;
239
240 // Change the file mode mask
241 umask(0);
242
243 // Create a new SID for the child process
244 sid = setsid();
245 if (sid < 0) {
246 usbmuxd_log(LL_FATAL, "setsid() failed.");
247 return -1;
248 }
249
250 pid = fork();
251 if (pid < 0) {
252 usbmuxd_log(LL_FATAL, "fork() failed (second).");
253 return pid;
254 }
255
256 if (pid > 0) {
257 // exit parent process
258 close(daemon_pipe);
259 exit(0);
260 }
261
262 // Change the current working directory.
263 if ((chdir("/")) < 0) {
264 usbmuxd_log(LL_FATAL, "chdir() failed");
265 return -2;
266 }
267 // Redirect standard files to /dev/null
268 if (!freopen("/dev/null", "r", stdin)) {
269 usbmuxd_log(LL_FATAL, "Redirection of stdin failed.");
270 return -3;
271 }
272 if (!freopen("/dev/null", "w", stdout)) {
273 usbmuxd_log(LL_FATAL, "Redirection of stdout failed.");
274 return -3;
275 }
276
277 return 0;
278}
279
280static int notify_parent(int status)
281{
282 int res;
283
284 report_to_parent = 0;
285 if ((res = write(daemon_pipe, &status, sizeof(int))) != sizeof(int)) {
286 usbmuxd_log(LL_FATAL, "Could not notify parent!");
287 if(res >= 0)
288 return -2;
289 else
290 return res;
291 }
292 close(daemon_pipe);
293 if (!freopen("/dev/null", "w", stderr)) {
294 usbmuxd_log(LL_FATAL, "Redirection of stderr failed.");
295 return -1;
296 }
297 return 0;
298}
299
300static void usage()
301{
302 printf("usage: usbmuxd [options]\n");
303 printf("\t-h|--help Print this message.\n");
304 printf("\t-v|--verbose Be verbose (use twice or more to increase).\n");
305 printf("\t-f|--foreground Do not daemonize (implies one -v).\n");
306 printf("\t-U|--user[=USER] Change to this user after startup (needs usb privileges).\n");
307 printf("\t If USER is not specified, defaults to usbmux.\n");
308 printf("\t-u|--udev Run in udev operation mode.\n");
309 printf("\t-x|--exit Tell a running instance to exit if there are no devices\n");
310 printf("\t connected (must be in udev mode).\n");
311 printf("\t-X|--force-exit Tell a running instance to exit, even if there are still\n");
312 printf("\t devices connected (always works).\n");
313 printf("\n");
314}
315
316static void parse_opts(int argc, char **argv)
317{
318 static struct option longopts[] = {
319 {"help", 0, NULL, 'h'},
320 {"foreground", 0, NULL, 'f'},
321 {"verbose", 0, NULL, 'v'},
322 {"user", 2, NULL, 'U'},
323 {"udev", 0, NULL, 'u'},
324 {"exit", 0, NULL, 'x'},
325 {"force-exit", 0, NULL, 'X'},
326 {NULL, 0, NULL, 0}
327 };
328 int c;
329
330 while (1) {
331 c = getopt_long(argc, argv, "hfvuU::xX", longopts, (int *) 0);
332 if (c == -1) {
333 break;
334 }
335
336 switch (c) {
337 case 'h':
338 usage();
339 exit(0);
340 case 'f':
341 foreground = 1;
342 break;
343 case 'v':
344 ++verbose;
345 break;
346 case 'U':
347 drop_privileges = 1;
348 if(optarg)
349 drop_user = optarg;
350 break;
351 case 'u':
352 opt_udev = 1;
353 break;
354 case 'x':
355 opt_exit = 1;
356 exit_signal = SIGUSR1;
357 break;
358 case 'X':
359 opt_exit = 1;
360 exit_signal = SIGTERM;
361 break;
362 default:
363 usage();
364 exit(2);
365 }
366 }
367}
368
369int main(int argc, char *argv[])
370{
371 int listenfd;
372 int res = 0;
373 int lfd;
374 struct flock lock;
375 char pids[10];
376
377 parse_opts(argc, argv);
378
379 argc -= optind;
380 argv += optind;
381
382 if (!foreground) {
383 verbose += LL_WARNING;
384 log_enable_syslog();
385 } else {
386 verbose += LL_NOTICE;
387 }
388
389 /* set log level to specified verbosity */
390 log_level = verbose;
391
392 usbmuxd_log(LL_NOTICE, "usbmux v0.1 starting up");
393 should_exit = 0;
394
395 set_signal_handlers();
396
397 res = lfd = open(lockfile, O_WRONLY|O_CREAT, 0644);
398 if(res == -1) {
399 usbmuxd_log(LL_FATAL, "Could not open lockfile");
400 goto terminate;
401 }
402 lock.l_type = F_WRLCK;
403 lock.l_whence = SEEK_SET;
404 lock.l_start = 0;
405 lock.l_len = 0;
406 fcntl(lfd, F_GETLK, &lock);
407 close(lfd);
408 if (lock.l_type != F_UNLCK) {
409 if (opt_exit) {
410 if (lock.l_pid && !kill(lock.l_pid, 0)) {
411 usbmuxd_log(LL_NOTICE, "Sending signal %d to instance with pid %d", exit_signal, lock.l_pid);
412 res = 0;
413 if (kill(lock.l_pid, exit_signal) < 0) {
414 usbmuxd_log(LL_FATAL, "Could not deliver signal %d to pid %d", exit_signal, lock.l_pid);
415 res = -1;
416 }
417 goto terminate;
418 } else {
419 usbmuxd_log(LL_ERROR, "Could not determine pid of the other running instance!");
420 res = -1;
421 goto terminate;
422 }
423 } else {
424 if (!opt_udev) {
425 usbmuxd_log(LL_ERROR, "Another instance is already running (pid %d). exiting.", lock.l_pid);
426 res = -1;
427 } else {
428 usbmuxd_log(LL_NOTICE, "Another instance is already running (pid %d). exiting.", lock.l_pid);
429 res = 0;
430 }
431 goto terminate;
432 }
433 }
434 unlink(lockfile);
435
436 if (opt_exit) {
437 usbmuxd_log(LL_NOTICE, "No running instance found, none killed. exiting.");
438 goto terminate;
439 }
440
441 if (!foreground) {
442 if ((res = daemonize()) < 0) {
443 fprintf(stderr, "usbmuxd: FATAL: Could not daemonize!\n");
444 usbmuxd_log(LL_FATAL, "Could not daemonize!");
445 goto terminate;
446 }
447 }
448
449 // now open the lockfile and place the lock
450 res = lfd = open(lockfile, O_WRONLY|O_CREAT|O_TRUNC|O_EXCL, 0644);
451 if(res < 0) {
452 usbmuxd_log(LL_FATAL, "Could not open lockfile");
453 goto terminate;
454 }
455 lock.l_type = F_WRLCK;
456 lock.l_whence = SEEK_SET;
457 lock.l_start = 0;
458 lock.l_len = 0;
459 if ((res = fcntl(lfd, F_SETLK, &lock)) < 0) {
460 usbmuxd_log(LL_FATAL, "Lockfile locking failed!");
461 goto terminate;
462 }
463 sprintf(pids, "%d", getpid());
464 if ((res = write(lfd, pids, strlen(pids))) != strlen(pids)) {
465 usbmuxd_log(LL_FATAL, "Could not write pidfile!");
466 if(res >= 0)
467 res = -2;
468 goto terminate;
469 }
470
471 usbmuxd_log(LL_INFO, "Creating socket");
472 res = listenfd = create_socket();
473 if(listenfd < 0)
474 goto terminate;
475
476 // drop elevated privileges
477 if (drop_privileges && (getuid() == 0 || geteuid() == 0)) {
478 struct passwd *pw = getpwnam(drop_user);
479 if (!pw) {
480 usbmuxd_log(LL_FATAL, "Dropping privileges failed, check if user '%s' exists!", drop_user);
481 res = -1;
482 goto terminate;
483 }
484
485 if ((res = initgroups(drop_user, pw->pw_gid)) < 0) {
486 usbmuxd_log(LL_FATAL, "Failed to drop privileges (cannot set supplementary groups)");
487 goto terminate;
488 }
489 if ((res = setgid(pw->pw_gid)) < 0) {
490 usbmuxd_log(LL_FATAL, "Failed to drop privileges (cannot set group ID to %d)", pw->pw_gid);
491 goto terminate;
492 }
493 if ((res = setuid(pw->pw_uid)) < 0) {
494 usbmuxd_log(LL_FATAL, "Failed to drop privileges (cannot set user ID to %d)", pw->pw_uid);
495 goto terminate;
496 }
497
498 // security check
499 if (setuid(0) != -1) {
500 usbmuxd_log(LL_FATAL, "Failed to drop privileges properly!");
501 res = -1;
502 goto terminate;
503 }
504 if (getuid() != pw->pw_uid || getgid() != pw->pw_gid) {
505 usbmuxd_log(LL_FATAL, "Failed to drop privileges properly!");
506 res = -1;
507 goto terminate;
508 }
509 usbmuxd_log(LL_NOTICE, "Successfully dropped privileges to '%s'", drop_user);
510 }
511
512 client_init();
513 device_init();
514 usbmuxd_log(LL_INFO, "Initializing USB");
515 if((res = usb_init()) < 0)
516 goto terminate;
517
518 usbmuxd_log(LL_INFO, "%d device%s detected", res, (res==1)?"":"s");
519
520 usbmuxd_log(LL_NOTICE, "Initialization complete");
521
522 if (report_to_parent)
523 if((res = notify_parent(0)) < 0)
524 goto terminate;
525
526 res = main_loop(listenfd);
527 if(res < 0)
528 usbmuxd_log(LL_FATAL, "main_loop failed");
529
530 usbmuxd_log(LL_NOTICE, "usbmux shutting down");
531 device_kill_connections();
532 usb_shutdown();
533 device_shutdown();
534 client_shutdown();
535 usbmuxd_log(LL_NOTICE, "Shutdown complete");
536
537terminate:
538 log_disable_syslog();
539
540 if (res < 0)
541 res = -res;
542 else
543 res = 0;
544 if (report_to_parent)
545 notify_parent(res);
546
547 return res;
548}
diff --git a/usbmuxd/usb-linux.c b/usbmuxd/usb-linux.c
new file mode 100644
index 0000000..6e99a95
--- /dev/null
+++ b/usbmuxd/usb-linux.c
@@ -0,0 +1,503 @@
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18
19*/
20
21#ifdef HAVE_CONFIG_H
22#include <config.h>
23#endif
24
25#include <stdio.h>
26#include <stdlib.h>
27#include <stdint.h>
28#include <string.h>
29
30#include <libusb.h>
31
32#include "usb.h"
33#include "log.h"
34#include "device.h"
35
36// interval for device connection/disconnection polling, in milliseconds
37// we need this because there is currently no asynchronous device discovery mechanism in libusb
38#define DEVICE_POLL_TIME 1000
39
40struct usb_device {
41 libusb_device_handle *dev;
42 uint8_t bus, address;
43 uint16_t vid, pid;
44 char serial[256];
45 int alive;
46 struct libusb_transfer *rx_xfer;
47 struct collection tx_xfers;
48};
49
50static struct collection device_list;
51
52static struct timeval next_dev_poll_time;
53
54static int devlist_failures;
55
56static void usb_disconnect(struct usb_device *dev)
57{
58 if(!dev->dev) {
59 return;
60 }
61
62 // kill the rx xfer and tx xfers and try to make sure the callbacks get called before we free the device
63 if(dev->rx_xfer) {
64 usbmuxd_log(LL_DEBUG, "usb_disconnect: cancelling RX xfer");
65 libusb_cancel_transfer(dev->rx_xfer);
66 }
67 FOREACH(struct libusb_transfer *xfer, &dev->tx_xfers) {
68 usbmuxd_log(LL_DEBUG, "usb_disconnect: cancelling TX xfer %p", xfer);
69 libusb_cancel_transfer(xfer);
70 } ENDFOREACH
71
72 while(dev->rx_xfer || collection_count(&dev->tx_xfers)) {
73 struct timeval tv;
74 int res;
75
76 tv.tv_sec = 0;
77 tv.tv_usec = 1000;
78 if((res = libusb_handle_events_timeout(NULL, &tv)) < 0) {
79 usbmuxd_log(LL_ERROR, "libusb_handle_events_timeout for usb_disconnect failed: %d", res);
80 break;
81 }
82 }
83 collection_free(&dev->tx_xfers);
84 libusb_release_interface(dev->dev, USB_INTERFACE);
85 libusb_close(dev->dev);
86 dev->dev = NULL;
87 collection_remove(&device_list, dev);
88 free(dev);
89}
90
91static void tx_callback(struct libusb_transfer *xfer)
92{
93 struct usb_device *dev = xfer->user_data;
94 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);
95 if(xfer->status != LIBUSB_TRANSFER_COMPLETED) {
96 switch(xfer->status) {
97 case LIBUSB_TRANSFER_COMPLETED: //shut up compiler
98 case LIBUSB_TRANSFER_ERROR:
99 // funny, this happens when we disconnect the device while waiting for a transfer, sometimes
100 usbmuxd_log(LL_INFO, "Device %d-%d TX aborted due to error or disconnect", dev->bus, dev->address);
101 break;
102 case LIBUSB_TRANSFER_TIMED_OUT:
103 usbmuxd_log(LL_ERROR, "TX transfer timed out for device %d-%d", dev->bus, dev->address);
104 break;
105 case LIBUSB_TRANSFER_CANCELLED:
106 usbmuxd_log(LL_DEBUG, "Device %d-%d TX transfer cancelled", dev->bus, dev->address);
107 break;
108 case LIBUSB_TRANSFER_STALL:
109 usbmuxd_log(LL_ERROR, "TX transfer stalled for device %d-%d", dev->bus, dev->address);
110 break;
111 case LIBUSB_TRANSFER_NO_DEVICE:
112 // other times, this happens, and also even when we abort the transfer after device removal
113 usbmuxd_log(LL_INFO, "Device %d-%d TX aborted due to disconnect", dev->bus, dev->address);
114 break;
115 case LIBUSB_TRANSFER_OVERFLOW:
116 usbmuxd_log(LL_ERROR, "TX transfer overflow for device %d-%d", dev->bus, dev->address);
117 break;
118 // and nothing happens (this never gets called) if the device is freed after a disconnect! (bad)
119 }
120 // we can't usb_disconnect here due to a deadlock, so instead mark it as dead and reap it after processing events
121 // we'll do device_remove there too
122 dev->alive = 0;
123 }
124 if(xfer->buffer)
125 free(xfer->buffer);
126 collection_remove(&dev->tx_xfers, xfer);
127 libusb_free_transfer(xfer);
128}
129
130int usb_send(struct usb_device *dev, const unsigned char *buf, int length)
131{
132 int res;
133 struct libusb_transfer *xfer = libusb_alloc_transfer(0);
134 libusb_fill_bulk_transfer(xfer, dev->dev, BULK_OUT, (void*)buf, length, tx_callback, dev, 0);
135 xfer->flags = LIBUSB_TRANSFER_SHORT_NOT_OK;
136 if((res = libusb_submit_transfer(xfer)) < 0) {
137 usbmuxd_log(LL_ERROR, "Failed to submit TX transfer %p len %d to device %d-%d: %d", buf, length, dev->bus, dev->address, res);
138 libusb_free_transfer(xfer);
139 return res;
140 }
141 collection_add(&dev->tx_xfers, xfer);
142 if((length % 512) == 0) {
143 usbmuxd_log(LL_DEBUG, "Send ZLP");
144 // Send Zero Length Packet
145 xfer = libusb_alloc_transfer(0);
146 void *buffer = malloc(1);
147 libusb_fill_bulk_transfer(xfer, dev->dev, BULK_OUT, buffer, 0, tx_callback, dev, 0);
148 xfer->flags = LIBUSB_TRANSFER_SHORT_NOT_OK;
149 if((res = libusb_submit_transfer(xfer)) < 0) {
150 usbmuxd_log(LL_ERROR, "Failed to submit TX ZLP transfer to device %d-%d: %d", dev->bus, dev->address, res);
151 libusb_free_transfer(xfer);
152 return res;
153 }
154 collection_add(&dev->tx_xfers, xfer);
155 }
156 return 0;
157}
158
159static void rx_callback(struct libusb_transfer *xfer)
160{
161 struct usb_device *dev = xfer->user_data;
162 usbmuxd_log(LL_SPEW, "RX callback dev %d-%d len %d status %d", dev->bus, dev->address, xfer->actual_length, xfer->status);
163 if(xfer->status == LIBUSB_TRANSFER_COMPLETED) {
164 device_data_input(dev, xfer->buffer, xfer->actual_length);
165 libusb_submit_transfer(xfer);
166 } else {
167 switch(xfer->status) {
168 case LIBUSB_TRANSFER_COMPLETED: //shut up compiler
169 case LIBUSB_TRANSFER_ERROR:
170 // funny, this happens when we disconnect the device while waiting for a transfer, sometimes
171 usbmuxd_log(LL_INFO, "Device %d-%d RX aborted due to error or disconnect", dev->bus, dev->address);
172 break;
173 case LIBUSB_TRANSFER_TIMED_OUT:
174 usbmuxd_log(LL_ERROR, "RX transfer timed out for device %d-%d", dev->bus, dev->address);
175 break;
176 case LIBUSB_TRANSFER_CANCELLED:
177 usbmuxd_log(LL_DEBUG, "Device %d-%d RX transfer cancelled", dev->bus, dev->address);
178 break;
179 case LIBUSB_TRANSFER_STALL:
180 usbmuxd_log(LL_ERROR, "RX transfer stalled for device %d-%d", dev->bus, dev->address);
181 break;
182 case LIBUSB_TRANSFER_NO_DEVICE:
183 // other times, this happens, and also even when we abort the transfer after device removal
184 usbmuxd_log(LL_INFO, "Device %d-%d RX aborted due to disconnect", dev->bus, dev->address);
185 break;
186 case LIBUSB_TRANSFER_OVERFLOW:
187 usbmuxd_log(LL_ERROR, "RX transfer overflow for device %d-%d", dev->bus, dev->address);
188 break;
189 // and nothing happens (this never gets called) if the device is freed after a disconnect! (bad)
190 }
191 free(xfer->buffer);
192 dev->rx_xfer = NULL;
193 libusb_free_transfer(xfer);
194 // we can't usb_disconnect here due to a deadlock, so instead mark it as dead and reap it after processing events
195 // we'll do device_remove there too
196 dev->alive = 0;
197 }
198}
199
200static int start_rx(struct usb_device *dev)
201{
202 int res;
203 void *buf;
204 dev->rx_xfer = libusb_alloc_transfer(0);
205 buf = malloc(USB_MRU);
206 libusb_fill_bulk_transfer(dev->rx_xfer, dev->dev, BULK_IN, buf, USB_MRU, rx_callback, dev, 0);
207 if((res = libusb_submit_transfer(dev->rx_xfer)) != 0) {
208 usbmuxd_log(LL_ERROR, "Failed to submit RX transfer to device %d-%d: %d", dev->bus, dev->address, res);
209 libusb_free_transfer(dev->rx_xfer);
210 dev->rx_xfer = NULL;
211 return res;
212 }
213 return 0;
214}
215
216static int usb_discover(void)
217{
218 int cnt, i, res;
219 int valid_count = 0;
220 libusb_device **devs;
221
222 cnt = libusb_get_device_list(NULL, &devs);
223 if(cnt < 0) {
224 usbmuxd_log(LL_WARNING, "Could not get device list: %d", cnt);
225 devlist_failures++;
226 // sometimes libusb fails getting the device list if you've just removed something
227 if(devlist_failures > 5) {
228 usbmuxd_log(LL_FATAL, "Too many errors getting device list\n");
229 return cnt;
230 } else {
231 gettimeofday(&next_dev_poll_time, NULL);
232 next_dev_poll_time.tv_usec += DEVICE_POLL_TIME * 1000;
233 next_dev_poll_time.tv_sec += next_dev_poll_time.tv_usec / 1000000;
234 next_dev_poll_time.tv_usec = next_dev_poll_time.tv_usec % 1000000;
235 return 0;
236 }
237 }
238 devlist_failures = 0;
239
240 usbmuxd_log(LL_SPEW, "usb_discover: scanning %d devices", cnt);
241
242 FOREACH(struct usb_device *usbdev, &device_list) {
243 usbdev->alive = 0;
244 } ENDFOREACH
245
246 for(i=0; i<cnt; i++) {
247 // the following are non-blocking operations on the device list
248 libusb_device *dev = devs[i];
249 uint8_t bus = libusb_get_bus_number(dev);
250 uint8_t address = libusb_get_device_address(dev);
251 struct libusb_device_descriptor devdesc;
252 int found = 0;
253 FOREACH(struct usb_device *usbdev, &device_list) {
254 if(usbdev->bus == bus && usbdev->address == address) {
255 valid_count++;
256 usbdev->alive = 1;
257 found = 1;
258 break;
259 }
260 } ENDFOREACH
261 if(found)
262 continue; //device already found
263 if((res = libusb_get_device_descriptor(dev, &devdesc)) != 0) {
264 usbmuxd_log(LL_WARNING, "Could not get device descriptor for device %d-%d: %d", bus, address, res);
265 continue;
266 }
267 if(devdesc.idVendor != VID_APPLE)
268 continue;
269 if( (devdesc.idProduct != PID_IPHONE2G) &&
270 (devdesc.idProduct != PID_ITOUCH1G) &&
271 (devdesc.idProduct != PID_IPHONE3G) &&
272 (devdesc.idProduct != PID_ITOUCH2G) &&
273 (devdesc.idProduct != PID_IPHONE3GS))
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 if((res = libusb_set_configuration(handle, USB_CONFIGURATION)) != 0) {
283 usbmuxd_log(LL_WARNING, "Could not set configuration %d for device %d-%d: %d", USB_CONFIGURATION, bus, address, res);
284 libusb_close(handle);
285 continue;
286 }
287 if((res = libusb_claim_interface(handle, USB_INTERFACE)) != 0) {
288 usbmuxd_log(LL_WARNING, "Could not claim interface %d for device %d-%d: %d", USB_INTERFACE, bus, address, res);
289 libusb_close(handle);
290 continue;
291 }
292 struct usb_device *usbdev;
293 usbdev = malloc(sizeof(struct usb_device));
294
295 if((res = libusb_get_string_descriptor_ascii(handle, devdesc.iSerialNumber, (uint8_t *)usbdev->serial, 256)) <= 0) {
296 usbmuxd_log(LL_WARNING, "Could not get serial number for device %d-%d: %d", bus, address, res);
297 libusb_release_interface(handle, USB_INTERFACE);
298 libusb_close(handle);
299 free(usbdev);
300 continue;
301 }
302 usbdev->serial[res] = 0;
303 usbdev->bus = bus;
304 usbdev->address = address;
305 usbdev->vid = devdesc.idVendor;
306 usbdev->pid = devdesc.idProduct;
307 usbdev->dev = handle;
308 usbdev->alive = 1;
309 collection_init(&usbdev->tx_xfers);
310
311 collection_add(&device_list, usbdev);
312
313 if(device_add(usbdev) < 0) {
314 usb_disconnect(usbdev);
315 continue;
316 }
317 if(start_rx(usbdev) < 0) {
318 device_remove(usbdev);
319 usb_disconnect(usbdev);
320 continue;
321 }
322 valid_count++;
323 }
324 FOREACH(struct usb_device *usbdev, &device_list) {
325 if(!usbdev->alive) {
326 device_remove(usbdev);
327 usb_disconnect(usbdev);
328 }
329 } ENDFOREACH
330
331 libusb_free_device_list(devs, 1);
332
333 gettimeofday(&next_dev_poll_time, NULL);
334 next_dev_poll_time.tv_usec += DEVICE_POLL_TIME * 1000;
335 next_dev_poll_time.tv_sec += next_dev_poll_time.tv_usec / 1000000;
336 next_dev_poll_time.tv_usec = next_dev_poll_time.tv_usec % 1000000;
337
338 return valid_count;
339}
340
341const char *usb_get_serial(struct usb_device *dev)
342{
343 if(!dev->dev) {
344 return NULL;
345 }
346 return dev->serial;
347}
348
349uint32_t usb_get_location(struct usb_device *dev)
350{
351 if(!dev->dev) {
352 return 0;
353 }
354 return (dev->bus << 16) | dev->address;
355}
356
357uint16_t usb_get_pid(struct usb_device *dev)
358{
359 if(!dev->dev) {
360 return 0;
361 }
362 return dev->pid;
363}
364
365void usb_get_fds(struct fdlist *list)
366{
367 const struct libusb_pollfd **usbfds;
368 const struct libusb_pollfd **p;
369 usbfds = libusb_get_pollfds(NULL);
370 if(!usbfds) {
371 usbmuxd_log(LL_ERROR, "libusb_get_pollfds failed");
372 return;
373 }
374 p = usbfds;
375 while(*p) {
376 fdlist_add(list, FD_USB, (*p)->fd, (*p)->events);
377 p++;
378 }
379 free(usbfds);
380}
381
382static int dev_poll_remain_ms(void)
383{
384 int msecs;
385 struct timeval tv;
386 gettimeofday(&tv, NULL);
387 msecs = (next_dev_poll_time.tv_sec - tv.tv_sec) * 1000;
388 msecs += (next_dev_poll_time.tv_usec - tv.tv_usec) / 1000;
389 if(msecs < 0)
390 return 0;
391 return msecs;
392}
393
394int usb_get_timeout(void)
395{
396 struct timeval tv;
397 int msec;
398 int res;
399 int pollrem;
400 pollrem = dev_poll_remain_ms();
401 res = libusb_get_next_timeout(NULL, &tv);
402 if(res == 0)
403 return pollrem;
404 if(res < 0) {
405 usbmuxd_log(LL_ERROR, "libusb_get_next_timeout failed: %d", res);
406 return pollrem;
407 }
408 msec = tv.tv_sec * 1000;
409 msec += tv.tv_usec / 1000;
410 if(msec > pollrem)
411 return pollrem;
412 return msec;
413}
414
415int usb_process(void)
416{
417 int res;
418 struct timeval tv;
419 tv.tv_sec = tv.tv_usec = 0;
420 res = libusb_handle_events_timeout(NULL, &tv);
421 if(res < 0) {
422 usbmuxd_log(LL_ERROR, "libusb_handle_events_timeout failed: %d", res);
423 return res;
424 }
425 // reap devices marked dead due to an RX error
426 FOREACH(struct usb_device *usbdev, &device_list) {
427 if(!usbdev->alive) {
428 device_remove(usbdev);
429 usb_disconnect(usbdev);
430 }
431 } ENDFOREACH
432
433 if(dev_poll_remain_ms() <= 0) {
434 res = usb_discover();
435 if(res < 0) {
436 usbmuxd_log(LL_ERROR, "usb_discover failed: %d", res);
437 return res;
438 }
439 }
440 return 0;
441}
442
443int usb_process_timeout(int msec)
444{
445 int res;
446 struct timeval tleft, tcur, tfin;
447 gettimeofday(&tcur, NULL);
448 tfin.tv_sec = tcur.tv_sec + (msec / 1000);
449 tfin.tv_usec = tcur.tv_usec + (msec % 1000) * 1000;
450 tfin.tv_sec += tfin.tv_usec / 1000000;
451 tfin.tv_usec %= 1000000;
452 while((tfin.tv_sec > tcur.tv_sec) || ((tfin.tv_sec == tcur.tv_sec) && (tfin.tv_usec > tcur.tv_usec))) {
453 tleft.tv_sec = tfin.tv_sec - tcur.tv_sec;
454 tleft.tv_usec = tfin.tv_usec - tcur.tv_usec;
455 if(tleft.tv_usec < 0) {
456 tleft.tv_usec += 1000000;
457 tleft.tv_sec -= 1;
458 }
459 res = libusb_handle_events_timeout(NULL, &tleft);
460 if(res < 0) {
461 usbmuxd_log(LL_ERROR, "libusb_handle_events_timeout failed: %d", res);
462 return res;
463 }
464 // reap devices marked dead due to an RX error
465 FOREACH(struct usb_device *usbdev, &device_list) {
466 if(!usbdev->alive) {
467 device_remove(usbdev);
468 usb_disconnect(usbdev);
469 }
470 } ENDFOREACH
471 gettimeofday(&tcur, NULL);
472 }
473 return 0;
474}
475
476int usb_init(void)
477{
478 int res;
479 usbmuxd_log(LL_DEBUG, "usb_init for linux / libusb 1.0");
480
481 devlist_failures = 0;
482 res = libusb_init(NULL);
483 //libusb_set_debug(NULL, 3);
484 if(res != 0) {
485 usbmuxd_log(LL_FATAL, "libusb_init failed: %d", res);
486 return -1;
487 }
488
489 collection_init(&device_list);
490
491 return usb_discover();
492}
493
494void usb_shutdown(void)
495{
496 usbmuxd_log(LL_DEBUG, "usb_shutdown");
497 FOREACH(struct usb_device *usbdev, &device_list) {
498 device_remove(usbdev);
499 usb_disconnect(usbdev);
500 } ENDFOREACH
501 collection_free(&device_list);
502 libusb_exit(NULL);
503}
diff --git a/usbmuxd/usb.h b/usbmuxd/usb.h
new file mode 100644
index 0000000..d13d9ba
--- /dev/null
+++ b/usbmuxd/usb.h
@@ -0,0 +1,67 @@
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18
19*/
20
21#ifndef __USB_H__
22#define __USB_H__
23
24#include <stdint.h>
25#include "utils.h"
26
27#define BULK_IN 0x85
28#define BULK_OUT 0x04
29
30// libusb fragments packets larger than this (usbfs limitation)
31// on input, this creates race conditions and other issues
32#define USB_MRU 16384
33
34// max transmission packet size
35// libusb fragments these too, but doesn't send ZLPs so we're safe
36// but we need to send a ZLP ourselves at the end (see usb-linux.c)
37// we're using 3 * 16384 to optimize for the fragmentation
38// this results in three URBs per full transfer, 32 USB packets each
39// if there are ZLP issues this should make them show up easily too
40#define USB_MTU (3 * 16384)
41
42#define USB_PACKET_SIZE 512
43
44#define VID_APPLE 0x5ac
45#define PID_IPHONE2G 0x1290
46#define PID_ITOUCH1G 0x1291
47#define PID_IPHONE3G 0x1292
48#define PID_ITOUCH2G 0x1293
49#define PID_IPHONE3GS 0x1294
50
51#define USB_CONFIGURATION 3
52#define USB_INTERFACE 1
53
54struct usb_device;
55
56int usb_init(void);
57void usb_shutdown(void);
58const char *usb_get_serial(struct usb_device *dev);
59uint32_t usb_get_location(struct usb_device *dev);
60uint16_t usb_get_pid(struct usb_device *dev);
61void usb_get_fds(struct fdlist *list);
62int usb_get_timeout(void);
63int usb_send(struct usb_device *dev, const unsigned char *buf, int length);
64int usb_process(void);
65int usb_process_timeout(int msec);
66
67#endif
diff --git a/usbmuxd/utils.c b/usbmuxd/utils.c
new file mode 100644
index 0000000..1ffa04a
--- /dev/null
+++ b/usbmuxd/utils.c
@@ -0,0 +1,110 @@
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18
19*/
20
21#ifdef HAVE_CONFIG_H
22#include <config.h>
23#endif
24
25#include <stdlib.h>
26#include <string.h>
27#include "utils.h"
28#include "log.h"
29
30void fdlist_create(struct fdlist *list)
31{
32 list->count = 0;
33 list->capacity = 4;
34 list->owners = malloc(sizeof(*list->owners) * list->capacity);
35 list->fds = malloc(sizeof(*list->fds) * list->capacity);
36}
37void fdlist_add(struct fdlist *list, enum fdowner owner, int fd, short events)
38{
39 if(list->count == list->capacity) {
40 list->capacity *= 2;
41 list->owners = realloc(list->owners, sizeof(*list->owners) * list->capacity);
42 list->fds = realloc(list->fds, sizeof(*list->fds) * list->capacity);
43 }
44 list->owners[list->count] = owner;
45 list->fds[list->count].fd = fd;
46 list->fds[list->count].events = events;
47 list->fds[list->count].revents = 0;
48 list->count++;
49}
50
51void fdlist_free(struct fdlist *list)
52{
53 list->count = 0;
54 list->capacity = 0;
55 free(list->owners);
56 list->owners = NULL;
57 free(list->fds);
58 list->fds = NULL;
59}
60
61void collection_init(struct collection *col)
62{
63 col->list = malloc(sizeof(void *));
64 memset(col->list, 0, sizeof(void *));
65 col->capacity = 1;
66}
67
68void collection_free(struct collection *col)
69{
70 free(col->list);
71 col->list = NULL;
72 col->capacity = 0;
73}
74
75void collection_add(struct collection *col, void *element)
76{
77 int i;
78 for(i=0; i<col->capacity; i++) {
79 if(!col->list[i]) {
80 col->list[i] = element;
81 return;
82 }
83 }
84 col->list = realloc(col->list, sizeof(void*) * col->capacity * 2);
85 memset(&col->list[col->capacity], 0, sizeof(void *) * col->capacity);
86 col->list[col->capacity] = element;
87 col->capacity *= 2;
88}
89
90void collection_remove(struct collection *col, void *element)
91{
92 int i;
93 for(i=0; i<col->capacity; i++) {
94 if(col->list[i] == element) {
95 col->list[i] = NULL;
96 return;
97 }
98 }
99 usbmuxd_log(LL_ERROR, "collection_remove: element %p not present in collection %p (cap %d)", element, col, col->capacity);
100}
101
102int collection_count(struct collection *col)
103{
104 int i, cnt = 0;
105 for(i=0; i<col->capacity; i++) {
106 if(col->list[i])
107 cnt++;
108 }
109 return cnt;
110}
diff --git a/usbmuxd/utils.h b/usbmuxd/utils.h
new file mode 100644
index 0000000..ad4ac9d
--- /dev/null
+++ b/usbmuxd/utils.h
@@ -0,0 +1,65 @@
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18
19*/
20
21#ifndef __UTILS_H__
22#define __UTILS_H__
23
24#include <poll.h>
25
26enum fdowner {
27 FD_LISTEN,
28 FD_CLIENT,
29 FD_USB
30};
31
32struct fdlist {
33 int count;
34 int capacity;
35 enum fdowner *owners;
36 struct pollfd *fds;
37};
38
39void fdlist_create(struct fdlist *list);
40void fdlist_add(struct fdlist *list, enum fdowner owner, int fd, short events);
41void fdlist_free(struct fdlist *list);
42
43struct collection {
44 void **list;
45 int capacity;
46};
47
48void collection_init(struct collection *col);
49void collection_add(struct collection *col, void *element);
50void collection_remove(struct collection *col, void *element);
51int collection_count(struct collection *col);
52void collection_free(struct collection *col);
53
54#define FOREACH(var, col) \
55 do { \
56 int _iter; \
57 for(_iter=0; _iter<(col)->capacity; _iter++) { \
58 if(!(col)->list[_iter]) continue; \
59 var = (col)->list[_iter];
60
61#define ENDFOREACH \
62 } \
63 } while(0);
64
65#endif