summaryrefslogtreecommitdiffstats
path: root/main.c
diff options
context:
space:
mode:
Diffstat (limited to 'main.c')
-rw-r--r--main.c853
1 files changed, 853 insertions, 0 deletions
diff --git a/main.c b/main.c
new file mode 100644
index 0000000..bf062b7
--- /dev/null
+++ b/main.c
@@ -0,0 +1,853 @@
1/*
2 * usbmuxd -- daemon for communication with iPhone/iPod via USB
3 *
4 * Copyright (c) 2009 Nikias Bassen. All Rights Reserved.
5 * Based upon iTunnel source code, Copyright (c) 2008 Jing Su.
6 * http://www.cs.toronto.edu/~jingsu/itunnel/
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21 */
22#include <stddef.h>
23#include <stdio.h>
24#include <errno.h>
25#include <stdlib.h>
26#include <string.h>
27#include <sys/socket.h>
28#include <sys/un.h>
29#include <sys/stat.h>
30#include <arpa/inet.h>
31#include <unistd.h>
32#include <signal.h>
33#include <pthread.h>
34#include <stdint.h>
35#include <usb.h>
36
37#include "usbmuxd.h"
38#include "sock_stuff.h"
39
40#include "iphone.h"
41
42#define DEFAULT_TIMEOUT 4000
43#define DEFAULT_CHILDREN_CAPACITY 10
44
45static int quit_flag = 0;
46static int fsock = -1;
47
48struct device_use_info {
49 uint32_t device_id;
50 iphone_device_t phone;
51 int use_count;
52};
53
54struct client_data {
55 volatile int dead;
56 int socket;
57 int tag;
58 pthread_t thread;
59 pthread_t handler;
60 pthread_t reader;
61 int reader_quit;
62 int reader_dead;
63 int handler_dead;
64 iphone_umux_client_t muxclient;
65 struct device_use_info *duinfo;
66};
67
68static struct device_use_info **device_use_list = NULL;
69static int device_use_count = 0;
70static pthread_mutex_t usbmux_mutex = PTHREAD_MUTEX_INITIALIZER;
71
72/**
73 * mutex for mutual exclusion of calling the iphone_mux_send function
74 * TODO: I don't know if we really need this?
75 */
76static pthread_mutex_t writer_mutex = PTHREAD_MUTEX_INITIALIZER;
77
78/**
79 * mutex to keep the reader threads from reading partial packages
80 */
81static pthread_mutex_t reader_mutex = PTHREAD_MUTEX_INITIALIZER;
82
83#ifdef DEBUG
84/**
85 * for debugging purposes.
86 */
87static void print_buffer(const char *data, const int length)
88{
89 int i;
90 int j;
91 unsigned char c;
92
93 for(i=0; i<length; i+=16) {
94 printf("%04x: ", i);
95 for (j=0;j<16;j++) {
96 if (i+j >= length) {
97 printf(" ");
98 continue;
99 }
100 printf("%02hhx ", *(data+i+j));
101 }
102 printf(" | ");
103 for(j=0;j<16;j++) {
104 if (i+j >= length)
105 break;
106 c = *(data+i+j);
107 if ((c < 32) || (c > 127)) {
108 printf(".");
109 continue;
110 }
111 printf("%c", c);
112 }
113 printf("\n");
114 }
115 printf("\n");
116}
117#endif
118
119/**
120 * Read incoming usbmuxd packet. If the packet is larger than
121 * the size specified by len, the data will be truncated.
122 *
123 * @param fd the file descriptor to read from.
124 * @param data pointer to a buffer to store the read data to.
125 * @param len the length of the data to be read. The buffer
126 * pointed to by data should be at least len bytes in size.
127 *
128 * @return
129 */
130static int usbmuxd_get_request(int fd, void *data, size_t len)
131{
132 uint32_t pktlen;
133 int recv_len;
134
135 if (peek_buf(fd, &pktlen, sizeof(pktlen)) < sizeof(pktlen)) {
136 return -errno;
137 }
138
139 if (len < pktlen) {
140 // target buffer is to small to hold this packet! fix it!
141 fprintf(stderr, "%s: WARNING -- packet (%d) is larger than target buffer (%d)! Truncating.\n", __func__, pktlen, len);
142 pktlen = len;
143 }
144
145 recv_len = recv_buf(fd, data, pktlen);
146 if ((recv_len > 0) && (recv_len < pktlen)) {
147 fprintf(stderr, "%s: Uh-oh, we got less than the packet's size, %d instead of %d...\n", __func__, recv_len, pktlen);
148 }
149
150 return recv_len;
151}
152
153/**
154 * Send a usbmuxd result packet with given tag and result_code.
155 *
156 * @param fd the file descriptor to write to.
157 * @param tag the tag value that identifies where this message belongs to.
158 * @param result_code the error value (0 = Success, most likely errno values otherwise)
159 *
160 * @return the return value returned by send_buf (normally the number of bytes sent)
161 */
162static int usbmuxd_send_result(int fd, uint32_t tag, uint32_t result_code)
163{
164 struct usbmuxd_result res;
165
166 res.header.length = sizeof(res);
167 res.header.reserved = 0;
168 res.header.type = USBMUXD_RESULT;
169 res.header.tag = tag;
170 res.result = result_code;
171
172 fprintf(stderr, "%s: tag=%d result=%d\n", __func__, res.header.tag, res.result);
173
174 return send_buf(fd, &res, sizeof(res));
175}
176
177/**
178 * this thread reads from the usb connection and writes the
179 * data to the connected client.
180 *
181 * @param arg pointer to a client_data structure.
182 *
183 * @return NULL in any case
184 */
185static void *usbmuxd_client_reader_thread(void *arg)
186{
187 struct client_data *cdata;
188
189 char rbuffer[512];
190 uint32_t rbuffersize = 512;
191 uint32_t rlen;
192 iphone_error_t err;
193 char *cursor;
194 ssize_t len;
195 int result;
196
197 if (!arg) {
198 fprintf(stderr, "%s: invalid client_data supplied!\n", __func__);
199 cdata->reader_dead = 1;
200 return NULL;
201 }
202
203 cdata = (struct client_data*)arg;
204
205 cdata->reader_dead = 0;
206
207 fprintf(stdout, "%s[%d:%d]: started\n", __func__, cdata->duinfo->device_id, cdata->duinfo->use_count);
208
209 while (!quit_flag && !cdata->reader_quit) {
210 result = check_fd(cdata->socket, FD_WRITE, DEFAULT_TIMEOUT);
211 if (result <= 0) {
212 if (result < 0) {
213 fprintf(stderr, "%s: select error: %s\n", __func__, strerror(errno));
214 }
215 continue;
216 }
217
218 rlen = 0;
219 //pthread_mutex_lock(&usbmux_mutex);
220 err = iphone_mux_recv_timeout(cdata->muxclient, rbuffer, rbuffersize, &rlen, DEFAULT_TIMEOUT);
221 //pthread_mutex_unlock(&usbmux_mutex);
222 if (err != 0) {
223 fprintf(stderr, "%s[%d:%d]: encountered USB read error: %d\n", __func__, cdata->duinfo->device_id, cdata->duinfo->use_count, err);
224 break;
225 }
226
227 cursor = rbuffer;
228 while (rlen > 0) {
229 len = send_buf(cdata->socket, cursor, rlen);
230 // calculate remainder
231 rlen -= len;
232 // advance cursor
233 cursor += len;
234 }
235 fsync(cdata->socket);
236 }
237
238 fprintf(stdout, "%s[%d:%d]: terminated\n", __func__, cdata->duinfo->device_id, cdata->duinfo->use_count);
239
240 cdata->reader_dead = 1;
241
242 return NULL;
243}
244
245/**
246 * This function handles the connecting procedure to a previously
247 * set up usbmux client.
248 * Sends a usbmuxd result packet denoting success or failure.
249 * A successful result is mandatory for later communication.
250 *
251 * @param cdata pointer to a previously initialized client_data structure
252 *
253 * @return
254 */
255static int usbmuxd_handleConnectResult(struct client_data *cdata)
256{
257 int result;
258 char buffer[512];
259 char err_type[64];
260 int err_code;
261 ssize_t maxlen = 512;
262 uint32_t rlen;
263 iphone_error_t err;
264
265 if (!cdata) {
266 fprintf(stderr, "%s: Invalid client_data provided!\n", __func__);
267 return -EINVAL;
268 }
269
270 result = check_fd(cdata->socket, FD_WRITE, DEFAULT_TIMEOUT);
271 if (result <= 0) {
272 if (result < 0) {
273 fprintf(stderr, "%s: select error: %s\n", __func__, strerror(errno));
274 return result;
275 }
276 } else {
277 result = 0;
278 err = iphone_mux_recv_timeout(cdata->muxclient, buffer, maxlen, &rlen, DEFAULT_TIMEOUT);
279 if (err != 0) {
280 fprintf(stderr, "%s: encountered USB read error: %d\n", __func__, err);
281 usbmuxd_send_result(cdata->socket, cdata->tag, -err);
282 return err;
283 } else {
284 if (rlen > 0) {
285 if ((buffer[0] == 1) && (rlen > 20) && !memcmp(buffer+1, "handleConnectResult:", 20)) {
286 // hm... we got an error message!
287 buffer[rlen] = 0;
288 fprintf(stderr, "%s: %s\n", __func__, buffer+22);
289
290 if (sscanf(buffer+22, "%s - %d\n", err_type, &err_code) == 2) {
291 usbmuxd_send_result(cdata->socket, cdata->tag, err_code);
292 return -err_code;
293 } else {
294 usbmuxd_send_result(cdata->socket, cdata->tag, ENODATA);
295 return -ENODATA;
296 }
297 } else {
298 // send success result
299 usbmuxd_send_result(cdata->socket, cdata->tag, 0);
300 // and the server greeting message
301 send_buf(cdata->socket, buffer, rlen);
302 }
303 } else {
304 // no server greeting? this seems to be ok. send success.
305 usbmuxd_send_result(cdata->socket, cdata->tag, 0);
306 }
307 }
308 //fsync(cdata->socket);
309 }
310 return result;
311}
312
313/**
314 * This thread handles the communication between the connected iPhone/iPod
315 * and the client that created the connection.
316 */
317static void *usbmuxd_client_handler_thread(void *arg)
318{
319 struct client_data *cdata;
320 int result;
321 char *cursor;
322 char buffer[1024];
323 ssize_t len;
324 ssize_t maxlen = sizeof(buffer);
325 uint32_t wlen;
326 iphone_error_t err;
327
328 if (!arg) {
329 fprintf(stderr, "%s: invalid client_data provided!\n", __func__);
330 return NULL;
331 }
332
333 cdata = (struct client_data*)arg;
334
335 fprintf(stdout, "%s[%d:%d]: started\n", __func__, cdata->duinfo->device_id,cdata->duinfo->use_count);
336
337 if (usbmuxd_handleConnectResult(cdata)) {
338 goto leave;
339 }
340
341 // starting mux reader thread
342 cdata->reader_quit = 0;
343 cdata->reader_dead = 0;
344 if (pthread_create(&cdata->reader, NULL, usbmuxd_client_reader_thread, cdata) != 0) {
345 fprintf(stderr, "%s: could not start client_reader thread\n", __func__);
346 cdata->reader = 0;
347 }
348
349 while (!quit_flag && !cdata->reader_dead) {
350 result = check_fd(cdata->socket, FD_READ, DEFAULT_TIMEOUT);
351 if (result <= 0) {
352 if (result < 0) {
353 fprintf(stderr, "%s: Error: checkfd: %s\n", __func__, strerror(errno));
354 }
355 continue;
356 }
357
358 // check_fd told us there's data available, so read from client
359 // and push to USB device.
360 len = recv(cdata->socket, buffer, maxlen, 0);
361 if (len == 0) {
362 break;
363 }
364 if (len < 0) {
365 fprintf(stderr, "%s[%d:%d]: Error: recv: %s\n", __func__, cdata->duinfo->device_id, cdata->duinfo->use_count, strerror(errno));
366 break;
367 }
368
369 cursor = buffer;
370
371 pthread_mutex_lock(&writer_mutex);
372 do {
373 wlen = 0;
374 err = iphone_mux_send(cdata->muxclient, cursor, len, &wlen);
375 if (err == IPHONE_E_TIMEOUT) {
376 // some kind of timeout... just be patient and retry.
377 } else if (err != IPHONE_E_SUCCESS) {
378 fprintf(stderr, "%s[%d:%d]: USB write error: %d\n", __func__, cdata->duinfo->device_id, cdata->duinfo->use_count, err);
379 len = -1;
380 break;
381 }
382
383 // calculate remainder.
384 len -= wlen;
385 // advance cursor appropiately.
386 cursor += wlen;
387 } while ((len > 0) && !quit_flag);
388 pthread_mutex_unlock(&writer_mutex);
389 if (len < 0) {
390 break;
391 }
392 }
393
394leave:
395 // cleanup
396 fprintf(stdout, "%s[%d:%d]: terminating\n", __func__, cdata->duinfo->device_id, cdata->duinfo->use_count);
397 if (cdata->reader != 0) {
398 cdata->reader_quit = 1;
399 pthread_join(cdata->reader, NULL);
400 }
401
402 cdata->handler_dead = 1;
403
404 fprintf(stdout, "%s[%d:%d]: terminated\n", __func__, cdata->duinfo->device_id, cdata->duinfo->use_count);
405 return NULL;
406}
407
408/**
409 * This thread is started when a new connection is accepted.
410 * It performs the handshake, then waits for the connect packet and
411 * on success it starts the usbmuxd_client_handler thread.
412 */
413static void *usbmuxd_client_init_thread(void *arg)
414{
415 struct client_data *cdata;
416 struct usbmuxd_hello hello;
417 struct usbmuxd_device_info_request dev_info_req;
418 struct usbmuxd_connect_request c_req;
419
420 struct usb_bus *bus;
421 struct usb_device *dev;
422
423 int recv_len;
424 int found = 0;
425 int res;
426 int i;
427 int sent_result;
428 iphone_error_t err;
429
430 iphone_device_t phone;
431 struct device_use_info *cur_dev = NULL;
432
433 if (!arg) {
434 fprintf(stderr, "%s: invalid client_data provided!\n", __func__);
435 return NULL;
436 }
437
438 cdata = (struct client_data*)arg;
439 cdata->dead = 0;
440
441 fprintf(stdout, "%s: started (fd=%d)\n", __func__, cdata->socket);
442
443 if ((recv_len = usbmuxd_get_request(cdata->socket, &hello, sizeof(hello))) <= 0) {
444 fprintf(stderr, "%s: No Hello packet received, error %s\n", __func__, strerror(errno));
445 goto leave;
446 }
447
448 if ((recv_len == sizeof(hello)) && (hello.header.length == sizeof(hello))
449 && (hello.header.reserved == 0) && (hello.header.type == USBMUXD_HELLO)) {
450 // send success response
451 usbmuxd_send_result(cdata->socket, hello.header.tag, 0);
452 } else {
453 // send error response and exit
454 fprintf(stderr, "%s: Invalid Hello packet received.\n", __func__);
455 // TODO is this required?!
456 usbmuxd_send_result(cdata->socket, hello.header.tag, EINVAL);
457 goto leave;
458 }
459
460 // gather data about all iPhones/iPods attached
461 usb_init();
462 usb_find_busses();
463 usb_find_devices();
464
465 for (bus = usb_get_busses(); bus; bus = bus->next) {
466 for (dev = bus->devices; dev; dev = dev->next) {
467 if (dev->descriptor.idVendor == 0x05ac
468 && dev->descriptor.idProduct >= 0x1290
469 && dev->descriptor.idProduct <= 0x1293)
470 {
471 fprintf(stdout, "%s: Found device on bus %d, id %d\n", __func__, bus->location, dev->devnum);
472 found++;
473
474 // construct packet
475 memset(&dev_info_req, 0, sizeof(dev_info_req));
476 dev_info_req.header.length = sizeof(dev_info_req);
477 dev_info_req.header.type = USBMUXD_DEVICE_INFO;
478 dev_info_req.device_info.device_id = dev->devnum;
479 dev_info_req.device_info.product_id = dev->descriptor.idProduct;
480 if (dev->descriptor.iSerialNumber) {
481 usb_dev_handle *udev;
482 //pthread_mutex_lock(&usbmux_mutex);
483 udev = usb_open(dev);
484 if (udev) {
485 usb_get_string_simple(udev, dev->descriptor.iSerialNumber, dev_info_req.device_info.serial_number, sizeof(dev_info_req.device_info.serial_number)+1);
486 usb_close(udev);
487 }
488 //pthread_mutex_unlock(&usbmux_mutex);
489 }
490
491#ifdef DEBUG
492 print_buffer((char*)&dev_info_req, sizeof(dev_info_req));
493#endif
494
495 // send it
496 if (send_buf(cdata->socket, &dev_info_req, sizeof(dev_info_req)) <= 0) {
497 fprintf(stderr, "%s: Error: Could not send device info: %s\n", __func__, strerror(errno));
498 found--;
499 }
500 }
501 }
502 }
503
504 // now wait for connect request
505 if (found <= 0) {
506 fprintf(stderr, "%s: No attached iPhone/iPod devices found.\n", __func__);
507 goto leave;
508 }
509
510 memset(&c_req, 0, sizeof(c_req));
511 if ((recv_len = usbmuxd_get_request(cdata->socket, &c_req, sizeof(c_req))) <= 0) {
512 fprintf(stderr, "%s: Did not receive any connect request.\n", __func__);
513 goto leave;
514 }
515
516 if (c_req.header.type != USBMUXD_CONNECT) {
517 fprintf(stderr, "%s: Unexpected packet of type %d received.\n", __func__, c_req.header.type);
518 goto leave;
519 }
520
521 fprintf(stdout, "%s: Setting up connection to usb device #%d on port %d\n", __func__, c_req.device_id, ntohs(c_req.tcp_dport));
522
523 // find the device, and open usb connection
524 phone = NULL;
525 cur_dev = NULL;
526 // first check if we already have an open connection
527 if (device_use_list) {
528 pthread_mutex_lock(&usbmux_mutex);
529 for (i = 0; i < device_use_count; i++) {
530 if (device_use_list[i]) {
531 if (device_use_list[i]->device_id == c_req.device_id) {
532 device_use_list[i]->use_count++;
533 cur_dev = device_use_list[i];
534 phone = cur_dev->phone;
535 break;
536 }
537 }
538 }
539 pthread_mutex_unlock(&usbmux_mutex);
540 }
541 if (!phone) {
542 // if not found, make a new connection
543 if (iphone_get_specific_device(0, c_req.device_id, &phone) != IPHONE_E_SUCCESS) {
544 fprintf(stderr, "%s: device_id %d could not be opened\n", __func__, c_req.device_id);
545 usbmuxd_send_result(cdata->socket, c_req.header.tag, ENODEV);
546 goto leave;
547 }
548 // add to device list
549 cur_dev = (struct device_use_info*)malloc(sizeof(struct device_use_info));
550 memset(cur_dev, 0, sizeof(struct device_use_info));
551 cur_dev->use_count = 1;
552 cur_dev->device_id = c_req.device_id;
553 cur_dev->phone = phone;
554
555 fprintf(stdout, "%s: device_use_count = %d\n", __func__, device_use_count);
556
557 pthread_mutex_lock(&usbmux_mutex);
558 device_use_list = (struct device_use_info**)realloc(device_use_list, sizeof(struct device_use_info*) * (device_use_count+1));
559 if (device_use_list) {
560 device_use_list[device_use_count] = cur_dev;
561 device_use_count++;
562 }
563 pthread_mutex_unlock(&usbmux_mutex);
564 } else {
565 fprintf(stdout, "%s: reusing usb connection device_id %d\n", __func__, c_req.device_id);
566 }
567
568 // setup connection to iPhone/iPod
569// pthread_mutex_lock(&usbmux_mutex);
570 res = iphone_mux_new_client(cur_dev->phone, 0, ntohs(c_req.tcp_dport), &(cdata->muxclient));
571// pthread_mutex_unlock(&usbmux_mutex);
572
573 if (res != 0) {
574 usbmuxd_send_result(cdata->socket, c_req.header.tag, res);
575 fprintf(stderr, "%s: mux_new_client returned %d, aborting.\n", __func__, res);
576 goto leave;
577 }
578
579 // start connection handler thread
580 cdata->handler_dead = 0;
581 cdata->tag = c_req.header.tag;
582 cdata->duinfo = cur_dev;
583 if (pthread_create(&cdata->handler, NULL, usbmuxd_client_handler_thread, cdata) != 0) {
584 fprintf(stderr, "%s: could not create usbmuxd_client_handler_thread!\n", __func__);
585 cdata->handler = 0;
586 goto leave;
587 }
588
589 sent_result = 0;
590
591 // start reading data from the connected device
592 while (!quit_flag && !cdata->handler_dead) {
593 pthread_mutex_lock(&reader_mutex);
594 iphone_mux_pullbulk(cur_dev->phone);
595 err = iphone_mux_get_error(cdata->muxclient);
596 pthread_mutex_unlock(&reader_mutex);
597 if (err != IPHONE_E_SUCCESS) {
598 break;
599 }
600 }
601
602 if (!sent_result) {
603 //fprintf(stderr, "Sending error message %d tag %d\n", err, c_req.header.tag);
604 err = iphone_mux_get_error(cdata->muxclient);
605 //usbmuxd_send_result(cdata->socket, c_req.header.tag, err);
606 }
607
608 fprintf(stdout, "%s: terminating\n", __func__);
609
610 // wait for handler thread to finish its work
611 if (cdata->handler != 0) {
612 pthread_join(cdata->handler, NULL);
613 }
614
615 // time to clean up
616 if (cdata && cdata->muxclient) { // should be non-NULL
617 iphone_mux_free_client(cdata->muxclient);
618 }
619
620leave:
621 // this has to be freed only if it's not in use anymore as it closes
622 // the USB connection
623 if (cur_dev) {
624 if (cur_dev->use_count > 1) {
625 cur_dev->use_count--;
626 } else {
627 iphone_free_device(cur_dev->phone);
628 cur_dev->use_count = 0;
629 free(cur_dev);
630 cur_dev = NULL;
631 pthread_mutex_lock(&usbmux_mutex);
632 if (device_use_count > 1) {
633 struct device_use_info **newlist;
634 int j;
635
636 newlist = (struct device_use_info**)malloc(sizeof(struct device_use_info*) * device_use_count-1);
637 for (i = 0; i < device_use_count; i++) {
638 if (device_use_list[i] != NULL) {
639 newlist[j++] = device_use_list[i];
640 }
641 }
642 free(device_use_list);
643 device_use_list = newlist;
644 } else {
645 free(device_use_list);
646 device_use_list = NULL;
647 }
648 pthread_mutex_unlock(&usbmux_mutex);
649 }
650 }
651
652 cdata->dead = 1;
653 close(cdata->socket);
654
655 fprintf(stdout, "%s: terminated\n", __func__);
656
657 return NULL;
658}
659
660/**
661 * make this program run detached from the current console
662 */
663static int daemonize()
664{
665 // TODO still to be implemented, also logging is missing!
666 return 0;
667}
668
669/**
670 * signal handler function for cleaning up properly
671 */
672static void clean_exit(int sig)
673{
674 if (sig == SIGINT) {
675 fprintf(stdout, "CTRL+C pressed\n");
676 }
677 quit_flag = 1;
678}
679
680/**
681 * thread function that performs accept() and starts the required child
682 * threads to perform the rest of the communication stuff.
683 */
684static void *usbmuxd_accept_thread(void *arg)
685{
686 struct sockaddr_un c_addr;
687 socklen_t len = sizeof(struct sockaddr_un);
688 struct client_data *cdata;
689 struct client_data **children = NULL;
690 int children_capacity = DEFAULT_CHILDREN_CAPACITY;
691 int i = 0;
692 int result = 0;
693 int cnt;
694
695 // Reserve space for 10 clients which should be enough. If not, the
696 // buffer gets enlarged later.
697 children = (struct client_data**)malloc(sizeof(struct client_data*) * children_capacity);
698 if (!children) {
699 fprintf(stderr, "%s: Out of memory when allocating memory for child threads. Terminating.\n", __func__);
700 exit(EXIT_FAILURE);
701 }
702 memset(children, 0, sizeof(struct client_data*) * children_capacity);
703
704 fprintf(stdout, "%s: waiting for connection\n", __func__);
705 while (!quit_flag) {
706 // Check the file descriptor before accepting a connection.
707 // If no connection attempt is made, just repeat...
708 result = check_fd(fsock, FD_READ, 1000);
709 if (result <= 0) {
710 if (result == 0) {
711 // cleanup
712 for (i = 0; i < children_capacity; i++) {
713 if (children[i]) {
714 if (children[i]->dead != 0) {
715 pthread_join(children[i]->thread, NULL);
716 fprintf(stdout, "%s: reclaimed client thread (fd=%d)\n", __func__, children[i]->socket);
717 free(children[i]);
718 children[i] = NULL;
719 cnt++;
720 } else {
721 cnt = 0;
722 }
723 } else {
724 cnt++;
725 }
726 }
727
728 if ((children_capacity > DEFAULT_CHILDREN_CAPACITY)
729 && ((children_capacity - cnt) <= DEFAULT_CHILDREN_CAPACITY)) {
730 children_capacity = DEFAULT_CHILDREN_CAPACITY;
731 children = realloc(children, sizeof(struct client_data*) * children_capacity);
732 }
733 continue;
734 } else {
735 fprintf(stderr, "select error: %s\n", strerror(errno));
736 continue;
737 }
738 }
739
740 cdata = (struct client_data*)malloc(sizeof(struct client_data));
741 memset(cdata, 0, sizeof(struct client_data));
742 if (!cdata) {
743 quit_flag = 1;
744 fprintf(stderr, "%s: Error: Out of memory! Terminating.\n", __func__);
745 break;
746 }
747
748 cdata->socket = accept(fsock, (struct sockaddr*)&c_addr, &len);
749 if (cdata->socket < 0) {
750 free(cdata);
751 if (errno == EINTR) {
752 continue;
753 } else {
754 fprintf(stderr, "%s: Error in accept: %s\n", __func__, strerror(errno));
755 continue;
756 }
757 }
758
759 fprintf(stdout, "%s: new client connected (fd=%d)\n", __func__, cdata->socket);
760
761 // create client thread:
762 if (pthread_create(&cdata->thread, NULL, usbmuxd_client_init_thread, cdata) == 0) {
763 for (i = 0; i < children_capacity; i++) {
764 if (children[i] == NULL) break;
765 }
766 if (i == children_capacity) {
767 // enlarge buffer
768 children_capacity++;
769 children = realloc(children, sizeof(struct client_data*) * children_capacity);
770 if (!children) {
771 fprintf(stderr, "%s: Out of memory when enlarging child thread buffer\n", __func__);
772 }
773 }
774 children[i] = cdata;
775 } else {
776 fprintf(stderr, "%s: Failed to create client_init_thread.\n", __func__);
777 close(cdata->socket);
778 free(cdata);
779 cdata = NULL;
780 }
781 }
782
783 fprintf(stdout, "%s: terminating\n", __func__);
784
785 // preparing for shutdown: wait for child threads to terminate (if any)
786 fprintf(stdout, "%s: waiting for child threads to terminate...\n", __func__);
787 for (i = 0; i < children_capacity; i++) {
788 if (children[i] != NULL) {
789 pthread_join(children[i]->thread, NULL);
790 free(children[i]);
791 }
792 }
793
794 // delete the children set.
795 free(children);
796 children = NULL;
797
798 fprintf(stdout, "%s: terminated.\n", __func__);
799
800 return NULL;
801}
802
803/**
804 * main function.
805 */
806int main(int argc, char **argv)
807{
808 int foreground = 1;
809 pthread_t acceptor;
810
811 fprintf(stdout, "usbmuxd: starting\n");
812
813 // TODO: Parameter checking.
814
815 fsock = create_unix_socket(USBMUXD_SOCKET_FILE);
816 if (fsock < 0) {
817 fprintf(stderr, "Could not create socket, exiting\n");
818 return -1;
819 }
820
821 chmod(USBMUXD_SOCKET_FILE, 0666);
822
823 if (!foreground) {
824 if (daemonize() < 0) {
825 exit(EXIT_FAILURE);
826 }
827 }
828
829 // signal(SIGHUP, reload_conf); // none yet
830 signal(SIGINT, clean_exit);
831 signal(SIGQUIT, clean_exit);
832 signal(SIGTERM, clean_exit);
833 signal(SIGPIPE, SIG_IGN);
834
835 if (pthread_create(&acceptor, NULL, usbmuxd_accept_thread, NULL) != 0) {
836 fprintf(stderr, "Failed to create server thread.\n");
837 close(fsock);
838 return -1;
839 }
840
841 // Relax here. Just wait for the accept thread to terminate.
842 pthread_join(acceptor, NULL);
843
844 fprintf(stdout, "usbmuxd: terminating\n");
845 if (fsock >= 0) {
846 close(fsock);
847 }
848
849 unlink(USBMUXD_SOCKET_FILE);
850
851 return 0;
852}
853