summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.github/FUNDING.yml3
-rw-r--r--.github/workflows/build.yml77
-rw-r--r--.gitignore39
-rw-r--r--AUTHORS15
-rw-r--r--CMakeLists.txt83
-rw-r--r--COPYING.LGPLv2.1502
-rw-r--r--Makefile.am13
-rw-r--r--Modules/CheckConstantExists.cmake38
-rw-r--r--Modules/FindPLIST.cmake31
-rw-r--r--Modules/FindUSB.cmake40
-rw-r--r--Modules/LibFindMacros.cmake99
-rw-r--r--Modules/VersionTag.cmake13
-rw-r--r--Modules/cmake_uninstall.cmake.in21
-rwxr-xr-xModules/describe.sh17
-rw-r--r--NEWS90
-rw-r--r--README218
-rw-r--r--README.devel50
-rw-r--r--README.md159
-rwxr-xr-xautogen.sh31
-rw-r--r--common/utils.c125
-rw-r--r--common/utils.h69
-rw-r--r--configure.ac195
-rw-r--r--daemon/CMakeLists.txt23
-rw-r--r--daemon/client.c616
-rw-r--r--daemon/client.h47
-rw-r--r--daemon/device.h52
-rw-r--r--daemon/log.h44
-rw-r--r--daemon/main.c618
-rw-r--r--daemon/usb-linux.c589
-rw-r--r--daemon/usb.h66
-rw-r--r--docs/Makefile.am3
-rw-r--r--docs/usbmuxd.880
-rwxr-xr-xgit-version-gen19
-rw-r--r--libusbmuxd.pc.in11
-rw-r--r--libusbmuxd/CMakeLists.txt29
-rw-r--r--libusbmuxd/libusbmuxd.c782
-rw-r--r--libusbmuxd/sock_stuff.c370
-rw-r--r--libusbmuxd/sock_stuff.h57
-rw-r--r--libusbmuxd/usbmuxd.h181
-rw-r--r--m4/as-compiler-flag.m462
-rw-r--r--python-client/.gitignore3
-rw-r--r--python-client/tcprelay.py148
-rw-r--r--python-client/usbmux.py246
-rw-r--r--src/Makefile.am32
-rw-r--r--src/client.c1038
-rw-r--r--src/client.h47
-rw-r--r--src/conf.c565
-rw-r--r--src/conf.h41
-rw-r--r--src/device.c (renamed from daemon/device.c)450
-rw-r--r--src/device.h56
-rw-r--r--src/log.c (renamed from daemon/log.c)56
-rw-r--r--src/log.h42
-rw-r--r--src/main.c931
-rw-r--r--src/preflight.c406
-rw-r--r--src/preflight.h28
-rw-r--r--src/usb.c1097
-rw-r--r--src/usb.h73
-rw-r--r--src/usbmuxd-proto.h (renamed from libusbmuxd/usbmuxd-proto.h)52
-rw-r--r--src/utils.c131
-rw-r--r--src/utils.h49
-rw-r--r--stuff/README25
-rw-r--r--stuff/com.openssh.sftp.plist41
-rw-r--r--systemd/Makefile.am21
-rw-r--r--systemd/usbmuxd.service.in7
-rw-r--r--tools/CMakeLists.txt11
-rw-r--r--tools/iproxy.c251
-rw-r--r--udev/39-usbmuxd.rules.in16
-rw-r--r--udev/85-usbmuxd.rules.in7
-rw-r--r--udev/CMakeLists.txt2
-rw-r--r--udev/Makefile.am20
70 files changed, 5790 insertions, 5679 deletions
diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml
new file mode 100644
index 0000000..e995b30
--- /dev/null
+++ b/.github/FUNDING.yml
@@ -0,0 +1,3 @@
1github: nikias
2patreon: nikias
3custom: ["https://www.paypal.me/NikiasBassen"]
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
new file mode 100644
index 0000000..1ced463
--- /dev/null
+++ b/.github/workflows/build.yml
@@ -0,0 +1,77 @@
1name: build
2
3on:
4 push:
5 pull_request:
6 schedule:
7 - cron: '0 0 1 * *'
8
9jobs:
10 build-linux-ubuntu:
11 runs-on: ubuntu-latest
12 steps:
13 - name: install dependencies
14 run: |
15 sudo apt-get update
16 sudo apt-get install libusb-1.0-0-dev
17 - name: prepare environment
18 run: |
19 echo "target_triplet=`gcc -dumpmachine`" >> $GITHUB_ENV
20 - name: fetch libplist
21 uses: dawidd6/action-download-artifact@v6
22 with:
23 github_token: ${{secrets.GITHUB_TOKEN}}
24 workflow: build.yml
25 name: libplist-latest_${{env.target_triplet}}
26 repo: libimobiledevice/libplist
27 - name: fetch libusbmuxd
28 uses: dawidd6/action-download-artifact@v6
29 with:
30 github_token: ${{secrets.GITHUB_TOKEN}}
31 workflow: build.yml
32 name: libusbmuxd-latest_${{env.target_triplet}}
33 repo: libimobiledevice/libusbmuxd
34 - name: fetch libimobiledevice-glue
35 uses: dawidd6/action-download-artifact@v6
36 with:
37 github_token: ${{secrets.GITHUB_TOKEN}}
38 workflow: build.yml
39 name: libimobiledevice-glue-latest_${{env.target_triplet}}
40 repo: libimobiledevice/libimobiledevice-glue
41 - name: fetch libimobiledevice
42 uses: dawidd6/action-download-artifact@v6
43 with:
44 github_token: ${{secrets.GITHUB_TOKEN}}
45 workflow: build.yml
46 name: libimobiledevice-latest_${{env.target_triplet}}
47 repo: libimobiledevice/libimobiledevice
48 - name: install external dependencies
49 run: |
50 mkdir extract
51 for I in *.tar; do
52 tar -C extract -xvf $I
53 done
54 sudo cp -r extract/* /
55 sudo ldconfig
56 - uses: actions/checkout@v4
57 with:
58 fetch-depth: 0
59 - name: autogen
60 run: ./autogen.sh PKG_CONFIG_PATH=/usr/local/lib/pkgconfig
61 - name: print config.log
62 if: ${{ failure() }}
63 run: cat config.log
64 - name: make
65 run: make
66 - name: make install
67 run: sudo make install
68 - name: prepare artifact
69 run: |
70 mkdir -p dest
71 DESTDIR=`pwd`/dest make install
72 tar -C dest -cf usbmuxd.tar --strip-components 1 .
73 - name: publish artifact
74 uses: actions/upload-artifact@v4
75 with:
76 name: usbmuxd-latest_${{env.target_triplet}}
77 path: usbmuxd.tar
diff --git a/.gitignore b/.gitignore
index 0784410..4e65390 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,38 @@
1# git-ls-files --others --exclude-from=.git/info/exclude
2# Lines that start with '#' are comments.
3# For a project mostly in C, the following would be a good set of
4# exclude patterns (uncomment them if you want to use them):
5*.[oa]
1*~ 6*~
2build 7*.po
3 8*.lo
9*.la
10autom4te.cache/*
11*.in
12*/.deps/*
13m4/*
14swig/*
15*.swp
16*.patch
17aclocal.m4
18config.h
19config.log
20config.sub
21config.guess
22config.status
23configure
24depcomp
25install-sh
26compile
27main
28ltmain.sh
29missing
30mkinstalldirs
31libtool
32*Makefile
33py-compile
34stamp-h1
35src/.libs
36src/usbmuxd
37udev/39-usbmuxd.rules
38systemd/usbmuxd.service
diff --git a/AUTHORS b/AUTHORS
index c196afd..e3b41fc 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -1,5 +1,14 @@
1Nikias Bassen 1Aaron Burghardt
2Hector Martin
3Bastien Nocera 2Bastien Nocera
4Paul Sladen 3Cerrato Renaud
4Christophe Fergeau
5David Sansome
6Hector Martin
7Jacob Myers
8John Maguire
5Martin Szulecki 9Martin Szulecki
10Mikkel Kamstrup Erlandsen
11Nikias Bassen
12Paul Sladen
13Peter Wu
14Satoshi Ohgoh \ No newline at end of file
diff --git a/CMakeLists.txt b/CMakeLists.txt
deleted file mode 100644
index 8ed2e37..0000000
--- a/CMakeLists.txt
+++ /dev/null
@@ -1,83 +0,0 @@
1PROJECT(usbmuxd)
2
3cmake_minimum_required(VERSION 2.6)
4
5set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/Modules/")
6
7include(VersionTag)
8
9set(USBMUXD_VERSION "${VERSION_TAG}")
10set(LIBUSBMUXD_VERSION "${VERSION_TAG}")
11set(LIBUSBMUXD_SOVERSION "1")
12
13message("-- Configuring usbmuxd v${VERSION_TAG}")
14
15if(NOT DEFINED LIB_SUFFIX)
16 if(CMAKE_SIZEOF_VOID_P EQUAL 8 AND ${CMAKE_SYSTEM_NAME} MATCHES "Linux")
17 set(LIB_SUFFIX "64" CACHE STRING "Define suffix of library directory name (32/64)" )
18 else()
19 set(LIB_SUFFIX "" CACHE STRING "Define suffix of library directory name (32/64)" )
20 endif()
21endif()
22
23# let CFLAGS env override this
24if(CMAKE_C_FLAGS STREQUAL "")
25 set(CMAKE_C_FLAGS "-O2")
26endif()
27
28option(WANT_PLIST "Build with protocol version 1 support using libplist" ON)
29
30set(OPT_INCLUDES "")
31set(OPT_LIBS "")
32if(WANT_PLIST)
33 find_package(PLIST)
34 if(PLIST_FOUND)
35 set(HAVE_PLIST ON)
36 set(OPT_INCLUDES ${OPT_INCLUDES} ${PLIST_INCLUDE_DIRS})
37 set(OPT_LIBS ${OPT_LIBS} ${PLIST_LIBRARIES})
38 else()
39 message("* NOTE: libplist was not found!")
40 message("* libusbmuxd/usbmuxd will be build WITHOUT support for version 1")
41 message("* of the usbmux protocol (plist based).")
42 endif()
43endif()
44
45option(WITH_USBMUXD "Build usbmux daemon (usbmuxd)" ON)
46if(WIN32 AND WITH_USBMUXD)
47 message("** NOTE: usbmuxd cannot be built on WIN32 due to missing libusb-1.0 support!")
48 message(" If you need your own usbmuxd you have to use usbmuxd-legacy which works")
49 message(" with libusb-0.1; otherwise just use the one that ships with iTunes.")
50 message(" Building of usbmuxd has been disabled.")
51 set(WITH_USBMUXD OFF)
52endif()
53if(WITH_USBMUXD)
54 message("-- Will build usbmuxd: YES")
55else()
56 message("-- Will build usbmuxd: NO")
57 message("** NOTE: will NOT build usbmuxd **")
58 if(WIN32 OR APPLE)
59 message("** Make sure iTunes is installed, otherwise this software will not work! **")
60 else()
61 message("** You'll need a working usbmuxd implementation for this software to work! **")
62 endif()
63endif()
64
65add_definitions(-Wall)
66
67add_subdirectory (libusbmuxd)
68if (WITH_USBMUXD)
69 add_subdirectory (daemon)
70 if (${CMAKE_SYSTEM_NAME} MATCHES "Linux")
71 add_subdirectory (udev)
72 endif()
73endif()
74add_subdirectory (tools)
75
76# pkg-config
77configure_file("${CMAKE_CURRENT_SOURCE_DIR}/libusbmuxd.pc.in" "${CMAKE_CURRENT_BINARY_DIR}/libusbmuxd.pc")
78# install pkg-config file
79install(FILES "${CMAKE_CURRENT_BINARY_DIR}/libusbmuxd.pc" DESTINATION lib${LIB_SUFFIX}/pkgconfig/)
80
81# add uninstall target
82configure_file("${CMAKE_SOURCE_DIR}/Modules/cmake_uninstall.cmake.in" "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake" IMMEDIATE @ONLY)
83add_custom_target(uninstall "${CMAKE_COMMAND}" -P "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake")
diff --git a/COPYING.LGPLv2.1 b/COPYING.LGPLv2.1
deleted file mode 100644
index 732811e..0000000
--- a/COPYING.LGPLv2.1
+++ /dev/null
@@ -1,502 +0,0 @@
1 GNU LESSER GENERAL PUBLIC LICENSE
2 Version 2.1, February 1999
3
4 Copyright (C) 1991, 1999 Free Software Foundation, Inc.
5 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
6 Everyone is permitted to copy and distribute verbatim copies
7 of this license document, but changing it is not allowed.
8
9[This is the first released version of the Lesser GPL. It also counts
10 as the successor of the GNU Library Public License, version 2, hence
11 the version number 2.1.]
12
13 Preamble
14
15 The licenses for most software are designed to take away your
16freedom to share and change it. By contrast, the GNU General Public
17Licenses are intended to guarantee your freedom to share and change
18free software--to make sure the software is free for all its users.
19
20 This license, the Lesser General Public License, applies to some
21specially designated software packages--typically libraries--of the
22Free Software Foundation and other authors who decide to use it. You
23can use it too, but we suggest you first think carefully about whether
24this license or the ordinary General Public License is the better
25strategy to use in any particular case, based on the explanations below.
26
27 When we speak of free software, we are referring to freedom of use,
28not price. Our General Public Licenses are designed to make sure that
29you have the freedom to distribute copies of free software (and charge
30for this service if you wish); that you receive source code or can get
31it if you want it; that you can change the software and use pieces of
32it in new free programs; and that you are informed that you can do
33these things.
34
35 To protect your rights, we need to make restrictions that forbid
36distributors to deny you these rights or to ask you to surrender these
37rights. These restrictions translate to certain responsibilities for
38you if you distribute copies of the library or if you modify it.
39
40 For example, if you distribute copies of the library, whether gratis
41or for a fee, you must give the recipients all the rights that we gave
42you. You must make sure that they, too, receive or can get the source
43code. If you link other code with the library, you must provide
44complete object files to the recipients, so that they can relink them
45with the library after making changes to the library and recompiling
46it. And you must show them these terms so they know their rights.
47
48 We protect your rights with a two-step method: (1) we copyright the
49library, and (2) we offer you this license, which gives you legal
50permission to copy, distribute and/or modify the library.
51
52 To protect each distributor, we want to make it very clear that
53there is no warranty for the free library. Also, if the library is
54modified by someone else and passed on, the recipients should know
55that what they have is not the original version, so that the original
56author's reputation will not be affected by problems that might be
57introduced by others.
58
59 Finally, software patents pose a constant threat to the existence of
60any free program. We wish to make sure that a company cannot
61effectively restrict the users of a free program by obtaining a
62restrictive license from a patent holder. Therefore, we insist that
63any patent license obtained for a version of the library must be
64consistent with the full freedom of use specified in this license.
65
66 Most GNU software, including some libraries, is covered by the
67ordinary GNU General Public License. This license, the GNU Lesser
68General Public License, applies to certain designated libraries, and
69is quite different from the ordinary General Public License. We use
70this license for certain libraries in order to permit linking those
71libraries into non-free programs.
72
73 When a program is linked with a library, whether statically or using
74a shared library, the combination of the two is legally speaking a
75combined work, a derivative of the original library. The ordinary
76General Public License therefore permits such linking only if the
77entire combination fits its criteria of freedom. The Lesser General
78Public License permits more lax criteria for linking other code with
79the library.
80
81 We call this license the "Lesser" General Public License because it
82does Less to protect the user's freedom than the ordinary General
83Public License. It also provides other free software developers Less
84of an advantage over competing non-free programs. These disadvantages
85are the reason we use the ordinary General Public License for many
86libraries. However, the Lesser license provides advantages in certain
87special circumstances.
88
89 For example, on rare occasions, there may be a special need to
90encourage the widest possible use of a certain library, so that it becomes
91a de-facto standard. To achieve this, non-free programs must be
92allowed to use the library. A more frequent case is that a free
93library does the same job as widely used non-free libraries. In this
94case, there is little to gain by limiting the free library to free
95software only, so we use the Lesser General Public License.
96
97 In other cases, permission to use a particular library in non-free
98programs enables a greater number of people to use a large body of
99free software. For example, permission to use the GNU C Library in
100non-free programs enables many more people to use the whole GNU
101operating system, as well as its variant, the GNU/Linux operating
102system.
103
104 Although the Lesser General Public License is Less protective of the
105users' freedom, it does ensure that the user of a program that is
106linked with the Library has the freedom and the wherewithal to run
107that program using a modified version of the Library.
108
109 The precise terms and conditions for copying, distribution and
110modification follow. Pay close attention to the difference between a
111"work based on the library" and a "work that uses the library". The
112former contains code derived from the library, whereas the latter must
113be combined with the library in order to run.
114
115 GNU LESSER GENERAL PUBLIC LICENSE
116 TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
117
118 0. This License Agreement applies to any software library or other
119program which contains a notice placed by the copyright holder or
120other authorized party saying it may be distributed under the terms of
121this Lesser General Public License (also called "this License").
122Each licensee is addressed as "you".
123
124 A "library" means a collection of software functions and/or data
125prepared so as to be conveniently linked with application programs
126(which use some of those functions and data) to form executables.
127
128 The "Library", below, refers to any such software library or work
129which has been distributed under these terms. A "work based on the
130Library" means either the Library or any derivative work under
131copyright law: that is to say, a work containing the Library or a
132portion of it, either verbatim or with modifications and/or translated
133straightforwardly into another language. (Hereinafter, translation is
134included without limitation in the term "modification".)
135
136 "Source code" for a work means the preferred form of the work for
137making modifications to it. For a library, complete source code means
138all the source code for all modules it contains, plus any associated
139interface definition files, plus the scripts used to control compilation
140and installation of the library.
141
142 Activities other than copying, distribution and modification are not
143covered by this License; they are outside its scope. The act of
144running a program using the Library is not restricted, and output from
145such a program is covered only if its contents constitute a work based
146on the Library (independent of the use of the Library in a tool for
147writing it). Whether that is true depends on what the Library does
148and what the program that uses the Library does.
149
150 1. You may copy and distribute verbatim copies of the Library's
151complete source code as you receive it, in any medium, provided that
152you conspicuously and appropriately publish on each copy an
153appropriate copyright notice and disclaimer of warranty; keep intact
154all the notices that refer to this License and to the absence of any
155warranty; and distribute a copy of this License along with the
156Library.
157
158 You may charge a fee for the physical act of transferring a copy,
159and you may at your option offer warranty protection in exchange for a
160fee.
161
162 2. You may modify your copy or copies of the Library or any portion
163of it, thus forming a work based on the Library, and copy and
164distribute such modifications or work under the terms of Section 1
165above, provided that you also meet all of these conditions:
166
167 a) The modified work must itself be a software library.
168
169 b) You must cause the files modified to carry prominent notices
170 stating that you changed the files and the date of any change.
171
172 c) You must cause the whole of the work to be licensed at no
173 charge to all third parties under the terms of this License.
174
175 d) If a facility in the modified Library refers to a function or a
176 table of data to be supplied by an application program that uses
177 the facility, other than as an argument passed when the facility
178 is invoked, then you must make a good faith effort to ensure that,
179 in the event an application does not supply such function or
180 table, the facility still operates, and performs whatever part of
181 its purpose remains meaningful.
182
183 (For example, a function in a library to compute square roots has
184 a purpose that is entirely well-defined independent of the
185 application. Therefore, Subsection 2d requires that any
186 application-supplied function or table used by this function must
187 be optional: if the application does not supply it, the square
188 root function must still compute square roots.)
189
190These requirements apply to the modified work as a whole. If
191identifiable sections of that work are not derived from the Library,
192and can be reasonably considered independent and separate works in
193themselves, then this License, and its terms, do not apply to those
194sections when you distribute them as separate works. But when you
195distribute the same sections as part of a whole which is a work based
196on the Library, the distribution of the whole must be on the terms of
197this License, whose permissions for other licensees extend to the
198entire whole, and thus to each and every part regardless of who wrote
199it.
200
201Thus, it is not the intent of this section to claim rights or contest
202your rights to work written entirely by you; rather, the intent is to
203exercise the right to control the distribution of derivative or
204collective works based on the Library.
205
206In addition, mere aggregation of another work not based on the Library
207with the Library (or with a work based on the Library) on a volume of
208a storage or distribution medium does not bring the other work under
209the scope of this License.
210
211 3. You may opt to apply the terms of the ordinary GNU General Public
212License instead of this License to a given copy of the Library. To do
213this, you must alter all the notices that refer to this License, so
214that they refer to the ordinary GNU General Public License, version 2,
215instead of to this License. (If a newer version than version 2 of the
216ordinary GNU General Public License has appeared, then you can specify
217that version instead if you wish.) Do not make any other change in
218these notices.
219
220 Once this change is made in a given copy, it is irreversible for
221that copy, so the ordinary GNU General Public License applies to all
222subsequent copies and derivative works made from that copy.
223
224 This option is useful when you wish to copy part of the code of
225the Library into a program that is not a library.
226
227 4. You may copy and distribute the Library (or a portion or
228derivative of it, under Section 2) in object code or executable form
229under the terms of Sections 1 and 2 above provided that you accompany
230it with the complete corresponding machine-readable source code, which
231must be distributed under the terms of Sections 1 and 2 above on a
232medium customarily used for software interchange.
233
234 If distribution of object code is made by offering access to copy
235from a designated place, then offering equivalent access to copy the
236source code from the same place satisfies the requirement to
237distribute the source code, even though third parties are not
238compelled to copy the source along with the object code.
239
240 5. A program that contains no derivative of any portion of the
241Library, but is designed to work with the Library by being compiled or
242linked with it, is called a "work that uses the Library". Such a
243work, in isolation, is not a derivative work of the Library, and
244therefore falls outside the scope of this License.
245
246 However, linking a "work that uses the Library" with the Library
247creates an executable that is a derivative of the Library (because it
248contains portions of the Library), rather than a "work that uses the
249library". The executable is therefore covered by this License.
250Section 6 states terms for distribution of such executables.
251
252 When a "work that uses the Library" uses material from a header file
253that is part of the Library, the object code for the work may be a
254derivative work of the Library even though the source code is not.
255Whether this is true is especially significant if the work can be
256linked without the Library, or if the work is itself a library. The
257threshold for this to be true is not precisely defined by law.
258
259 If such an object file uses only numerical parameters, data
260structure layouts and accessors, and small macros and small inline
261functions (ten lines or less in length), then the use of the object
262file is unrestricted, regardless of whether it is legally a derivative
263work. (Executables containing this object code plus portions of the
264Library will still fall under Section 6.)
265
266 Otherwise, if the work is a derivative of the Library, you may
267distribute the object code for the work under the terms of Section 6.
268Any executables containing that work also fall under Section 6,
269whether or not they are linked directly with the Library itself.
270
271 6. As an exception to the Sections above, you may also combine or
272link a "work that uses the Library" with the Library to produce a
273work containing portions of the Library, and distribute that work
274under terms of your choice, provided that the terms permit
275modification of the work for the customer's own use and reverse
276engineering for debugging such modifications.
277
278 You must give prominent notice with each copy of the work that the
279Library is used in it and that the Library and its use are covered by
280this License. You must supply a copy of this License. If the work
281during execution displays copyright notices, you must include the
282copyright notice for the Library among them, as well as a reference
283directing the user to the copy of this License. Also, you must do one
284of these things:
285
286 a) Accompany the work with the complete corresponding
287 machine-readable source code for the Library including whatever
288 changes were used in the work (which must be distributed under
289 Sections 1 and 2 above); and, if the work is an executable linked
290 with the Library, with the complete machine-readable "work that
291 uses the Library", as object code and/or source code, so that the
292 user can modify the Library and then relink to produce a modified
293 executable containing the modified Library. (It is understood
294 that the user who changes the contents of definitions files in the
295 Library will not necessarily be able to recompile the application
296 to use the modified definitions.)
297
298 b) Use a suitable shared library mechanism for linking with the
299 Library. A suitable mechanism is one that (1) uses at run time a
300 copy of the library already present on the user's computer system,
301 rather than copying library functions into the executable, and (2)
302 will operate properly with a modified version of the library, if
303 the user installs one, as long as the modified version is
304 interface-compatible with the version that the work was made with.
305
306 c) Accompany the work with a written offer, valid for at
307 least three years, to give the same user the materials
308 specified in Subsection 6a, above, for a charge no more
309 than the cost of performing this distribution.
310
311 d) If distribution of the work is made by offering access to copy
312 from a designated place, offer equivalent access to copy the above
313 specified materials from the same place.
314
315 e) Verify that the user has already received a copy of these
316 materials or that you have already sent this user a copy.
317
318 For an executable, the required form of the "work that uses the
319Library" must include any data and utility programs needed for
320reproducing the executable from it. However, as a special exception,
321the materials to be distributed need not include anything that is
322normally distributed (in either source or binary form) with the major
323components (compiler, kernel, and so on) of the operating system on
324which the executable runs, unless that component itself accompanies
325the executable.
326
327 It may happen that this requirement contradicts the license
328restrictions of other proprietary libraries that do not normally
329accompany the operating system. Such a contradiction means you cannot
330use both them and the Library together in an executable that you
331distribute.
332
333 7. You may place library facilities that are a work based on the
334Library side-by-side in a single library together with other library
335facilities not covered by this License, and distribute such a combined
336library, provided that the separate distribution of the work based on
337the Library and of the other library facilities is otherwise
338permitted, and provided that you do these two things:
339
340 a) Accompany the combined library with a copy of the same work
341 based on the Library, uncombined with any other library
342 facilities. This must be distributed under the terms of the
343 Sections above.
344
345 b) Give prominent notice with the combined library of the fact
346 that part of it is a work based on the Library, and explaining
347 where to find the accompanying uncombined form of the same work.
348
349 8. You may not copy, modify, sublicense, link with, or distribute
350the Library except as expressly provided under this License. Any
351attempt otherwise to copy, modify, sublicense, link with, or
352distribute the Library is void, and will automatically terminate your
353rights under this License. However, parties who have received copies,
354or rights, from you under this License will not have their licenses
355terminated so long as such parties remain in full compliance.
356
357 9. You are not required to accept this License, since you have not
358signed it. However, nothing else grants you permission to modify or
359distribute the Library or its derivative works. These actions are
360prohibited by law if you do not accept this License. Therefore, by
361modifying or distributing the Library (or any work based on the
362Library), you indicate your acceptance of this License to do so, and
363all its terms and conditions for copying, distributing or modifying
364the Library or works based on it.
365
366 10. Each time you redistribute the Library (or any work based on the
367Library), the recipient automatically receives a license from the
368original licensor to copy, distribute, link with or modify the Library
369subject to these terms and conditions. You may not impose any further
370restrictions on the recipients' exercise of the rights granted herein.
371You are not responsible for enforcing compliance by third parties with
372this License.
373
374 11. If, as a consequence of a court judgment or allegation of patent
375infringement or for any other reason (not limited to patent issues),
376conditions are imposed on you (whether by court order, agreement or
377otherwise) that contradict the conditions of this License, they do not
378excuse you from the conditions of this License. If you cannot
379distribute so as to satisfy simultaneously your obligations under this
380License and any other pertinent obligations, then as a consequence you
381may not distribute the Library at all. For example, if a patent
382license would not permit royalty-free redistribution of the Library by
383all those who receive copies directly or indirectly through you, then
384the only way you could satisfy both it and this License would be to
385refrain entirely from distribution of the Library.
386
387If any portion of this section is held invalid or unenforceable under any
388particular circumstance, the balance of the section is intended to apply,
389and the section as a whole is intended to apply in other circumstances.
390
391It is not the purpose of this section to induce you to infringe any
392patents or other property right claims or to contest validity of any
393such claims; this section has the sole purpose of protecting the
394integrity of the free software distribution system which is
395implemented by public license practices. Many people have made
396generous contributions to the wide range of software distributed
397through that system in reliance on consistent application of that
398system; it is up to the author/donor to decide if he or she is willing
399to distribute software through any other system and a licensee cannot
400impose that choice.
401
402This section is intended to make thoroughly clear what is believed to
403be a consequence of the rest of this License.
404
405 12. If the distribution and/or use of the Library is restricted in
406certain countries either by patents or by copyrighted interfaces, the
407original copyright holder who places the Library under this License may add
408an explicit geographical distribution limitation excluding those countries,
409so that distribution is permitted only in or among countries not thus
410excluded. In such case, this License incorporates the limitation as if
411written in the body of this License.
412
413 13. The Free Software Foundation may publish revised and/or new
414versions of the Lesser General Public License from time to time.
415Such new versions will be similar in spirit to the present version,
416but may differ in detail to address new problems or concerns.
417
418Each version is given a distinguishing version number. If the Library
419specifies a version number of this License which applies to it and
420"any later version", you have the option of following the terms and
421conditions either of that version or of any later version published by
422the Free Software Foundation. If the Library does not specify a
423license version number, you may choose any version ever published by
424the Free Software Foundation.
425
426 14. If you wish to incorporate parts of the Library into other free
427programs whose distribution conditions are incompatible with these,
428write to the author to ask for permission. For software which is
429copyrighted by the Free Software Foundation, write to the Free
430Software Foundation; we sometimes make exceptions for this. Our
431decision will be guided by the two goals of preserving the free status
432of all derivatives of our free software and of promoting the sharing
433and reuse of software generally.
434
435 NO WARRANTY
436
437 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
438WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
439EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
440OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
441KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
442IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
443PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
444LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
445THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
446
447 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
448WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
449AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
450FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
451CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
452LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
453RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
454FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
455SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
456DAMAGES.
457
458 END OF TERMS AND CONDITIONS
459
460 How to Apply These Terms to Your New Libraries
461
462 If you develop a new library, and you want it to be of the greatest
463possible use to the public, we recommend making it free software that
464everyone can redistribute and change. You can do so by permitting
465redistribution under these terms (or, alternatively, under the terms of the
466ordinary General Public License).
467
468 To apply these terms, attach the following notices to the library. It is
469safest to attach them to the start of each source file to most effectively
470convey the exclusion of warranty; and each file should have at least the
471"copyright" line and a pointer to where the full notice is found.
472
473 <one line to give the library's name and a brief idea of what it does.>
474 Copyright (C) <year> <name of author>
475
476 This library is free software; you can redistribute it and/or
477 modify it under the terms of the GNU Lesser General Public
478 License as published by the Free Software Foundation; either
479 version 2.1 of the License, or (at your option) any later version.
480
481 This library is distributed in the hope that it will be useful,
482 but WITHOUT ANY WARRANTY; without even the implied warranty of
483 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
484 Lesser General Public License for more details.
485
486 You should have received a copy of the GNU Lesser General Public
487 License along with this library; if not, write to the Free Software
488 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
489
490Also add information on how to contact you by electronic and paper mail.
491
492You should also get your employer (if you work as a programmer) or your
493school, if any, to sign a "copyright disclaimer" for the library, if
494necessary. Here is a sample; alter the names:
495
496 Yoyodyne, Inc., hereby disclaims all copyright interest in the
497 library `Frob' (a library for tweaking knobs) written by James Random Hacker.
498
499 <signature of Ty Coon>, 1 April 1990
500 Ty Coon, President of Vice
501
502That's all there is to it!
diff --git a/Makefile.am b/Makefile.am
new file mode 100644
index 0000000..6da23b6
--- /dev/null
+++ b/Makefile.am
@@ -0,0 +1,13 @@
1AUTOMAKE_OPTIONS = foreign
2ACLOCAL_AMFLAGS = -I m4
3SUBDIRS = src $(UDEV_SUB) $(SYSTEMD_SUB) docs
4
5EXTRA_DIST = \
6 docs \
7 COPYING.GPLv2 \
8 COPYING.GPLv3 \
9 README.md
10
11DISTCHECK_CONFIGURE_FLAGS = \
12 --with-udevrulesdir=$$dc_install_base/$(udevrulesdir) \
13 --with-systemdsystemunitdir=$$dc_install_base/$(systemdsystemunitdir) \ No newline at end of file
diff --git a/Modules/CheckConstantExists.cmake b/Modules/CheckConstantExists.cmake
deleted file mode 100644
index 3d6d97e..0000000
--- a/Modules/CheckConstantExists.cmake
+++ /dev/null
@@ -1,38 +0,0 @@
1# - Check if the given constant exists (as an enum, define, or whatever)
2# CHECK_CONSTANT_EXISTS (CONSTANT HEADER VARIABLE)
3#
4# CONSTANT - the name of the constant you are interested in
5# HEADER - the header(s) where the prototype should be declared
6# VARIABLE - variable to store the result
7#
8# The following variables may be set before calling this macro to
9# modify the way the check is run:
10#
11# CMAKE_REQUIRED_FLAGS = string of compile command line flags
12# CMAKE_REQUIRED_DEFINITIONS = list of macros to define (-DFOO=bar)
13# CMAKE_REQUIRED_INCLUDES = list of include directories
14#
15# Example: CHECK_CONSTANT_EXISTS(O_NOFOLLOW fcntl.h HAVE_O_NOFOLLOW)
16
17
18INCLUDE(CheckCSourceCompiles)
19
20MACRO (CHECK_CONSTANT_EXISTS _CONSTANT _HEADER _RESULT)
21 SET(_INCLUDE_FILES)
22 FOREACH (it ${_HEADER})
23 SET(_INCLUDE_FILES "${_INCLUDE_FILES}#include <${it}>\n")
24 ENDFOREACH (it)
25
26 SET(_CHECK_CONSTANT_SOURCE_CODE "
27${_INCLUDE_FILES}
28void cmakeRequireConstant(int dummy,...){(void)dummy;}
29int main()
30{
31 cmakeRequireConstant(0,${_CONSTANT});
32 return 0;
33}
34")
35 CHECK_C_SOURCE_COMPILES("${_CHECK_CONSTANT_SOURCE_CODE}" ${_RESULT})
36
37ENDMACRO (CHECK_CONSTANT_EXISTS)
38
diff --git a/Modules/FindPLIST.cmake b/Modules/FindPLIST.cmake
deleted file mode 100644
index d51aa74..0000000
--- a/Modules/FindPLIST.cmake
+++ /dev/null
@@ -1,31 +0,0 @@
1# - Try to find libplist
2# Once done, this will define
3#
4# PLIST_FOUND - system has libplist
5# PLIST_INCLUDE_DIRS - the libplist include directories
6# PLIST_LIBRARIES - link these to use libplist
7
8include(LibFindMacros)
9
10# Dependencies
11
12# Use pkg-config to get hints about paths
13libfind_pkg_check_modules(PLIST_PKGCONF libplist >= 0.15)
14
15# Include dir
16find_path(PLIST_INCLUDE_DIR
17 NAMES plist/plist.h
18 PATHS ${PLIST_PKGCONF_INCLUDE_DIRS}
19)
20
21# Finally the library itself
22find_library(PLIST_LIBRARY
23 NAMES plist
24 PATHS ${PLIST_PKGCONF_LIBRARY_DIRS}
25)
26
27# Set the include dir variables and the libraries and let libfind_process do the rest.
28# NOTE: Singular variables for this library, plural for libraries that this lib depends on.
29set(PLIST_PROCESS_INCLUDES PLIST_INCLUDE_DIR)
30set(PLIST_PROCESS_LIBS PLIST_LIBRARY)
31libfind_process(PLIST)
diff --git a/Modules/FindUSB.cmake b/Modules/FindUSB.cmake
deleted file mode 100644
index 486864f..0000000
--- a/Modules/FindUSB.cmake
+++ /dev/null
@@ -1,40 +0,0 @@
1# - Try to find libusb-1.0
2# Once done, this will define
3#
4# USB_FOUND - system has libusb-1.0
5# USB_INCLUDE_DIRS - the libusb-1.0 include directories
6# USB_LIBRARIES - link these to use libusb-1.0
7
8include(LibFindMacros)
9
10# Dependencies
11
12# pkg-config + libusb fails on FreeBSD, though libusb is in base
13if(NOT(${CMAKE_SYSTEM_NAME} MATCHES "FreeBSD"))
14 # Use pkg-config to get hints about paths
15 libfind_pkg_check_modules(USB_PKGCONF libusb-1.0>=1.0.3)
16 # We want to look for libusb-1.0
17 set(USB_LIBRARY_NAME usb-1.0)
18else()
19 set(USB_PKGCONF_INCLUDE_DIRS /usr/include)
20 set(USB_PKGCONF_LIBRARY_DIRS /usr/lib)
21 set(USB_LIBRARY_NAME usb)
22endif()
23
24# Include dir
25find_path(USB_INCLUDE_DIR
26 NAMES libusb.h
27 PATHS ${USB_PKGCONF_INCLUDE_DIRS}
28)
29
30# Finally the library itself
31find_library(USB_LIBRARY
32 NAMES ${USB_LIBRARY_NAME}
33 PATHS ${USB_PKGCONF_LIBRARY_DIRS}
34)
35
36# Set the include dir variables and the libraries and let libfind_process do the rest.
37# NOTE: Singular variables for this library, plural for libraries this this lib depends on.
38set(USB_PROCESS_INCLUDES USB_INCLUDE_DIR)
39set(USB_PROCESS_LIBS USB_LIBRARY)
40libfind_process(USB)
diff --git a/Modules/LibFindMacros.cmake b/Modules/LibFindMacros.cmake
deleted file mode 100644
index 795d6b7..0000000
--- a/Modules/LibFindMacros.cmake
+++ /dev/null
@@ -1,99 +0,0 @@
1# Works the same as find_package, but forwards the "REQUIRED" and "QUIET" arguments
2# used for the current package. For this to work, the first parameter must be the
3# prefix of the current package, then the prefix of the new package etc, which are
4# passed to find_package.
5macro (libfind_package PREFIX)
6 set (LIBFIND_PACKAGE_ARGS ${ARGN})
7 if (${PREFIX}_FIND_QUIETLY)
8 set (LIBFIND_PACKAGE_ARGS ${LIBFIND_PACKAGE_ARGS} QUIET)
9 endif (${PREFIX}_FIND_QUIETLY)
10 if (${PREFIX}_FIND_REQUIRED)
11 set (LIBFIND_PACKAGE_ARGS ${LIBFIND_PACKAGE_ARGS} REQUIRED)
12 endif (${PREFIX}_FIND_REQUIRED)
13 find_package(${LIBFIND_PACKAGE_ARGS})
14endmacro (libfind_package)
15
16# Damn CMake developers made the UsePkgConfig system deprecated in the same release (2.6)
17# where they added pkg_check_modules. Consequently I need to support both in my scripts
18# to avoid those deprecated warnings. Here's a helper that does just that.
19# Works identically to pkg_check_modules, except that no checks are needed prior to use.
20macro (libfind_pkg_check_modules PREFIX PKGNAME)
21 if (${CMAKE_MAJOR_VERSION} EQUAL 2 AND ${CMAKE_MINOR_VERSION} EQUAL 4)
22 include(UsePkgConfig)
23 pkgconfig(${PKGNAME} ${PREFIX}_INCLUDE_DIRS ${PREFIX}_LIBRARY_DIRS ${PREFIX}_LDFLAGS ${PREFIX}_CFLAGS)
24 else (${CMAKE_MAJOR_VERSION} EQUAL 2 AND ${CMAKE_MINOR_VERSION} EQUAL 4)
25 find_package(PkgConfig)
26 if (PKG_CONFIG_FOUND)
27 pkg_check_modules(${PREFIX} ${PKGNAME})
28 endif (PKG_CONFIG_FOUND)
29 endif (${CMAKE_MAJOR_VERSION} EQUAL 2 AND ${CMAKE_MINOR_VERSION} EQUAL 4)
30endmacro (libfind_pkg_check_modules)
31
32# Do the final processing once the paths have been detected.
33# If include dirs are needed, ${PREFIX}_PROCESS_INCLUDES should be set to contain
34# all the variables, each of which contain one include directory.
35# Ditto for ${PREFIX}_PROCESS_LIBS and library files.
36# Will set ${PREFIX}_FOUND, ${PREFIX}_INCLUDE_DIRS and ${PREFIX}_LIBRARIES.
37# Also handles errors in case library detection was required, etc.
38macro (libfind_process PREFIX)
39 # Skip processing if already processed during this run
40 if (NOT ${PREFIX}_FOUND)
41 # Start with the assumption that the library was found
42 set (${PREFIX}_FOUND TRUE)
43
44 # Process all includes and set _FOUND to false if any are missing
45 foreach (i ${${PREFIX}_PROCESS_INCLUDES})
46 if (${i})
47 set (${PREFIX}_INCLUDE_DIRS ${${PREFIX}_INCLUDE_DIRS} ${${i}})
48 mark_as_advanced(${i})
49 else (${i})
50 set (${PREFIX}_FOUND FALSE)
51 endif (${i})
52 endforeach (i)
53
54 # Process all libraries and set _FOUND to false if any are missing
55 foreach (i ${${PREFIX}_PROCESS_LIBS})
56 if (${i})
57 set (${PREFIX}_LIBRARIES ${${PREFIX}_LIBRARIES} ${${i}})
58 mark_as_advanced(${i})
59 else (${i})
60 set (${PREFIX}_FOUND FALSE)
61 endif (${i})
62 endforeach (i)
63
64 # Print message and/or exit on fatal error
65 if (${PREFIX}_FOUND)
66 if (NOT ${PREFIX}_FIND_QUIETLY)
67 message (STATUS "Found ${PREFIX} ${${PREFIX}_VERSION}")
68 endif (NOT ${PREFIX}_FIND_QUIETLY)
69 else (${PREFIX}_FOUND)
70 if (${PREFIX}_FIND_REQUIRED)
71 foreach (i ${${PREFIX}_PROCESS_INCLUDES} ${${PREFIX}_PROCESS_LIBS})
72 message("${i}=${${i}}")
73 endforeach (i)
74 message (FATAL_ERROR "Required library ${PREFIX} NOT FOUND.\nInstall the library (dev version) and try again. If the library is already installed, use ccmake to set the missing variables manually.")
75 endif (${PREFIX}_FIND_REQUIRED)
76 endif (${PREFIX}_FOUND)
77 endif (NOT ${PREFIX}_FOUND)
78endmacro (libfind_process)
79
80macro(libfind_library PREFIX basename)
81 set(TMP "")
82 if(MSVC80)
83 set(TMP -vc80)
84 endif(MSVC80)
85 if(MSVC90)
86 set(TMP -vc90)
87 endif(MSVC90)
88 set(${PREFIX}_LIBNAMES ${basename}${TMP})
89 if(${ARGC} GREATER 2)
90 set(${PREFIX}_LIBNAMES ${basename}${TMP}-${ARGV2})
91 string(REGEX REPLACE "\\." "_" TMP ${${PREFIX}_LIBNAMES})
92 set(${PREFIX}_LIBNAMES ${${PREFIX}_LIBNAMES} ${TMP})
93 endif(${ARGC} GREATER 2)
94 find_library(${PREFIX}_LIBRARY
95 NAMES ${${PREFIX}_LIBNAMES}
96 PATHS ${${PREFIX}_PKGCONF_LIBRARY_DIRS}
97 )
98endmacro(libfind_library)
99
diff --git a/Modules/VersionTag.cmake b/Modules/VersionTag.cmake
deleted file mode 100644
index 682ab3e..0000000
--- a/Modules/VersionTag.cmake
+++ /dev/null
@@ -1,13 +0,0 @@
1execute_process(
2 COMMAND "sh" "${CMAKE_SOURCE_DIR}/Modules/describe.sh"
3 WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
4 OUTPUT_VARIABLE DESCRIBE
5 OUTPUT_STRIP_TRAILING_WHITESPACE
6)
7
8if(DESCRIBE STREQUAL "")
9 set (VERSION_TAG "UNKNOWN")
10else()
11 string(REGEX REPLACE "^v" "" VERSION_TAG "${DESCRIBE}")
12endif()
13
diff --git a/Modules/cmake_uninstall.cmake.in b/Modules/cmake_uninstall.cmake.in
deleted file mode 100644
index 4bfb0bf..0000000
--- a/Modules/cmake_uninstall.cmake.in
+++ /dev/null
@@ -1,21 +0,0 @@
1IF(NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt")
2 MESSAGE(FATAL_ERROR "Cannot find install manifest: \"@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt\"")
3ENDIF(NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt")
4
5FILE(READ "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt" files)
6STRING(REGEX REPLACE "\n" ";" files "${files}")
7FOREACH(file ${files})
8 MESSAGE(STATUS "Uninstalling \"$ENV{DESTDIR}${file}\"")
9 IF(EXISTS "$ENV{DESTDIR}${file}")
10 EXEC_PROGRAM(
11 "@CMAKE_COMMAND@" ARGS "-E remove \"$ENV{DESTDIR}${file}\""
12 OUTPUT_VARIABLE rm_out
13 RETURN_VALUE rm_retval
14 )
15 IF(NOT "${rm_retval}" STREQUAL 0)
16 MESSAGE(FATAL_ERROR "Problem when removing \"$ENV{DESTDIR}${file}\"")
17 ENDIF(NOT "${rm_retval}" STREQUAL 0)
18 ELSE(EXISTS "$ENV{DESTDIR}${file}")
19 MESSAGE(STATUS "File \"$ENV{DESTDIR}${file}\" does not exist.")
20 ENDIF(EXISTS "$ENV{DESTDIR}${file}")
21ENDFOREACH(file)
diff --git a/Modules/describe.sh b/Modules/describe.sh
deleted file mode 100755
index 6425ed5..0000000
--- a/Modules/describe.sh
+++ /dev/null
@@ -1,17 +0,0 @@
1#!/bin/bash
2
3# Check for git and a git repo.
4if head=`git rev-parse --verify HEAD 2>/dev/null`; then
5 /bin/echo -n `git describe`
6
7 # Are there uncommitted changes?
8 git update-index --refresh --unmerged > /dev/null
9 git diff-index --quiet HEAD || /bin/echo -n -dirty
10else
11# Check for version tag
12 if [ -e version.tag ]; then
13 /bin/echo -n `cat version.tag`
14 fi
15fi
16
17echo
diff --git a/NEWS b/NEWS
new file mode 100644
index 0000000..b2ba013
--- /dev/null
+++ b/NEWS
@@ -0,0 +1,90 @@
1Version 1.1.1
2~~~~~~~~~~~~~
3
4* Changes:
5 - Make use of libusb hotplug events for device discovery
6 - Use clock_gettime() instead of gettimeofday() to avoid timing issues
7 when calculating packet timeouts
8 - Get correct USB device speed instead of hardcoded value
9 - Bump libusb dependency to 1.0.9
10 - Use non-blocking sockets for client communication to avoid hanging
11 - Use correct manual section (8) for manpage
12 - Log pid of connecting clients if supported
13 - Implement device discovery using libusb hotplug events
14 - Fix wrong timeout value in debug messages
15 - Log error message if writing a config file fails
16 - Fix blocking by using libusb asynchronous I/O for getting initial device
17 information
18 - Tag all udev events with systemd tag
19 - Fix occasional USB reconfiguration due to udev rules being run again
20 - Fix wrong timestamps when running in foreground
21 - Set socket options for client connections to improve performance
22 - Implement "ListListeners" usbmux command handling
23 - Bump libimobiledevice dependency to 1.3.0
24 - Bump libplist dependency to 2.2.0
25 - Add support for iPhone XS/XR UDID format
26 - Add option to allow logging to dedicated logfile
27 - Convert README file to markdown format
28 - Fix USB reconnection issues on virtual machines with iOS 11+ devices
29 - Add support for connecting with T2 chip
30 - Various memory leak, deadlock and invalid free fixes
31 - Show actual libusb version in debug message on startup
32 - Enable libusb debugging output
33 - Log client process name alongside pid if possible on Linux
34 - Unify and improve log message output
35 - Improve README.md with project description, installation, contributing and
36 usage sections
37
38Version 1.1.0
39~~~~~~~~~~~~~
40
41* Changes:
42 - Check for pkg-config availability in autogen.sh
43 - Fix missing "usbmuxd-proto.h" in released tarbal
44 - Fix typo which broke udev support in rule for udev activation mode
45 - Fix broken configure summary on OS X and change activation method
46 to "manual"
47 - Compile bits of udev and systemd support only if needed
48 - Bump autoconf requirement to 2.64
49
50Version 1.0.9
51~~~~~~~~~~~~~
52
53* Changes:
54 - Fix broken handling of "--user" argument value
55 - Add new "--enable-exit" argument to enable handling of "--exit" requests
56 - Add new "--disable-hotplug" argument to disable automatic device discovery
57 - Add new "--systemd" argument to startup with right settings for systemd
58 - Add man page for usbmuxd
59 - Update copyright headers to match best practice
60 - Flush input buffer on connection teardown
61 - Add systemd based activation with fallback for udev and manual activation
62 - Add initial NEWS file and update README with latest information
63 - Rename header guards according to C++ standard
64 - Implement support for usbmux protocol version 2 which improves stability
65 - Fix crash when old libusbmuxd was used as a client
66 - Add "--version" argument to print version number and exit
67 - Update command line usage to match best practice and other tools
68 - Massively improve read performance by using 3 parallel transfers
69 - Bump requirement to libplist 1.11
70 - Remove trailing "\n" from usbmuxd_log() messages
71 - Improve thread safety
72 - Fix crash on 64bit systems in process_send()
73 - Fix possible leak of file descriptors
74 - Implement "ReadBUID", "ReadPairRecord", "SavePairRecord" and
75 "DeletePairRecord" commands
76 - Make plist based protocol support mandatory
77 - Return proper error packet on client version mismatch
78 - Implement "ListDevices" command
79 - Manage pair records in a directory compatible to native usbmuxd
80 - Output USB configuration changes in log
81 - Set "deactivated" USB configuration on iOS device by default as usbmuxd will
82 bring device to "life" on it's own now
83 - Add udev rule installation with autotools
84 - Lower udev rules numbering to make sure to start as early as possible
85 - Split out client library into "libusbmuxd" and depend on "libimobiledevice"
86 - Implement preflight worker to support iOS 7 based trust dialog handling
87 - Install usbmuxd binary to sbindir instead of bindir
88 - Switch from CMake to autotools build system
89
90This is the first release after the usbmuxd/libusbmuxd project split.
diff --git a/README b/README
deleted file mode 100644
index bad7531..0000000
--- a/README
+++ /dev/null
@@ -1,218 +0,0 @@
1Background
2==========
3
4'usbmuxd' stands for "USB multiplexing daemon". This daemon is in charge of
5multiplexing connections over USB to an iPhone or iPod touch. To users, it means
6you can sync your music, contacts, photos, etc. over USB. To developers, it
7means you can connect to any listening localhost socket on the device. usbmuxd
8is not used for tethering data transfer, which uses a dedicated USB interface as
9a virtual network device.
10
11Multiple connections to different TCP ports can happen in parallel. An example
12(and useful) tool called 'iproxy' is included that allows you to forward
13localhost ports to the device---allows SSH over USB on jailbroken devices, or
14allowing access the lockdown daemon (and then to all of the file access, sync,
15notification and backup services running on the device).
16
17The higher-level layers are handled by libimobiledevice. 'ifuse' is then able
18to sit on top of this and mount your device's AFC filesystem share.
19
20There is also a Python implementation of the client library in the python-client
21library, and an example tcprelay.py which performs a similar function to iproxy.
22This implementation supports OSX and Windows and the new iTunes plist-based
23usbmuxd protocol, so it is portable and will run on those operating systems with
24no modification, using Apple's native usbmuxd. This is useful if you need to
25tunnel to your device from another OS in a pinch. Run python tcpclient.py --help
26for usage information.
27
28License
29=======
30
31The contents of this package are licensed under the GNU General Public License,
32versions 2 or 3 (see COPYING.GPLv2 and COPYING.GPLv3), except for libuxbmuxd
33which is licensed under the GNU Lesser General Public License, version 2.1 or,
34at your option, any later version (see COPYING.LGPLv2.1). If a more permissive
35license is specified at the top of a source file, it takes precedence over this.
36
37Legal
38=====
39
40Apple, iPhone, and iPod touch are trademarks of Apple Inc., registered in the
41U.S. and other countries.
42
43Building
44========
45
46 mkdir build
47 cd build
48 cmake ..
49 make
50 sudo make install
51
52You should also create a 'usbmux' user that has access to USB devices on your
53system. Alternatively, you can pass a different username after the -U argument.
54
55Running (with magic)
56====================
57
58 (Unplug + replug your jailbroken device)
59 ./iproxy 2222 22 &
60 ssh -p 2222 root@localhost
61
62Hopefully you get the normal SSH login prompt. You may still lots of debugging
63output for the moment. If this is getting in the way of your ssh login, then
64run the 'ssh' command from a different xterminal or virtual console. Of course,
65you need to have OpenSSH installed on your jailbroken device for this to work.
66
67If you have iFuse, you can run "ifuse <mountpoint">. This doesn't require
68iproxy and works on all devices, jailbroken or not.
69
70Running (without magic)
71=======================
72
73If 'udev' is _not_ automatically running on your machine and picking up the new
74.rules file, you will need to start usbmuxd by hand first. Check it's running
75and that there is only one copy with 'ps aux | grep
76usbmuxd'.
77
78 sudo usbmuxd -U -v -v &
79 ./iproxy 2222 22 &
80 ssh -p 2222 root@localhost
81
82Tip: Starting SSH if disabled
83=============================
84
85If your device is rooted, but SSH isn't started and you _cannot_ (for instance,
86cracked/broken screen) get to the Services control panel on the device, then you
87can start the SSH service over the USB by mounting the (jailbroken) filesystem.
88
89You will need to mount it using 'ifuse --afc2' (to access the root directory of
90the device), and then edit:
91
92 /Library/LaunchDaemons/com.openssh.sshd.plist
93
94to _remove_ the lines:
95
96 <key>Disabled</key>
97 <true/>
98
99Reboot the device and then sshd should be running.
100
101TODO
102====
103
104The server currently assumes that the device is well-behaved and does not do a
105bunch of checks like looking for the expected SEQ and ACK numbers from it. This
106is normally not an issue, but it's annoying for debugging because lost packets
107(which shouldn't happen, but can happen if the code is buggy) mean that stuff
108gets out of sync and then might crash and burn dozens of packets later.
109
110The server needs more testing, and some optimizing.
111
112Someone should probably do some edge-case testing on the TCP stuff.
113
114The outgoing ACK handling on the server probably needs some thought. Currently,
115when there's an outstanding ACK, we send it after a timeout (to avoid sending
116a no-payload ACK packet for everything the phone sends us). However, there's
117probably a better way of doing this.
118
119Architecture information
120========================
121
122The iPhone / iPod Touch basically implements a rather strange USB networking
123system that operates at a higher level. It is of course completely proprietary.
124Generally speaking, this is what goes on in a typical usage scenario:
125
1260. iTunes opens a connection to usbmuxd and asks it for device notifications
1271. User inserts phone into computer
1282. usbmuxd notices the phone and pings it with a version packet
1293. phone replies
1304. usbmuxd now considers the phone to be connected and tells iTunes
1315. iTunes opens another separate connection to usbmuxd and asks it to connect
132 to, say, the afc port on the device
1336. usbmuxd sends a pseudo-TCP SYN packet to the phone
1347. the phone's kernel driver receives the SYN packet and itself opens a
135 TCP connection to localhost on the afc port
1368. the phone replies with a pseudo-TCP SYN/ACK indicating that the port is open
137 and the connection can proceed
1387. usbmuxd sends a final ACK to the phone
1398. usbmuxd replies to iTunes with a "connection successful" message
1409. any data that iTunes writes to the usbmuxd socket from now on is forwarded,
141 through pseudo-TCP, through USB, back into a more regular TCP connection to
142 localhost, to the afc daemon on the phone, and vice versa
143
144The usbmuxd protocol is a relatively simple binary message protocol documented
145here:
146
147http://wikee.iphwn.org/usb:usbmux
148
149Note that once a connection is established the UNIX socket essentially becomes
150a dedicated pipe to the TCP connction and no more high-level control is
151possible (closing the socket closes the TCP connection). Ditto for the "listen
152for devices" mode - usbmuxd will reject any commands in such mode, and the
153socket essentially becomes a dedicated device notification pipe. This means
154that you need, at minimum, TWO connections to usbmuxd to do anything useful.
155
156On Windows, usbmuxd works the same way but a TCP connection to localhost port
15727015 replaces the UNIX socket. On OSX, the UNIX socket is /var/run/usbmuxd. The
158server and client implemented here default the same /var/run/usbmuxd socket.
159
160The phone protocol operates over a pair of USB bulk endpoints. There is an outer
161layer used for packet size info and a "protocol" (version and TCP are the only
162two options), and that header is followed by TCP headers for actual data comms.
163However, the protocol isn't actual TCP, just a custom protocol which for some
164reason uses a standard TCP header and leaves most fields unused.
165
166There is no reordering or retransmission. There are no checksums, no URG, no
167PSH, no non-ACK, no FIN. What there *is* is the SEQ/ACK/window mechanism used
168for flow control, and RST is used as the only connection teardown mechanism (and
169also for "connection refused"), and the connection startup is SYN/SYNACK/ACK.
170
171Windows are constant-scaled by 8 bits. This is legal TCP as long as the
172corresponding option is negotiated. Of course, no such negotiation happens on
173this protocol.
174
175Note that, since there are no retransmissions, there is some overlap between ACK
176and window for flow control. For example, the server doesn't ever touch its
177window size, and just refuses to ACK stuff if its buffers are full and the
178client isn't reading data. The phone happily seems to stop sending stuff.
179
180Also, if the phone RSTs you out of nowhere, look at the packet payload for a
181textual error message. Note: if it claims to suffer from amnesia, that probably
182means you overflowed its input buffer by ignoring its flow control / window
183size. Go figure. Probably a logic bug in the kernel code.
184
185Note that all normal packets need to have flags set to ACK (and only ACK). There
186is no support for, erm, not-acking. Keep the ack number field valid at all
187times.
188
189The usbmuxd CONNECT request port field is byte-swapped (network-endian). This is
190even more annoying for the plist based protocol, since it's even true there
191(where the field is plain text). So even for the plain text int, you need to
192swap the bytes (port 22 becomes <integer>5632</integer>). I have no clue if this
193is the case on the new plist protocol on PPC macs (is the newer iTunes available
194for those?)
195
196There are a bunch of gotchas due to the USB framing, and this is even worse
197because implementations tend to get it wrong (i.e. libusb, and this is the
198reason for the patch). Basically, USB Bulk offers, at the low level, the ability
199to transfer packets from 0 to wMaxPacketSize (512 here) bytes, period. There is
200no other support for higher level framing of transfers. The way you do those is
201by breaking them up into packets, and the final shorter packet marks the end of
202the transfer. The critical bit is that, if the transfer happens to be divisible
203by 512, you send a zero-length packet (ZLP) to indicate the end of the transfer.
204Libusb doesn't set this option by default and the iPhone gets packets stuck to
205each other, which it doesn't like. Actually, this framing is sort of redundant
206because the usbmux packet header includes a length field, but the phone still
207wants the ZLPs or else it breaks. To make matters worse, usbdevfs imposes a max
208transfer size of 16k, so libusb breaks transfers into that size. This is okay
209for sending as long as the ZLP is only added to the last transfer (the patch
210does that), but it can easily cause nasty race conditions on RX due to libusb
211doing multiple outstanding reads at the same time and then cancelling the rest
212when shorter data arrives (but what if some data got into the other requests
213already?), so we only do 16k reads and stick them together ourselves by looking
214at the packet size header. We still depend on ZLPs being sent to end transfers
215at non-16k boundaries that are multiples of 512, but that seems to work fine. I
216guess the ZLPs might cause spurious 0-byte transfers to show up on RX if things
217line up right, but we ignore those. By the way, the maximum packet/transfer size
218is 65535 bytes due to the 16-bit length header of the usbmux protocol.
diff --git a/README.devel b/README.devel
deleted file mode 100644
index 727e095..0000000
--- a/README.devel
+++ /dev/null
@@ -1,50 +0,0 @@
1Background
2==========
3
4'libusbmuxd' makes it really simple to talk to a running 'usbmuxd' and
5hides all the details for you. There are two function calls:
6
7usbmuxd_scan()
8--------------
9
10This returns a list of all available iPhone-like devices that are
11available for talking to. The returned array contains the USB
12product_id, hex formatted serial_number of any iPhones/iTouches and a
13non-descript 'handle' for all those devices that are within range (as
14of March 2009, that means a device directly plugged into the
15computer's USB port).
16
17Once you have found the device you want to communicate with, take its
18'handle' and pass this to usbmuxd_connect().
19
20usbmuxd_connect()
21-----------------
22
23This takes a handle, a destination port number and tries to setup
24a proxy a connection. It returns a file-descriptor which you should
25be able to read(), write() and select() on like any other active network
26socket connection.
27
28
29Technical details
30=================
31
32When usbmuxd is running (normally started, or stopped as a result of
33'udev' auto-insertion messages), it provides a socket interface in
34'/var/run/usbmuxd' that is designed to be compatible with the socket
35interface that is provided on MacOSX.
36
37The structures for communicating over this device are documented
38in the 'usbmuxd-proto.h', but you shouldn't need to view them
39directly if you are using the libusbmuxd.so library for easy access.
40
41
42Example
43=======
44
45#include <usbmuxd.h>
46
47...
48
49gcc -o leetphone leetphone.c -lusbmuxd
50
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..0725e31
--- /dev/null
+++ b/README.md
@@ -0,0 +1,159 @@
1# usbmuxd
2
3*A socket daemon to multiplex connections from and to iOS devices.*
4
5![build](https://github.com/libimobiledevice/usbmuxd/actions/workflows/build.yml/badge.svg)
6
7## Features
8
9usbmuxd stands for "USB multiplexing daemon". This daemon is in charge of
10multiplexing connections over USB to an iOS device.
11
12To users, it means you can use various applications to interact with your
13device.
14
15To developers, it means you can connect to any listening localhost socket on
16the device.
17
18Some key features are:
19
20- **Implementation**: Open-Source implementation of proprietary usbmuxd daemon
21- **Cross-Platform:** Tested on Linux, macOS, Windows and Android platforms
22- **Linux**: Supports udev and systemd for automatic activation
23- **Compatibility**: Supports latest device firmware releases
24- **Scalability**: Supports multiple connections to different ports in parallel
25
26usbmuxd is not used for tethering data transfers which uses a dedicated USB
27interface to act as a virtual network device.
28
29The higher-level layers, especially if you want to write an application to
30interact with the device, are handled by [libimobiledevice](https://github.com/libimobiledevice/libimobiledevice.git).
31
32The low-level layer is handled by [libusbmuxd](https://github.com/libimobiledevice/libusbmuxd.git).
33
34## Installation / Getting started
35
36### Debian / Ubuntu Linux
37
38First install all required dependencies and build tools:
39```shell
40sudo apt-get install \
41 build-essential \
42 pkg-config \
43 checkinstall \
44 git \
45 autoconf \
46 automake \
47 libtool-bin \
48 libplist-dev \
49 libusbmuxd-dev \
50 libimobiledevice-dev \
51 libimobiledevice-glue-dev \
52 libusb-1.0-0-dev \
53 udev
54```
55
56If systemd is not installed and should control spawning the daemon use:
57```shell
58sudo apt-get install \
59 systemd
60```
61
62Then clone the actual project repository:
63```shell
64git clone https://github.com/libimobiledevice/usbmuxd.git
65cd usbmuxd
66```
67
68Now you can build and install it:
69```shell
70./autogen.sh
71make
72sudo make install
73```
74
75If you require a custom prefix or other option being passed to `./configure`
76you can pass them directly to `./autogen.sh` like this:
77```bash
78./autogen.sh --prefix=/opt/local --without-preflight --without-systemd
79make
80sudo make install
81```
82
83To output a list of available configure options use:
84```bash
85./autogen.sh --help
86```
87
88## Usage
89
90The daemon is automatically started by udev or systemd depending on what you
91have configured upon hotplug of an iOS device and exits if the last device
92was unplugged.
93
94When usbmuxd is running it provides a socket interface at `/var/run/usbmuxd`
95that is designed to be compatible with the socket interface that is provided
96on macOS.
97
98You should also create an `usbmux` user that has access to USB devices on your
99system. Alternatively, just pass a different username using the `-U` argument.
100
101The daemon also manages pairing records with iOS devices and the host in
102`/var/lib/lockdown` (Linux) or `/var/db/lockdown` (macOS).
103
104Ensure proper permissions are setup for the daemon to access the directory.
105
106For debugging purposes it is helpful to start usbmuxd using the foreground `-f`
107argument and enable verbose mode `-v` to get suitable logs.
108
109Please consult the usage information or manual page for a full documentation of
110available command line options:
111```shell
112usbmuxd --help
113man usbmuxd
114```
115
116## Contributing
117
118We welcome contributions from anyone and are grateful for every pull request!
119
120If you'd like to contribute, please fork the `master` branch, change, commit and
121send a pull request for review. Once approved it can be merged into the main
122code base.
123
124If you plan to contribute larger changes or a major refactoring, please create a
125ticket first to discuss the idea upfront to ensure less effort for everyone.
126
127Please make sure your contribution adheres to:
128* Try to follow the code style of the project
129* Commit messages should describe the change well without being too short
130* Try to split larger changes into individual commits of a common domain
131* Use your real name and a valid email address for your commits
132
133We are still working on the guidelines so bear with us!
134
135## Links
136
137* Homepage: https://libimobiledevice.org/
138* Repository: https://github.com/libimobiledevice/usbmuxd.git
139* Repository (Mirror): https://git.libimobiledevice.org/usbmuxd.git
140* Issue Tracker: https://github.com/libimobiledevice/usbmuxd/issues
141* Mailing List: https://lists.libimobiledevice.org/mailman/listinfo/libimobiledevice-devel
142* Twitter: https://twitter.com/libimobiledev
143
144## License
145
146This library and utilities are licensed under the [GNU General Public License v3.0](https://www.gnu.org/licenses/gpl-3.0.en.html),
147also included in the repository in the `COPYING.GPLv3` file.
148
149## Credits
150
151The initial usbmuxd daemon implementation was authored by Hector Martin.
152
153Apple, iPhone, iPad, iPod, iPod Touch, Apple TV, Apple Watch, Mac, iOS,
154iPadOS, tvOS, watchOS, and macOS are trademarks of Apple Inc.
155
156usbmuxd is an independent software application and has not been
157authorized, sponsored, or otherwise approved by Apple Inc.
158
159README Updated on: 2024-12-02
diff --git a/autogen.sh b/autogen.sh
new file mode 100755
index 0000000..5936e8a
--- /dev/null
+++ b/autogen.sh
@@ -0,0 +1,31 @@
1#!/bin/sh
2
3olddir=`pwd`
4srcdir=`dirname $0`
5test -z "$srcdir" && srcdir=.
6
7(
8 cd "$srcdir"
9
10 gprefix=`which glibtoolize 2>&1 >/dev/null`
11 if [ $? -eq 0 ]; then
12 glibtoolize --force
13 else
14 libtoolize --force
15 fi
16 aclocal -I m4
17 autoheader
18 automake --add-missing
19 autoconf
20
21 requires_pkgconfig=`which pkg-config 2>&1 >/dev/null`
22 if [ $? -ne 0 ]; then
23 echo "Missing required pkg-config. Please install it on your system and run again."
24 fi
25
26 cd "$olddir"
27)
28
29if [ -z "$NOCONFIGURE" ]; then
30 $srcdir/configure "$@"
31fi
diff --git a/common/utils.c b/common/utils.c
deleted file mode 100644
index db628ce..0000000
--- a/common/utils.c
+++ /dev/null
@@ -1,125 +0,0 @@
1/*
2 usbmuxd - iPhone/iPod Touch USB multiplex server daemon
3
4Copyright (C) 2009 Hector Martin "marcan" <hector@marcansoft.com>
5Copyright (C) 2009 Nikias Bassen <nikias@gmx.li>
6
7This program is free software; you can redistribute it and/or modify
8it under the terms of the GNU General Public License as published by
9the Free Software Foundation, either version 2 or version 3.
10
11This program is distributed in the hope that it will be useful,
12but WITHOUT ANY WARRANTY; without even the implied warranty of
13MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14GNU General Public License for more details.
15
16You should have received a copy of the GNU General Public License
17along with this program; if not, write to the Free Software
18Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19
20*/
21
22#ifdef HAVE_CONFIG_H
23#include <config.h>
24#endif
25
26#include <stdlib.h>
27#include <string.h>
28#include <stdio.h>
29#include "utils.h"
30
31#ifdef USBMUXD_DAEMON
32# include "log.h"
33# define util_error(...) usbmuxd_log(LL_ERROR, __VA_ARGS__)
34#else
35# define util_error(...) fprintf(stderr, __VA_ARGS__)
36#endif
37
38#ifdef USBMUXD_DAEMON
39void fdlist_create(struct fdlist *list)
40{
41 list->count = 0;
42 list->capacity = 4;
43 list->owners = malloc(sizeof(*list->owners) * list->capacity);
44 list->fds = malloc(sizeof(*list->fds) * list->capacity);
45}
46void fdlist_add(struct fdlist *list, enum fdowner owner, int fd, short events)
47{
48 if(list->count == list->capacity) {
49 list->capacity *= 2;
50 list->owners = realloc(list->owners, sizeof(*list->owners) * list->capacity);
51 list->fds = realloc(list->fds, sizeof(*list->fds) * list->capacity);
52 }
53 list->owners[list->count] = owner;
54 list->fds[list->count].fd = fd;
55 list->fds[list->count].events = events;
56 list->fds[list->count].revents = 0;
57 list->count++;
58}
59
60void fdlist_free(struct fdlist *list)
61{
62 list->count = 0;
63 list->capacity = 0;
64 free(list->owners);
65 list->owners = NULL;
66 free(list->fds);
67 list->fds = NULL;
68}
69
70void fdlist_reset(struct fdlist *list)
71{
72 list->count = 0;
73}
74#endif
75
76void collection_init(struct collection *col)
77{
78 col->list = malloc(sizeof(void *));
79 memset(col->list, 0, sizeof(void *));
80 col->capacity = 1;
81}
82
83void collection_free(struct collection *col)
84{
85 free(col->list);
86 col->list = NULL;
87 col->capacity = 0;
88}
89
90void collection_add(struct collection *col, void *element)
91{
92 int i;
93 for(i=0; i<col->capacity; i++) {
94 if(!col->list[i]) {
95 col->list[i] = element;
96 return;
97 }
98 }
99 col->list = realloc(col->list, sizeof(void*) * col->capacity * 2);
100 memset(&col->list[col->capacity], 0, sizeof(void *) * col->capacity);
101 col->list[col->capacity] = element;
102 col->capacity *= 2;
103}
104
105void collection_remove(struct collection *col, void *element)
106{
107 int i;
108 for(i=0; i<col->capacity; i++) {
109 if(col->list[i] == element) {
110 col->list[i] = NULL;
111 return;
112 }
113 }
114 util_error("collection_remove: element %p not present in collection %p (cap %d)", element, col, col->capacity);
115}
116
117int collection_count(struct collection *col)
118{
119 int i, cnt = 0;
120 for(i=0; i<col->capacity; i++) {
121 if(col->list[i])
122 cnt++;
123 }
124 return cnt;
125}
diff --git a/common/utils.h b/common/utils.h
deleted file mode 100644
index ee79e48..0000000
--- a/common/utils.h
+++ /dev/null
@@ -1,69 +0,0 @@
1/*
2 usbmuxd - iPhone/iPod Touch USB multiplex server daemon
3
4Copyright (C) 2009 Hector Martin "marcan" <hector@marcansoft.com>
5Copyright (C) 2009 Nikias Bassen <nikias@gmx.li>
6
7This program is free software; you can redistribute it and/or modify
8it under the terms of the GNU General Public License as published by
9the Free Software Foundation, either version 2 or version 3.
10
11This program is distributed in the hope that it will be useful,
12but WITHOUT ANY WARRANTY; without even the implied warranty of
13MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14GNU General Public License for more details.
15
16You should have received a copy of the GNU General Public License
17along with this program; if not, write to the Free Software
18Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19
20*/
21
22#ifndef __UTILS_H__
23#define __UTILS_H__
24
25#ifdef USBMUXD_DAEMON
26#include <poll.h>
27
28enum fdowner {
29 FD_LISTEN,
30 FD_CLIENT,
31 FD_USB
32};
33
34struct fdlist {
35 int count;
36 int capacity;
37 enum fdowner *owners;
38 struct pollfd *fds;
39};
40
41void fdlist_create(struct fdlist *list);
42void fdlist_add(struct fdlist *list, enum fdowner owner, int fd, short events);
43void fdlist_free(struct fdlist *list);
44void fdlist_reset(struct fdlist *list);
45#endif
46
47struct collection {
48 void **list;
49 int capacity;
50};
51
52void collection_init(struct collection *col);
53void collection_add(struct collection *col, void *element);
54void collection_remove(struct collection *col, void *element);
55int collection_count(struct collection *col);
56void collection_free(struct collection *col);
57
58#define FOREACH(var, col) \
59 do { \
60 int _iter; \
61 for(_iter=0; _iter<(col)->capacity; _iter++) { \
62 if(!(col)->list[_iter]) continue; \
63 var = (col)->list[_iter];
64
65#define ENDFOREACH \
66 } \
67 } while(0);
68
69#endif
diff --git a/configure.ac b/configure.ac
new file mode 100644
index 0000000..3bf88ef
--- /dev/null
+++ b/configure.ac
@@ -0,0 +1,195 @@
1# -*- Autoconf -*-
2# Process this file with autoconf to produce a configure script.
3
4AC_PREREQ([2.68])
5AC_INIT([usbmuxd], [m4_esyscmd(./git-version-gen $RELEASE_VERSION)], [https://github.com/libimobiledevice/usbmuxd/issues], [], [https://libimobiledevice.org])
6AM_INIT_AUTOMAKE([dist-bzip2 no-dist-gzip check-news])
7m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES])
8AC_CONFIG_SRCDIR([src/])
9AC_CONFIG_HEADERS([config.h])
10AC_CONFIG_MACRO_DIR([m4])
11
12# Check if we have a version defined
13if test -z $PACKAGE_VERSION; then
14 AC_MSG_ERROR([PACKAGE_VERSION is not defined. Make sure to configure a source tree checked out from git or that .tarball-version is present.])
15fi
16
17# Checks for programs.
18AC_PROG_CC
19AM_PROG_CC_C_O
20LT_INIT
21
22# Checks for libraries.
23PKG_CHECK_MODULES(libusb, libusb-1.0 >= 1.0.9)
24PKG_CHECK_MODULES(libplist, libplist-2.0 >= 2.6.0)
25PKG_CHECK_MODULES(libimobiledevice, libimobiledevice-1.0 >= 1.3.0, have_limd=yes, have_limd=no)
26PKG_CHECK_MODULES(limd_glue, libimobiledevice-glue-1.0 >= 1.0.0)
27
28AC_ARG_WITH([preflight],
29 [AS_HELP_STRING([--without-preflight],
30 [do not build with preflight worker support @<:@default=yes@:>@])],
31 [with_preflight=$withval],
32 [with_preflight=yes])
33
34if test "x$have_limd" = "xyes"; then
35 if test "x$with_preflight" != "xyes"; then
36 have_limd=no
37 echo "*** Note: preflight worker support has been disabled ***"
38 else
39 AC_DEFINE(HAVE_LIBIMOBILEDEVICE, 1, [Define if you have libimobiledevice support])
40 AC_SUBST(libimobiledevice_CFLAGS)
41 AC_SUBST(libimobiledevice_LIBS)
42 CACHED_CFLAGS="$CFLAGS"
43 CFLAGS="$CFLAGS $libimobiledevice_CFLAGS"
44 AC_CACHE_CHECK(for enum idevice_connection_type, ac_cv_enum_idevice_connection_type,
45 AC_COMPILE_IFELSE([AC_LANG_PROGRAM([
46 #include <libimobiledevice/libimobiledevice.h>
47 ], [
48 enum idevice_connection_type conn_type = CONNECTION_USBMUXD;
49 ])], ac_cv_enum_idevice_connection_type=yes, ac_cv_enum_idevice_connection_type=no))
50 CFLAGS="$CACHED_CFLAGS"
51 if (test "$ac_cv_enum_idevice_connection_type" = "yes"); then
52 AC_DEFINE(HAVE_ENUM_IDEVICE_CONNECTION_TYPE, 1, [Define if enum idevice_connection_type is available])
53 fi
54 fi
55else
56 if test "x$with_preflight" == "xyes"; then
57 AC_MSG_ERROR([preflight worker support requested but libimobiledevice could not be found])
58 fi
59fi
60
61AC_ARG_WITH([udevrulesdir],
62 AS_HELP_STRING([--with-udevrulesdir=DIR],
63 [Directory for udev rules]),
64 [],
65 [with_udevrulesdir=auto])
66if test "x$with_udevrulesdir" = "xauto"; then
67 udevdir=$($PKG_CONFIG --variable=udevdir udev)
68 if test "x$udevdir" != "x"; then
69 with_udevrulesdir=$udevdir"/rules.d"
70 else
71 with_udevrulesdir=$prefix/lib/udev/rules.d
72 fi
73fi
74
75AC_ARG_WITH([systemd],
76 [AS_HELP_STRING([--without-systemd],
77 [do not build with systemd support @<:@default=yes@:>@])],
78 [with_systemd=$withval],
79 [with_systemd=yes])
80
81AC_ARG_WITH([systemdsystemunitdir],
82 [AS_HELP_STRING([--with-systemdsystemunitdir=DIR],
83 [directory for systemd service files])],
84 [with_systemdsystemunitdir=$withval],
85 [with_systemdsystemunitdir=$($PKG_CONFIG --variable=systemdsystemunitdir systemd)])
86
87if test "x$with_systemdsystemunitdir" != xno; then
88 AC_SUBST([systemdsystemunitdir], [$with_systemdsystemunitdir])
89fi
90AM_CONDITIONAL(WANT_SYSTEMD, [test -n "$with_systemdsystemunitdir" -a "x$with_systemdsystemunitdir" != xno -a "x$with_systemd" = "xyes" ])
91
92if test "x$with_systemd" = xyes; then
93 udev_activation_rule="ENV{SYSTEMD_WANTS}=\"usbmuxd.service\""
94else
95 udev_activation_rule="RUN+=\"@sbindir@/usbmuxd --user usbmux --udev\""
96fi
97AC_SUBST(udev_activation_rule)
98
99# Checks for header files.
100AC_CHECK_HEADERS([stdint.h stdlib.h string.h])
101
102# Checks for typedefs, structures, and compiler characteristics.
103AC_C_CONST
104AC_TYPE_SIZE_T
105AC_TYPE_SSIZE_T
106AC_TYPE_UINT16_T
107AC_TYPE_UINT32_T
108AC_TYPE_UINT8_T
109
110# Check if clock_gettime requires -lrt (old GNU systems)
111AC_SEARCH_LIBS([clock_gettime],[rt posix4])
112
113# Checks for library functions.
114AC_CHECK_FUNCS([strcasecmp strdup strerror strndup malloc realloc])
115AC_CHECK_FUNCS([ppoll clock_gettime localtime_r])
116
117# Check for operating system
118AC_MSG_CHECKING([whether to enable WIN32 build settings])
119UDEV_SUB=
120SYSTEMD_SUB=
121case ${host_os} in
122 *mingw32*|*cygwin*)
123 win32=true
124 AC_MSG_RESULT([yes])
125 AC_CHECK_TOOL([WINDRES], [windres], AC_MSG_ERROR([windres not found]))
126 AC_SUBST(WINDRES)
127 activation_method="manual"
128 ;;
129 darwin*)
130 win32=false
131 AC_MSG_RESULT([no])
132 # No support for launchd yet
133 activation_method="manual"
134 ;;
135 *)
136 win32=false
137 AC_MSG_RESULT([no])
138 UDEV_SUB=udev
139 AC_SUBST([udevrulesdir], [$with_udevrulesdir])
140 AC_DEFINE(HAVE_UDEV, 1, [Define to enable udev support])
141 activation_method="systemd"
142 if test "x$with_systemd" != "xyes"; then
143 echo "*** Note: support for systemd activation has been disabled, using udev activation instead ***"
144 activation_method="udev"
145 else
146 AC_DEFINE(HAVE_SYSTEMD, 1, [Define to enable systemd support])
147 SYSTEMD_SUB=systemd
148 fi
149 ;;
150esac
151AM_CONDITIONAL(WIN32, test x$win32 = xtrue)
152
153AC_SUBST([UDEV_SUB])
154AC_SUBST([SYSTEMD_SUB])
155
156AS_COMPILER_FLAGS(GLOBAL_CFLAGS, "-Wall -Wextra -Wmissing-declarations -Wredundant-decls -Wshadow -Wpointer-arith -Wwrite-strings -Wswitch-default -Wno-unused-parameter")
157AC_SUBST(GLOBAL_CFLAGS)
158
159m4_ifdef([AM_SILENT_RULES],[AM_SILENT_RULES([yes])])
160
161# workaround for older autoconf versions
162if test "x$runstatedir" == "x"; then
163 runstatedir=$localstatedir/run
164fi
165
166AC_CONFIG_FILES([
167Makefile
168src/Makefile
169udev/Makefile
170systemd/Makefile
171docs/Makefile
172])
173AC_OUTPUT
174
175echo "
176Configuration for $PACKAGE $VERSION:
177-------------------------------------------
178
179 install prefix ............: $prefix
180 preflight worker support ..: $have_limd
181 activation method .........: $activation_method"
182
183if test "x$activation_method" = "xsystemd"; then
184 echo " systemd unit directory ....: ${systemdsystemunitdir}"
185fi
186
187if test -n "$udevrulesdir"; then
188 echo " udev rules directory ......: ${udevrulesdir}"
189fi
190
191echo " compiler ..................: ${CC}
192
193 Now type 'make' to build $PACKAGE $VERSION,
194 and then 'make install' for installation.
195"
diff --git a/daemon/CMakeLists.txt b/daemon/CMakeLists.txt
deleted file mode 100644
index c323f7b..0000000
--- a/daemon/CMakeLists.txt
+++ /dev/null
@@ -1,23 +0,0 @@
1find_package(USB REQUIRED)
2include_directories(${USB_INCLUDE_DIRS})
3include_directories(${OPT_INCLUDES})
4set(LIBS ${LIBS} ${USB_LIBRARIES} ${OPT_LIBS})
5if(HAVE_PLIST)
6 add_definitions("-DHAVE_PLIST")
7 message("-- usbmuxd will be built with protocol version 1 support")
8endif()
9include_directories (${CMAKE_SOURCE_DIR}/common)
10include_directories (${CMAKE_SOURCE_DIR}/daemon)
11include_directories (${CMAKE_SOURCE_DIR}/libusbmuxd)
12
13add_definitions(-DUSBMUXD_DAEMON -DUSBMUXD_VERSION="${USBMUXD_VERSION}")
14add_executable(usbmuxd main.c usb-linux.c log.c ${CMAKE_SOURCE_DIR}/common/utils.c device.c client.c)
15target_link_libraries(usbmuxd ${LIBS})
16
17install(TARGETS usbmuxd RUNTIME DESTINATION sbin)
18
19message("
20* REMINDER
21* Remember to add a user named 'usbmux' with USB access permissions
22* for the udev hotplugging feature to work out of the box.
23")
diff --git a/daemon/client.c b/daemon/client.c
deleted file mode 100644
index ac1045a..0000000
--- a/daemon/client.c
+++ /dev/null
@@ -1,616 +0,0 @@
1/*
2 usbmuxd - iPhone/iPod Touch USB multiplex server daemon
3
4Copyright (C) 2009 Hector Martin "marcan" <hector@marcansoft.com>
5Copyright (C) 2009 Nikias Bassen <nikias@gmx.li>
6
7This program is free software; you can redistribute it and/or modify
8it under the terms of the GNU General Public License as published by
9the Free Software Foundation, either version 2 or version 3.
10
11This program is distributed in the hope that it will be useful,
12but WITHOUT ANY WARRANTY; without even the implied warranty of
13MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14GNU General Public License for more details.
15
16You should have received a copy of the GNU General Public License
17along with this program; if not, write to the Free Software
18Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19
20*/
21
22#ifdef HAVE_CONFIG_H
23#include <config.h>
24#endif
25
26#include <stdlib.h>
27#include <string.h>
28#include <errno.h>
29#include <unistd.h>
30#include <sys/types.h>
31#include <sys/socket.h>
32#include <sys/un.h>
33#include <arpa/inet.h>
34
35#ifdef HAVE_PLIST
36#include <plist/plist.h>
37#endif
38
39#include "log.h"
40#include "usb.h"
41#include "client.h"
42#include "device.h"
43
44#ifdef HAVE_PLIST
45#define CMD_BUF_SIZE 1024
46#else
47#define CMD_BUF_SIZE 256
48#endif
49#define REPLY_BUF_SIZE 1024
50
51enum client_state {
52 CLIENT_COMMAND, // waiting for command
53 CLIENT_LISTEN, // listening for devices
54 CLIENT_CONNECTING1, // issued connection request
55 CLIENT_CONNECTING2, // connection established, but waiting for response message to get sent
56 CLIENT_CONNECTED, // connected
57 CLIENT_DEAD
58};
59
60struct mux_client {
61 int fd;
62 unsigned char *ob_buf;
63 uint32_t ob_size;
64 uint32_t ob_capacity;
65 unsigned char *ib_buf;
66 uint32_t ib_size;
67 uint32_t ib_capacity;
68 short events, devents;
69 uint32_t connect_tag;
70 int connect_device;
71 enum client_state state;
72 uint32_t proto_version;
73};
74
75static struct collection client_list;
76
77int client_read(struct mux_client *client, void *buffer, uint32_t len)
78{
79 usbmuxd_log(LL_SPEW, "client_read fd %d buf %p len %d", client->fd, buffer, len);
80 if(client->state != CLIENT_CONNECTED) {
81 usbmuxd_log(LL_ERROR, "Attempted to read from client %d not in CONNECTED state", client->fd);
82 return -1;
83 }
84 return recv(client->fd, buffer, len, 0);
85}
86
87int client_write(struct mux_client *client, void *buffer, uint32_t len)
88{
89 usbmuxd_log(LL_SPEW, "client_write fd %d buf %p len %d", client->fd, buffer, len);
90 if(client->state != CLIENT_CONNECTED) {
91 usbmuxd_log(LL_ERROR, "Attempted to write to client %d not in CONNECTED state", client->fd);
92 return -1;
93 }
94 return send(client->fd, buffer, len, 0);
95}
96
97int client_set_events(struct mux_client *client, short events)
98{
99 if((client->state != CLIENT_CONNECTED) && (client->state != CLIENT_CONNECTING2)) {
100 usbmuxd_log(LL_ERROR, "client_set_events to client %d not in CONNECTED state", client->fd);
101 return -1;
102 }
103 client->devents = events;
104 if(client->state == CLIENT_CONNECTED)
105 client->events = events;
106 return 0;
107}
108
109int client_accept(int listenfd)
110{
111 struct sockaddr_un addr;
112 int cfd;
113 socklen_t len = sizeof(struct sockaddr_un);
114 cfd = accept(listenfd, (struct sockaddr *)&addr, &len);
115 if (cfd < 0) {
116 usbmuxd_log(LL_ERROR, "accept() failed (%s)", strerror(errno));
117 return cfd;
118 }
119
120 struct mux_client *client;
121 client = malloc(sizeof(struct mux_client));
122 memset(client, 0, sizeof(struct mux_client));
123
124 client->fd = cfd;
125 client->ob_buf = malloc(REPLY_BUF_SIZE);
126 client->ob_size = 0;
127 client->ob_capacity = REPLY_BUF_SIZE;
128 client->ib_buf = malloc(CMD_BUF_SIZE);
129 client->ib_size = 0;
130 client->ib_capacity = CMD_BUF_SIZE;
131 client->state = CLIENT_COMMAND;
132 client->events = POLLIN;
133
134 collection_add(&client_list, client);
135
136 usbmuxd_log(LL_INFO, "New client on fd %d", client->fd);
137 return client->fd;
138}
139
140void client_close(struct mux_client *client)
141{
142 usbmuxd_log(LL_INFO, "Disconnecting client fd %d", client->fd);
143 if(client->state == CLIENT_CONNECTING1 || client->state == CLIENT_CONNECTING2) {
144 usbmuxd_log(LL_INFO, "Client died mid-connect, aborting device %d connection", client->connect_device);
145 client->state = CLIENT_DEAD;
146 device_abort_connect(client->connect_device, client);
147 }
148 close(client->fd);
149 if(client->ob_buf)
150 free(client->ob_buf);
151 if(client->ib_buf)
152 free(client->ib_buf);
153 collection_remove(&client_list, client);
154 free(client);
155}
156
157void client_get_fds(struct fdlist *list)
158{
159 FOREACH(struct mux_client *client, &client_list) {
160 fdlist_add(list, FD_CLIENT, client->fd, client->events);
161 } ENDFOREACH
162}
163
164static int send_pkt(struct mux_client *client, uint32_t tag, enum usbmuxd_msgtype msg, void *payload, int payload_length)
165{
166 struct usbmuxd_header hdr;
167 hdr.version = client->proto_version;
168 hdr.length = sizeof(hdr) + payload_length;
169 hdr.message = msg;
170 hdr.tag = tag;
171 usbmuxd_log(LL_DEBUG, "send_pkt fd %d tag %d msg %d payload_length %d", client->fd, tag, msg, payload_length);
172 if((client->ob_capacity - client->ob_size) < hdr.length) {
173 usbmuxd_log(LL_ERROR, "Client %d output buffer full (%d bytes) while sending message %d (%d bytes)", client->fd, client->ob_capacity, hdr.message, hdr.length);
174 client_close(client);
175 return -1;
176 }
177 memcpy(client->ob_buf + client->ob_size, &hdr, sizeof(hdr));
178 if(payload && payload_length)
179 memcpy(client->ob_buf + client->ob_size + sizeof(hdr), payload, payload_length);
180 client->ob_size += hdr.length;
181 client->events |= POLLOUT;
182 return hdr.length;
183}
184
185static int send_result(struct mux_client *client, uint32_t tag, uint32_t result)
186{
187 int res = -1;
188#ifdef HAVE_PLIST
189 if (client->proto_version == 1) {
190 /* XML plist packet */
191 char *xml = NULL;
192 uint32_t xmlsize = 0;
193 plist_t dict = plist_new_dict();
194 plist_dict_insert_item(dict, "MessageType", plist_new_string("Result"));
195 plist_dict_insert_item(dict, "Number", plist_new_uint(result));
196 plist_to_xml(dict, &xml, &xmlsize);
197 plist_free(dict);
198 if (xml) {
199 res = send_pkt(client, tag, MESSAGE_PLIST, xml, xmlsize);
200 free(xml);
201 } else {
202 usbmuxd_log(LL_ERROR, "%s: Could not convert plist to xml", __func__);
203 }
204 } else
205#endif
206 {
207 /* binary packet */
208 res = send_pkt(client, tag, MESSAGE_RESULT, &result, sizeof(uint32_t));
209 }
210 return res;
211}
212
213int client_notify_connect(struct mux_client *client, enum usbmuxd_result result)
214{
215 usbmuxd_log(LL_SPEW, "client_notify_connect fd %d result %d", client->fd, result);
216 if(client->state == CLIENT_DEAD)
217 return -1;
218 if(client->state != CLIENT_CONNECTING1) {
219 usbmuxd_log(LL_ERROR, "client_notify_connect when client %d is not in CONNECTING1 state", client->fd);
220 return -1;
221 }
222 if(send_result(client, client->connect_tag, result) < 0)
223 return -1;
224 if(result == RESULT_OK) {
225 client->state = CLIENT_CONNECTING2;
226 client->events = POLLOUT; // wait for the result packet to go through
227 // no longer need this
228 free(client->ib_buf);
229 client->ib_buf = NULL;
230 } else {
231 client->state = CLIENT_COMMAND;
232 }
233 return 0;
234}
235
236static int notify_device_add(struct mux_client *client, struct device_info *dev)
237{
238 int res = -1;
239#ifdef HAVE_PLIST
240 if (client->proto_version == 1) {
241 /* XML plist packet */
242 char *xml = NULL;
243 uint32_t xmlsize = 0;
244 plist_t dict = plist_new_dict();
245 plist_dict_insert_item(dict, "MessageType", plist_new_string("Attached"));
246 plist_t props = plist_new_dict();
247 // TODO: get current usb speed
248 plist_dict_insert_item(props, "ConnectionSpeed", plist_new_uint(480000000));
249 plist_dict_insert_item(props, "ConnectionType", plist_new_string("USB"));
250 plist_dict_insert_item(props, "DeviceID", plist_new_uint(dev->id));
251 plist_dict_insert_item(props, "LocationID", plist_new_uint(dev->location));
252 plist_dict_insert_item(props, "ProductID", plist_new_uint(dev->pid));
253 plist_dict_insert_item(props, "SerialNumber", plist_new_string(dev->serial));
254 plist_dict_insert_item(dict, "Properties", props);
255 plist_to_xml(dict, &xml, &xmlsize);
256 plist_free(dict);
257 if (xml) {
258 res = send_pkt(client, 0, MESSAGE_PLIST, xml, xmlsize);
259 free(xml);
260 } else {
261 usbmuxd_log(LL_ERROR, "%s: Could not convert plist to xml", __func__);
262 }
263 } else
264#endif
265 {
266 /* binary packet */
267 struct usbmuxd_device_record dmsg;
268 memset(&dmsg, 0, sizeof(dmsg));
269 dmsg.device_id = dev->id;
270 strncpy(dmsg.serial_number, dev->serial, 256);
271 dmsg.serial_number[255] = 0;
272 dmsg.location = dev->location;
273 dmsg.product_id = dev->pid;
274 res = send_pkt(client, 0, MESSAGE_DEVICE_ADD, &dmsg, sizeof(dmsg));
275 }
276 return res;
277}
278
279static int notify_device_remove(struct mux_client *client, uint32_t device_id)
280{
281 int res = -1;
282#ifdef HAVE_PLIST
283 if (client->proto_version == 1) {
284 /* XML plist packet */
285 char *xml = NULL;
286 uint32_t xmlsize = 0;
287 plist_t dict = plist_new_dict();
288 plist_dict_insert_item(dict, "MessageType", plist_new_string("Detached"));
289 plist_dict_insert_item(dict, "DeviceID", plist_new_uint(device_id));
290 plist_to_xml(dict, &xml, &xmlsize);
291 plist_free(dict);
292 if (xml) {
293 res = send_pkt(client, 0, MESSAGE_PLIST, xml, xmlsize);
294 free(xml);
295 } else {
296 usbmuxd_log(LL_ERROR, "%s: Could not convert plist to xml", __func__);
297 }
298 } else
299#endif
300 {
301 /* binary packet */
302 res = send_pkt(client, 0, MESSAGE_DEVICE_REMOVE, &device_id, sizeof(uint32_t));
303 }
304 return res;
305}
306
307static int start_listen(struct mux_client *client)
308{
309 struct device_info *devs;
310 struct device_info *dev;
311 int count, i;
312
313 client->state = CLIENT_LISTEN;
314 count = device_get_count();
315 if(!count)
316 return 0;
317 devs = malloc(sizeof(struct device_info) * count);
318 count = device_get_list(devs);
319
320 // going to need a larger buffer for many devices
321 int needed_buffer = count * (sizeof(struct usbmuxd_device_record) + sizeof(struct usbmuxd_header)) + REPLY_BUF_SIZE;
322 if(client->ob_capacity < needed_buffer) {
323 usbmuxd_log(LL_DEBUG, "Enlarging client %d reply buffer %d -> %d to make space for device notifications", client->fd, client->ob_capacity, needed_buffer);
324 client->ob_buf = realloc(client->ob_buf, needed_buffer);
325 client->ob_capacity = needed_buffer;
326 }
327 dev = devs;
328 for(i=0; i<count; i++) {
329 if(notify_device_add(client, dev++) < 0) {
330 free(devs);
331 return -1;
332 }
333 }
334 free(devs);
335 return count;
336}
337
338static int client_command(struct mux_client *client, struct usbmuxd_header *hdr)
339{
340 int res;
341 usbmuxd_log(LL_DEBUG, "Client command in fd %d len %d ver %d msg %d tag %d", client->fd, hdr->length, hdr->version, hdr->message, hdr->tag);
342
343 if(client->state != CLIENT_COMMAND) {
344 usbmuxd_log(LL_ERROR, "Client %d command received in the wrong state", client->fd);
345 if(send_result(client, hdr->tag, RESULT_BADCOMMAND) < 0)
346 return -1;
347 client_close(client);
348 return -1;
349 }
350
351 struct usbmuxd_connect_request *ch;
352#ifdef HAVE_PLIST
353 char *payload;
354 uint32_t payload_size;
355#endif
356
357 switch(hdr->message) {
358#ifdef HAVE_PLIST
359 case MESSAGE_PLIST:
360 client->proto_version = 1;
361 payload = (char*)(hdr) + sizeof(struct usbmuxd_header);
362 payload_size = hdr->length - sizeof(struct usbmuxd_header);
363 plist_t dict = NULL;
364 plist_from_xml(payload, payload_size, &dict);
365 if (!dict) {
366 usbmuxd_log(LL_ERROR, "Could not parse plist from payload!");
367 return -1;
368 } else {
369 char *message = NULL;
370 plist_t node = plist_dict_get_item(dict, "MessageType");
371 plist_get_string_val(node, &message);
372 if (!message) {
373 usbmuxd_log(LL_ERROR, "Could not extract MessageType from plist!");
374 plist_free(dict);
375 return -1;
376 }
377 if (!strcmp(message, "Listen")) {
378 free(message);
379 plist_free(dict);
380 if (send_result(client, hdr->tag, 0) < 0)
381 return -1;
382 usbmuxd_log(LL_DEBUG, "Client %d now LISTENING", client->fd);
383 return start_listen(client);
384 } else if (!strcmp(message, "Connect")) {
385 uint64_t val;
386 uint16_t portnum = 0;
387 uint32_t device_id = 0;
388 free(message);
389 // get device id
390 node = plist_dict_get_item(dict, "DeviceID");
391 if (!node) {
392 usbmuxd_log(LL_ERROR, "Received connect request without device_id!");
393 plist_free(dict);
394 if (send_result(client, hdr->tag, RESULT_BADDEV) < 0)
395 return -1;
396 return 0;
397 }
398 val = 0;
399 plist_get_uint_val(node, &val);
400 device_id = (uint32_t)val;
401
402 // get port number
403 node = plist_dict_get_item(dict, "PortNumber");
404 if (!node) {
405 usbmuxd_log(LL_ERROR, "Received connect request without port number!");
406 plist_free(dict);
407 if (send_result(client, hdr->tag, RESULT_BADCOMMAND) < 0)
408 return -1;
409 return 0;
410 }
411 val = 0;
412 plist_get_uint_val(node, &val);
413 portnum = (uint16_t)val;
414
415 usbmuxd_log(LL_DEBUG, "Client %d connection request to device %d port %d", client->fd, device_id, ntohs(portnum));
416 res = device_start_connect(device_id, ntohs(portnum), client);
417 if(res < 0) {
418 if (send_result(client, hdr->tag, -res) < 0)
419 return -1;
420 } else {
421 client->connect_tag = hdr->tag;
422 client->connect_device = device_id;
423 client->state = CLIENT_CONNECTING1;
424 }
425 return 0;
426 } else {
427 usbmuxd_log(LL_ERROR, "Unexpected command '%s' received!", message);
428 free(message);
429 plist_free(dict);
430 if (send_result(client, hdr->tag, RESULT_BADCOMMAND) < 0)
431 return -1;
432 return 0;
433 }
434 }
435 // should not be reached?!
436 return -1;
437#endif
438 case MESSAGE_LISTEN:
439 if(send_result(client, hdr->tag, 0) < 0)
440 return -1;
441 usbmuxd_log(LL_DEBUG, "Client %d now LISTENING", client->fd);
442 return start_listen(client);
443 case MESSAGE_CONNECT:
444 ch = (void*)hdr;
445 usbmuxd_log(LL_DEBUG, "Client %d connection request to device %d port %d", client->fd, ch->device_id, ntohs(ch->port));
446 res = device_start_connect(ch->device_id, ntohs(ch->port), client);
447 if(res < 0) {
448 if(send_result(client, hdr->tag, -res) < 0)
449 return -1;
450 } else {
451 client->connect_tag = hdr->tag;
452 client->connect_device = ch->device_id;
453 client->state = CLIENT_CONNECTING1;
454 }
455 return 0;
456 default:
457 usbmuxd_log(LL_ERROR, "Client %d invalid command %d", client->fd, hdr->message);
458 if(send_result(client, hdr->tag, RESULT_BADCOMMAND) < 0)
459 return -1;
460 return 0;
461 }
462 return -1;
463}
464
465static void process_send(struct mux_client *client)
466{
467 int res;
468 if(!client->ob_size) {
469 usbmuxd_log(LL_WARNING, "Client %d OUT process but nothing to send?", client->fd);
470 client->events &= ~POLLOUT;
471 return;
472 }
473 res = send(client->fd, client->ob_buf, client->ob_size, 0);
474 if(res <= 0) {
475 usbmuxd_log(LL_ERROR, "Send to client fd %d failed: %d %s", client->fd, res, strerror(errno));
476 client_close(client);
477 return;
478 }
479 if(res == client->ob_size) {
480 client->ob_size = 0;
481 client->events &= ~POLLOUT;
482 if(client->state == CLIENT_CONNECTING2) {
483 usbmuxd_log(LL_DEBUG, "Client %d switching to CONNECTED state", client->fd);
484 client->state = CLIENT_CONNECTED;
485 client->events = client->devents;
486 // no longer need this
487 free(client->ob_buf);
488 client->ob_buf = NULL;
489 }
490 } else {
491 client->ob_size -= res;
492 memmove(client->ob_buf, client->ob_buf + res, client->ob_size);
493 }
494}
495static void process_recv(struct mux_client *client)
496{
497 int res;
498 int did_read = 0;
499 if(client->ib_size < sizeof(struct usbmuxd_header)) {
500 res = recv(client->fd, client->ib_buf + client->ib_size, sizeof(struct usbmuxd_header) - client->ib_size, 0);
501 if(res <= 0) {
502 if(res < 0)
503 usbmuxd_log(LL_ERROR, "Receive from client fd %d failed: %s", client->fd, strerror(errno));
504 else
505 usbmuxd_log(LL_INFO, "Client %d connection closed", client->fd);
506 client_close(client);
507 return;
508 }
509 client->ib_size += res;
510 if(client->ib_size < sizeof(struct usbmuxd_header))
511 return;
512 did_read = 1;
513 }
514 struct usbmuxd_header *hdr = (void*)client->ib_buf;
515#ifdef HAVE_PLIST
516 if((hdr->version != 0) && (hdr->version != 1)) {
517 usbmuxd_log(LL_INFO, "Client %d version mismatch: expected 0 or 1, got %d", client->fd, hdr->version);
518#else
519 if(hdr->version != USBMUXD_PROTOCOL_VERSION) {
520 usbmuxd_log(LL_INFO, "Client %d version mismatch: expected %d, got %d", client->fd, USBMUXD_PROTOCOL_VERSION, hdr->version);
521#endif
522 client_close(client);
523 return;
524 }
525 if(hdr->length > client->ib_capacity) {
526 usbmuxd_log(LL_INFO, "Client %d message is too long (%d bytes)", client->fd, hdr->length);
527 client_close(client);
528 return;
529 }
530 if(hdr->length < sizeof(struct usbmuxd_header)) {
531 usbmuxd_log(LL_ERROR, "Client %d message is too short (%d bytes)", client->fd, hdr->length);
532 client_close(client);
533 return;
534 }
535 if(client->ib_size < hdr->length) {
536 if(did_read)
537 return; //maybe we would block, so defer to next loop
538 res = recv(client->fd, client->ib_buf + client->ib_size, hdr->length - client->ib_size, 0);
539 if(res < 0) {
540 usbmuxd_log(LL_ERROR, "Receive from client fd %d failed: %s", client->fd, strerror(errno));
541 client_close(client);
542 return;
543 } else if(res == 0) {
544 usbmuxd_log(LL_INFO, "Client %d connection closed", client->fd);
545 client_close(client);
546 return;
547 }
548 client->ib_size += res;
549 if(client->ib_size < hdr->length)
550 return;
551 }
552 client_command(client, hdr);
553 client->ib_size = 0;
554}
555
556void client_process(int fd, short events)
557{
558 struct mux_client *client = NULL;
559 FOREACH(struct mux_client *lc, &client_list) {
560 if(lc->fd == fd) {
561 client = lc;
562 break;
563 }
564 } ENDFOREACH
565
566 if(!client) {
567 usbmuxd_log(LL_INFO, "client_process: fd %d not found in client list", fd);
568 return;
569 }
570
571 if(client->state == CLIENT_CONNECTED) {
572 usbmuxd_log(LL_SPEW, "client_process in CONNECTED state");
573 device_client_process(client->connect_device, client, events);
574 } else {
575 if(events & POLLIN) {
576 process_recv(client);
577 } else if(events & POLLOUT) { //not both in case client died as part of process_recv
578 process_send(client);
579 }
580 }
581
582}
583
584void client_device_add(struct device_info *dev)
585{
586 usbmuxd_log(LL_DEBUG, "client_device_add: id %d, location 0x%x, serial %s", dev->id, dev->location, dev->serial);
587 FOREACH(struct mux_client *client, &client_list) {
588 if(client->state == CLIENT_LISTEN)
589 notify_device_add(client, dev);
590 } ENDFOREACH
591}
592void client_device_remove(int device_id)
593{
594 uint32_t id = device_id;
595 usbmuxd_log(LL_DEBUG, "client_device_remove: id %d", device_id);
596 FOREACH(struct mux_client *client, &client_list) {
597 if(client->state == CLIENT_LISTEN)
598 notify_device_remove(client, id);
599 } ENDFOREACH
600}
601
602
603void client_init(void)
604{
605 usbmuxd_log(LL_DEBUG, "client_init");
606 collection_init(&client_list);
607}
608
609void client_shutdown(void)
610{
611 usbmuxd_log(LL_DEBUG, "client_shutdown");
612 FOREACH(struct mux_client *client, &client_list) {
613 client_close(client);
614 } ENDFOREACH
615 collection_free(&client_list);
616}
diff --git a/daemon/client.h b/daemon/client.h
deleted file mode 100644
index 60d8348..0000000
--- a/daemon/client.h
+++ /dev/null
@@ -1,47 +0,0 @@
1/*
2 usbmuxd - iPhone/iPod Touch USB multiplex server daemon
3
4Copyright (C) 2009 Hector Martin "marcan" <hector@marcansoft.com>
5Copyright (C) 2009 Nikias Bassen <nikias@gmx.li>
6
7This program is free software; you can redistribute it and/or modify
8it under the terms of the GNU General Public License as published by
9the Free Software Foundation, either version 2 or version 3.
10
11This program is distributed in the hope that it will be useful,
12but WITHOUT ANY WARRANTY; without even the implied warranty of
13MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14GNU General Public License for more details.
15
16You should have received a copy of the GNU General Public License
17along with this program; if not, write to the Free Software
18Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19
20*/
21
22#ifndef __CLIENT_H__
23#define __CLIENT_H__
24
25#include <stdint.h>
26#include "usbmuxd-proto.h"
27
28struct device_info;
29struct mux_client;
30
31int client_read(struct mux_client *client, void *buffer, uint32_t len);
32int client_write(struct mux_client *client, void *buffer, uint32_t len);
33int client_set_events(struct mux_client *client, short events);
34void client_close(struct mux_client *client);
35int client_notify_connect(struct mux_client *client, enum usbmuxd_result result);
36
37void client_device_add(struct device_info *dev);
38void client_device_remove(int device_id);
39
40int client_accept(int fd);
41void client_get_fds(struct fdlist *list);
42void client_process(int fd, short events);
43
44void client_init(void);
45void client_shutdown(void);
46
47#endif
diff --git a/daemon/device.h b/daemon/device.h
deleted file mode 100644
index ea77069..0000000
--- a/daemon/device.h
+++ /dev/null
@@ -1,52 +0,0 @@
1/*
2 usbmuxd - iPhone/iPod Touch USB multiplex server daemon
3
4Copyright (C) 2009 Hector Martin "marcan" <hector@marcansoft.com>
5
6This program is free software; you can redistribute it and/or modify
7it under the terms of the GNU General Public License as published by
8the Free Software Foundation, either version 2 or version 3.
9
10This program is distributed in the hope that it will be useful,
11but WITHOUT ANY WARRANTY; without even the implied warranty of
12MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13GNU General Public License for more details.
14
15You should have received a copy of the GNU General Public License
16along with this program; if not, write to the Free Software
17Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18
19*/
20
21#ifndef __DEVICE_H__
22#define __DEVICE_H__
23
24#include "usb.h"
25#include "client.h"
26
27struct device_info {
28 int id;
29 const char *serial;
30 uint32_t location;
31 uint16_t pid;
32};
33
34void device_data_input(struct usb_device *dev, unsigned char *buf, uint32_t length);
35
36int device_add(struct usb_device *dev);
37void device_remove(struct usb_device *dev);
38
39int device_start_connect(int device_id, uint16_t port, struct mux_client *client);
40void device_client_process(int device_id, struct mux_client *client, short events);
41void device_abort_connect(int device_id, struct mux_client *client);
42
43int device_get_count(void);
44int device_get_list(struct device_info *p);
45
46int device_get_timeout(void);
47void device_check_timeouts(void);
48
49void device_init(void);
50void device_kill_connections(void);
51void device_shutdown(void);
52#endif
diff --git a/daemon/log.h b/daemon/log.h
deleted file mode 100644
index eeefa41..0000000
--- a/daemon/log.h
+++ /dev/null
@@ -1,44 +0,0 @@
1/*
2 usbmuxd - iPhone/iPod Touch USB multiplex server daemon
3
4Copyright (C) 2009 Hector Martin "marcan" <hector@marcansoft.com>
5Copyright (C) 2009 Nikias Bassen <nikias@gmx.li>
6
7This program is free software; you can redistribute it and/or modify
8it under the terms of the GNU General Public License as published by
9the Free Software Foundation, either version 2 or version 3.
10
11This program is distributed in the hope that it will be useful,
12but WITHOUT ANY WARRANTY; without even the implied warranty of
13MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14GNU General Public License for more details.
15
16You should have received a copy of the GNU General Public License
17along with this program; if not, write to the Free Software
18Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19
20*/
21
22#ifndef __LOG_H__
23#define __LOG_H__
24
25enum loglevel {
26 LL_FATAL = 0,
27 LL_ERROR,
28 LL_WARNING,
29 LL_NOTICE,
30 LL_INFO,
31 LL_DEBUG,
32 LL_SPEW,
33 LL_FLOOD,
34};
35
36extern int log_level;
37
38void log_enable_syslog();
39void log_disable_syslog();
40
41void usbmuxd_log(enum loglevel level, const char *fmt, ...) __attribute__ ((format (printf, 2, 3)));
42
43
44#endif
diff --git a/daemon/main.c b/daemon/main.c
deleted file mode 100644
index 140bee1..0000000
--- a/daemon/main.c
+++ /dev/null
@@ -1,618 +0,0 @@
1/*
2 usbmuxd - iPhone/iPod Touch USB multiplex server daemon
3
4Copyright (C) 2009 Hector Martin "marcan" <hector@marcansoft.com>
5Copyright (C) 2009 Nikias Bassen <nikias@gmx.li>
6Copyright (C) 2009 Paul Sladen <libiphone@paul.sladen.org>
7
8This program is free software; you can redistribute it and/or modify
9it under the terms of the GNU General Public License as published by
10the Free Software Foundation, either version 2 or version 3.
11
12This program is distributed in the hope that it will be useful,
13but WITHOUT ANY WARRANTY; without even the implied warranty of
14MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15GNU General Public License for more details.
16
17You should have received a copy of the GNU General Public License
18along with this program; if not, write to the Free Software
19Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20
21*/
22
23#define _BSD_SOURCE
24#define _GNU_SOURCE
25
26#ifdef HAVE_CONFIG_H
27#include <config.h>
28#endif
29
30#include <stdio.h>
31#include <errno.h>
32#include <string.h>
33#include <stdlib.h>
34#include <signal.h>
35#include <unistd.h>
36#include <sys/socket.h>
37#include <sys/un.h>
38#include <sys/stat.h>
39#include <sys/types.h>
40#include <fcntl.h>
41#include <getopt.h>
42#include <pwd.h>
43#include <grp.h>
44
45#include "log.h"
46#include "usb.h"
47#include "device.h"
48#include "client.h"
49
50static const char *socket_path = "/var/run/usbmuxd";
51static const char *lockfile = "/var/run/usbmuxd.pid";
52
53int should_exit;
54int should_discover;
55
56static int verbose = 0;
57static int foreground = 0;
58static int drop_privileges = 0;
59static const char *drop_user = NULL;
60static int opt_udev = 0;
61static int opt_exit = 0;
62static int exit_signal = 0;
63static int daemon_pipe;
64
65static int report_to_parent = 0;
66
67int create_socket(void) {
68 struct sockaddr_un bind_addr;
69 int listenfd;
70
71 if(unlink(socket_path) == -1 && errno != ENOENT) {
72 usbmuxd_log(LL_FATAL, "unlink(%s) failed: %s", socket_path, strerror(errno));
73 return -1;
74 }
75
76 listenfd = socket(AF_UNIX, SOCK_STREAM, 0);
77 if (listenfd == -1) {
78 usbmuxd_log(LL_FATAL, "socket() failed: %s", strerror(errno));
79 return -1;
80 }
81
82 bzero(&bind_addr, sizeof(bind_addr));
83 bind_addr.sun_family = AF_UNIX;
84 strcpy(bind_addr.sun_path, socket_path);
85 if (bind(listenfd, (struct sockaddr*)&bind_addr, sizeof(bind_addr)) != 0) {
86 usbmuxd_log(LL_FATAL, "bind() failed: %s", strerror(errno));
87 return -1;
88 }
89
90 // Start listening
91 if (listen(listenfd, 5) != 0) {
92 usbmuxd_log(LL_FATAL, "listen() failed: %s", strerror(errno));
93 return -1;
94 }
95
96 chmod(socket_path, 0666);
97
98 return listenfd;
99}
100
101void handle_signal(int sig)
102{
103 if (sig != SIGUSR1 && sig != SIGUSR2) {
104 usbmuxd_log(LL_NOTICE,"Caught signal %d, exiting", sig);
105 should_exit = 1;
106 } else {
107 if(opt_udev) {
108 if (sig == SIGUSR1) {
109 usbmuxd_log(LL_INFO, "Caught SIGUSR1, checking if we can terminate (no more devices attached)...");
110 if (device_get_count() > 0) {
111 // we can't quit, there are still devices attached.
112 usbmuxd_log(LL_NOTICE, "Refusing to terminate, there are still devices attached. Kill me with signal 15 (TERM) to force quit.");
113 } else {
114 // it's safe to quit
115 should_exit = 1;
116 }
117 } else if (sig == SIGUSR2) {
118 usbmuxd_log(LL_INFO, "Caught SIGUSR2, scheduling device discovery");
119 should_discover = 1;
120 }
121 } else {
122 usbmuxd_log(LL_INFO, "Caught SIGUSR1/2 but we weren't started in --udev mode, ignoring");
123 }
124 }
125}
126
127void set_signal_handlers(void)
128{
129 struct sigaction sa;
130 sigset_t set;
131
132 // Mask all signals we handle. They will be unmasked by ppoll().
133 sigemptyset(&set);
134 sigaddset(&set, SIGINT);
135 sigaddset(&set, SIGQUIT);
136 sigaddset(&set, SIGTERM);
137 sigaddset(&set, SIGUSR1);
138 sigaddset(&set, SIGUSR2);
139 sigprocmask(SIG_SETMASK, &set, NULL);
140
141 memset(&sa, 0, sizeof(struct sigaction));
142 sa.sa_handler = handle_signal;
143 sigaction(SIGINT, &sa, NULL);
144 sigaction(SIGQUIT, &sa, NULL);
145 sigaction(SIGTERM, &sa, NULL);
146 sigaction(SIGUSR1, &sa, NULL);
147 sigaction(SIGUSR2, &sa, NULL);
148}
149
150#if defined(__FreeBSD__) || defined(__APPLE__)
151static int ppoll(struct pollfd *fds, nfds_t nfds, const struct timespec *timeout, const sigset_t *sigmask)
152{
153 int ready;
154 sigset_t origmask;
155 int to = timeout->tv_sec*1000 + timeout->tv_nsec/1000000;
156
157 sigprocmask(SIG_SETMASK, sigmask, &origmask);
158 ready = poll(fds, nfds, to);
159 sigprocmask(SIG_SETMASK, &origmask, NULL);
160
161 return ready;
162}
163#endif
164
165int main_loop(int listenfd)
166{
167 int to, cnt, i, dto;
168 struct fdlist pollfds;
169 struct timespec tspec;
170
171 sigset_t empty_sigset;
172 sigemptyset(&empty_sigset); // unmask all signals
173
174 fdlist_create(&pollfds);
175 while(!should_exit) {
176 usbmuxd_log(LL_FLOOD, "main_loop iteration");
177 to = usb_get_timeout();
178 usbmuxd_log(LL_FLOOD, "USB timeout is %d ms", to);
179 dto = device_get_timeout();
180 usbmuxd_log(LL_FLOOD, "Device timeout is %d ms", to);
181 if(dto < to)
182 to = dto;
183
184 fdlist_reset(&pollfds);
185 fdlist_add(&pollfds, FD_LISTEN, listenfd, POLLIN);
186 usb_get_fds(&pollfds);
187 client_get_fds(&pollfds);
188 usbmuxd_log(LL_FLOOD, "fd count is %d", pollfds.count);
189
190 tspec.tv_sec = to / 1000;
191 tspec.tv_nsec = (to % 1000) * 1000000;
192 cnt = ppoll(pollfds.fds, pollfds.count, &tspec, &empty_sigset);
193 usbmuxd_log(LL_FLOOD, "poll() returned %d", cnt);
194 if(cnt == -1) {
195 if(errno == EINTR) {
196 if(should_exit) {
197 usbmuxd_log(LL_INFO, "Event processing interrupted");
198 break;
199 }
200 if(should_discover) {
201 should_discover = 0;
202 usbmuxd_log(LL_INFO, "Device discovery triggered by udev");
203 usb_discover();
204 }
205 }
206 } else if(cnt == 0) {
207 if(usb_process() < 0) {
208 usbmuxd_log(LL_FATAL, "usb_process() failed");
209 fdlist_free(&pollfds);
210 return -1;
211 }
212 device_check_timeouts();
213 } else {
214 int done_usb = 0;
215 for(i=0; i<pollfds.count; i++) {
216 if(pollfds.fds[i].revents) {
217 if(!done_usb && pollfds.owners[i] == FD_USB) {
218 if(usb_process() < 0) {
219 usbmuxd_log(LL_FATAL, "usb_process() failed");
220 fdlist_free(&pollfds);
221 return -1;
222 }
223 done_usb = 1;
224 }
225 if(pollfds.owners[i] == FD_LISTEN) {
226 if(client_accept(listenfd) < 0) {
227 usbmuxd_log(LL_FATAL, "client_accept() failed");
228 fdlist_free(&pollfds);
229 return -1;
230 }
231 }
232 if(pollfds.owners[i] == FD_CLIENT) {
233 client_process(pollfds.fds[i].fd, pollfds.fds[i].revents);
234 }
235 }
236 }
237 }
238 }
239 fdlist_free(&pollfds);
240 return 0;
241}
242
243/**
244 * make this program run detached from the current console
245 */
246static int daemonize(void)
247{
248 pid_t pid;
249 pid_t sid;
250 int pfd[2];
251 int res;
252
253 // already a daemon
254 if (getppid() == 1)
255 return 0;
256
257 if((res = pipe(pfd)) < 0) {
258 usbmuxd_log(LL_FATAL, "pipe() failed.");
259 return res;
260 }
261
262 pid = fork();
263 if (pid < 0) {
264 usbmuxd_log(LL_FATAL, "fork() failed.");
265 return pid;
266 }
267
268 if (pid > 0) {
269 // exit parent process
270 int status;
271 close(pfd[1]);
272
273 if((res = read(pfd[0],&status,sizeof(int))) != sizeof(int)) {
274 fprintf(stderr, "usbmuxd: ERROR: Failed to get init status from child, check syslog for messages.\n");
275 exit(1);
276 }
277 if(status != 0)
278 fprintf(stderr, "usbmuxd: ERROR: Child process exited with error %d, check syslog for messages.\n", status);
279 exit(status);
280 }
281 // At this point we are executing as the child process
282 // but we need to do one more fork
283
284 daemon_pipe = pfd[1];
285 close(pfd[0]);
286 report_to_parent = 1;
287
288 // Change the file mode mask
289 umask(0);
290
291 // Create a new SID for the child process
292 sid = setsid();
293 if (sid < 0) {
294 usbmuxd_log(LL_FATAL, "setsid() failed.");
295 return -1;
296 }
297
298 pid = fork();
299 if (pid < 0) {
300 usbmuxd_log(LL_FATAL, "fork() failed (second).");
301 return pid;
302 }
303
304 if (pid > 0) {
305 // exit parent process
306 close(daemon_pipe);
307 exit(0);
308 }
309
310 // Change the current working directory.
311 if ((chdir("/")) < 0) {
312 usbmuxd_log(LL_FATAL, "chdir() failed");
313 return -2;
314 }
315 // Redirect standard files to /dev/null
316 if (!freopen("/dev/null", "r", stdin)) {
317 usbmuxd_log(LL_FATAL, "Redirection of stdin failed.");
318 return -3;
319 }
320 if (!freopen("/dev/null", "w", stdout)) {
321 usbmuxd_log(LL_FATAL, "Redirection of stdout failed.");
322 return -3;
323 }
324
325 return 0;
326}
327
328static int notify_parent(int status)
329{
330 int res;
331
332 report_to_parent = 0;
333 if ((res = write(daemon_pipe, &status, sizeof(int))) != sizeof(int)) {
334 usbmuxd_log(LL_FATAL, "Could not notify parent!");
335 if(res >= 0)
336 return -2;
337 else
338 return res;
339 }
340 close(daemon_pipe);
341 if (!freopen("/dev/null", "w", stderr)) {
342 usbmuxd_log(LL_FATAL, "Redirection of stderr failed.");
343 return -1;
344 }
345 return 0;
346}
347
348static void usage()
349{
350 printf("usage: usbmuxd [options]\n");
351 printf("\t-h|--help Print this message.\n");
352 printf("\t-v|--verbose Be verbose (use twice or more to increase).\n");
353 printf("\t-f|--foreground Do not daemonize (implies one -v).\n");
354 printf("\t-U|--user USER Change to this user after startup (needs usb privileges).\n");
355 printf("\t-u|--udev Run in udev operation mode.\n");
356 printf("\t-x|--exit Tell a running instance to exit if there are no devices\n");
357 printf("\t connected (must be in udev mode).\n");
358 printf("\t-X|--force-exit Tell a running instance to exit, even if there are still\n");
359 printf("\t devices connected (always works).\n");
360 printf("\n");
361}
362
363static void parse_opts(int argc, char **argv)
364{
365 static struct option longopts[] = {
366 {"help", 0, NULL, 'h'},
367 {"foreground", 0, NULL, 'f'},
368 {"verbose", 0, NULL, 'v'},
369 {"user", 2, NULL, 'U'},
370 {"udev", 0, NULL, 'u'},
371 {"exit", 0, NULL, 'x'},
372 {"force-exit", 0, NULL, 'X'},
373 {NULL, 0, NULL, 0}
374 };
375 int c;
376
377 while (1) {
378 c = getopt_long(argc, argv, "hfvuU:xX", longopts, (int *) 0);
379 if (c == -1) {
380 break;
381 }
382
383 switch (c) {
384 case 'h':
385 usage();
386 exit(0);
387 case 'f':
388 foreground = 1;
389 break;
390 case 'v':
391 ++verbose;
392 break;
393 case 'U':
394 drop_privileges = 1;
395 drop_user = optarg;
396 break;
397 case 'u':
398 opt_udev = 1;
399 break;
400 case 'x':
401 opt_exit = 1;
402 exit_signal = SIGUSR1;
403 break;
404 case 'X':
405 opt_exit = 1;
406 exit_signal = SIGTERM;
407 break;
408 default:
409 usage();
410 exit(2);
411 }
412 }
413}
414
415int main(int argc, char *argv[])
416{
417 int listenfd;
418 int res = 0;
419 int lfd;
420 struct flock lock;
421 char pids[10];
422
423 parse_opts(argc, argv);
424
425 argc -= optind;
426 argv += optind;
427
428 if (!foreground) {
429 verbose += LL_WARNING;
430 log_enable_syslog();
431 } else {
432 verbose += LL_NOTICE;
433 }
434
435 /* set log level to specified verbosity */
436 log_level = verbose;
437
438 usbmuxd_log(LL_NOTICE, "usbmuxd v%s starting up", USBMUXD_VERSION);
439 should_exit = 0;
440 should_discover = 0;
441
442 set_signal_handlers();
443 signal(SIGPIPE, SIG_IGN);
444
445 res = lfd = open(lockfile, O_WRONLY|O_CREAT, 0644);
446 if(res == -1) {
447 usbmuxd_log(LL_FATAL, "Could not open lockfile");
448 goto terminate;
449 }
450 lock.l_type = F_WRLCK;
451 lock.l_whence = SEEK_SET;
452 lock.l_start = 0;
453 lock.l_len = 0;
454 fcntl(lfd, F_GETLK, &lock);
455 close(lfd);
456 if (lock.l_type != F_UNLCK) {
457 if (opt_exit) {
458 if (lock.l_pid && !kill(lock.l_pid, 0)) {
459 usbmuxd_log(LL_NOTICE, "Sending signal %d to instance with pid %d", exit_signal, lock.l_pid);
460 res = 0;
461 if (kill(lock.l_pid, exit_signal) < 0) {
462 usbmuxd_log(LL_FATAL, "Could not deliver signal %d to pid %d", exit_signal, lock.l_pid);
463 res = -1;
464 }
465 goto terminate;
466 } else {
467 usbmuxd_log(LL_ERROR, "Could not determine pid of the other running instance!");
468 res = -1;
469 goto terminate;
470 }
471 } else {
472 if (!opt_udev) {
473 usbmuxd_log(LL_ERROR, "Another instance is already running (pid %d). exiting.", lock.l_pid);
474 res = -1;
475 } else {
476 usbmuxd_log(LL_NOTICE, "Another instance is already running (pid %d). Telling it to check for devices.", lock.l_pid);
477 if (lock.l_pid && !kill(lock.l_pid, 0)) {
478 usbmuxd_log(LL_NOTICE, "Sending signal SIGUSR2 to instance with pid %d", lock.l_pid);
479 res = 0;
480 if (kill(lock.l_pid, SIGUSR2) < 0) {
481 usbmuxd_log(LL_FATAL, "Could not deliver SIGUSR2 to pid %d", lock.l_pid);
482 res = -1;
483 }
484 } else {
485 usbmuxd_log(LL_ERROR, "Could not determine pid of the other running instance!");
486 res = -1;
487 }
488 }
489 goto terminate;
490 }
491 }
492 unlink(lockfile);
493
494 if (opt_exit) {
495 usbmuxd_log(LL_NOTICE, "No running instance found, none killed. exiting.");
496 goto terminate;
497 }
498
499 if (!foreground) {
500 if ((res = daemonize()) < 0) {
501 fprintf(stderr, "usbmuxd: FATAL: Could not daemonize!\n");
502 usbmuxd_log(LL_FATAL, "Could not daemonize!");
503 goto terminate;
504 }
505 }
506
507 // now open the lockfile and place the lock
508 res = lfd = open(lockfile, O_WRONLY|O_CREAT|O_TRUNC|O_EXCL, 0644);
509 if(res < 0) {
510 usbmuxd_log(LL_FATAL, "Could not open lockfile");
511 goto terminate;
512 }
513 lock.l_type = F_WRLCK;
514 lock.l_whence = SEEK_SET;
515 lock.l_start = 0;
516 lock.l_len = 0;
517 if ((res = fcntl(lfd, F_SETLK, &lock)) < 0) {
518 usbmuxd_log(LL_FATAL, "Lockfile locking failed!");
519 goto terminate;
520 }
521 sprintf(pids, "%d", getpid());
522 if ((res = write(lfd, pids, strlen(pids))) != strlen(pids)) {
523 usbmuxd_log(LL_FATAL, "Could not write pidfile!");
524 if(res >= 0)
525 res = -2;
526 goto terminate;
527 }
528
529 usbmuxd_log(LL_INFO, "Creating socket");
530 res = listenfd = create_socket();
531 if(listenfd < 0)
532 goto terminate;
533
534 // drop elevated privileges
535 if (drop_privileges && (getuid() == 0 || geteuid() == 0)) {
536 struct passwd *pw;
537 if (!drop_user) {
538 usbmuxd_log(LL_FATAL, "No user to drop privileges to?");
539 res = -1;
540 goto terminate;
541 }
542 pw = getpwnam(drop_user);
543 if (!pw) {
544 usbmuxd_log(LL_FATAL, "Dropping privileges failed, check if user '%s' exists!", drop_user);
545 res = -1;
546 goto terminate;
547 }
548 if (pw->pw_uid == 0) {
549 usbmuxd_log(LL_INFO, "Not dropping privileges to root");
550 } else {
551 if ((res = initgroups(drop_user, pw->pw_gid)) < 0) {
552 usbmuxd_log(LL_FATAL, "Failed to drop privileges (cannot set supplementary groups)");
553 goto terminate;
554 }
555 if ((res = setgid(pw->pw_gid)) < 0) {
556 usbmuxd_log(LL_FATAL, "Failed to drop privileges (cannot set group ID to %d)", pw->pw_gid);
557 goto terminate;
558 }
559 if ((res = setuid(pw->pw_uid)) < 0) {
560 usbmuxd_log(LL_FATAL, "Failed to drop privileges (cannot set user ID to %d)", pw->pw_uid);
561 goto terminate;
562 }
563
564 // security check
565 if (setuid(0) != -1) {
566 usbmuxd_log(LL_FATAL, "Failed to drop privileges properly!");
567 res = -1;
568 goto terminate;
569 }
570 if (getuid() != pw->pw_uid || getgid() != pw->pw_gid) {
571 usbmuxd_log(LL_FATAL, "Failed to drop privileges properly!");
572 res = -1;
573 goto terminate;
574 }
575 usbmuxd_log(LL_NOTICE, "Successfully dropped privileges to '%s'", drop_user);
576 }
577 }
578
579 client_init();
580 device_init();
581 usbmuxd_log(LL_INFO, "Initializing USB");
582 if((res = usb_init()) < 0)
583 goto terminate;
584
585 usbmuxd_log(LL_INFO, "%d device%s detected", res, (res==1)?"":"s");
586
587 usbmuxd_log(LL_NOTICE, "Initialization complete");
588
589 if (report_to_parent)
590 if((res = notify_parent(0)) < 0)
591 goto terminate;
592
593 if(opt_udev)
594 usb_autodiscover(0); // discovery triggered by udev
595
596 res = main_loop(listenfd);
597 if(res < 0)
598 usbmuxd_log(LL_FATAL, "main_loop failed");
599
600 usbmuxd_log(LL_NOTICE, "usbmuxd shutting down");
601 device_kill_connections();
602 usb_shutdown();
603 device_shutdown();
604 client_shutdown();
605 usbmuxd_log(LL_NOTICE, "Shutdown complete");
606
607terminate:
608 log_disable_syslog();
609
610 if (res < 0)
611 res = -res;
612 else
613 res = 0;
614 if (report_to_parent)
615 notify_parent(res);
616
617 return res;
618}
diff --git a/daemon/usb-linux.c b/daemon/usb-linux.c
deleted file mode 100644
index 0860b46..0000000
--- a/daemon/usb-linux.c
+++ /dev/null
@@ -1,589 +0,0 @@
1/*
2 usbmuxd - iPhone/iPod Touch USB multiplex server daemon
3
4Copyright (C) 2009 Hector Martin "marcan" <hector@marcansoft.com>
5Copyright (C) 2009 Nikias Bassen <nikias@gmx.li>
6Copyright (C) 2009 Martin Szulecki <opensuse@sukimashita.com>
7
8This program is free software; you can redistribute it and/or modify
9it under the terms of the GNU General Public License as published by
10the Free Software Foundation, either version 2 or version 3.
11
12This program is distributed in the hope that it will be useful,
13but WITHOUT ANY WARRANTY; without even the implied warranty of
14MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15GNU General Public License for more details.
16
17You should have received a copy of the GNU General Public License
18along with this program; if not, write to the Free Software
19Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20
21*/
22
23#ifdef HAVE_CONFIG_H
24#include <config.h>
25#endif
26
27#include <stdio.h>
28#include <stdlib.h>
29#include <stdint.h>
30#include <string.h>
31
32#include <libusb.h>
33
34#include "usb.h"
35#include "log.h"
36#include "device.h"
37
38// interval for device connection/disconnection polling, in milliseconds
39// we need this because there is currently no asynchronous device discovery mechanism in libusb
40#define DEVICE_POLL_TIME 1000
41
42struct usb_device {
43 libusb_device_handle *dev;
44 uint8_t bus, address;
45 uint16_t vid, pid;
46 char serial[256];
47 int alive;
48 uint8_t interface, ep_in, ep_out;
49 struct libusb_transfer *rx_xfer;
50 struct collection tx_xfers;
51 int wMaxPacketSize;
52};
53
54static struct collection device_list;
55
56static struct timeval next_dev_poll_time;
57
58static int devlist_failures;
59static int device_polling;
60
61static void usb_disconnect(struct usb_device *dev)
62{
63 if(!dev->dev) {
64 return;
65 }
66
67 // kill the rx xfer and tx xfers and try to make sure the callbacks get called before we free the device
68 if(dev->rx_xfer) {
69 usbmuxd_log(LL_DEBUG, "usb_disconnect: cancelling RX xfer");
70 libusb_cancel_transfer(dev->rx_xfer);
71 }
72 FOREACH(struct libusb_transfer *xfer, &dev->tx_xfers) {
73 usbmuxd_log(LL_DEBUG, "usb_disconnect: cancelling TX xfer %p", xfer);
74 libusb_cancel_transfer(xfer);
75 } ENDFOREACH
76
77 while(dev->rx_xfer || collection_count(&dev->tx_xfers)) {
78 struct timeval tv;
79 int res;
80
81 tv.tv_sec = 0;
82 tv.tv_usec = 1000;
83 if((res = libusb_handle_events_timeout(NULL, &tv)) < 0) {
84 usbmuxd_log(LL_ERROR, "libusb_handle_events_timeout for usb_disconnect failed: %d", res);
85 break;
86 }
87 }
88 collection_free(&dev->tx_xfers);
89 libusb_release_interface(dev->dev, dev->interface);
90 libusb_close(dev->dev);
91 dev->dev = NULL;
92 collection_remove(&device_list, dev);
93 free(dev);
94}
95
96static void tx_callback(struct libusb_transfer *xfer)
97{
98 struct usb_device *dev = xfer->user_data;
99 usbmuxd_log(LL_SPEW, "TX callback dev %d-%d len %d -> %d status %d", dev->bus, dev->address, xfer->length, xfer->actual_length, xfer->status);
100 if(xfer->status != LIBUSB_TRANSFER_COMPLETED) {
101 switch(xfer->status) {
102 case LIBUSB_TRANSFER_COMPLETED: //shut up compiler
103 case LIBUSB_TRANSFER_ERROR:
104 // funny, this happens when we disconnect the device while waiting for a transfer, sometimes
105 usbmuxd_log(LL_INFO, "Device %d-%d TX aborted due to error or disconnect", dev->bus, dev->address);
106 break;
107 case LIBUSB_TRANSFER_TIMED_OUT:
108 usbmuxd_log(LL_ERROR, "TX transfer timed out for device %d-%d", dev->bus, dev->address);
109 break;
110 case LIBUSB_TRANSFER_CANCELLED:
111 usbmuxd_log(LL_DEBUG, "Device %d-%d TX transfer cancelled", dev->bus, dev->address);
112 break;
113 case LIBUSB_TRANSFER_STALL:
114 usbmuxd_log(LL_ERROR, "TX transfer stalled for device %d-%d", dev->bus, dev->address);
115 break;
116 case LIBUSB_TRANSFER_NO_DEVICE:
117 // other times, this happens, and also even when we abort the transfer after device removal
118 usbmuxd_log(LL_INFO, "Device %d-%d TX aborted due to disconnect", dev->bus, dev->address);
119 break;
120 case LIBUSB_TRANSFER_OVERFLOW:
121 usbmuxd_log(LL_ERROR, "TX transfer overflow for device %d-%d", dev->bus, dev->address);
122 break;
123 // and nothing happens (this never gets called) if the device is freed after a disconnect! (bad)
124 }
125 // we can't usb_disconnect here due to a deadlock, so instead mark it as dead and reap it after processing events
126 // we'll do device_remove there too
127 dev->alive = 0;
128 }
129 if(xfer->buffer)
130 free(xfer->buffer);
131 collection_remove(&dev->tx_xfers, xfer);
132 libusb_free_transfer(xfer);
133}
134
135int usb_send(struct usb_device *dev, const unsigned char *buf, int length)
136{
137 int res;
138 struct libusb_transfer *xfer = libusb_alloc_transfer(0);
139 libusb_fill_bulk_transfer(xfer, dev->dev, dev->ep_out, (void*)buf, length, tx_callback, dev, 0);
140 if((res = libusb_submit_transfer(xfer)) < 0) {
141 usbmuxd_log(LL_ERROR, "Failed to submit TX transfer %p len %d to device %d-%d: %d", buf, length, dev->bus, dev->address, res);
142 libusb_free_transfer(xfer);
143 return res;
144 }
145 collection_add(&dev->tx_xfers, xfer);
146 if (length % dev->wMaxPacketSize == 0) {
147 usbmuxd_log(LL_DEBUG, "Send ZLP");
148 // Send Zero Length Packet
149 xfer = libusb_alloc_transfer(0);
150 void *buffer = malloc(1);
151 libusb_fill_bulk_transfer(xfer, dev->dev, dev->ep_out, buffer, 0, tx_callback, dev, 0);
152 if((res = libusb_submit_transfer(xfer)) < 0) {
153 usbmuxd_log(LL_ERROR, "Failed to submit TX ZLP transfer to device %d-%d: %d", dev->bus, dev->address, res);
154 libusb_free_transfer(xfer);
155 return res;
156 }
157 collection_add(&dev->tx_xfers, xfer);
158 }
159 return 0;
160}
161
162static void rx_callback(struct libusb_transfer *xfer)
163{
164 struct usb_device *dev = xfer->user_data;
165 usbmuxd_log(LL_SPEW, "RX callback dev %d-%d len %d status %d", dev->bus, dev->address, xfer->actual_length, xfer->status);
166 if(xfer->status == LIBUSB_TRANSFER_COMPLETED) {
167 device_data_input(dev, xfer->buffer, xfer->actual_length);
168 libusb_submit_transfer(xfer);
169 } else {
170 switch(xfer->status) {
171 case LIBUSB_TRANSFER_COMPLETED: //shut up compiler
172 case LIBUSB_TRANSFER_ERROR:
173 // funny, this happens when we disconnect the device while waiting for a transfer, sometimes
174 usbmuxd_log(LL_INFO, "Device %d-%d RX aborted due to error or disconnect", dev->bus, dev->address);
175 break;
176 case LIBUSB_TRANSFER_TIMED_OUT:
177 usbmuxd_log(LL_ERROR, "RX transfer timed out for device %d-%d", dev->bus, dev->address);
178 break;
179 case LIBUSB_TRANSFER_CANCELLED:
180 usbmuxd_log(LL_DEBUG, "Device %d-%d RX transfer cancelled", dev->bus, dev->address);
181 break;
182 case LIBUSB_TRANSFER_STALL:
183 usbmuxd_log(LL_ERROR, "RX transfer stalled for device %d-%d", dev->bus, dev->address);
184 break;
185 case LIBUSB_TRANSFER_NO_DEVICE:
186 // other times, this happens, and also even when we abort the transfer after device removal
187 usbmuxd_log(LL_INFO, "Device %d-%d RX aborted due to disconnect", dev->bus, dev->address);
188 break;
189 case LIBUSB_TRANSFER_OVERFLOW:
190 usbmuxd_log(LL_ERROR, "RX transfer overflow for device %d-%d", dev->bus, dev->address);
191 break;
192 // and nothing happens (this never gets called) if the device is freed after a disconnect! (bad)
193 }
194 free(xfer->buffer);
195 dev->rx_xfer = NULL;
196 libusb_free_transfer(xfer);
197 // we can't usb_disconnect here due to a deadlock, so instead mark it as dead and reap it after processing events
198 // we'll do device_remove there too
199 dev->alive = 0;
200 }
201}
202
203static int start_rx(struct usb_device *dev)
204{
205 int res;
206 void *buf;
207 dev->rx_xfer = libusb_alloc_transfer(0);
208 buf = malloc(USB_MRU);
209 libusb_fill_bulk_transfer(dev->rx_xfer, dev->dev, dev->ep_in, buf, USB_MRU, rx_callback, dev, 0);
210 if((res = libusb_submit_transfer(dev->rx_xfer)) != 0) {
211 usbmuxd_log(LL_ERROR, "Failed to submit RX transfer to device %d-%d: %d", dev->bus, dev->address, res);
212 libusb_free_transfer(dev->rx_xfer);
213 dev->rx_xfer = NULL;
214 return res;
215 }
216 return 0;
217}
218
219int usb_discover(void)
220{
221 int cnt, i, j, res;
222 int valid_count = 0;
223 libusb_device **devs;
224
225 cnt = libusb_get_device_list(NULL, &devs);
226 if(cnt < 0) {
227 usbmuxd_log(LL_WARNING, "Could not get device list: %d", cnt);
228 devlist_failures++;
229 // sometimes libusb fails getting the device list if you've just removed something
230 if(devlist_failures > 5) {
231 usbmuxd_log(LL_FATAL, "Too many errors getting device list\n");
232 return cnt;
233 } else {
234 gettimeofday(&next_dev_poll_time, NULL);
235 next_dev_poll_time.tv_usec += DEVICE_POLL_TIME * 1000;
236 next_dev_poll_time.tv_sec += next_dev_poll_time.tv_usec / 1000000;
237 next_dev_poll_time.tv_usec = next_dev_poll_time.tv_usec % 1000000;
238 return 0;
239 }
240 }
241 devlist_failures = 0;
242
243 usbmuxd_log(LL_SPEW, "usb_discover: scanning %d devices", cnt);
244
245 FOREACH(struct usb_device *usbdev, &device_list) {
246 usbdev->alive = 0;
247 } ENDFOREACH
248
249 for(i=0; i<cnt; i++) {
250 // the following are non-blocking operations on the device list
251 libusb_device *dev = devs[i];
252 uint8_t bus = libusb_get_bus_number(dev);
253 uint8_t address = libusb_get_device_address(dev);
254 struct libusb_device_descriptor devdesc;
255 int found = 0;
256 FOREACH(struct usb_device *usbdev, &device_list) {
257 if(usbdev->bus == bus && usbdev->address == address) {
258 valid_count++;
259 usbdev->alive = 1;
260 found = 1;
261 break;
262 }
263 } ENDFOREACH
264 if(found)
265 continue; //device already found
266 if((res = libusb_get_device_descriptor(dev, &devdesc)) != 0) {
267 usbmuxd_log(LL_WARNING, "Could not get device descriptor for device %d-%d: %d", bus, address, res);
268 continue;
269 }
270 if(devdesc.idVendor != VID_APPLE)
271 continue;
272 if((devdesc.idProduct < PID_RANGE_LOW) ||
273 (devdesc.idProduct > PID_RANGE_MAX))
274 continue;
275 libusb_device_handle *handle;
276 usbmuxd_log(LL_INFO, "Found new device with v/p %04x:%04x at %d-%d", devdesc.idVendor, devdesc.idProduct, bus, address);
277 // potentially blocking operations follow; they will only run when new devices are detected, which is acceptable
278 if((res = libusb_open(dev, &handle)) != 0) {
279 usbmuxd_log(LL_WARNING, "Could not open device %d-%d: %d", bus, address, res);
280 continue;
281 }
282 int current_config = 0;
283 if((res = libusb_get_configuration(handle, &current_config)) != 0) {
284 usbmuxd_log(LL_WARNING, "Could not get configuration for device %d-%d: %d", bus, address, res);
285 libusb_close(handle);
286 continue;
287 }
288 if (current_config != devdesc.bNumConfigurations) {
289 struct libusb_config_descriptor *config;
290 if((res = libusb_get_active_config_descriptor(dev, &config)) != 0) {
291 usbmuxd_log(LL_NOTICE, "Could not get old configuration descriptor for device %d-%d: %d", bus, address, res);
292 } else {
293 for(j=0; j<config->bNumInterfaces; j++) {
294 const struct libusb_interface_descriptor *intf = &config->interface[j].altsetting[0];
295 if((res = libusb_kernel_driver_active(handle, intf->bInterfaceNumber)) < 0) {
296 usbmuxd_log(LL_NOTICE, "Could not check kernel ownership of interface %d for device %d-%d: %d", intf->bInterfaceNumber, bus, address, res);
297 continue;
298 }
299 if(res == 1) {
300 usbmuxd_log(LL_INFO, "Detaching kernel driver for device %d-%d, interface %d", bus, address, intf->bInterfaceNumber);
301 if((res = libusb_detach_kernel_driver(handle, intf->bInterfaceNumber)) < 0) {
302 usbmuxd_log(LL_WARNING, "Could not detach kernel driver (%d), configuration change will probably fail!", res);
303 continue;
304 }
305 }
306 }
307 libusb_free_config_descriptor(config);
308 }
309 if((res = libusb_set_configuration(handle, devdesc.bNumConfigurations)) != 0) {
310 usbmuxd_log(LL_WARNING, "Could not set configuration %d for device %d-%d: %d", devdesc.bNumConfigurations, bus, address, res);
311 libusb_close(handle);
312 continue;
313 }
314 }
315
316 struct libusb_config_descriptor *config;
317 if((res = libusb_get_active_config_descriptor(dev, &config)) != 0) {
318 usbmuxd_log(LL_WARNING, "Could not get configuration descriptor for device %d-%d: %d", bus, address, res);
319 libusb_close(handle);
320 continue;
321 }
322
323 struct usb_device *usbdev;
324 usbdev = malloc(sizeof(struct usb_device));
325 memset(usbdev, 0, sizeof(*usbdev));
326
327 for(j=0; j<config->bNumInterfaces; j++) {
328 const struct libusb_interface_descriptor *intf = &config->interface[j].altsetting[0];
329 if(intf->bInterfaceClass != INTERFACE_CLASS ||
330 intf->bInterfaceSubClass != INTERFACE_SUBCLASS ||
331 intf->bInterfaceProtocol != INTERFACE_PROTOCOL)
332 continue;
333 if(intf->bNumEndpoints != 2) {
334 usbmuxd_log(LL_WARNING, "Endpoint count mismatch for interface %d of device %d-%d", intf->bInterfaceNumber, bus, address);
335 continue;
336 }
337 if((intf->endpoint[0].bEndpointAddress & 0x80) != LIBUSB_ENDPOINT_OUT ||
338 (intf->endpoint[1].bEndpointAddress & 0x80) != LIBUSB_ENDPOINT_IN) {
339 usbmuxd_log(LL_WARNING, "Endpoint type mismatch for interface %d of device %d-%d", intf->bInterfaceNumber, bus, address);
340 continue;
341 }
342 usbdev->interface = intf->bInterfaceNumber;
343 usbdev->ep_out = intf->endpoint[0].bEndpointAddress;
344 usbdev->ep_in = intf->endpoint[1].bEndpointAddress;
345 usbmuxd_log(LL_INFO, "Found interface %d with endpoints %02x/%02x for device %d-%d", usbdev->interface, usbdev->ep_out, usbdev->ep_in, bus, address);
346 break;
347 }
348 libusb_free_config_descriptor(config);
349
350 if(j == config->bNumInterfaces) {
351 usbmuxd_log(LL_WARNING, "Could not find a suitable USB interface for device %d-%d", bus, address);
352 libusb_close(handle);
353 free(usbdev);
354 continue;
355 }
356
357 if((res = libusb_claim_interface(handle, usbdev->interface)) != 0) {
358 usbmuxd_log(LL_WARNING, "Could not claim interface %d for device %d-%d: %d", usbdev->interface, bus, address, res);
359 libusb_close(handle);
360 free(usbdev);
361 continue;
362 }
363
364 if((res = libusb_get_string_descriptor_ascii(handle, devdesc.iSerialNumber, (uint8_t *)usbdev->serial, 256)) <= 0) {
365 usbmuxd_log(LL_WARNING, "Could not get serial number for device %d-%d: %d", bus, address, res);
366 libusb_release_interface(handle, usbdev->interface);
367 libusb_close(handle);
368 free(usbdev);
369 continue;
370 }
371 usbdev->serial[res] = 0;
372 usbdev->bus = bus;
373 usbdev->address = address;
374 usbdev->vid = devdesc.idVendor;
375 usbdev->pid = devdesc.idProduct;
376 usbdev->dev = handle;
377 usbdev->alive = 1;
378 usbdev->wMaxPacketSize = libusb_get_max_packet_size(dev, usbdev->ep_out);
379 if (usbdev->wMaxPacketSize <= 0) {
380 usbmuxd_log(LL_ERROR, "Could not determine wMaxPacketSize for device %d-%d, setting to 64", usbdev->bus, usbdev->address);
381 usbdev->wMaxPacketSize = 64;
382 } else {
383 usbmuxd_log(LL_INFO, "Using wMaxPacketSize=%d for device %d-%d", usbdev->wMaxPacketSize, usbdev->bus, usbdev->address);
384 }
385
386 collection_init(&usbdev->tx_xfers);
387
388 collection_add(&device_list, usbdev);
389
390 if(device_add(usbdev) < 0) {
391 usb_disconnect(usbdev);
392 continue;
393 }
394 if(start_rx(usbdev) < 0) {
395 device_remove(usbdev);
396 usb_disconnect(usbdev);
397 continue;
398 }
399 valid_count++;
400 }
401 FOREACH(struct usb_device *usbdev, &device_list) {
402 if(!usbdev->alive) {
403 device_remove(usbdev);
404 usb_disconnect(usbdev);
405 }
406 } ENDFOREACH
407
408 libusb_free_device_list(devs, 1);
409
410 gettimeofday(&next_dev_poll_time, NULL);
411 next_dev_poll_time.tv_usec += DEVICE_POLL_TIME * 1000;
412 next_dev_poll_time.tv_sec += next_dev_poll_time.tv_usec / 1000000;
413 next_dev_poll_time.tv_usec = next_dev_poll_time.tv_usec % 1000000;
414
415 return valid_count;
416}
417
418const char *usb_get_serial(struct usb_device *dev)
419{
420 if(!dev->dev) {
421 return NULL;
422 }
423 return dev->serial;
424}
425
426uint32_t usb_get_location(struct usb_device *dev)
427{
428 if(!dev->dev) {
429 return 0;
430 }
431 return (dev->bus << 16) | dev->address;
432}
433
434uint16_t usb_get_pid(struct usb_device *dev)
435{
436 if(!dev->dev) {
437 return 0;
438 }
439 return dev->pid;
440}
441
442void usb_get_fds(struct fdlist *list)
443{
444 const struct libusb_pollfd **usbfds;
445 const struct libusb_pollfd **p;
446 usbfds = libusb_get_pollfds(NULL);
447 if(!usbfds) {
448 usbmuxd_log(LL_ERROR, "libusb_get_pollfds failed");
449 return;
450 }
451 p = usbfds;
452 while(*p) {
453 fdlist_add(list, FD_USB, (*p)->fd, (*p)->events);
454 p++;
455 }
456 free(usbfds);
457}
458
459void usb_autodiscover(int enable)
460{
461 usbmuxd_log(LL_DEBUG, "usb polling enable: %d", enable);
462 device_polling = enable;
463}
464
465static int dev_poll_remain_ms(void)
466{
467 int msecs;
468 struct timeval tv;
469 if(!device_polling)
470 return 100000; // devices will never be polled if this is > 0
471 gettimeofday(&tv, NULL);
472 msecs = (next_dev_poll_time.tv_sec - tv.tv_sec) * 1000;
473 msecs += (next_dev_poll_time.tv_usec - tv.tv_usec) / 1000;
474 if(msecs < 0)
475 return 0;
476 return msecs;
477}
478
479int usb_get_timeout(void)
480{
481 struct timeval tv;
482 int msec;
483 int res;
484 int pollrem;
485 pollrem = dev_poll_remain_ms();
486 res = libusb_get_next_timeout(NULL, &tv);
487 if(res == 0)
488 return pollrem;
489 if(res < 0) {
490 usbmuxd_log(LL_ERROR, "libusb_get_next_timeout failed: %d", res);
491 return pollrem;
492 }
493 msec = tv.tv_sec * 1000;
494 msec += tv.tv_usec / 1000;
495 if(msec > pollrem)
496 return pollrem;
497 return msec;
498}
499
500int usb_process(void)
501{
502 int res;
503 struct timeval tv;
504 tv.tv_sec = tv.tv_usec = 0;
505 res = libusb_handle_events_timeout(NULL, &tv);
506 if(res < 0) {
507 usbmuxd_log(LL_ERROR, "libusb_handle_events_timeout failed: %d", res);
508 return res;
509 }
510 // reap devices marked dead due to an RX error
511 FOREACH(struct usb_device *usbdev, &device_list) {
512 if(!usbdev->alive) {
513 device_remove(usbdev);
514 usb_disconnect(usbdev);
515 }
516 } ENDFOREACH
517
518 if(dev_poll_remain_ms() <= 0) {
519 res = usb_discover();
520 if(res < 0) {
521 usbmuxd_log(LL_ERROR, "usb_discover failed: %d", res);
522 return res;
523 }
524 }
525 return 0;
526}
527
528int usb_process_timeout(int msec)
529{
530 int res;
531 struct timeval tleft, tcur, tfin;
532 gettimeofday(&tcur, NULL);
533 tfin.tv_sec = tcur.tv_sec + (msec / 1000);
534 tfin.tv_usec = tcur.tv_usec + (msec % 1000) * 1000;
535 tfin.tv_sec += tfin.tv_usec / 1000000;
536 tfin.tv_usec %= 1000000;
537 while((tfin.tv_sec > tcur.tv_sec) || ((tfin.tv_sec == tcur.tv_sec) && (tfin.tv_usec > tcur.tv_usec))) {
538 tleft.tv_sec = tfin.tv_sec - tcur.tv_sec;
539 tleft.tv_usec = tfin.tv_usec - tcur.tv_usec;
540 if(tleft.tv_usec < 0) {
541 tleft.tv_usec += 1000000;
542 tleft.tv_sec -= 1;
543 }
544 res = libusb_handle_events_timeout(NULL, &tleft);
545 if(res < 0) {
546 usbmuxd_log(LL_ERROR, "libusb_handle_events_timeout failed: %d", res);
547 return res;
548 }
549 // reap devices marked dead due to an RX error
550 FOREACH(struct usb_device *usbdev, &device_list) {
551 if(!usbdev->alive) {
552 device_remove(usbdev);
553 usb_disconnect(usbdev);
554 }
555 } ENDFOREACH
556 gettimeofday(&tcur, NULL);
557 }
558 return 0;
559}
560
561int usb_init(void)
562{
563 int res;
564 usbmuxd_log(LL_DEBUG, "usb_init for linux / libusb 1.0");
565
566 devlist_failures = 0;
567 device_polling = 1;
568 res = libusb_init(NULL);
569 //libusb_set_debug(NULL, 3);
570 if(res != 0) {
571 usbmuxd_log(LL_FATAL, "libusb_init failed: %d", res);
572 return -1;
573 }
574
575 collection_init(&device_list);
576
577 return usb_discover();
578}
579
580void usb_shutdown(void)
581{
582 usbmuxd_log(LL_DEBUG, "usb_shutdown");
583 FOREACH(struct usb_device *usbdev, &device_list) {
584 device_remove(usbdev);
585 usb_disconnect(usbdev);
586 } ENDFOREACH
587 collection_free(&device_list);
588 libusb_exit(NULL);
589}
diff --git a/daemon/usb.h b/daemon/usb.h
deleted file mode 100644
index 9884d7f..0000000
--- a/daemon/usb.h
+++ /dev/null
@@ -1,66 +0,0 @@
1/*
2 usbmuxd - iPhone/iPod Touch USB multiplex server daemon
3
4Copyright (C) 2009 Hector Martin "marcan" <hector@marcansoft.com>
5Copyright (C) 2009 Nikias Bassen <nikias@gmx.li>
6Copyright (C) 2009 Martin Szulecki <opensuse@sukimashita.com>
7
8This program is free software; you can redistribute it and/or modify
9it under the terms of the GNU General Public License as published by
10the Free Software Foundation, either version 2 or version 3.
11
12This program is distributed in the hope that it will be useful,
13but WITHOUT ANY WARRANTY; without even the implied warranty of
14MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15GNU General Public License for more details.
16
17You should have received a copy of the GNU General Public License
18along with this program; if not, write to the Free Software
19Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20
21*/
22
23#ifndef __USB_H__
24#define __USB_H__
25
26#include <stdint.h>
27#include "utils.h"
28
29#define INTERFACE_CLASS 255
30#define INTERFACE_SUBCLASS 254
31#define INTERFACE_PROTOCOL 2
32
33// libusb fragments packets larger than this (usbfs limitation)
34// on input, this creates race conditions and other issues
35#define USB_MRU 16384
36
37// max transmission packet size
38// libusb fragments these too, but doesn't send ZLPs so we're safe
39// but we need to send a ZLP ourselves at the end (see usb-linux.c)
40// we're using 3 * 16384 to optimize for the fragmentation
41// this results in three URBs per full transfer, 32 USB packets each
42// if there are ZLP issues this should make them show up easily too
43#define USB_MTU (3 * 16384)
44
45#define USB_PACKET_SIZE 512
46
47#define VID_APPLE 0x5ac
48#define PID_RANGE_LOW 0x1290
49#define PID_RANGE_MAX 0x129f
50
51struct usb_device;
52
53int usb_init(void);
54void usb_shutdown(void);
55const char *usb_get_serial(struct usb_device *dev);
56uint32_t usb_get_location(struct usb_device *dev);
57uint16_t usb_get_pid(struct usb_device *dev);
58void usb_get_fds(struct fdlist *list);
59int usb_get_timeout(void);
60int usb_send(struct usb_device *dev, const unsigned char *buf, int length);
61int usb_discover(void);
62void usb_autodiscover(int enable);
63int usb_process(void);
64int usb_process_timeout(int msec);
65
66#endif
diff --git a/docs/Makefile.am b/docs/Makefile.am
new file mode 100644
index 0000000..70a61ca
--- /dev/null
+++ b/docs/Makefile.am
@@ -0,0 +1,3 @@
1man_MANS = usbmuxd.8
2
3EXTRA_DIST = $(man_MANS)
diff --git a/docs/usbmuxd.8 b/docs/usbmuxd.8
new file mode 100644
index 0000000..590afdc
--- /dev/null
+++ b/docs/usbmuxd.8
@@ -0,0 +1,80 @@
1.TH "usbmuxd" 8
2.SH NAME
3usbmuxd \- Expose a socket to multiplex connections from and to iOS devices.
4.SH SYNOPSIS
5.B usbmuxd
6[OPTIONS]
7
8.SH DESCRIPTION
9
10usbmuxd stands for "USB multiplexing daemon". This daemon is in charge of
11multiplexing connections over USB to an iOS device. To users, it means
12you can sync your music, contacts, photos, etc. over USB. To developers, it
13means you can connect to any listening localhost socket on the device. usbmuxd
14is not used for tethering data transfer which uses a dedicated USB interface as
15a virtual network device. Multiple connections to different TCP ports can happen
16in parallel. The higher-level layers are handled by libimobiledevice.
17
18When usbmuxd is running (normally started, or stopped as a result of "udev"
19auto-insertion messages or by systemd) it provides a socket interface in
20"/var/run/usbmuxd" that is designed to be compatible with the socket interface
21that is provided on Mac OS X.
22
23You should also create a "usbmux" user that has access to USB devices on your
24system. Alternatively, you can pass a different username using the -U argument.
25
26Due to iOS 7 the daemon now also manages pairing records with iOS devices and
27the host in "/var/lib/lockdown" (Linux) or "/var/db/lockdown" (Mac OS X).
28Ensure proper permissions are setup for the daemon to access the directory.
29
30.SH OPTIONS
31.TP
32.B \-U, \-\-user USER
33Change to this user after startup (needs USB privileges).
34.TP
35.B \-f, \-\-foreground
36Do not daemonize (implies one -v).
37.TP
38.B \-n, \-\-disable-hotplug
39Disables automatic discovery of devices on hotplug. Starting another instance
40will trigger discovery instead.
41.TP
42.B \-z, \-\-enable-exit
43Enable "--exit" request from other instances and exit automatically if no
44device is attached.
45.TP
46.B \-u, \-\-udev
47Run in udev operation mode (implies -n and -z).
48.TP
49.B \-s, \-\-systemd
50Run in systemd operation mode (implies -z and -f).
51.TP
52.B \-x, \-\-exit
53Notify a running instance to exit if there are no devices connected (sends
54SIGUSR1 to running instance) and exit.
55.TP
56.B \-X, \-\-force-exit
57Notify a running instance to exit even if there are still devices connected
58(always works) and exit.
59.TP
60.B \-v, \-\-verbose
61be verbose (use twice or more to increase verbose level).
62.TP
63.B \-V, \-\-version
64print version information and exit.
65.TP
66.B \-h, \-\-help
67prints usage information.
68
69.SH AUTHOR
70The first usbmuxd daemon implementation was authored by Hector Martin.
71
72Now mainly developed by Nikias Bassen, Martin Szulecki and contributors (see AUTHORS file).
73
74.SH SEE ALSO
75idevice_id(1), iproxy(1).
76
77.SH ON THE WEB
78https://libimobiledevice.org
79
80https://github.com/libimobiledevice/usbmuxd
diff --git a/git-version-gen b/git-version-gen
new file mode 100755
index 0000000..3eb6a42
--- /dev/null
+++ b/git-version-gen
@@ -0,0 +1,19 @@
1#!/bin/sh
2SRCDIR=`dirname $0`
3if test -n "$1"; then
4 VER=$1
5else
6 if test -d "${SRCDIR}/.git" && test -x "`which git`" ; then
7 git update-index -q --refresh
8 if ! VER=`git describe --tags --dirty 2>/dev/null`; then
9 COMMIT=`git rev-parse --short HEAD`
10 DIRTY=`git diff --quiet HEAD || echo "-dirty"`
11 VER=`sed -n '1,/RE/s/Version \(.*\)/\1/p' ${SRCDIR}/NEWS`-git-${COMMIT}${DIRTY}
12 fi
13 else
14 if test -f "${SRCDIR}/.tarball-version"; then
15 VER=`cat "${SRCDIR}/.tarball-version"`
16 fi
17 fi
18fi
19printf %s "$VER"
diff --git a/libusbmuxd.pc.in b/libusbmuxd.pc.in
deleted file mode 100644
index 1ecd8ed..0000000
--- a/libusbmuxd.pc.in
+++ /dev/null
@@ -1,11 +0,0 @@
1prefix=${CMAKE_INSTALL_PREFIX}
2exec_prefix=${CMAKE_INSTALL_PREFIX}
3libdir=${CMAKE_INSTALL_PREFIX}/lib${LIB_SUFFIX}
4includedir=${CMAKE_INSTALL_PREFIX}/include
5
6Name: libusbmuxd
7Description: A library to communicate with the usbmux daemon
8Version: ${USBMUXD_VERSION}
9Libs: -L${CMAKE_INSTALL_PREFIX}/lib${LIB_SUFFIX} -lusbmuxd
10Cflags: -I${CMAKE_INSTALL_PREFIX}/include
11
diff --git a/libusbmuxd/CMakeLists.txt b/libusbmuxd/CMakeLists.txt
deleted file mode 100644
index 81203d3..0000000
--- a/libusbmuxd/CMakeLists.txt
+++ /dev/null
@@ -1,29 +0,0 @@
1include_directories (${CMAKE_SOURCE_DIR}/common)
2find_package(Threads)
3
4add_library (libusbmuxd SHARED libusbmuxd.c sock_stuff.c ${CMAKE_SOURCE_DIR}/common/utils.c)
5find_library (PTHREAD pthread)
6
7if (HAVE_PLIST)
8 add_definitions("-DHAVE_PLIST")
9 message("-- libusbmuxd will be built with protocol version 1 support")
10endif()
11if(WIN32)
12 set(OPT_LIBS ${OPT_LIBS} ws2_32)
13endif()
14include_directories(${OPT_INCLUDES})
15target_link_libraries (libusbmuxd ${CMAKE_THREAD_LIBS_INIT} ${OPT_LIBS})
16
17# 'lib' is a UNIXism, the proper CMake target is usbmuxd
18# But we can't use that due to the conflict with the usbmuxd daemon,
19# so instead change the library output base name to usbmuxd here
20set_target_properties(libusbmuxd PROPERTIES OUTPUT_NAME usbmuxd)
21set_target_properties(libusbmuxd PROPERTIES VERSION ${LIBUSBMUXD_VERSION})
22set_target_properties(libusbmuxd PROPERTIES SOVERSION ${LIBUSBMUXD_SOVERSION})
23
24install(TARGETS libusbmuxd
25 RUNTIME DESTINATION bin
26 ARCHIVE DESTINATION lib${LIB_SUFFIX}
27 LIBRARY DESTINATION lib${LIB_SUFFIX}
28)
29install(FILES usbmuxd.h usbmuxd-proto.h DESTINATION include)
diff --git a/libusbmuxd/libusbmuxd.c b/libusbmuxd/libusbmuxd.c
deleted file mode 100644
index 5eaf8e6..0000000
--- a/libusbmuxd/libusbmuxd.c
+++ /dev/null
@@ -1,782 +0,0 @@
1/*
2 libusbmuxd - client library to talk to usbmuxd
3
4Copyright (C) 2009-2010 Nikias Bassen <nikias@gmx.li>
5Copyright (C) 2009 Paul Sladen <libiphone@paul.sladen.org>
6Copyright (C) 2009 Martin Szulecki <opensuse@sukimashita.com>
7
8This library is free software; you can redistribute it and/or modify
9it under the terms of the GNU Lesser General Public License as
10published by the Free Software Foundation, either version 2.1 of the
11License, or (at your option) any later version.
12
13This library is distributed in the hope that it will be useful,
14but WITHOUT ANY WARRANTY; without even the implied warranty of
15MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16GNU General Public License for more details.
17
18You should have received a copy of the GNU Lesser General Public
19License along with this program; if not, write to the Free Software
20Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21
22*/
23
24#include <stdint.h>
25#include <stdlib.h>
26#include <errno.h>
27#include <stdio.h>
28#include <string.h>
29#ifdef WIN32
30#include <windows.h>
31#include <winsock2.h>
32#define sleep(x) Sleep(x*1000)
33#else
34#include <sys/socket.h>
35#include <arpa/inet.h>
36#endif
37#include <unistd.h>
38#include <signal.h>
39#include <pthread.h>
40
41#ifdef HAVE_PLIST
42#include <plist/plist.h>
43#define PLIST_BUNDLE_ID "com.marcansoft.usbmuxd"
44#define PLIST_CLIENT_VERSION_STRING "usbmuxd built for freedom"
45#define PLIST_PROGNAME "libusbmuxd"
46#endif
47
48// usbmuxd public interface
49#include "usbmuxd.h"
50// usbmuxd protocol
51#include "usbmuxd-proto.h"
52// socket utility functions
53#include "sock_stuff.h"
54// misc utility functions
55#include "utils.h"
56
57static struct collection devices;
58static usbmuxd_event_cb_t event_cb = NULL;
59pthread_t devmon;
60static int listenfd = -1;
61
62static int use_tag = 0;
63static int proto_version = 0;
64
65/**
66 * Finds a device info record by its handle.
67 * if the record is not found, NULL is returned.
68 */
69static usbmuxd_device_info_t *devices_find(int handle)
70{
71 FOREACH(usbmuxd_device_info_t *dev, &devices) {
72 if (dev && dev->handle == handle) {
73 return dev;
74 }
75 } ENDFOREACH
76 return NULL;
77}
78
79/**
80 * Creates a socket connection to usbmuxd.
81 * For Mac/Linux it is a unix domain socket,
82 * for Windows it is a tcp socket.
83 */
84static int connect_usbmuxd_socket()
85{
86#if defined(WIN32) || defined(__CYGWIN__)
87 return connect_socket("127.0.0.1", USBMUXD_SOCKET_PORT);
88#else
89 return connect_unix_socket(USBMUXD_SOCKET_FILE);
90#endif
91}
92
93static int receive_packet(int sfd, struct usbmuxd_header *header, void **payload, int timeout)
94{
95 int recv_len;
96 struct usbmuxd_header hdr;
97 char *payload_loc = NULL;
98
99 header->length = 0;
100 header->version = 0;
101 header->message = 0;
102 header->tag = 0;
103
104 recv_len = recv_buf_timeout(sfd, &hdr, sizeof(hdr), 0, timeout);
105 if (recv_len < 0) {
106 return recv_len;
107 } else if (recv_len < sizeof(hdr)) {
108 return recv_len;
109 }
110
111 uint32_t payload_size = hdr.length - sizeof(hdr);
112 if (payload_size > 0) {
113 payload_loc = (char*)malloc(payload_size);
114 if (recv_buf_timeout(sfd, payload_loc, payload_size, 0, 5000) != payload_size) {
115 fprintf(stderr, "%s: Error receiving payload of size %d\n", __func__, payload_size);
116 free(payload_loc);
117 return -EBADMSG;
118 }
119 }
120
121#ifdef HAVE_PLIST
122 if (hdr.message == MESSAGE_PLIST) {
123 char *message = NULL;
124 plist_t plist = NULL;
125 plist_from_xml(payload_loc, payload_size, &plist);
126 free(payload_loc);
127
128 if (!plist) {
129 fprintf(stderr, "%s: Error getting plist from payload!\n", __func__);
130 return -EBADMSG;
131 }
132
133 plist_t node = plist_dict_get_item(plist, "MessageType");
134 if (plist_get_node_type(node) != PLIST_STRING) {
135 fprintf(stderr, "%s: Error getting message type from plist!\n", __func__);
136 free(plist);
137 return -EBADMSG;
138 }
139
140 plist_get_string_val(node, &message);
141 if (message) {
142 uint64_t val = 0;
143 if (strcmp(message, "Result") == 0) {
144 /* result message */
145 uint32_t dwval = 0;
146 plist_t n = plist_dict_get_item(plist, "Number");
147 plist_get_uint_val(n, &val);
148 *payload = malloc(sizeof(uint32_t));
149 dwval = val;
150 memcpy(*payload, &dwval, sizeof(dwval));
151 hdr.length = sizeof(hdr) + sizeof(dwval);
152 hdr.message = MESSAGE_RESULT;
153 } else if (strcmp(message, "Attached") == 0) {
154 /* device add message */
155 struct usbmuxd_device_record *dev = NULL;
156 plist_t props = plist_dict_get_item(plist, "Properties");
157 if (!props) {
158 fprintf(stderr, "%s: Could not get properties for message '%s' from plist!\n", __func__, message);
159 plist_free(plist);
160 return -EBADMSG;
161 }
162 dev = (struct usbmuxd_device_record*)malloc(sizeof(struct usbmuxd_device_record));
163 memset(dev, 0, sizeof(struct usbmuxd_device_record));
164
165 plist_t n = plist_dict_get_item(props, "DeviceID");
166 plist_get_uint_val(n, &val);
167 dev->device_id = (uint32_t)val;
168
169 n = plist_dict_get_item(props, "ProductID");
170 plist_get_uint_val(n, &val);
171 dev->product_id = (uint32_t)val;
172
173 n = plist_dict_get_item(props, "SerialNumber");
174 char *strval = NULL;
175 plist_get_string_val(n, &strval);
176 if (strval) {
177 strcpy(dev->serial_number, strval);
178 free(strval);
179 }
180 n = plist_dict_get_item(props, "LocationID");
181 plist_get_uint_val(n, &val);
182 dev->location = (uint32_t)val;
183 *payload = (void*)dev;
184 hdr.length = sizeof(hdr) + sizeof(struct usbmuxd_device_record);
185 hdr.message = MESSAGE_DEVICE_ADD;
186 } else if (strcmp(message, "Detached") == 0) {
187 /* device remove message */
188 uint32_t dwval = 0;
189 plist_t n = plist_dict_get_item(plist, "DeviceID");
190 if (n) {
191 plist_get_uint_val(n, &val);
192 *payload = malloc(sizeof(uint32_t));
193 dwval = val;
194 memcpy(*payload, &dwval, sizeof(dwval));
195 hdr.length = sizeof(hdr) + sizeof(dwval);
196 hdr.message = MESSAGE_DEVICE_REMOVE;
197 }
198 } else {
199 fprintf(stderr, "%s: Unexpected message '%s' in plist!\n", __func__, message);
200 plist_free(plist);
201 return -EBADMSG;
202 }
203 }
204 plist_free(plist);
205 } else
206#endif
207 {
208 *payload = payload_loc;
209 }
210
211 memcpy(header, &hdr, sizeof(hdr));
212
213 return hdr.length;
214}
215
216/**
217 * Retrieves the result code to a previously sent request.
218 */
219static int usbmuxd_get_result(int sfd, uint32_t tag, uint32_t * result)
220{
221 struct usbmuxd_header hdr;
222 int recv_len;
223 uint32_t *res = NULL;
224
225 if (!result) {
226 return -EINVAL;
227 }
228 *result = -1;
229
230 if ((recv_len = receive_packet(sfd, &hdr, (void**)&res, 5000)) < 0) {
231 fprintf(stderr, "%s: Error receiving packet: %d\n", __func__, errno);
232 if (res)
233 free(res);
234 return -errno;
235 }
236 if (recv_len < sizeof(hdr)) {
237 fprintf(stderr, "%s: Received packet is too small!\n", __func__);
238 if (res)
239 free(res);
240 return -EPROTO;
241 }
242
243 if (hdr.message == MESSAGE_RESULT) {
244 int ret = 0;
245 if (res && (hdr.tag == tag)) {
246 memcpy(result, res, sizeof(uint32_t));
247 ret = 1;
248 }
249 if (res)
250 free(res);
251 return ret;
252 }
253 fprintf(stderr, "%s: Unexpected message of type %d received!\n", __func__, hdr.message);
254 if (res)
255 free(res);
256 return -EPROTO;
257}
258
259static int send_packet(int sfd, uint32_t message, uint32_t tag, void *payload, uint32_t payload_size)
260{
261 struct usbmuxd_header header;
262
263 header.length = sizeof(struct usbmuxd_header);
264 header.version = proto_version;
265 header.message = message;
266 header.tag = tag;
267 if (payload && (payload_size > 0)) {
268 header.length += payload_size;
269 }
270 int sent = send_buf(sfd, &header, sizeof(header));
271 if (sent != sizeof(header)) {
272 fprintf(stderr, "%s: ERROR: could not send packet header\n", __func__);
273 return -1;
274 }
275 if (payload && (payload_size > 0)) {
276 sent += send_buf(sfd, payload, payload_size);
277 }
278 if (sent != (int)header.length) {
279 fprintf(stderr, "%s: ERROR: could not send whole packet\n", __func__);
280 close_socket(sfd);
281 return -1;
282 }
283 return sent;
284}
285
286static int send_listen_packet(int sfd, uint32_t tag)
287{
288 int res = 0;
289#ifdef HAVE_PLIST
290 if (proto_version == 1) {
291 /* plist packet */
292 char *payload = NULL;
293 uint32_t payload_size = 0;
294 plist_t plist;
295
296 /* construct message plist */
297 plist = plist_new_dict();
298 plist_dict_insert_item(plist, "BundleID", plist_new_string(PLIST_BUNDLE_ID));
299 plist_dict_insert_item(plist, "ClientVersionString", plist_new_string(PLIST_CLIENT_VERSION_STRING));
300 plist_dict_insert_item(plist, "MessageType", plist_new_string("Listen"));
301 plist_dict_insert_item(plist, "ProgName", plist_new_string(PLIST_PROGNAME));
302 plist_to_xml(plist, &payload, &payload_size);
303 plist_free(plist);
304
305 res = send_packet(sfd, MESSAGE_PLIST, tag, payload, payload_size);
306 free(payload);
307 } else
308#endif
309 {
310 /* binary packet */
311 res = send_packet(sfd, MESSAGE_LISTEN, tag, NULL, 0);
312 }
313 return res;
314}
315
316static int send_connect_packet(int sfd, uint32_t tag, uint32_t device_id, uint16_t port)
317{
318 int res = 0;
319#ifdef HAVE_PLIST
320 if (proto_version == 1) {
321 /* plist packet */
322 char *payload = NULL;
323 uint32_t payload_size = 0;
324 plist_t plist;
325
326 /* construct message plist */
327 plist = plist_new_dict();
328 plist_dict_insert_item(plist, "BundleID", plist_new_string(PLIST_BUNDLE_ID));
329 plist_dict_insert_item(plist, "ClientVersionString", plist_new_string(PLIST_CLIENT_VERSION_STRING));
330 plist_dict_insert_item(plist, "MessageType", plist_new_string("Connect"));
331 plist_dict_insert_item(plist, "DeviceID", plist_new_uint(device_id));
332 plist_dict_insert_item(plist, "PortNumber", plist_new_uint(htons(port)));
333 plist_dict_insert_item(plist, "ProgName", plist_new_string(PLIST_PROGNAME));
334 plist_to_xml(plist, &payload, &payload_size);
335 plist_free(plist);
336
337 res = send_packet(sfd, MESSAGE_PLIST, tag, (void*)payload, payload_size);
338 free(payload);
339 } else
340#endif
341 {
342 /* binary packet */
343 struct {
344 uint32_t device_id;
345 uint16_t port;
346 uint16_t reserved;
347 } conninfo;
348
349 conninfo.device_id = device_id;
350 conninfo.port = htons(port);
351 conninfo.reserved = 0;
352
353 res = send_packet(sfd, MESSAGE_CONNECT, tag, &conninfo, sizeof(conninfo));
354 }
355 return res;
356}
357
358/**
359 * Generates an event, i.e. calls the callback function.
360 * A reference to a populated usbmuxd_event_t with information about the event
361 * and the corresponding device will be passed to the callback function.
362 */
363static void generate_event(usbmuxd_event_cb_t callback, const usbmuxd_device_info_t *dev, enum usbmuxd_event_type event, void *user_data)
364{
365 usbmuxd_event_t ev;
366
367 if (!callback || !dev) {
368 return;
369 }
370
371 ev.event = event;
372 memcpy(&ev.device, dev, sizeof(usbmuxd_device_info_t));
373
374 callback(&ev, user_data);
375}
376
377/**
378 * Tries to connect to usbmuxd and wait if it is not running.
379 *
380 * TODO inotify support should come here
381 */
382static int usbmuxd_listen()
383{
384 int sfd;
385 uint32_t res = -1;
386
387#ifdef HAVE_PLIST
388retry:
389#endif
390 sfd = connect_usbmuxd_socket();
391 if (sfd < 0) {
392 while (event_cb) {
393 if ((sfd = connect_usbmuxd_socket()) > 0) {
394 break;
395 }
396 sleep(1);
397 }
398 }
399
400 if (sfd < 0) {
401 fprintf(stderr, "%s: ERROR: usbmuxd was supposed to be running here...\n", __func__);
402 return sfd;
403 }
404
405 use_tag++;
406 if (send_listen_packet(sfd, use_tag) <= 0) {
407 fprintf(stderr, "%s: ERROR: could not send listen packet\n", __func__);
408 close_socket(sfd);
409 return -1;
410 }
411 if (usbmuxd_get_result(sfd, use_tag, &res) && (res != 0)) {
412 close_socket(sfd);
413#ifdef HAVE_PLIST
414 if ((res == RESULT_BADVERSION) && (proto_version != 1)) {
415 proto_version = 1;
416 goto retry;
417 }
418#endif
419 fprintf(stderr, "%s: ERROR: did not get OK but %d\n", __func__, res);
420 return -1;
421 }
422
423 return sfd;
424}
425
426/**
427 * Waits for an event to occur, i.e. a packet coming from usbmuxd.
428 * Calls generate_event to pass the event via callback to the client program.
429 */
430int get_next_event(int sfd, usbmuxd_event_cb_t callback, void *user_data)
431{
432 struct usbmuxd_header hdr;
433 void *payload = NULL;
434
435 /* block until we receive something */
436 if (receive_packet(sfd, &hdr, &payload, 0) < 0) {
437 // when then usbmuxd connection fails,
438 // generate remove events for every device that
439 // is still present so applications know about it
440 FOREACH(usbmuxd_device_info_t *dev, &devices) {
441 generate_event(callback, dev, UE_DEVICE_REMOVE, user_data);
442 collection_remove(&devices, dev);
443 } ENDFOREACH
444 return -EIO;
445 }
446
447 if ((hdr.length > sizeof(hdr)) && !payload) {
448 fprintf(stderr, "%s: Invalid packet received, payload is missing!\n", __func__);
449 return -EBADMSG;
450 }
451
452 if (hdr.message == MESSAGE_DEVICE_ADD) {
453 struct usbmuxd_device_record *dev = payload;
454 usbmuxd_device_info_t *devinfo = (usbmuxd_device_info_t*)malloc(sizeof(usbmuxd_device_info_t));
455 if (!devinfo) {
456 fprintf(stderr, "%s: Out of memory!\n", __func__);
457 free(payload);
458 return -1;
459 }
460
461 devinfo->handle = dev->device_id;
462 devinfo->product_id = dev->product_id;
463 memset(devinfo->uuid, '\0', sizeof(devinfo->uuid));
464 memcpy(devinfo->uuid, dev->serial_number, sizeof(devinfo->uuid));
465
466 collection_add(&devices, devinfo);
467 generate_event(callback, devinfo, UE_DEVICE_ADD, user_data);
468 } else if (hdr.message == MESSAGE_DEVICE_REMOVE) {
469 uint32_t handle;
470 usbmuxd_device_info_t *devinfo;
471
472 memcpy(&handle, payload, sizeof(uint32_t));
473
474 devinfo = devices_find(handle);
475 if (!devinfo) {
476 fprintf(stderr, "%s: WARNING: got device remove message for handle %d, but couldn't find the corresponding handle in the device list. This event will be ignored.\n", __func__, handle);
477 } else {
478 generate_event(callback, devinfo, UE_DEVICE_REMOVE, user_data);
479 collection_remove(&devices, devinfo);
480 }
481 } else {
482 fprintf(stderr, "%s: Unexpected message type %d length %d received!\n", __func__, hdr.message, hdr.length);
483 }
484 if (payload) {
485 free(payload);
486 }
487 return 0;
488}
489
490/**
491 * Device Monitor thread function.
492 *
493 * This function sets up a connection to usbmuxd
494 */
495static void *device_monitor(void *data)
496{
497 collection_init(&devices);
498
499 while (event_cb) {
500
501 listenfd = usbmuxd_listen();
502 if (listenfd < 0) {
503 continue;
504 }
505
506 while (event_cb) {
507 int res = get_next_event(listenfd, event_cb, data);
508 if (res < 0) {
509 break;
510 }
511 }
512 }
513
514 collection_free(&devices);
515
516 return NULL;
517}
518
519int usbmuxd_subscribe(usbmuxd_event_cb_t callback, void *user_data)
520{
521 int res;
522
523 if (!callback) {
524 return -EINVAL;
525 }
526 event_cb = callback;
527
528 res = pthread_create(&devmon, NULL, device_monitor, user_data);
529 if (res != 0) {
530 fprintf(stderr, "%s: ERROR: Could not start device watcher thread!\n", __func__);
531 return res;
532 }
533 return 0;
534}
535
536int usbmuxd_unsubscribe()
537{
538 event_cb = NULL;
539
540 if (pthread_kill(devmon, 0) == 0) {
541 close_socket(listenfd);
542 listenfd = -1;
543 pthread_kill(devmon, SIGINT);
544 pthread_join(devmon, NULL);
545 }
546
547 return 0;
548}
549
550int usbmuxd_get_device_list(usbmuxd_device_info_t **device_list)
551{
552 int sfd;
553 int listen_success = 0;
554 uint32_t res;
555 usbmuxd_device_info_t *newlist = NULL;
556 struct usbmuxd_header hdr;
557 struct usbmuxd_device_record *dev_info;
558 int dev_cnt = 0;
559 void *payload = NULL;
560
561#ifdef HAVE_PLIST
562retry:
563#endif
564 sfd = connect_usbmuxd_socket();
565 if (sfd < 0) {
566 fprintf(stderr, "%s: error opening socket!\n", __func__);
567 return sfd;
568 }
569
570 use_tag++;
571 if (send_listen_packet(sfd, use_tag) > 0) {
572 res = -1;
573 // get response
574 if (usbmuxd_get_result(sfd, use_tag, &res) && (res == 0)) {
575 listen_success = 1;
576 } else {
577 close_socket(sfd);
578#ifdef HAVE_PLIST
579 if ((res == RESULT_BADVERSION) && (proto_version != 1)) {
580 proto_version = 1;
581 goto retry;
582 }
583#endif
584 fprintf(stderr,
585 "%s: Did not get response to scan request (with result=0)...\n",
586 __func__);
587 return res;
588 }
589 }
590
591 if (!listen_success) {
592 fprintf(stderr, "%s: Could not send listen request!\n", __func__);
593 return -1;
594 }
595
596 *device_list = NULL;
597 // receive device list
598 while (1) {
599 if (receive_packet(sfd, &hdr, &payload, 1000) > 0) {
600 if (hdr.message == MESSAGE_DEVICE_ADD) {
601 dev_info = payload;
602 newlist = (usbmuxd_device_info_t *) realloc(*device_list, sizeof(usbmuxd_device_info_t) * (dev_cnt + 1));
603 if (newlist) {
604 newlist[dev_cnt].handle =
605 (int) dev_info->device_id;
606 newlist[dev_cnt].product_id =
607 dev_info->product_id;
608 memset(newlist[dev_cnt].uuid, '\0',
609 sizeof(newlist[dev_cnt].uuid));
610 memcpy(newlist[dev_cnt].uuid,
611 dev_info->serial_number,
612 sizeof(newlist[dev_cnt].uuid));
613 *device_list = newlist;
614 dev_cnt++;
615 } else {
616 fprintf(stderr,
617 "%s: ERROR: out of memory when trying to realloc!\n",
618 __func__);
619 if (payload)
620 free(payload);
621 break;
622 }
623 } else {
624 fprintf(stderr, "%s: Unexpected message %d\n", __func__, hdr.message);
625 }
626 if (payload)
627 free(payload);
628 } else {
629 // we _should_ have all of them now.
630 // or perhaps an error occured.
631 break;
632 }
633 }
634
635 // explicitly close connection
636 close_socket(sfd);
637
638 // terminating zero record
639 newlist = (usbmuxd_device_info_t*) realloc(*device_list, sizeof(usbmuxd_device_info_t) * (dev_cnt + 1));
640 memset(newlist + dev_cnt, 0, sizeof(usbmuxd_device_info_t));
641 *device_list = newlist;
642
643 return dev_cnt;
644}
645
646int usbmuxd_device_list_free(usbmuxd_device_info_t **device_list)
647{
648 if (device_list) {
649 free(*device_list);
650 }
651 return 0;
652}
653
654int usbmuxd_get_device_by_uuid(const char *uuid, usbmuxd_device_info_t *device)
655{
656 usbmuxd_device_info_t *dev_list = NULL;
657
658 if (!device) {
659 return -EINVAL;
660 }
661 if (usbmuxd_get_device_list(&dev_list) < 0) {
662 return -ENODEV;
663 }
664
665 int i;
666 int result = 0;
667 for (i = 0; dev_list[i].handle > 0; i++) {
668 if (!uuid) {
669 device->handle = dev_list[i].handle;
670 device->product_id = dev_list[i].product_id;
671 strcpy(device->uuid, dev_list[i].uuid);
672 result = 1;
673 break;
674 }
675 if (!strcmp(uuid, dev_list[i].uuid)) {
676 device->handle = dev_list[i].handle;
677 device->product_id = dev_list[i].product_id;
678 strcpy(device->uuid, dev_list[i].uuid);
679 result = 1;
680 break;
681 }
682 }
683
684 free(dev_list);
685
686 return result;
687}
688
689int usbmuxd_connect(const int handle, const unsigned short port)
690{
691 int sfd;
692 int connected = 0;
693 uint32_t res = -1;
694
695#ifdef HAVE_PLIST
696retry:
697#endif
698 sfd = connect_usbmuxd_socket();
699 if (sfd < 0) {
700 fprintf(stderr, "%s: Error: Connection to usbmuxd failed: %s\n",
701 __func__, strerror(errno));
702 return sfd;
703 }
704
705 use_tag++;
706 if (send_connect_packet(sfd, use_tag, (uint32_t)handle, (uint16_t)port) <= 0) {
707 fprintf(stderr, "%s: Error sending connect message!\n", __func__);
708 } else {
709 // read ACK
710 //fprintf(stderr, "%s: Reading connect result...\n", __func__);
711 if (usbmuxd_get_result(sfd, use_tag, &res)) {
712 if (res == 0) {
713 //fprintf(stderr, "%s: Connect success!\n", __func__);
714 connected = 1;
715 } else {
716#ifdef HAVE_PLIST
717 if ((res == RESULT_BADVERSION) && (proto_version == 0)) {
718 proto_version = 1;
719 close_socket(sfd);
720 goto retry;
721 }
722#endif
723 fprintf(stderr, "%s: Connect failed, Error code=%d\n",
724 __func__, res);
725 }
726 }
727 }
728
729 if (connected) {
730 return sfd;
731 }
732
733 close_socket(sfd);
734
735 return -1;
736}
737
738int usbmuxd_disconnect(int sfd)
739{
740 return close_socket(sfd);
741}
742
743int usbmuxd_send(int sfd, const char *data, uint32_t len, uint32_t *sent_bytes)
744{
745 int num_sent;
746
747 if (sfd < 0) {
748 return -EINVAL;
749 }
750
751 num_sent = send(sfd, (void*)data, len, 0);
752 if (num_sent < 0) {
753 *sent_bytes = 0;
754 fprintf(stderr, "%s: Error %d when sending: %s\n", __func__, num_sent, strerror(errno));
755 return num_sent;
756 } else if ((uint32_t)num_sent < len) {
757 fprintf(stderr, "%s: Warning: Did not send enough (only %d of %d)\n", __func__, num_sent, len);
758 }
759
760 *sent_bytes = num_sent;
761
762 return 0;
763}
764
765int usbmuxd_recv_timeout(int sfd, char *data, uint32_t len, uint32_t *recv_bytes, unsigned int timeout)
766{
767 int num_recv = recv_buf_timeout(sfd, (void*)data, len, 0, timeout);
768 if (num_recv < 0) {
769 *recv_bytes = 0;
770 return num_recv;
771 }
772
773 *recv_bytes = num_recv;
774
775 return 0;
776}
777
778int usbmuxd_recv(int sfd, char *data, uint32_t len, uint32_t *recv_bytes)
779{
780 return usbmuxd_recv_timeout(sfd, data, len, recv_bytes, 5000);
781}
782
diff --git a/libusbmuxd/sock_stuff.c b/libusbmuxd/sock_stuff.c
deleted file mode 100644
index edc738e..0000000
--- a/libusbmuxd/sock_stuff.c
+++ /dev/null
@@ -1,370 +0,0 @@
1/*
2 libusbmuxd - client library to talk to usbmuxd
3
4Copyright (C) 2009 Nikias Bassen <nikias@gmx.li>
5Copyright (C) 2009 Paul Sladen <libiphone@paul.sladen.org>
6Copyright (C) 2009 Martin Szulecki <opensuse@sukimashita.com>
7
8This library is free software; you can redistribute it and/or modify
9it under the terms of the GNU Lesser General Public License as
10published by the Free Software Foundation, either version 2.1 of the
11License, or (at your option) any later version.
12
13This library is distributed in the hope that it will be useful,
14but WITHOUT ANY WARRANTY; without even the implied warranty of
15MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16GNU General Public License for more details.
17
18You should have received a copy of the GNU Lesser General Public
19License along with this program; if not, write to the Free Software
20Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21
22*/
23
24#include <stdio.h>
25#include <stddef.h>
26#include <stdlib.h>
27#include <string.h>
28#include <unistd.h>
29#include <errno.h>
30#include <sys/time.h>
31#include <sys/stat.h>
32#ifdef WIN32
33#include <windows.h>
34#include <winsock2.h>
35static int wsa_init = 0;
36#else
37#include <sys/socket.h>
38#include <sys/un.h>
39#include <netinet/in.h>
40#include <netdb.h>
41#include <arpa/inet.h>
42#endif
43#include "sock_stuff.h"
44
45#define RECV_TIMEOUT 20000
46
47static int verbose = 0;
48
49void sock_stuff_set_verbose(int level)
50{
51 verbose = level;
52}
53
54#ifndef WIN32
55int create_unix_socket(const char *filename)
56{
57 struct sockaddr_un name;
58 int sock;
59 size_t size;
60
61 // remove if still present
62 unlink(filename);
63
64 /* Create the socket. */
65 sock = socket(PF_LOCAL, SOCK_STREAM, 0);
66 if (sock < 0) {
67 perror("socket");
68 return -1;
69 }
70
71 /* Bind a name to the socket. */
72 name.sun_family = AF_LOCAL;
73 strncpy(name.sun_path, filename, sizeof(name.sun_path));
74 name.sun_path[sizeof(name.sun_path) - 1] = '\0';
75
76 /* The size of the address is
77 the offset of the start of the filename,
78 plus its length,
79 plus one for the terminating null byte.
80 Alternatively you can just do:
81 size = SUN_LEN (&name);
82 */
83 size = (offsetof(struct sockaddr_un, sun_path)
84 + strlen(name.sun_path) + 1);
85
86 if (bind(sock, (struct sockaddr *) &name, size) < 0) {
87 perror("bind");
88 close_socket(sock);
89 return -1;
90 }
91
92 if (listen(sock, 10) < 0) {
93 perror("listen");
94 close_socket(sock);
95 return -1;
96 }
97
98 return sock;
99}
100
101int connect_unix_socket(const char *filename)
102{
103 struct sockaddr_un name;
104 int sfd = -1;
105 size_t size;
106 struct stat fst;
107
108 // check if socket file exists...
109 if (stat(filename, &fst) != 0) {
110 if (verbose >= 2)
111 fprintf(stderr, "%s: stat '%s': %s\n", __func__, filename,
112 strerror(errno));
113 return -1;
114 }
115 // ... and if it is a unix domain socket
116 if (!S_ISSOCK(fst.st_mode)) {
117 if (verbose >= 2)
118 fprintf(stderr, "%s: File '%s' is not a socket!\n", __func__,
119 filename);
120 return -1;
121 }
122 // make a new socket
123 if ((sfd = socket(PF_LOCAL, SOCK_STREAM, 0)) < 0) {
124 if (verbose >= 2)
125 fprintf(stderr, "%s: socket: %s\n", __func__, strerror(errno));
126 return -1;
127 }
128 // and connect to 'filename'
129 name.sun_family = AF_LOCAL;
130 strncpy(name.sun_path, filename, sizeof(name.sun_path));
131 name.sun_path[sizeof(name.sun_path) - 1] = 0;
132
133 size = (offsetof(struct sockaddr_un, sun_path)
134 + strlen(name.sun_path) + 1);
135
136 if (connect(sfd, (struct sockaddr *) &name, size) < 0) {
137 close_socket(sfd);
138 if (verbose >= 2)
139 fprintf(stderr, "%s: connect: %s\n", __func__,
140 strerror(errno));
141 return -1;
142 }
143
144 return sfd;
145}
146#endif
147
148int create_socket(uint16_t port)
149{
150 int sfd = -1;
151 int yes = 1;
152#ifdef WIN32
153 WSADATA wsa_data;
154 if (!wsa_init) {
155 if (WSAStartup(MAKEWORD(2,2), &wsa_data) != ERROR_SUCCESS) {
156 fprintf(stderr, "WSAStartup failed!\n");
157 ExitProcess(-1);
158 }
159 wsa_init = 1;
160 }
161#endif
162 struct sockaddr_in saddr;
163
164 if (0 > (sfd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP))) {
165 perror("socket()");
166 return -1;
167 }
168
169 if (setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, (void*)&yes, sizeof(int)) == -1) {
170 perror("setsockopt()");
171 close_socket(sfd);
172 return -1;
173 }
174
175 memset((void *) &saddr, 0, sizeof(saddr));
176 saddr.sin_family = AF_INET;
177 saddr.sin_addr.s_addr = htonl(INADDR_ANY);
178 saddr.sin_port = htons(port);
179
180 if (0 > bind(sfd, (struct sockaddr *) &saddr, sizeof(saddr))) {
181 perror("bind()");
182 close_socket(sfd);
183 return -1;
184 }
185
186 if (listen(sfd, 1) == -1) {
187 perror("listen()");
188 close_socket(sfd);
189 return -1;
190 }
191
192 return sfd;
193}
194
195#if defined(WIN32) || defined(__CYGWIN__)
196int connect_socket(const char *addr, uint16_t port)
197{
198 int sfd = -1;
199 int yes = 1;
200 struct hostent *hp;
201 struct sockaddr_in saddr;
202#ifdef WIN32
203 WSADATA wsa_data;
204 if (!wsa_init) {
205 if (WSAStartup(MAKEWORD(2,2), &wsa_data) != ERROR_SUCCESS) {
206 fprintf(stderr, "WSAStartup failed!\n");
207 ExitProcess(-1);
208 }
209 wsa_init = 1;
210 }
211#endif
212
213 if (!addr) {
214 errno = EINVAL;
215 return -1;
216 }
217
218 if ((hp = gethostbyname(addr)) == NULL) {
219 if (verbose >= 2)
220 fprintf(stderr, "%s: unknown host '%s'\n", __func__, addr);
221 return -1;
222 }
223
224 if (!hp->h_addr) {
225 if (verbose >= 2)
226 fprintf(stderr, "%s: gethostbyname returned NULL address!\n",
227 __func__);
228 return -1;
229 }
230
231 if (0 > (sfd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP))) {
232 perror("socket()");
233 return -1;
234 }
235
236 if (setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, (void*)&yes, sizeof(int)) == -1) {
237 perror("setsockopt()");
238 close_socket(sfd);
239 return -1;
240 }
241
242 memset((void *) &saddr, 0, sizeof(saddr));
243 saddr.sin_family = AF_INET;
244 saddr.sin_addr.s_addr = *(uint32_t *) hp->h_addr;
245 saddr.sin_port = htons(port);
246
247 if (connect(sfd, (struct sockaddr *) &saddr, sizeof(saddr)) < 0) {
248 perror("connect");
249 close_socket(sfd);
250 return -2;
251 }
252
253 return sfd;
254}
255#endif /* WIN32 || __CYGWIN__ */
256
257int check_fd(int fd, fd_mode fdm, unsigned int timeout)
258{
259 fd_set fds;
260 int sret;
261 int eagain;
262 struct timeval to;
263 struct timeval *pto;
264
265 if (fd <= 0) {
266 if (verbose >= 2)
267 fprintf(stderr, "ERROR: invalid fd in check_fd %d\n", fd);
268 return -1;
269 }
270
271 FD_ZERO(&fds);
272 FD_SET(fd, &fds);
273
274 if (timeout > 0) {
275 to.tv_sec = (time_t) (timeout / 1000);
276 to.tv_usec = (time_t) ((timeout - (to.tv_sec * 1000)) * 1000);
277 pto = &to;
278 } else {
279 pto = NULL;
280 }
281
282 sret = -1;
283
284 do {
285 eagain = 0;
286 switch (fdm) {
287 case FDM_READ:
288 sret = select(fd + 1, &fds, NULL, NULL, pto);
289 break;
290 case FDM_WRITE:
291 sret = select(fd + 1, NULL, &fds, NULL, pto);
292 break;
293 case FDM_EXCEPT:
294 sret = select(fd + 1, NULL, NULL, &fds, pto);
295 break;
296 default:
297 return -1;
298 }
299
300 if (sret < 0) {
301 switch (errno) {
302 case EINTR:
303 // interrupt signal in select
304 if (verbose >= 2)
305 fprintf(stderr, "%s: EINTR\n", __func__);
306 eagain = 1;
307 break;
308 case EAGAIN:
309 if (verbose >= 2)
310 fprintf(stderr, "%s: EAGAIN\n", __func__);
311 break;
312 default:
313 if (verbose >= 2)
314 fprintf(stderr, "%s: select failed: %s\n", __func__,
315 strerror(errno));
316 return -1;
317 }
318 }
319 } while (eagain);
320
321 return sret;
322}
323
324int close_socket(int fd) {
325#ifdef WIN32
326 return closesocket(fd);
327#else
328 return close(fd);
329#endif
330}
331
332int recv_buf(int fd, void *data, size_t length)
333{
334 return recv_buf_timeout(fd, data, length, 0, RECV_TIMEOUT);
335}
336
337int peek_buf(int fd, void *data, size_t length)
338{
339 return recv_buf_timeout(fd, data, length, MSG_PEEK, RECV_TIMEOUT);
340}
341
342int recv_buf_timeout(int fd, void *data, size_t length, int flags,
343 unsigned int timeout)
344{
345 int res;
346 int result;
347
348 // check if data is available
349 res = check_fd(fd, FDM_READ, timeout);
350 if (res <= 0) {
351 return res;
352 }
353 // if we get here, there _is_ data available
354 result = recv(fd, data, length, flags);
355 if (res > 0 && result == 0) {
356 // but this is an error condition
357 if (verbose >= 3)
358 fprintf(stderr, "%s: fd=%d recv returned 0\n", __func__, fd);
359 return -EAGAIN;
360 }
361 if (result < 0) {
362 return -errno;
363 }
364 return result;
365}
366
367int send_buf(int fd, void *data, size_t length)
368{
369 return send(fd, data, length, 0);
370}
diff --git a/libusbmuxd/sock_stuff.h b/libusbmuxd/sock_stuff.h
deleted file mode 100644
index eb9622c..0000000
--- a/libusbmuxd/sock_stuff.h
+++ /dev/null
@@ -1,57 +0,0 @@
1/*
2 libusbmuxd - client library to talk to usbmuxd
3
4Copyright (C) 2009 Nikias Bassen <nikias@gmx.li>
5Copyright (C) 2009 Paul Sladen <libiphone@paul.sladen.org>
6Copyright (C) 2009 Martin Szulecki <opensuse@sukimashita.com>
7
8This library is free software; you can redistribute it and/or modify
9it under the terms of the GNU Lesser General Public License as
10published by the Free Software Foundation, either version 2.1 of the
11License, or (at your option) any later version.
12
13This library is distributed in the hope that it will be useful,
14but WITHOUT ANY WARRANTY; without even the implied warranty of
15MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16GNU General Public License for more details.
17
18You should have received a copy of the GNU Lesser General Public
19License along with this program; if not, write to the Free Software
20Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21
22*/
23
24#ifndef __SOCK_STUFF_H
25#define __SOCK_STUFF_H
26
27#include <stdint.h>
28
29enum fd_mode {
30 FDM_READ,
31 FDM_WRITE,
32 FDM_EXCEPT
33};
34typedef enum fd_mode fd_mode;
35
36#ifndef WIN32
37int create_unix_socket(const char *filename);
38int connect_unix_socket(const char *filename);
39#endif
40int create_socket(uint16_t port);
41#if defined(WIN32) || defined(__CYGWIN__)
42int connect_socket(const char *addr, uint16_t port);
43#endif
44int check_fd(int fd, fd_mode fdm, unsigned int timeout);
45
46int close_socket(int fd);
47
48int recv_buf(int fd, void *data, size_t size);
49int peek_buf(int fd, void *data, size_t size);
50int recv_buf_timeout(int fd, void *data, size_t size, int flags,
51 unsigned int timeout);
52
53int send_buf(int fd, void *data, size_t size);
54
55void sock_stuff_set_verbose(int level);
56
57#endif /* __SOCK_STUFF_H */
diff --git a/libusbmuxd/usbmuxd.h b/libusbmuxd/usbmuxd.h
deleted file mode 100644
index eabd216..0000000
--- a/libusbmuxd/usbmuxd.h
+++ /dev/null
@@ -1,181 +0,0 @@
1/*
2 libusbmuxd - client library to talk to usbmuxd
3
4Copyright (C) 2009 Nikias Bassen <nikias@gmx.li>
5Copyright (C) 2009 Paul Sladen <libiphone@paul.sladen.org>
6Copyright (C) 2009 Martin Szulecki <opensuse@sukimashita.com>
7
8This library is free software; you can redistribute it and/or modify
9it under the terms of the GNU Lesser General Public License as
10published by the Free Software Foundation, either version 2.1 of the
11License, or (at your option) any later version.
12
13This library is distributed in the hope that it will be useful,
14but WITHOUT ANY WARRANTY; without even the implied warranty of
15MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16GNU General Public License for more details.
17
18You should have received a copy of the GNU Lesser General Public
19License along with this program; if not, write to the Free Software
20Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21
22*/
23
24#ifndef __USBMUXD_H
25#define __USBMUXD_H
26#include <stdint.h>
27
28#ifdef __cplusplus
29extern "C" {
30#endif
31
32/**
33 * Device information structure holding data to identify the device.
34 * The relevant 'handle' should be passed to 'usbmuxd_connect()', to
35 * start a proxy connection. The value 'handle' should be considered
36 * opaque and no presumption made about the meaning of its value.
37 */
38typedef struct {
39 int handle;
40 int product_id;
41 char uuid[41];
42} usbmuxd_device_info_t;
43
44/**
45 * event types for event callback function
46 */
47enum usbmuxd_event_type {
48 UE_DEVICE_ADD = 1,
49 UE_DEVICE_REMOVE
50};
51
52/**
53 * Event structure that will be passed to the callback function.
54 * 'event' will contains the type of the event, and 'device' will contains
55 * information about the device.
56 */
57typedef struct {
58 int event;
59 usbmuxd_device_info_t device;
60} usbmuxd_event_t;
61
62/**
63 * Callback function prototype.
64 */
65typedef void (*usbmuxd_event_cb_t) (const usbmuxd_event_t *event, void *user_data);
66
67/**
68 * Subscribe a callback function so that applications get to know about
69 * device add/remove events.
70 *
71 * @param callback A callback function that is executed when an event occurs.
72 *
73 * @return 0 on success or negative on error.
74 */
75int usbmuxd_subscribe(usbmuxd_event_cb_t callback, void *user_data);
76
77/**
78 * Unsubscribe callback.
79 *
80 * @return only 0 for now.
81 */
82int usbmuxd_unsubscribe();
83
84/**
85 * Contacts usbmuxd and retrieves a list of connected devices.
86 *
87 * @param device_list A pointer to an array of usbmuxd_device_info_t
88 * that will hold records of the connected devices. The last record
89 * is a null-terminated record with all fields set to 0/NULL.
90 * @note The user has to free the list returned.
91 *
92 * @return number of attached devices, zero on no devices, or negative
93 * if an error occured.
94 */
95int usbmuxd_get_device_list(usbmuxd_device_info_t **device_list);
96
97/**
98 * Frees the device list returned by an usbmuxd_get_device_list call
99 *
100 * @param device_list A pointer to an array of usbmuxd_device_info_t to free.
101 *
102 * @return 0 on success, -1 on error.
103 */
104int usbmuxd_device_list_free(usbmuxd_device_info_t **device_list);
105
106/**
107 * Gets device information for the device specified by uuid.
108 *
109 * @param uuid A device uuid of the device to look for. If uuid is NULL,
110 * This function will return the first device found.
111 * @param device Pointer to a previously allocated (or static)
112 * usbmuxd_device_info_t that will be filled with the device info.
113 *
114 * @return 0 if no matching device is connected, 1 if the device was found,
115 * or a negative value on error.
116 */
117int usbmuxd_get_device_by_uuid(const char *uuid, usbmuxd_device_info_t *device);
118
119/**
120 * Request proxy connect to
121 *
122 * @param handle returned by 'usbmuxd_scan()'
123 *
124 * @param tcp_port TCP port number on device, in range 0-65535.
125 * common values are 62078 for lockdown, and 22 for SSH.
126 *
127 * @return file descriptor socket of the connection, or -1 on error
128 */
129int usbmuxd_connect(const int handle, const unsigned short tcp_port);
130
131/**
132 * Disconnect. For now, this just closes the socket file descriptor.
133 *
134 * @param sfd socker file descriptor returned by usbmuxd_connect()
135 *
136 * @return 0 on success, -1 on error.
137 */
138int usbmuxd_disconnect(int sfd);
139
140/**
141 * Send data to the specified socket.
142 *
143 * @param sfd socket file descriptor returned by usbmuxd_connect()
144 * @param data buffer to send
145 * @param len size of buffer to send
146 * @param sent_bytes how many bytes sent
147 *
148 * @return 0 on success, a negative errno value otherwise.
149 */
150int usbmuxd_send(int sfd, const char *data, uint32_t len, uint32_t *sent_bytes);
151
152/**
153 * Receive data from the specified socket.
154 *
155 * @param sfd socket file descriptor returned by usbmuxd_connect()
156 * @param data buffer to put the data to
157 * @param len number of bytes to receive
158 * @param recv_bytes number of bytes received
159 * @param timeout how many milliseconds to wait for data
160 *
161 * @return 0 on success, a negative errno value otherwise.
162 */
163int usbmuxd_recv_timeout(int sfd, char *data, uint32_t len, uint32_t *recv_bytes, unsigned int timeout);
164
165/**
166 * Receive data from the specified socket with a default timeout.
167 *
168 * @param sfd socket file descriptor returned by usbmuxd_connect()
169 * @param data buffer to put the data to
170 * @param len number of bytes to receive
171 * @param recv_bytes number of bytes received
172 *
173 * @return 0 on success, a negative errno value otherwise.
174 */
175int usbmuxd_recv(int sfd, char *data, uint32_t len, uint32_t *recv_bytes);
176
177#ifdef __cplusplus
178}
179#endif
180
181#endif /* __USBMUXD_H */
diff --git a/m4/as-compiler-flag.m4 b/m4/as-compiler-flag.m4
new file mode 100644
index 0000000..baab5d9
--- /dev/null
+++ b/m4/as-compiler-flag.m4
@@ -0,0 +1,62 @@
1dnl as-compiler-flag.m4 0.1.0
2
3dnl autostars m4 macro for detection of compiler flags
4
5dnl David Schleef <ds@schleef.org>
6
7dnl $Id: as-compiler-flag.m4,v 1.1 2005/12/15 23:35:19 ds Exp $
8
9dnl AS_COMPILER_FLAG(CFLAGS, ACTION-IF-ACCEPTED, [ACTION-IF-NOT-ACCEPTED])
10dnl Tries to compile with the given CFLAGS.
11dnl Runs ACTION-IF-ACCEPTED if the compiler can compile with the flags,
12dnl and ACTION-IF-NOT-ACCEPTED otherwise.
13
14AC_DEFUN([AS_COMPILER_FLAG],
15[
16 AC_MSG_CHECKING([to see if compiler understands $1])
17
18 save_CFLAGS="$CFLAGS"
19 CFLAGS="$CFLAGS $1"
20
21 AC_COMPILE_IFELSE([AC_LANG_PROGRAM([],[])], [flag_ok=yes], [flag_ok=no])
22 CFLAGS="$save_CFLAGS"
23
24 if test "X$flag_ok" = Xyes ; then
25 m4_ifvaln([$2],[$2])
26 true
27 else
28 m4_ifvaln([$3],[$3])
29 true
30 fi
31 AC_MSG_RESULT([$flag_ok])
32])
33
34dnl AS_COMPILER_FLAGS(VAR, FLAGS)
35dnl Tries to compile with the given CFLAGS.
36
37AC_DEFUN([AS_COMPILER_FLAGS],
38[
39 list=$2
40 flags_supported=""
41 flags_unsupported=""
42 AC_MSG_CHECKING([for supported compiler flags])
43 for each in $list
44 do
45 save_CFLAGS="$CFLAGS"
46 CFLAGS="$CFLAGS $each"
47 AC_COMPILE_IFELSE([AC_LANG_PROGRAM([],[])], [flag_ok=yes], [flag_ok=no])
48 CFLAGS="$save_CFLAGS"
49
50 if test "X$flag_ok" = Xyes ; then
51 flags_supported="$flags_supported $each"
52 else
53 flags_unsupported="$flags_unsupported $each"
54 fi
55 done
56 AC_MSG_RESULT([$flags_supported])
57 if test "X$flags_unsupported" != X ; then
58 AC_MSG_WARN([unsupported compiler flags: $flags_unsupported])
59 fi
60 $1="$$1 $flags_supported"
61])
62
diff --git a/python-client/.gitignore b/python-client/.gitignore
deleted file mode 100644
index 5da7ef5..0000000
--- a/python-client/.gitignore
+++ /dev/null
@@ -1,3 +0,0 @@
1*.pyc
2*.pyo
3
diff --git a/python-client/tcprelay.py b/python-client/tcprelay.py
deleted file mode 100644
index add200c..0000000
--- a/python-client/tcprelay.py
+++ /dev/null
@@ -1,148 +0,0 @@
1#!/usr/bin/python
2# -*- coding: utf-8 -*-
3#
4# tcprelay.py - TCP connection relay for usbmuxd
5#
6# Copyright (C) 2009 Hector Martin "marcan" <hector@marcansoft.com>
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 or version 3.
11#
12# This program is distributed in the hope that it will be useful,
13# but WITHOUT ANY WARRANTY; without even the implied warranty of
14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15# GNU General Public License for more details.
16#
17# You should have received a copy of the GNU General Public License
18# along with this program; if not, write to the Free Software
19# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20
21import usbmux
22import SocketServer
23import select
24from optparse import OptionParser
25import sys
26import threading
27
28class SocketRelay(object):
29 def __init__(self, a, b, maxbuf=65535):
30 self.a = a
31 self.b = b
32 self.atob = ""
33 self.btoa = ""
34 self.maxbuf = maxbuf
35 def handle(self):
36 while True:
37 rlist = []
38 wlist = []
39 xlist = [self.a, self.b]
40 if self.atob:
41 wlist.append(self.b)
42 if self.btoa:
43 wlist.append(self.a)
44 if len(self.atob) < self.maxbuf:
45 rlist.append(self.a)
46 if len(self.btoa) < self.maxbuf:
47 rlist.append(self.b)
48 rlo, wlo, xlo = select.select(rlist, wlist, xlist)
49 if xlo:
50 return
51 if self.a in wlo:
52 n = self.a.send(self.btoa)
53 self.btoa = self.btoa[n:]
54 if self.b in wlo:
55 n = self.b.send(self.atob)
56 self.atob = self.atob[n:]
57 if self.a in rlo:
58 s = self.a.recv(self.maxbuf - len(self.atob))
59 if not s:
60 return
61 self.atob += s
62 if self.b in rlo:
63 s = self.b.recv(self.maxbuf - len(self.btoa))
64 if not s:
65 return
66 self.btoa += s
67 #print "Relay iter: %8d atob, %8d btoa, lists: %r %r %r"%(len(self.atob), len(self.btoa), rlo, wlo, xlo)
68
69class TCPRelay(SocketServer.BaseRequestHandler):
70 def handle(self):
71 print "Incoming connection to %d"%self.server.server_address[1]
72 mux = usbmux.USBMux(options.sockpath)
73 print "Waiting for devices..."
74 if not mux.devices:
75 mux.process(1.0)
76 if not mux.devices:
77 print "No device found"
78 self.request.close()
79 return
80 dev = mux.devices[0]
81 print "Connecting to device %s"%str(dev)
82 dsock = mux.connect(dev, self.server.rport)
83 lsock = self.request
84 print "Connection established, relaying data"
85 try:
86 fwd = SocketRelay(dsock, lsock, self.server.bufsize * 1024)
87 fwd.handle()
88 finally:
89 dsock.close()
90 lsock.close()
91 print "Connection closed"
92
93class TCPServer(SocketServer.TCPServer):
94 allow_reuse_address = True
95
96class ThreadedTCPServer(SocketServer.ThreadingMixIn, TCPServer):
97 pass
98
99HOST = "localhost"
100
101parser = OptionParser(usage="usage: %prog [OPTIONS] RemotePort[:LocalPort] [RemotePort[:LocalPort]]...")
102parser.add_option("-t", "--threaded", dest='threaded', action='store_true', default=False, help="use threading to handle multiple connections at once")
103parser.add_option("-b", "--bufsize", dest='bufsize', action='store', metavar='KILOBYTES', type='int', default=128, help="specify buffer size for socket forwarding")
104parser.add_option("-s", "--socket", dest='sockpath', action='store', metavar='PATH', type='str', default=None, help="specify the path of the usbmuxd socket")
105
106options, args = parser.parse_args()
107
108serverclass = TCPServer
109if options.threaded:
110 serverclass = ThreadedTCPServer
111
112if len(args) == 0:
113 parser.print_help()
114 sys.exit(1)
115
116ports = []
117
118for arg in args:
119 try:
120 if ':' in arg:
121 rport, lport = arg.split(":")
122 rport = int(rport)
123 lport = int(lport)
124 ports.append((rport, lport))
125 else:
126 ports.append((int(arg), int(arg)))
127 except:
128 parser.print_help()
129 sys.exit(1)
130
131servers=[]
132
133for rport, lport in ports:
134 print "Forwarding local port %d to remote port %d"%(lport, rport)
135 server = serverclass((HOST, lport), TCPRelay)
136 server.rport = rport
137 server.bufsize = options.bufsize
138 servers.append(server)
139
140alive = True
141
142while alive:
143 try:
144 rl, wl, xl = select.select(servers, [], [])
145 for server in rl:
146 server.handle_request()
147 except:
148 alive = False
diff --git a/python-client/usbmux.py b/python-client/usbmux.py
deleted file mode 100644
index 79ec26a..0000000
--- a/python-client/usbmux.py
+++ /dev/null
@@ -1,246 +0,0 @@
1#!/usr/bin/python
2# -*- coding: utf-8 -*-
3#
4# usbmux.py - usbmux client library for Python
5#
6# Copyright (C) 2009 Hector Martin "marcan" <hector@marcansoft.com>
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 or version 3.
11#
12# This program is distributed in the hope that it will be useful,
13# but WITHOUT ANY WARRANTY; without even the implied warranty of
14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15# GNU General Public License for more details.
16#
17# You should have received a copy of the GNU General Public License
18# along with this program; if not, write to the Free Software
19# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20
21import socket, struct, select, sys
22
23try:
24 import plistlib
25 haveplist = True
26except:
27 haveplist = False
28
29class MuxError(Exception):
30 pass
31
32class MuxVersionError(MuxError):
33 pass
34
35class SafeStreamSocket:
36 def __init__(self, address, family):
37 self.sock = socket.socket(family, socket.SOCK_STREAM)
38 self.sock.connect(address)
39 def send(self, msg):
40 totalsent = 0
41 while totalsent < len(msg):
42 sent = self.sock.send(msg[totalsent:])
43 if sent == 0:
44 raise MuxError("socket connection broken")
45 totalsent = totalsent + sent
46 def recv(self, size):
47 msg = ''
48 while len(msg) < size:
49 chunk = self.sock.recv(size-len(msg))
50 if chunk == '':
51 raise MuxError("socket connection broken")
52 msg = msg + chunk
53 return msg
54
55class MuxDevice(object):
56 def __init__(self, devid, usbprod, serial, location):
57 self.devid = devid
58 self.usbprod = usbprod
59 self.serial = serial
60 self.location = location
61 def __str__(self):
62 return "<MuxDevice: ID %d ProdID 0x%04x Serial '%s' Location 0x%x>"%(self.devid, self.usbprod, self.serial, self.location)
63
64class BinaryProtocol(object):
65 TYPE_RESULT = 1
66 TYPE_CONNECT = 2
67 TYPE_LISTEN = 3
68 TYPE_DEVICE_ADD = 4
69 TYPE_DEVICE_REMOVE = 5
70 VERSION = 0
71 def __init__(self, socket):
72 self.socket = socket
73 self.connected = False
74
75 def _pack(self, req, payload):
76 if req == self.TYPE_CONNECT:
77 return struct.pack("IH", payload['DeviceID'], payload['PortNumber']) + "\x00\x00"
78 elif req == self.TYPE_LISTEN:
79 return ""
80 else:
81 raise ValueError("Invalid outgoing request type %d"%req)
82
83 def _unpack(self, resp, payload):
84 if resp == self.TYPE_RESULT:
85 return {'Number':struct.unpack("I", payload)[0]}
86 elif resp == self.TYPE_DEVICE_ADD:
87 devid, usbpid, serial, pad, location = struct.unpack("IH256sHI", payload)
88 serial = serial.split("\0")[0]
89 return {'DeviceID': devid, 'Properties': {'LocationID': location, 'SerialNumber': serial, 'ProductID': usbpid}}
90 elif resp == self.TYPE_DEVICE_REMOVE:
91 devid = struct.unpack("I", payload)[0]
92 return {'DeviceID': devid}
93 else:
94 raise MuxError("Invalid incoming request type %d"%req)
95
96 def sendpacket(self, req, tag, payload={}):
97 payload = self._pack(req, payload)
98 if self.connected:
99 raise MuxError("Mux is connected, cannot issue control packets")
100 length = 16 + len(payload)
101 data = struct.pack("IIII", length, self.VERSION, req, tag) + payload
102 self.socket.send(data)
103 def getpacket(self):
104 if self.connected:
105 raise MuxError("Mux is connected, cannot issue control packets")
106 dlen = self.socket.recv(4)
107 dlen = struct.unpack("I", dlen)[0]
108 body = self.socket.recv(dlen - 4)
109 version, resp, tag = struct.unpack("III",body[:0xc])
110 if version != self.VERSION:
111 raise MuxVersionError("Version mismatch: expected %d, got %d"%(self.VERSION,version))
112 payload = self._unpack(resp, body[0xc:])
113 return (resp, tag, payload)
114
115class PlistProtocol(BinaryProtocol):
116 TYPE_RESULT = "Result"
117 TYPE_CONNECT = "Connect"
118 TYPE_LISTEN = "Listen"
119 TYPE_DEVICE_ADD = "Attached"
120 TYPE_DEVICE_REMOVE = "Detached" #???
121 TYPE_PLIST = 8
122 VERSION = 1
123 def __init__(self, socket):
124 if not haveplist:
125 raise Exception("You need the plistlib module")
126 BinaryProtocol.__init__(self, socket)
127
128 def _pack(self, req, payload):
129 return payload
130
131 def _unpack(self, resp, payload):
132 return payload
133
134 def sendpacket(self, req, tag, payload={}):
135 payload['ClientVersionString'] = 'usbmux.py by marcan'
136 if isinstance(req, int):
137 req = [self.TYPE_CONNECT, self.TYPE_LISTEN][req-2]
138 payload['MessageType'] = req
139 payload['ProgName'] = 'tcprelay'
140 BinaryProtocol.sendpacket(self, self.TYPE_PLIST, tag, plistlib.writePlistToString(payload))
141 def getpacket(self):
142 resp, tag, payload = BinaryProtocol.getpacket(self)
143 if resp != self.TYPE_PLIST:
144 raise MuxError("Received non-plist type %d"%resp)
145 payload = plistlib.readPlistFromString(payload)
146 return payload['MessageType'], tag, payload
147
148class MuxConnection(object):
149 def __init__(self, socketpath, protoclass):
150 self.socketpath = socketpath
151 if sys.platform in ['win32', 'cygwin']:
152 family = socket.AF_INET
153 address = ('127.0.0.1', 27015)
154 else:
155 family = socket.AF_UNIX
156 address = self.socketpath
157 self.socket = SafeStreamSocket(address, family)
158 self.proto = protoclass(self.socket)
159 self.pkttag = 1
160 self.devices = []
161
162 def _getreply(self):
163 while True:
164 resp, tag, data = self.proto.getpacket()
165 if resp == self.proto.TYPE_RESULT:
166 return tag, data
167 else:
168 raise MuxError("Invalid packet type received: %d"%resp)
169 def _processpacket(self):
170 resp, tag, data = self.proto.getpacket()
171 if resp == self.proto.TYPE_DEVICE_ADD:
172 self.devices.append(MuxDevice(data['DeviceID'], data['Properties']['ProductID'], data['Properties']['SerialNumber'], data['Properties']['LocationID']))
173 elif resp == self.proto.TYPE_DEVICE_REMOVE:
174 for dev in self.devices:
175 if dev.devid == data['DeviceID']:
176 self.devices.remove(dev)
177 elif resp == self.proto.TYPE_RESULT:
178 raise MuxError("Unexpected result: %d"%resp)
179 else:
180 raise MuxError("Invalid packet type received: %d"%resp)
181 def _exchange(self, req, payload={}):
182 mytag = self.pkttag
183 self.pkttag += 1
184 self.proto.sendpacket(req, mytag, payload)
185 recvtag, data = self._getreply()
186 if recvtag != mytag:
187 raise MuxError("Reply tag mismatch: expected %d, got %d"%(mytag, recvtag))
188 return data['Number']
189
190 def listen(self):
191 ret = self._exchange(self.proto.TYPE_LISTEN)
192 if ret != 0:
193 raise MuxError("Listen failed: error %d"%ret)
194 def process(self, timeout=None):
195 if self.proto.connected:
196 raise MuxError("Socket is connected, cannot process listener events")
197 rlo, wlo, xlo = select.select([self.socket.sock], [], [self.socket.sock], timeout)
198 if xlo:
199 self.socket.sock.close()
200 raise MuxError("Exception in listener socket")
201 if rlo:
202 self._processpacket()
203 def connect(self, device, port):
204 ret = self._exchange(self.proto.TYPE_CONNECT, {'DeviceID':device.devid, 'PortNumber':((port<<8) & 0xFF00) | (port>>8)})
205 if ret != 0:
206 raise MuxError("Connect failed: error %d"%ret)
207 self.proto.connected = True
208 return self.socket.sock
209 def close(self):
210 self.socket.sock.close()
211
212class USBMux(object):
213 def __init__(self, socketpath=None):
214 if socketpath is None:
215 if sys.platform == 'darwin':
216 socketpath = "/var/run/usbmuxd"
217 else:
218 socketpath = "/var/run/usbmuxd"
219 self.socketpath = socketpath
220 self.listener = MuxConnection(socketpath, BinaryProtocol)
221 try:
222 self.listener.listen()
223 self.version = 0
224 self.protoclass = BinaryProtocol
225 except MuxVersionError:
226 self.listener = MuxConnection(socketpath, PlistProtocol)
227 self.listener.listen()
228 self.protoclass = PlistProtocol
229 self.version = 1
230 self.devices = self.listener.devices
231 def process(self, timeout=None):
232 self.listener.process(timeout)
233 def connect(self, device, port):
234 connector = MuxConnection(self.socketpath, self.protoclass)
235 return connector.connect(device, port)
236
237if __name__ == "__main__":
238 mux = USBMux()
239 print "Waiting for devices..."
240 if not mux.devices:
241 mux.process(0.1)
242 while True:
243 print "Devices:"
244 for dev in mux.devices:
245 print dev
246 mux.process()
diff --git a/src/Makefile.am b/src/Makefile.am
new file mode 100644
index 0000000..8a96e46
--- /dev/null
+++ b/src/Makefile.am
@@ -0,0 +1,32 @@
1AM_CPPFLAGS = \
2 -I$(top_srcdir)/include \
3 -I$(top_srcdir)
4
5AM_CFLAGS = \
6 $(GLOBAL_CFLAGS) \
7 $(libplist_CFLAGS) \
8 $(libusb_CFLAGS) \
9 $(limd_glue_CFLAGS) \
10 $(libimobiledevice_CFLAGS)
11
12AM_LDFLAGS = \
13 $(libplist_LIBS) \
14 $(libusb_LIBS) \
15 $(limd_glue_LIBS) \
16 $(libimobiledevice_LIBS) \
17 $(libpthread_LIBS)
18
19sbin_PROGRAMS = usbmuxd
20
21usbmuxd_CFLAGS = $(AM_CFLAGS)
22usbmuxd_LDFLAGS = $(AM_LDFLAGS) -no-undefined
23usbmuxd_SOURCES = \
24 client.c client.h \
25 device.c device.h \
26 preflight.c preflight.h \
27 log.c log.h \
28 usbmuxd-proto.h \
29 usb.c usb.h \
30 utils.c utils.h \
31 conf.c conf.h \
32 main.c
diff --git a/src/client.c b/src/client.c
new file mode 100644
index 0000000..2ac04bf
--- /dev/null
+++ b/src/client.c
@@ -0,0 +1,1038 @@
1/*
2 * client.c
3 *
4 * Copyright (C) 2009 Hector Martin <hector@marcansoft.com>
5 * Copyright (C) 2009 Nikias Bassen <nikias@gmx.li>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 2 or version 3.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 */
20
21#ifdef HAVE_CONFIG_H
22#include <config.h>
23#endif
24
25#define _GNU_SOURCE 1
26
27#include <stdlib.h>
28#include <string.h>
29#include <stdio.h>
30#include <errno.h>
31#include <unistd.h>
32#include <sys/types.h>
33#include <sys/socket.h>
34#include <netinet/in.h>
35#include <netinet/tcp.h>
36#include <sys/un.h>
37#include <arpa/inet.h>
38#include <fcntl.h>
39
40#include <plist/plist.h>
41#include <libimobiledevice-glue/collection.h>
42#include <libimobiledevice-glue/thread.h>
43
44#include "log.h"
45#include "usb.h"
46#include "client.h"
47#include "device.h"
48#include "conf.h"
49
50#define CMD_BUF_SIZE 0x10000
51#define REPLY_BUF_SIZE 0x10000
52
53enum client_state {
54 CLIENT_COMMAND, // waiting for command
55 CLIENT_LISTEN, // listening for devices
56 CLIENT_CONNECTING1, // issued connection request
57 CLIENT_CONNECTING2, // connection established, but waiting for response message to get sent
58 CLIENT_CONNECTED, // connected
59 CLIENT_DEAD
60};
61
62struct mux_client {
63 int fd;
64 unsigned char *ob_buf;
65 uint32_t ob_size;
66 uint32_t ob_capacity;
67 unsigned char *ib_buf;
68 uint32_t ib_size;
69 uint32_t ib_capacity;
70 short events, devents;
71 uint32_t connect_tag;
72 int connect_device;
73 enum client_state state;
74 uint32_t proto_version;
75 uint32_t number;
76 plist_t info;
77};
78
79static struct collection client_list;
80mutex_t client_list_mutex;
81static uint32_t client_number = 0;
82
83#ifdef SO_PEERCRED
84static char* _get_process_name_by_pid(const int pid)
85{
86 char* name = (char*)calloc(1024, sizeof(char));
87 if(name) {
88 sprintf(name, "/proc/%d/cmdline", pid);
89 FILE* f = fopen(name, "r");
90 if(f) {
91 size_t size;
92 size = fread(name, sizeof(char), 1024, f);
93 if(size > 0) {
94 if('\n' == name[size-1])
95 name[size-1]='\0';
96 }
97 fclose(f);
98 }
99 }
100 return name;
101}
102#endif
103
104/**
105 * Receive raw data from the client socket.
106 *
107 * @param client Client to read from.
108 * @param buffer Buffer to store incoming data.
109 * @param len Max number of bytes to read.
110 * @return Same as recv() system call. Number of bytes read; when < 0 errno will be set.
111 */
112int client_read(struct mux_client *client, void *buffer, uint32_t len)
113{
114 usbmuxd_log(LL_SPEW, "client_read fd %d buf %p len %d", client->fd, buffer, len);
115 if(client->state != CLIENT_CONNECTED) {
116 usbmuxd_log(LL_ERROR, "Attempted to read from client %d not in CONNECTED state", client->fd);
117 return -1;
118 }
119 return recv(client->fd, buffer, len, 0);
120}
121
122/**
123 * Send raw data to the client socket.
124 *
125 * @param client Client to send to.
126 * @param buffer The data to send.
127 * @param len Number of bytes to write.
128 * @return Same as system call send(). Number of bytes written; when < 0 errno will be set.
129 */
130int client_write(struct mux_client *client, void *buffer, uint32_t len)
131{
132 int sret = -1;
133
134 usbmuxd_log(LL_SPEW, "client_write fd %d buf %p len %d", client->fd, buffer, len);
135 if(client->state != CLIENT_CONNECTED) {
136 usbmuxd_log(LL_ERROR, "Attempted to write to client %d not in CONNECTED state", client->fd);
137 return -1;
138 }
139
140 sret = send(client->fd, buffer, len, 0);
141 if (sret < 0) {
142 if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) {
143 usbmuxd_log(LL_DEBUG, "client_write: fd %d not ready for writing", client->fd);
144 sret = 0;
145 } else {
146 usbmuxd_log(LL_ERROR, "ERROR: client_write: sending to fd %d failed: %s", client->fd, strerror(errno));
147 }
148 }
149 return sret;
150}
151
152/**
153 * Set event mask to use for ppoll()ing the client socket.
154 * Typically POLLOUT and/or POLLIN. Note that this overrides
155 * the current mask, that is, it is not ORing the argument
156 * into the current mask.
157 *
158 * @param client The client to set the event mask on.
159 * @param events The event mask to sert.
160 * @return 0 on success, -1 on error.
161 */
162int client_set_events(struct mux_client *client, short events)
163{
164 if((client->state != CLIENT_CONNECTED) && (client->state != CLIENT_CONNECTING2)) {
165 usbmuxd_log(LL_ERROR, "client_set_events to client %d not in CONNECTED state", client->fd);
166 return -1;
167 }
168 client->devents = events;
169 if(client->state == CLIENT_CONNECTED)
170 client->events = events;
171 return 0;
172}
173
174/**
175 * Wait for an inbound connection on the usbmuxd socket
176 * and create a new mux_client instance for it, and store
177 * the client in the client list.
178 *
179 * @param listenfd the socket fd to accept() on.
180 * @return The connection fd for the client, or < 0 for error
181 * in which case errno will be set.
182 */
183int client_accept(int listenfd)
184{
185 struct sockaddr_un addr;
186 int cfd;
187 socklen_t len = sizeof(struct sockaddr_un);
188 cfd = accept(listenfd, (struct sockaddr *)&addr, &len);
189 if (cfd < 0) {
190 usbmuxd_log(LL_ERROR, "accept() failed (%s)", strerror(errno));
191 return cfd;
192 }
193
194 int flags = fcntl(cfd, F_GETFL, 0);
195 if (flags < 0) {
196 usbmuxd_log(LL_ERROR, "ERROR: Could not get socket flags!");
197 } else {
198 if (fcntl(cfd, F_SETFL, flags | O_NONBLOCK) < 0) {
199 usbmuxd_log(LL_ERROR, "ERROR: Could not set socket to non-blocking mode");
200 }
201 }
202
203 int bufsize = 0x20000;
204 if (setsockopt(cfd, SOL_SOCKET, SO_SNDBUF, &bufsize, sizeof(int)) == -1) {
205 usbmuxd_log(LL_WARNING, "Could not set send buffer for client socket");
206 }
207 if (setsockopt(cfd, SOL_SOCKET, SO_RCVBUF, &bufsize, sizeof(int)) == -1) {
208 usbmuxd_log(LL_WARNING, "Could not set receive buffer for client socket");
209 }
210
211 int yes = 1;
212 setsockopt(cfd, IPPROTO_TCP, TCP_NODELAY, (void*)&yes, sizeof(int));
213
214 struct mux_client *client;
215 client = malloc(sizeof(struct mux_client));
216 memset(client, 0, sizeof(struct mux_client));
217
218 client->fd = cfd;
219 client->ob_buf = malloc(REPLY_BUF_SIZE);
220 client->ob_size = 0;
221 client->ob_capacity = REPLY_BUF_SIZE;
222 client->ib_buf = malloc(CMD_BUF_SIZE);
223 client->ib_size = 0;
224 client->ib_capacity = CMD_BUF_SIZE;
225 client->state = CLIENT_COMMAND;
226 client->events = POLLIN;
227 client->info = NULL;
228
229 mutex_lock(&client_list_mutex);
230 client->number = client_number++;
231 collection_add(&client_list, client);
232 mutex_unlock(&client_list_mutex);
233
234#ifdef SO_PEERCRED
235 if (log_level >= LL_INFO) {
236 struct ucred cr;
237 len = sizeof(struct ucred);
238 getsockopt(client->fd, SOL_SOCKET, SO_PEERCRED, &cr, &len);
239
240 if (getpid() == cr.pid) {
241 usbmuxd_log(LL_INFO, "Client %d accepted: %s[%d]", client->fd, PACKAGE_NAME, cr.pid);
242 } else {
243 char* process_name = _get_process_name_by_pid(cr.pid);
244 usbmuxd_log(LL_INFO, "Client %d accepted: %s[%d]", client->fd, process_name, cr.pid);
245 free(process_name);
246 }
247 }
248#else
249 usbmuxd_log(LL_INFO, "Client %d accepted", client->fd);
250#endif
251 return client->fd;
252}
253
254void client_close(struct mux_client *client)
255{
256 int found = 0;
257 mutex_lock(&client_list_mutex);
258 FOREACH(struct mux_client *lc, &client_list) {
259 if (client == lc) {
260 found = 1;
261 break;
262 }
263 } ENDFOREACH
264 if (!found) {
265 // in case we get called again but client was already freed
266 usbmuxd_log(LL_DEBUG, "%s: ignoring for non-existing client %p", __func__, client);
267 mutex_unlock(&client_list_mutex);
268 return;
269 }
270#ifdef SO_PEERCRED
271 if (log_level >= LL_INFO) {
272 struct ucred cr;
273 socklen_t len = sizeof(struct ucred);
274 getsockopt(client->fd, SOL_SOCKET, SO_PEERCRED, &cr, &len);
275
276 if (getpid() == cr.pid) {
277 usbmuxd_log(LL_INFO, "Client %d is going to be disconnected: %s[%d]", client->fd, PACKAGE_NAME, cr.pid);
278 } else {
279 char* process_name = _get_process_name_by_pid(cr.pid);
280 usbmuxd_log(LL_INFO, "Client %d is going to be disconnected: %s[%d]", client->fd, process_name, cr.pid);
281 free(process_name);
282 }
283 }
284#else
285 usbmuxd_log(LL_INFO, "Client %d is going to be disconnected", client->fd);
286#endif
287 if(client->state == CLIENT_CONNECTING1 || client->state == CLIENT_CONNECTING2) {
288 usbmuxd_log(LL_INFO, "Client died mid-connect, aborting device %d connection", client->connect_device);
289 client->state = CLIENT_DEAD;
290 device_abort_connect(client->connect_device, client);
291 }
292 close(client->fd);
293 free(client->ob_buf);
294 free(client->ib_buf);
295 plist_free(client->info);
296
297 collection_remove(&client_list, client);
298 mutex_unlock(&client_list_mutex);
299 free(client);
300}
301
302void client_get_fds(struct fdlist *list)
303{
304 mutex_lock(&client_list_mutex);
305 FOREACH(struct mux_client *client, &client_list) {
306 fdlist_add(list, FD_CLIENT, client->fd, client->events);
307 } ENDFOREACH
308 mutex_unlock(&client_list_mutex);
309}
310
311static int output_buffer_add_message(struct mux_client *client, uint32_t tag, enum usbmuxd_msgtype msg, void *payload, int payload_length)
312{
313 struct usbmuxd_header hdr;
314 hdr.version = client->proto_version;
315 hdr.length = sizeof(hdr) + payload_length;
316 hdr.message = msg;
317 hdr.tag = tag;
318 usbmuxd_log(LL_DEBUG, "Client %d output buffer got tag %d msg %d payload_length %d", client->fd, tag, msg, payload_length);
319
320 uint32_t available = client->ob_capacity - client->ob_size;
321 /* the output buffer _should_ be large enough, but just in case */
322 if(available < hdr.length) {
323 unsigned char* new_buf;
324 uint32_t new_size = ((client->ob_capacity + hdr.length + 4096) / 4096) * 4096;
325 usbmuxd_log(LL_DEBUG, "%s: Enlarging client %d output buffer %d -> %d", __func__, client->fd, client->ob_capacity, new_size);
326 new_buf = realloc(client->ob_buf, new_size);
327 if (!new_buf) {
328 usbmuxd_log(LL_FATAL, "%s: Failed to realloc.", __func__);
329 return -1;
330 }
331 client->ob_buf = new_buf;
332 client->ob_capacity = new_size;
333 }
334 memcpy(client->ob_buf + client->ob_size, &hdr, sizeof(hdr));
335 if(payload && payload_length)
336 memcpy(client->ob_buf + client->ob_size + sizeof(hdr), payload, payload_length);
337 client->ob_size += hdr.length;
338 client->events |= POLLOUT;
339 return hdr.length;
340}
341
342static int send_plist(struct mux_client *client, uint32_t tag, plist_t plist)
343{
344 int res = -1;
345 char *xml = NULL;
346 uint32_t xmlsize = 0;
347 plist_to_xml(plist, &xml, &xmlsize);
348 if (xml) {
349 res = output_buffer_add_message(client, tag, MESSAGE_PLIST, xml, xmlsize);
350 free(xml);
351 } else {
352 usbmuxd_log(LL_ERROR, "%s: Could not convert plist to xml", __func__);
353 }
354 return res;
355}
356
357static int send_result(struct mux_client *client, uint32_t tag, uint32_t result)
358{
359 int res = -1;
360 if (client->proto_version == 1) {
361 /* XML plist packet */
362 plist_t dict = plist_new_dict();
363 plist_dict_set_item(dict, "MessageType", plist_new_string("Result"));
364 plist_dict_set_item(dict, "Number", plist_new_uint(result));
365 res = send_plist(client, tag, dict);
366 plist_free(dict);
367 } else {
368 /* binary packet */
369 res = output_buffer_add_message(client, tag, MESSAGE_RESULT, &result, sizeof(uint32_t));
370 }
371 return res;
372}
373
374int client_notify_connect(struct mux_client *client, enum usbmuxd_result result)
375{
376 usbmuxd_log(LL_SPEW, "client_notify_connect fd %d result %d", client->fd, result);
377 if(client->state == CLIENT_DEAD)
378 return -1;
379 if(client->state != CLIENT_CONNECTING1) {
380 usbmuxd_log(LL_ERROR, "client_notify_connect when client %d is not in CONNECTING1 state", client->fd);
381 return -1;
382 }
383 if(send_result(client, client->connect_tag, result) < 0)
384 return -1;
385 if(result == RESULT_OK) {
386 client->state = CLIENT_CONNECTING2;
387 client->events = POLLOUT; // wait for the result packet to go through
388 // no longer need this
389 free(client->ib_buf);
390 client->ib_buf = NULL;
391 } else {
392 client->state = CLIENT_COMMAND;
393 }
394 return 0;
395}
396
397static plist_t create_device_attached_plist(struct device_info *dev)
398{
399 plist_t dict = plist_new_dict();
400 plist_dict_set_item(dict, "MessageType", plist_new_string("Attached"));
401 plist_dict_set_item(dict, "DeviceID", plist_new_uint(dev->id));
402 plist_t props = plist_new_dict();
403 plist_dict_set_item(props, "ConnectionSpeed", plist_new_uint(dev->speed));
404 plist_dict_set_item(props, "ConnectionType", plist_new_string("USB"));
405 plist_dict_set_item(props, "DeviceID", plist_new_uint(dev->id));
406 plist_dict_set_item(props, "LocationID", plist_new_uint(dev->location));
407 plist_dict_set_item(props, "ProductID", plist_new_uint(dev->pid));
408 plist_dict_set_item(props, "SerialNumber", plist_new_string(dev->serial));
409 plist_dict_set_item(dict, "Properties", props);
410 return dict;
411}
412
413static int send_device_list(struct mux_client *client, uint32_t tag)
414{
415 int res = -1;
416 plist_t dict = plist_new_dict();
417 plist_t devices = plist_new_array();
418
419 struct device_info *devs = NULL;
420 struct device_info *dev;
421 int i;
422
423 int count = device_get_list(0, &devs);
424 dev = devs;
425 for (i = 0; devs && i < count; i++) {
426 plist_t device = create_device_attached_plist(dev++);
427 if (device) {
428 plist_array_append_item(devices, device);
429 }
430 }
431 if (devs)
432 free(devs);
433
434 plist_dict_set_item(dict, "DeviceList", devices);
435 res = send_plist(client, tag, dict);
436 plist_free(dict);
437 return res;
438}
439
440static int send_listener_list(struct mux_client *client, uint32_t tag)
441{
442 int res = -1;
443
444 plist_t dict = plist_new_dict();
445 plist_t listeners = plist_new_array();
446
447 mutex_lock(&client_list_mutex);
448 FOREACH(struct mux_client *lc, &client_list) {
449 if (lc->state == CLIENT_LISTEN) {
450 plist_t n = NULL;
451 plist_t l = plist_new_dict();
452 plist_dict_set_item(l, "Blacklisted", plist_new_bool(0));
453 n = NULL;
454 if (lc->info) {
455 plist_dict_copy_item(l, lc->info, "BundleID", NULL);
456 }
457 plist_dict_set_item(l, "ConnType", plist_new_uint(0));
458
459 n = NULL;
460 char *progname = NULL;
461 if (lc->info) {
462 n = plist_dict_get_item(lc->info, "ProgName");
463 }
464 if (n) {
465 plist_get_string_val(n, &progname);
466 }
467 if (!progname) {
468 progname = strdup("unknown");
469 }
470 char *idstring = malloc(strlen(progname) + 12);
471 sprintf(idstring, "%u-%s", client->number, progname);
472
473 plist_dict_set_item(l, "ID String", plist_new_string(idstring));
474 free(idstring);
475 plist_dict_set_item(l, "ProgName", plist_new_string(progname));
476 free(progname);
477
478 n = NULL;
479 uint64_t version = 0;
480 if (lc->info) {
481 n = plist_dict_get_item(lc->info, "kLibUSBMuxVersion");
482 }
483 if (n) {
484 plist_get_uint_val(n, &version);
485 }
486 plist_dict_set_item(l, "kLibUSBMuxVersion", plist_new_uint(version));
487
488 plist_array_append_item(listeners, l);
489 }
490 } ENDFOREACH
491 mutex_unlock(&client_list_mutex);
492
493 plist_dict_set_item(dict, "ListenerList", listeners);
494 res = send_plist(client, tag, dict);
495 plist_free(dict);
496
497 return res;
498}
499
500static int send_system_buid(struct mux_client *client, uint32_t tag)
501{
502 int res = -1;
503 char* buid = NULL;
504
505 config_get_system_buid(&buid);
506
507 plist_t dict = plist_new_dict();
508 plist_dict_set_item(dict, "BUID", plist_new_string(buid));
509 free(buid);
510 res = send_plist(client, tag, dict);
511 plist_free(dict);
512 return res;
513}
514
515static int send_pair_record(struct mux_client *client, uint32_t tag, const char* record_id)
516{
517 int res = -1;
518 char* record_data = NULL;
519 uint64_t record_size = 0;
520
521 if (!record_id) {
522 return send_result(client, tag, EINVAL);
523 }
524
525 config_get_device_record(record_id, &record_data, &record_size);
526
527 if (record_data) {
528 plist_t dict = plist_new_dict();
529 plist_dict_set_item(dict, "PairRecordData", plist_new_data(record_data, record_size));
530 free(record_data);
531 res = send_plist(client, tag, dict);
532 plist_free(dict);
533 } else {
534 res = send_result(client, tag, ENOENT);
535 }
536 return res;
537}
538
539static int send_device_add(struct mux_client *client, struct device_info *dev)
540{
541 int res = -1;
542 if (client->proto_version == 1) {
543 /* XML plist packet */
544 plist_t dict = create_device_attached_plist(dev);
545 res = send_plist(client, 0, dict);
546 plist_free(dict);
547 } else {
548 /* binary packet */
549 struct usbmuxd_device_record dmsg;
550 memset(&dmsg, 0, sizeof(dmsg));
551 dmsg.device_id = dev->id;
552 strncpy(dmsg.serial_number, dev->serial, 256);
553 dmsg.serial_number[255] = 0;
554 dmsg.location = dev->location;
555 dmsg.product_id = dev->pid;
556 res = output_buffer_add_message(client, 0, MESSAGE_DEVICE_ADD, &dmsg, sizeof(dmsg));
557 }
558 return res;
559}
560
561static int send_device_remove(struct mux_client *client, uint32_t device_id)
562{
563 int res = -1;
564 if (client->proto_version == 1) {
565 /* XML plist packet */
566 plist_t dict = plist_new_dict();
567 plist_dict_set_item(dict, "MessageType", plist_new_string("Detached"));
568 plist_dict_set_item(dict, "DeviceID", plist_new_uint(device_id));
569 res = send_plist(client, 0, dict);
570 plist_free(dict);
571 } else {
572 /* binary packet */
573 res = output_buffer_add_message(client, 0, MESSAGE_DEVICE_REMOVE, &device_id, sizeof(uint32_t));
574 }
575 return res;
576}
577
578static int send_device_paired(struct mux_client *client, uint32_t device_id)
579{
580 int res = -1;
581 if (client->proto_version == 1) {
582 /* XML plist packet */
583 plist_t dict = plist_new_dict();
584 plist_dict_set_item(dict, "MessageType", plist_new_string("Paired"));
585 plist_dict_set_item(dict, "DeviceID", plist_new_uint(device_id));
586 res = send_plist(client, 0, dict);
587 plist_free(dict);
588 }
589 else {
590 /* binary packet */
591 res = output_buffer_add_message(client, 0, MESSAGE_DEVICE_PAIRED, &device_id, sizeof(uint32_t));
592 }
593 return res;
594}
595
596static int start_listen(struct mux_client *client)
597{
598 struct device_info *devs = NULL;
599 struct device_info *dev;
600 int count, i;
601
602 client->state = CLIENT_LISTEN;
603
604 count = device_get_list(0, &devs);
605 dev = devs;
606 for(i=0; devs && i < count; i++) {
607 if(send_device_add(client, dev++) < 0) {
608 free(devs);
609 return -1;
610 }
611 }
612 if (devs)
613 free(devs);
614
615 return count;
616}
617
618static char* plist_dict_get_string_val(plist_t dict, const char* key)
619{
620 if (!dict || plist_get_node_type(dict) != PLIST_DICT)
621 return NULL;
622 plist_t item = plist_dict_get_item(dict, key);
623 if (!item || plist_get_node_type(item) != PLIST_STRING)
624 return NULL;
625 char *str = NULL;
626 plist_get_string_val(item, &str);
627 return str;
628}
629
630static void update_client_info(struct mux_client *client, plist_t dict)
631{
632 plist_t info = plist_new_dict();
633 plist_dict_copy_item(info, dict, "BundleID", NULL);
634 plist_dict_copy_item(info, dict, "ClientVersionString", NULL);
635 plist_dict_copy_item(info, dict, "ProgName", NULL);
636 plist_dict_copy_item(info, dict, "kLibUSBMuxVersion", NULL);
637 plist_free(client->info);
638 client->info = info;
639}
640
641static int handle_command(struct mux_client *client, struct usbmuxd_header *hdr)
642{
643 int res;
644 usbmuxd_log(LL_DEBUG, "Client %d command len %d ver %d msg %d tag %d", client->fd, hdr->length, hdr->version, hdr->message, hdr->tag);
645
646 if(client->state != CLIENT_COMMAND) {
647 usbmuxd_log(LL_ERROR, "Client %d command received in the wrong state, got %d but want %d", client->fd, client->state, CLIENT_COMMAND);
648 if(send_result(client, hdr->tag, RESULT_BADCOMMAND) < 0)
649 return -1;
650 client_close(client);
651 return -1;
652 }
653
654 if((hdr->version != 0) && (hdr->version != 1)) {
655 usbmuxd_log(LL_INFO, "Client %d version mismatch: expected 0 or 1, got %d", client->fd, hdr->version);
656 send_result(client, hdr->tag, RESULT_BADVERSION);
657 return 0;
658 }
659
660 struct usbmuxd_connect_request *ch;
661 char *payload;
662 uint32_t payload_size;
663
664 switch(hdr->message) {
665 case MESSAGE_PLIST:
666 client->proto_version = 1;
667 payload = (char*)(hdr) + sizeof(struct usbmuxd_header);
668 payload_size = hdr->length - sizeof(struct usbmuxd_header);
669 plist_t dict = NULL;
670 plist_from_xml(payload, payload_size, &dict);
671 if (!dict) {
672 usbmuxd_log(LL_ERROR, "Could not parse plist from payload!");
673 return -1;
674 } else {
675 char *message = NULL;
676 plist_t node = plist_dict_get_item(dict, "MessageType");
677 if (!node || plist_get_node_type(node) != PLIST_STRING) {
678 usbmuxd_log(LL_ERROR, "Could not read valid MessageType node from plist!");
679 plist_free(dict);
680 return -1;
681 }
682 plist_get_string_val(node, &message);
683 if (!message) {
684 usbmuxd_log(LL_ERROR, "Could not extract MessageType from plist!");
685 plist_free(dict);
686 return -1;
687 }
688 update_client_info(client, dict);
689 if (!strcmp(message, "Listen")) {
690 free(message);
691 plist_free(dict);
692 if (send_result(client, hdr->tag, 0) < 0)
693 return -1;
694 usbmuxd_log(LL_DEBUG, "Client %d now LISTENING", client->fd);
695 return start_listen(client);
696 } else if (!strcmp(message, "Connect")) {
697 uint64_t val;
698 uint16_t portnum = 0;
699 uint32_t device_id = 0;
700 free(message);
701 // get device id
702 node = plist_dict_get_item(dict, "DeviceID");
703 if (!node) {
704 usbmuxd_log(LL_ERROR, "Received connect request without device_id!");
705 plist_free(dict);
706 if (send_result(client, hdr->tag, RESULT_BADDEV) < 0)
707 return -1;
708 return 0;
709 }
710 val = 0;
711 plist_get_uint_val(node, &val);
712 device_id = (uint32_t)val;
713
714 // get port number
715 node = plist_dict_get_item(dict, "PortNumber");
716 if (!node) {
717 usbmuxd_log(LL_ERROR, "Received connect request without port number!");
718 plist_free(dict);
719 if (send_result(client, hdr->tag, RESULT_BADCOMMAND) < 0)
720 return -1;
721 return 0;
722 }
723 val = 0;
724 plist_get_uint_val(node, &val);
725 portnum = (uint16_t)val;
726 plist_free(dict);
727
728 usbmuxd_log(LL_DEBUG, "Client %d requesting connection to device %d port %d", client->fd, device_id, ntohs(portnum));
729 res = device_start_connect(device_id, ntohs(portnum), client);
730 if(res < 0) {
731 if (send_result(client, hdr->tag, -res) < 0)
732 return -1;
733 } else {
734 client->connect_tag = hdr->tag;
735 client->connect_device = device_id;
736 client->state = CLIENT_CONNECTING1;
737 }
738 return 0;
739 } else if (!strcmp(message, "ListDevices")) {
740 free(message);
741 plist_free(dict);
742 if (send_device_list(client, hdr->tag) < 0)
743 return -1;
744 return 0;
745 } else if (!strcmp(message, "ListListeners")) {
746 free(message);
747 plist_free(dict);
748 if (send_listener_list(client, hdr->tag) < 0)
749 return -1;
750 return 0;
751 } else if (!strcmp(message, "ReadBUID")) {
752 free(message);
753 plist_free(dict);
754 if (send_system_buid(client, hdr->tag) < 0)
755 return -1;
756 return 0;
757 } else if (!strcmp(message, "ReadPairRecord")) {
758 free(message);
759 char* record_id = plist_dict_get_string_val(dict, "PairRecordID");
760 plist_free(dict);
761
762 res = send_pair_record(client, hdr->tag, record_id);
763 if (record_id)
764 free(record_id);
765 if (res < 0)
766 return -1;
767 return 0;
768 } else if (!strcmp(message, "SavePairRecord")) {
769 uint32_t rval = RESULT_OK;
770 free(message);
771 char* record_id = plist_dict_get_string_val(dict, "PairRecordID");
772 char* record_data = NULL;
773 uint64_t record_size = 0;
774 plist_t rdata = plist_dict_get_item(dict, "PairRecordData");
775 if (rdata && plist_get_node_type(rdata) == PLIST_DATA) {
776 plist_get_data_val(rdata, &record_data, &record_size);
777 }
778
779 if (record_id && record_data) {
780 res = config_set_device_record(record_id, record_data, record_size);
781 if (res < 0) {
782 rval = -res;
783 } else {
784 plist_t p_dev_id = plist_dict_get_item(dict, "DeviceID");
785 uint32_t dev_id = 0;
786 if (p_dev_id && plist_get_node_type(p_dev_id) == PLIST_UINT) {
787 uint64_t u_dev_id = 0;
788 plist_get_uint_val(p_dev_id, &u_dev_id);
789 dev_id = (uint32_t)u_dev_id;
790 }
791 if (dev_id > 0) {
792 struct device_info *devs = NULL;
793 struct device_info *dev;
794 int i;
795 int count = device_get_list(1, &devs);
796 int found = 0;
797 dev = devs;
798 for (i = 0; devs && i < count; i++, dev++) {
799 if ((uint32_t)dev->id == dev_id && (strcmp(dev->serial, record_id) == 0)) {
800 found++;
801 break;
802 }
803 }
804 if (!found) {
805 usbmuxd_log(LL_ERROR, "ERROR: SavePairRecord: DeviceID %d (%s) is not connected\n", dev_id, record_id);
806 } else {
807 client_device_paired(dev_id);
808 }
809 free(devs);
810 }
811 }
812 free(record_id);
813 } else {
814 rval = EINVAL;
815 }
816 free(record_data);
817 plist_free(dict);
818 if (send_result(client, hdr->tag, rval) < 0)
819 return -1;
820 return 0;
821 } else if (!strcmp(message, "DeletePairRecord")) {
822 uint32_t rval = RESULT_OK;
823 free(message);
824 char* record_id = plist_dict_get_string_val(dict, "PairRecordID");
825 plist_free(dict);
826 if (record_id) {
827 res = config_remove_device_record(record_id);
828 if (res < 0) {
829 rval = -res;
830 }
831 free(record_id);
832 } else {
833 rval = EINVAL;
834 }
835 if (send_result(client, hdr->tag, rval) < 0)
836 return -1;
837 return 0;
838 } else {
839 usbmuxd_log(LL_ERROR, "Unexpected command '%s' received!", message);
840 free(message);
841 plist_free(dict);
842 if (send_result(client, hdr->tag, RESULT_BADCOMMAND) < 0)
843 return -1;
844 return 0;
845 }
846 }
847 // should not be reached?!
848 return -1;
849 case MESSAGE_LISTEN:
850 if(send_result(client, hdr->tag, 0) < 0)
851 return -1;
852 usbmuxd_log(LL_DEBUG, "Client %d now LISTENING", client->fd);
853 return start_listen(client);
854 case MESSAGE_CONNECT:
855 ch = (void*)hdr;
856 usbmuxd_log(LL_DEBUG, "Client %d connection request to device %d port %d", client->fd, ch->device_id, ntohs(ch->port));
857 res = device_start_connect(ch->device_id, ntohs(ch->port), client);
858 if(res < 0) {
859 if(send_result(client, hdr->tag, -res) < 0)
860 return -1;
861 } else {
862 client->connect_tag = hdr->tag;
863 client->connect_device = ch->device_id;
864 client->state = CLIENT_CONNECTING1;
865 }
866 return 0;
867 default:
868 usbmuxd_log(LL_ERROR, "Client %d invalid command %d", client->fd, hdr->message);
869 if(send_result(client, hdr->tag, RESULT_BADCOMMAND) < 0)
870 return -1;
871 return 0;
872 }
873 return -1;
874}
875
876static void output_buffer_process(struct mux_client *client)
877{
878 int res;
879 if(!client->ob_size) {
880 usbmuxd_log(LL_WARNING, "Client %d OUT process but nothing to send?", client->fd);
881 client->events &= ~POLLOUT;
882 return;
883 }
884 res = send(client->fd, client->ob_buf, client->ob_size, 0);
885 if(res <= 0) {
886 usbmuxd_log(LL_ERROR, "Sending to client fd %d failed: %d %s", client->fd, res, strerror(errno));
887 client_close(client);
888 return;
889 }
890 if((uint32_t)res == client->ob_size) {
891 client->ob_size = 0;
892 client->events &= ~POLLOUT;
893 if(client->state == CLIENT_CONNECTING2) {
894 usbmuxd_log(LL_DEBUG, "Client %d switching to CONNECTED state", client->fd);
895 client->state = CLIENT_CONNECTED;
896 client->events = client->devents;
897 // no longer need this
898 free(client->ob_buf);
899 client->ob_buf = NULL;
900 }
901 } else {
902 client->ob_size -= res;
903 memmove(client->ob_buf, client->ob_buf + res, client->ob_size);
904 }
905}
906static void input_buffer_process(struct mux_client *client)
907{
908 int res;
909 int did_read = 0;
910 if(client->ib_size < sizeof(struct usbmuxd_header)) {
911 res = recv(client->fd, client->ib_buf + client->ib_size, sizeof(struct usbmuxd_header) - client->ib_size, 0);
912 if(res <= 0) {
913 if(res < 0)
914 usbmuxd_log(LL_ERROR, "Receive from client fd %d failed: %s", client->fd, strerror(errno));
915 else
916 usbmuxd_log(LL_INFO, "Client %d connection closed", client->fd);
917 client_close(client);
918 return;
919 }
920 client->ib_size += res;
921 if(client->ib_size < sizeof(struct usbmuxd_header))
922 return;
923 did_read = 1;
924 }
925 struct usbmuxd_header *hdr = (void*)client->ib_buf;
926 if(hdr->length > client->ib_capacity) {
927 usbmuxd_log(LL_INFO, "Client %d message is too long (%d bytes)", client->fd, hdr->length);
928 client_close(client);
929 return;
930 }
931 if(hdr->length < sizeof(struct usbmuxd_header)) {
932 usbmuxd_log(LL_ERROR, "Client %d message is too short (%d bytes)", client->fd, hdr->length);
933 client_close(client);
934 return;
935 }
936 if(client->ib_size < hdr->length) {
937 if(did_read)
938 return; //maybe we would block, so defer to next loop
939 res = recv(client->fd, client->ib_buf + client->ib_size, hdr->length - client->ib_size, 0);
940 if(res < 0) {
941 usbmuxd_log(LL_ERROR, "Receive from client fd %d failed: %s", client->fd, strerror(errno));
942 client_close(client);
943 return;
944 } else if(res == 0) {
945 usbmuxd_log(LL_INFO, "Client %d connection closed", client->fd);
946 client_close(client);
947 return;
948 }
949 client->ib_size += res;
950 if(client->ib_size < hdr->length)
951 return;
952 }
953 handle_command(client, hdr);
954 client->ib_size = 0;
955}
956
957void client_process(int fd, short events)
958{
959 struct mux_client *client = NULL;
960 mutex_lock(&client_list_mutex);
961 FOREACH(struct mux_client *lc, &client_list) {
962 if(lc->fd == fd) {
963 client = lc;
964 break;
965 }
966 } ENDFOREACH
967 mutex_unlock(&client_list_mutex);
968
969 if(!client) {
970 usbmuxd_log(LL_INFO, "client_process: fd %d not found in client list", fd);
971 return;
972 }
973
974 if(client->state == CLIENT_CONNECTED) {
975 usbmuxd_log(LL_SPEW, "client_process in CONNECTED state");
976 device_client_process(client->connect_device, client, events);
977 } else {
978 if(events & POLLIN) {
979 input_buffer_process(client);
980 } else if(events & POLLOUT) { //not both in case client died as part of process_recv
981 output_buffer_process(client);
982 }
983 }
984
985}
986
987void client_device_add(struct device_info *dev)
988{
989 mutex_lock(&client_list_mutex);
990 usbmuxd_log(LL_DEBUG, "client_device_add: id %d, location 0x%x, serial %s", dev->id, dev->location, dev->serial);
991 device_set_visible(dev->id);
992 FOREACH(struct mux_client *client, &client_list) {
993 if(client->state == CLIENT_LISTEN)
994 send_device_add(client, dev);
995 } ENDFOREACH
996 mutex_unlock(&client_list_mutex);
997}
998
999void client_device_remove(int device_id)
1000{
1001 mutex_lock(&client_list_mutex);
1002 uint32_t id = device_id;
1003 usbmuxd_log(LL_DEBUG, "client_device_remove: id %d", device_id);
1004 FOREACH(struct mux_client *client, &client_list) {
1005 if(client->state == CLIENT_LISTEN)
1006 send_device_remove(client, id);
1007 } ENDFOREACH
1008 mutex_unlock(&client_list_mutex);
1009}
1010
1011void client_device_paired(int device_id)
1012{
1013 mutex_lock(&client_list_mutex);
1014 uint32_t id = device_id;
1015 usbmuxd_log(LL_DEBUG, "client_device_paired: id %d", device_id);
1016 FOREACH(struct mux_client *client, &client_list) {
1017 if (client->state == CLIENT_LISTEN)
1018 send_device_paired(client, id);
1019 } ENDFOREACH
1020 mutex_unlock(&client_list_mutex);
1021}
1022
1023void client_init(void)
1024{
1025 usbmuxd_log(LL_DEBUG, "client_init");
1026 collection_init(&client_list);
1027 mutex_init(&client_list_mutex);
1028}
1029
1030void client_shutdown(void)
1031{
1032 usbmuxd_log(LL_DEBUG, "client_shutdown");
1033 FOREACH(struct mux_client *client, &client_list) {
1034 client_close(client);
1035 } ENDFOREACH
1036 mutex_destroy(&client_list_mutex);
1037 collection_free(&client_list);
1038}
diff --git a/src/client.h b/src/client.h
new file mode 100644
index 0000000..6cac4db
--- /dev/null
+++ b/src/client.h
@@ -0,0 +1,47 @@
1/*
2 * client.h
3 *
4 * Copyright (C) 2009 Hector Martin <hector@marcansoft.com>
5 * Copyright (C) 2009 Nikias Bassen <nikias@gmx.li>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 2 or version 3.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 */
20
21#ifndef CLIENT_H
22#define CLIENT_H
23
24#include <stdint.h>
25#include "usbmuxd-proto.h"
26
27struct device_info;
28struct mux_client;
29
30int client_read(struct mux_client *client, void *buffer, uint32_t len);
31int client_write(struct mux_client *client, void *buffer, uint32_t len);
32int client_set_events(struct mux_client *client, short events);
33void client_close(struct mux_client *client);
34int client_notify_connect(struct mux_client *client, enum usbmuxd_result result);
35
36void client_device_add(struct device_info *dev);
37void client_device_remove(int device_id);
38void client_device_paired(int device_id);
39
40int client_accept(int fd);
41void client_get_fds(struct fdlist *list);
42void client_process(int fd, short events);
43
44void client_init(void);
45void client_shutdown(void);
46
47#endif
diff --git a/src/conf.c b/src/conf.c
new file mode 100644
index 0000000..2f0968d
--- /dev/null
+++ b/src/conf.c
@@ -0,0 +1,565 @@
1/*
2 * conf.c
3 *
4 * Copyright (C) 2013 Nikias Bassen <nikias@gmx.li>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 2 or version 3.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18 */
19
20#ifdef HAVE_CONFIG_H
21#include <config.h>
22#endif
23
24#include <stdio.h>
25#include <stdint.h>
26#include <stdlib.h>
27#include <string.h>
28#include <time.h>
29#ifdef HAVE_SYS_TYPES_H
30#include <sys/types.h>
31#endif
32
33#include <dirent.h>
34#include <libgen.h>
35#include <sys/stat.h>
36#include <errno.h>
37#include <ctype.h>
38
39#include <libimobiledevice-glue/utils.h>
40#include <plist/plist.h>
41
42#include "conf.h"
43#include "utils.h"
44#include "log.h"
45
46#ifdef WIN32
47#include <shlobj.h>
48
49#define DIR_SEP '\\'
50#define DIR_SEP_S "\\"
51#else
52#define DIR_SEP '/'
53#define DIR_SEP_S "/"
54#endif
55
56#define CONFIG_SYSTEM_BUID_KEY "SystemBUID"
57#define CONFIG_HOST_ID_KEY "HostID"
58
59#define CONFIG_EXT ".plist"
60
61#ifdef WIN32
62#define CONFIG_DIR "Apple"DIR_SEP_S"Lockdown"
63#else
64#define CONFIG_DIR "lockdown"
65#endif
66
67#define CONFIG_FILE "SystemConfiguration"CONFIG_EXT
68
69static char *__config_dir = NULL;
70
71#ifdef WIN32
72static char *config_utf16_to_utf8(wchar_t *unistr, long len, long *items_read, long *items_written)
73{
74 if (!unistr || (len <= 0)) return NULL;
75 char *outbuf = (char*)malloc(3*(len+1));
76 int p = 0;
77 int i = 0;
78
79 wchar_t wc;
80
81 while (i < len) {
82 wc = unistr[i++];
83 if (wc >= 0x800) {
84 outbuf[p++] = (char)(0xE0 + ((wc >> 12) & 0xF));
85 outbuf[p++] = (char)(0x80 + ((wc >> 6) & 0x3F));
86 outbuf[p++] = (char)(0x80 + (wc & 0x3F));
87 } else if (wc >= 0x80) {
88 outbuf[p++] = (char)(0xC0 + ((wc >> 6) & 0x1F));
89 outbuf[p++] = (char)(0x80 + (wc & 0x3F));
90 } else {
91 outbuf[p++] = (char)(wc & 0x7F);
92 }
93 }
94 if (items_read) {
95 *items_read = i;
96 }
97 if (items_written) {
98 *items_written = p;
99 }
100 outbuf[p] = 0;
101
102 return outbuf;
103}
104#endif
105
106const char *config_get_config_dir()
107{
108 char *base_config_dir = NULL;
109
110 if (__config_dir)
111 return __config_dir;
112
113#ifdef WIN32
114 wchar_t path[MAX_PATH+1];
115 HRESULT hr;
116 LPITEMIDLIST pidl = NULL;
117 BOOL b = FALSE;
118
119 hr = SHGetSpecialFolderLocation (NULL, CSIDL_COMMON_APPDATA, &pidl);
120 if (hr == S_OK) {
121 b = SHGetPathFromIDListW (pidl, path);
122 if (b) {
123 base_config_dir = config_utf16_to_utf8 (path, wcslen(path), NULL, NULL);
124 CoTaskMemFree (pidl);
125 }
126 }
127#else
128#ifdef __APPLE__
129 base_config_dir = strdup("/var/db");
130#else
131 base_config_dir = strdup("/var/lib");
132#endif
133#endif
134 __config_dir = string_concat(base_config_dir, DIR_SEP_S, CONFIG_DIR, NULL);
135
136 if (__config_dir) {
137 int i = strlen(__config_dir)-1;
138 while ((i > 0) && (__config_dir[i] == DIR_SEP)) {
139 __config_dir[i--] = '\0';
140 }
141 }
142
143 free(base_config_dir);
144
145 usbmuxd_log(LL_DEBUG, "Initialized config_dir to %s", __config_dir);
146
147 return __config_dir;
148}
149
150static int __mkdir(const char *dir, int mode)
151{
152#ifdef WIN32
153 return mkdir(dir);
154#else
155 return mkdir(dir, mode);
156#endif
157}
158
159static int mkdir_with_parents(const char *dir, int mode)
160{
161 if (!dir) return -1;
162 if (__mkdir(dir, mode) == 0) {
163 return 0;
164 } else {
165 if (errno == EEXIST) return 0;
166 }
167 int res;
168 char *parent = strdup(dir);
169 char* parentdir = dirname(parent);
170 if (parentdir) {
171 res = mkdir_with_parents(parentdir, mode);
172 } else {
173 res = -1;
174 }
175 free(parent);
176 return res;
177}
178
179/**
180 * Creates a freedesktop compatible configuration directory.
181 */
182static int config_create_config_dir(void)
183{
184 const char *config_path = config_get_config_dir();
185 struct stat st;
186 int res = stat(config_path, &st);
187 if (res != 0) {
188 res = mkdir_with_parents(config_path, 0755);
189 }
190 return res;
191}
192
193int config_set_config_dir(const char* path)
194{
195 if (!path) {
196 return -1;
197 }
198 free(__config_dir);
199 __config_dir = strdup(path);
200 usbmuxd_log(LL_DEBUG, "Setting config_dir to %s", __config_dir);
201 return config_create_config_dir();
202}
203
204static int get_rand(int min, int max)
205{
206 int retval = (rand() % (max - min)) + min;
207 return retval;
208}
209
210static char *config_generate_uuid(int idx)
211{
212 char *uuid = (char *) malloc(sizeof(char) * 37);
213 const char *chars = "ABCDEF0123456789";
214 srand(time(NULL) - idx);
215 int i = 0;
216
217 for (i = 0; i < 36; i++) {
218 if (i == 8 || i == 13 || i == 18 || i == 23) {
219 uuid[i] = '-';
220 continue;
221 } else {
222 uuid[i] = chars[get_rand(0, 16)];
223 }
224 }
225 /* make it a real string */
226 uuid[36] = '\0';
227 return uuid;
228}
229
230/**
231 * Generates a valid BUID for this system (which is actually a UUID).
232 *
233 * @return A null terminated string containing a valid BUID.
234 */
235static char *config_generate_system_buid()
236{
237 return config_generate_uuid(1);
238}
239
240static int internal_set_value(const char *config_file, const char *key, plist_t value)
241{
242 if (!config_file)
243 return 0;
244
245 /* read file into plist */
246 plist_t config = NULL;
247
248 plist_read_from_file(config_file, &config, NULL);
249 if (!config) {
250 config = plist_new_dict();
251 plist_dict_set_item(config, key, value);
252 } else {
253 plist_t n = plist_dict_get_item(config, key);
254 if (n) {
255 plist_dict_remove_item(config, key);
256 }
257 plist_dict_set_item(config, key, value);
258 remove(config_file);
259 }
260
261 /* store in config file */
262 char *value_string = NULL;
263 if (plist_get_node_type(value) == PLIST_STRING) {
264 plist_get_string_val(value, &value_string);
265 usbmuxd_log(LL_DEBUG, "Setting key %s to %s in config file %s", key, value_string, config_file);
266 if (value_string)
267 free(value_string);
268 } else {
269 usbmuxd_log(LL_DEBUG, "Setting key %s in config file %s", key, config_file);
270 }
271
272 int res = (plist_write_to_file(config, config_file, PLIST_FORMAT_XML, 0) == PLIST_ERR_SUCCESS);
273
274 plist_free(config);
275
276 return res;
277}
278
279static int config_set_value(const char *key, plist_t value)
280{
281 const char *config_path = NULL;
282 char *config_file = NULL;
283
284 /* Make sure config directory exists */
285 if (config_create_config_dir() < 0) {
286 usbmuxd_log(LL_ERROR, "ERROR: Failed to create config directory\n");
287 return -1;
288 }
289
290 config_path = config_get_config_dir();
291 config_file = string_concat(config_path, DIR_SEP_S, CONFIG_FILE, NULL);
292
293 int result = internal_set_value(config_file, key, value);
294 if (!result) {
295 usbmuxd_log(LL_ERROR, "ERROR: Failed to write to '%s'", config_file);
296 }
297
298 free(config_file);
299
300 return result;
301}
302
303static int internal_get_value(const char* config_file, const char *key, plist_t *value)
304{
305 *value = NULL;
306
307 /* now parse file to get the SystemBUID */
308 plist_t config = NULL;
309 if (plist_read_from_file(config_file, &config, NULL) == PLIST_ERR_SUCCESS) {
310 usbmuxd_log(LL_DEBUG, "Reading key %s from config file %s", key, config_file);
311 plist_t n = plist_dict_get_item(config, key);
312 if (n) {
313 *value = plist_copy(n);
314 n = NULL;
315 }
316 }
317 plist_free(config);
318
319 return 1;
320}
321
322static int config_get_value(const char *key, plist_t *value)
323{
324 const char *config_path = NULL;
325 char *config_file = NULL;
326
327 config_path = config_get_config_dir();
328 config_file = string_concat(config_path, DIR_SEP_S, CONFIG_FILE, NULL);
329
330 int result = internal_get_value(config_file, key, value);
331
332 free(config_file);
333
334 return result;
335}
336
337/**
338 * Store SystemBUID in config file.
339 *
340 * @param system_buid A null terminated string containing a valid SystemBUID.
341 */
342static int config_set_system_buid(const char *system_buid)
343{
344 return config_set_value(CONFIG_SYSTEM_BUID_KEY, plist_new_string(system_buid));
345}
346
347/**
348 * Determines whether a pairing record is present for the given device.
349 *
350 * @param udid The device UDID as given by the device.
351 *
352 * @return 1 if there's a pairing record for the given udid or 0 otherwise.
353 */
354int config_has_device_record(const char *udid)
355{
356 int res = 0;
357 if (!udid) return 0;
358
359 /* ensure config directory exists */
360 if (config_create_config_dir() < 0) {
361 usbmuxd_log(LL_ERROR, "ERROR: Failed to create config directory\n");
362 return -1;
363 }
364
365 /* build file path */
366 const char *config_path = config_get_config_dir();
367 char *device_record_file = string_concat(config_path, DIR_SEP_S, udid, CONFIG_EXT, NULL);
368
369 struct stat st;
370
371 if ((stat(device_record_file, &st) == 0) && S_ISREG(st.st_mode))
372 res = 1;
373
374 free(device_record_file);
375
376 return res;
377}
378
379/**
380 * Reads the BUID from a previously generated configuration file.
381 *
382 * @param system_buid pointer to a variable that will be set to point to a
383 * newly allocated string containing the BUID.
384 *
385 * @note It is the responsibility of the calling function to free the returned system_buid
386 */
387void config_get_system_buid(char **system_buid)
388{
389 plist_t value = NULL;
390
391 config_get_value(CONFIG_SYSTEM_BUID_KEY, &value);
392
393 if (value && (plist_get_node_type(value) == PLIST_STRING)) {
394 plist_get_string_val(value, system_buid);
395 usbmuxd_log(LL_DEBUG, "Got %s %s", CONFIG_SYSTEM_BUID_KEY, *system_buid);
396 }
397
398 if (value)
399 plist_free(value);
400
401 if (!*system_buid) {
402 /* no config, generate system_buid */
403 usbmuxd_log(LL_DEBUG, "No previous %s found", CONFIG_SYSTEM_BUID_KEY);
404 *system_buid = config_generate_system_buid();
405 if (!config_set_system_buid(*system_buid)) {
406 usbmuxd_log(LL_WARNING, "WARNING: Failed to store SystemBUID, this might be a problem");
407 }
408 }
409
410 usbmuxd_log(LL_DEBUG, "Using %s as %s", *system_buid, CONFIG_SYSTEM_BUID_KEY);
411}
412
413/**
414 * Store a pairing record for the given device identifier.
415 *
416 * @param udid device identifier
417 * @param record_data buffer containing a pairing record
418 * @param record_size size of buffer passed in record_data
419 *
420 * @return 0 on success or a negative errno otherwise.
421 */
422int config_set_device_record(const char *udid, char* record_data, uint64_t record_size)
423{
424 int res = 0;
425
426 if (!udid || !record_data || record_size < 8)
427 return -EINVAL;
428
429 /* verify udid input */
430 const char* u = udid;
431 while (*u != '\0') {
432 if (!isalnum(*u) && (*u != '-')) {
433 usbmuxd_log(LL_ERROR, "ERROR: %s: udid contains invalid character.\n", __func__);
434 return -EINVAL;
435 }
436 u++;
437 }
438
439 plist_t plist = NULL;
440 plist_from_memory(record_data, record_size, &plist, NULL);
441
442 if (!plist || plist_get_node_type(plist) != PLIST_DICT) {
443 if (plist)
444 plist_free(plist);
445 return -EINVAL;
446 }
447
448 /* ensure config directory exists */
449 if (config_create_config_dir() < 0) {
450 usbmuxd_log(LL_ERROR, "ERROR: Failed to create config directory\n");
451 return -1;
452 }
453
454 /* build file path */
455 const char *config_path = config_get_config_dir();
456 char *device_record_file = string_concat(config_path, DIR_SEP_S, udid, CONFIG_EXT, NULL);
457
458 remove(device_record_file);
459
460 /* store file */
461 if (plist_write_to_file(plist, device_record_file, PLIST_FORMAT_XML, 0) != PLIST_ERR_SUCCESS) {
462 usbmuxd_log(LL_DEBUG, "Could not open '%s' for writing: %s", device_record_file, strerror(errno));
463 res = -ENOENT;
464 }
465 free(device_record_file);
466 if (plist)
467 plist_free(plist);
468
469 return res;
470}
471
472/**
473 * Retrieve a pairing record for the given device identifier
474 *
475 * @param udid device identifier
476 * @param record_data pointer to a variable that will be set to point to a
477 * newly allocated buffer holding the pairing record
478 * @param record_size pointer to a variable that will be set to the size
479 * of the buffer given in record_data.
480 *
481 * @return 0 on success or a negative errno otherwise.
482 */
483int config_get_device_record(const char *udid, char **record_data, uint64_t *record_size)
484{
485 int res = 0;
486
487 /* ensure config directory exists */
488 if (config_create_config_dir() < 0) {
489 usbmuxd_log(LL_ERROR, "ERROR: Failed to create config directory\n");
490 return -1;
491 }
492
493 /* build file path */
494 const char *config_path = config_get_config_dir();
495 char *device_record_file = string_concat(config_path, DIR_SEP_S, udid, CONFIG_EXT, NULL);
496
497 /* read file */
498 buffer_read_from_filename(device_record_file, record_data, record_size);
499 if (!*record_data) {
500 usbmuxd_log(LL_ERROR, "ERROR: Failed to read '%s': %s", device_record_file, strerror(errno));
501 res = -ENOENT;
502 }
503 free(device_record_file);
504
505 return res;
506}
507
508/**
509 * Remove the pairing record stored for a device from this host.
510 *
511 * @param udid The udid of the device
512 *
513 * @return 0 on success or a negative errno otherwise.
514 */
515int config_remove_device_record(const char *udid)
516{
517 int res = 0;
518
519 /* build file path */
520 const char *config_path = config_get_config_dir();
521 char *device_record_file = string_concat(config_path, DIR_SEP_S, udid, CONFIG_EXT, NULL);
522
523 /* remove file */
524 if (remove(device_record_file) != 0) {
525 res = -errno;
526 usbmuxd_log(LL_DEBUG, "Could not remove %s: %s", device_record_file, strerror(errno));
527 }
528
529 free(device_record_file);
530
531 return res;
532}
533
534static int config_device_record_get_value(const char *udid, const char *key, plist_t *value)
535{
536 const char *config_path = NULL;
537 char *config_file = NULL;
538
539 config_path = config_get_config_dir();
540 config_file = string_concat(config_path, DIR_SEP_S, udid, CONFIG_EXT, NULL);
541
542 int result = internal_get_value(config_file, key, value);
543
544 free(config_file);
545
546 return result;
547}
548
549void config_device_record_get_host_id(const char *udid, char **host_id)
550{
551 plist_t value = NULL;
552
553 config_device_record_get_value(udid, CONFIG_HOST_ID_KEY, &value);
554
555 if (value && (plist_get_node_type(value) == PLIST_STRING)) {
556 plist_get_string_val(value, host_id);
557 }
558
559 if (value)
560 plist_free(value);
561
562 if (!*host_id) {
563 usbmuxd_log(LL_ERROR, "ERROR: Could not get HostID from pairing record for udid %s", udid);
564 }
565}
diff --git a/src/conf.h b/src/conf.h
new file mode 100644
index 0000000..294ca18
--- /dev/null
+++ b/src/conf.h
@@ -0,0 +1,41 @@
1/*
2 * conf.h
3 *
4 * Copyright (C) 2013 Nikias Bassen <nikias@gmx.li>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 2 or version 3.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18 */
19
20#ifndef CONF_H
21#define CONF_H
22
23#ifdef HAVE_CONFIG_H
24#include <config.h>
25#endif
26
27#include <plist/plist.h>
28
29const char *config_get_config_dir();
30int config_set_config_dir(const char* path);
31
32void config_get_system_buid(char **system_buid);
33
34int config_has_device_record(const char *udid);
35int config_get_device_record(const char *udid, char **record_data, uint64_t *record_size);
36int config_set_device_record(const char *udid, char* record_data, uint64_t record_size);
37int config_remove_device_record(const char *udid);
38
39void config_device_record_get_host_id(const char *udid, char **host_id);
40
41#endif
diff --git a/daemon/device.c b/src/device.c
index 0a143b2..ce73718 100644
--- a/daemon/device.c
+++ b/src/device.c
@@ -1,23 +1,24 @@
1/* 1/*
2 usbmuxd - iPhone/iPod Touch USB multiplex server daemon 2 * device.c
3 3 *
4Copyright (C) 2009 Hector Martin "marcan" <hector@marcansoft.com> 4 * Copyright (C) 2009 Hector Martin "marcan" <hector@marcansoft.com>
5 5 * Copyright (C) 2014 Mikkel Kamstrup Erlandsen <mikkel.kamstrup@xamarin.com>
6This program is free software; you can redistribute it and/or modify 6 *
7it under the terms of the GNU General Public License as published by 7 * This program is free software; you can redistribute it and/or modify
8the Free Software Foundation, either version 2 or version 3. 8 * it under the terms of the GNU General Public License as published by
9 9 * the Free Software Foundation, either version 2 or version 3.
10This program is distributed in the hope that it will be useful, 10 *
11but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * This program is distributed in the hope that it will be useful,
12MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13GNU General Public License for more details. 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 14 * GNU General Public License for more details.
15You should have received a copy of the GNU General Public License 15 *
16along with this program; if not, write to the Free Software 16 * You should have received a copy of the GNU General Public License
17Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 17 * along with this program; if not, write to the Free Software
18 18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19*/ 19 */
20 20
21#define _DEFAULT_SOURCE
21#define _BSD_SOURCE 22#define _BSD_SOURCE
22 23
23#ifdef HAVE_CONFIG_H 24#ifdef HAVE_CONFIG_H
@@ -31,8 +32,14 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
31#include <string.h> 32#include <string.h>
32#include <stdint.h> 33#include <stdint.h>
33#include <inttypes.h> 34#include <inttypes.h>
35#include <unistd.h>
36
37#include <libimobiledevice-glue/collection.h>
38#include <libimobiledevice-glue/thread.h>
39
34#include "device.h" 40#include "device.h"
35#include "client.h" 41#include "client.h"
42#include "preflight.h"
36#include "usb.h" 43#include "usb.h"
37#include "log.h" 44#include "log.h"
38 45
@@ -47,6 +54,8 @@ int next_device_id;
47 54
48enum mux_protocol { 55enum mux_protocol {
49 MUX_PROTO_VERSION = 0, 56 MUX_PROTO_VERSION = 0,
57 MUX_PROTO_CONTROL = 1,
58 MUX_PROTO_SETUP = 2,
50 MUX_PROTO_TCP = IPPROTO_TCP, 59 MUX_PROTO_TCP = IPPROTO_TCP,
51}; 60};
52 61
@@ -68,6 +77,9 @@ struct mux_header
68{ 77{
69 uint32_t protocol; 78 uint32_t protocol;
70 uint32_t length; 79 uint32_t length;
80 uint32_t magic;
81 uint16_t tx_seq;
82 uint16_t rx_seq;
71}; 83};
72 84
73struct version_header 85struct version_header
@@ -106,25 +118,58 @@ struct mux_device
106 struct usb_device *usbdev; 118 struct usb_device *usbdev;
107 int id; 119 int id;
108 enum mux_dev_state state; 120 enum mux_dev_state state;
121 int visible;
109 struct collection connections; 122 struct collection connections;
110 uint16_t next_sport; 123 uint16_t next_sport;
111 unsigned char *pktbuf; 124 unsigned char *pktbuf;
112 uint32_t pktlen; 125 uint32_t pktlen;
126 void *preflight_cb_data;
127 int version;
128 uint16_t rx_seq;
129 uint16_t tx_seq;
113}; 130};
114 131
115static struct collection device_list; 132static struct collection device_list;
133mutex_t device_list_mutex;
116 134
117uint64_t mstime64(void) 135static struct mux_device* get_mux_device_for_id(int device_id)
118{ 136{
119 struct timeval tv; 137 struct mux_device *dev = NULL;
120 gettimeofday(&tv, NULL); 138 mutex_lock(&device_list_mutex);
121 return tv.tv_sec * 1000 + tv.tv_usec / 1000; 139 FOREACH(struct mux_device *cdev, &device_list) {
140 if(cdev->id == device_id) {
141 dev = cdev;
142 break;
143 }
144 } ENDFOREACH
145 mutex_unlock(&device_list_mutex);
146
147 return dev;
148}
149
150static struct mux_connection* get_mux_connection(int device_id, struct mux_client *client)
151{
152 struct mux_connection *conn = NULL;
153 FOREACH(struct mux_device *dev, &device_list) {
154 if(dev->id == device_id) {
155 FOREACH(struct mux_connection *lconn, &dev->connections) {
156 if(lconn->client == client) {
157 conn = lconn;
158 break;
159 }
160 } ENDFOREACH
161 break;
162 }
163 } ENDFOREACH
164
165 return conn;
122} 166}
123 167
124static int get_next_device_id(void) 168static int get_next_device_id(void)
125{ 169{
126 while(1) { 170 while(1) {
127 int ok = 1; 171 int ok = 1;
172 mutex_lock(&device_list_mutex);
128 FOREACH(struct mux_device *dev, &device_list) { 173 FOREACH(struct mux_device *dev, &device_list) {
129 if(dev->id == next_device_id) { 174 if(dev->id == next_device_id) {
130 next_device_id++; 175 next_device_id++;
@@ -132,6 +177,7 @@ static int get_next_device_id(void)
132 break; 177 break;
133 } 178 }
134 } ENDFOREACH 179 } ENDFOREACH
180 mutex_unlock(&device_list_mutex);
135 if(ok) 181 if(ok)
136 return next_device_id++; 182 return next_device_id++;
137 } 183 }
@@ -147,6 +193,9 @@ static int send_packet(struct mux_device *dev, enum mux_protocol proto, void *he
147 case MUX_PROTO_VERSION: 193 case MUX_PROTO_VERSION:
148 hdrlen = sizeof(struct version_header); 194 hdrlen = sizeof(struct version_header);
149 break; 195 break;
196 case MUX_PROTO_SETUP:
197 hdrlen = 0;
198 break;
150 case MUX_PROTO_TCP: 199 case MUX_PROTO_TCP:
151 hdrlen = sizeof(struct tcphdr); 200 hdrlen = sizeof(struct tcphdr);
152 break; 201 break;
@@ -156,7 +205,9 @@ static int send_packet(struct mux_device *dev, enum mux_protocol proto, void *he
156 } 205 }
157 usbmuxd_log(LL_SPEW, "send_packet(%d, 0x%x, %p, %p, %d)", dev->id, proto, header, data, length); 206 usbmuxd_log(LL_SPEW, "send_packet(%d, 0x%x, %p, %p, %d)", dev->id, proto, header, data, length);
158 207
159 int total = sizeof(struct mux_header) + hdrlen + length; 208 int mux_header_size = ((dev->version < 2) ? 8 : sizeof(struct mux_header));
209
210 int total = mux_header_size + hdrlen + length;
160 211
161 if(total > USB_MTU) { 212 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); 213 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);
@@ -167,9 +218,19 @@ static int send_packet(struct mux_device *dev, enum mux_protocol proto, void *he
167 struct mux_header *mhdr = (struct mux_header *)buffer; 218 struct mux_header *mhdr = (struct mux_header *)buffer;
168 mhdr->protocol = htonl(proto); 219 mhdr->protocol = htonl(proto);
169 mhdr->length = htonl(total); 220 mhdr->length = htonl(total);
170 memcpy(buffer + sizeof(struct mux_header), header, hdrlen); 221 if (dev->version >= 2) {
222 mhdr->magic = htonl(0xfeedface);
223 if (proto == MUX_PROTO_SETUP) {
224 dev->tx_seq = 0;
225 dev->rx_seq = 0xFFFF;
226 }
227 mhdr->tx_seq = htons(dev->tx_seq);
228 mhdr->rx_seq = htons(dev->rx_seq);
229 dev->tx_seq++;
230 }
231 memcpy(buffer + mux_header_size, header, hdrlen);
171 if(data && length) 232 if(data && length)
172 memcpy(buffer + sizeof(struct mux_header) + hdrlen, data, length); 233 memcpy(buffer + mux_header_size + hdrlen, data, length);
173 234
174 if((res = usb_send(dev->usbdev, buffer, total)) < 0) { 235 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); 236 usbmuxd_log(LL_ERROR, "usb_send failed while sending packet (len %d) to device %d: %d", total, dev->id, res);
@@ -241,6 +302,7 @@ static int send_tcp(struct mux_connection *conn, uint8_t flags, const unsigned c
241static void connection_teardown(struct mux_connection *conn) 302static void connection_teardown(struct mux_connection *conn)
242{ 303{
243 int res; 304 int res;
305 int size;
244 if(conn->state == CONN_DEAD) 306 if(conn->state == CONN_DEAD)
245 return; 307 return;
246 usbmuxd_log(LL_DEBUG, "connection_teardown dev %d sport %d dport %d", conn->dev->id, conn->sport, conn->dport); 308 usbmuxd_log(LL_DEBUG, "connection_teardown dev %d sport %d dport %d", conn->dev->id, conn->sport, conn->dport);
@@ -254,26 +316,45 @@ static void connection_teardown(struct mux_connection *conn)
254 client_notify_connect(conn->client, RESULT_CONNREFUSED); 316 client_notify_connect(conn->client, RESULT_CONNREFUSED);
255 } else { 317 } else {
256 conn->state = CONN_DEAD; 318 conn->state = CONN_DEAD;
319 if((conn->events & POLLOUT) && conn->ib_size > 0){
320 usbmuxd_log(LL_DEBUG, "%s: flushing buffer to client (%u bytes)", __func__, conn->ib_size);
321 uint64_t tm_last = mstime64();
322 while(1){
323 size = client_write(conn->client, conn->ib_buf, conn->ib_size);
324 if(size < 0) {
325 usbmuxd_log(LL_ERROR, "%s: aborting buffer flush to client after error.", __func__);
326 break;
327 } else if (size == 0) {
328 uint64_t tm_now = mstime64();
329 if (tm_now - tm_last > 1000) {
330 usbmuxd_log(LL_ERROR, "%s: aborting buffer flush to client after unsuccessfully attempting for %dms.", __func__, (int)(tm_now - tm_last));
331 break;
332 }
333 usleep(10000);
334 continue;
335 }
336 if(size == (int)conn->ib_size) {
337 conn->ib_size = 0;
338 break;
339 } else {
340 conn->ib_size -= size;
341 memmove(conn->ib_buf, conn->ib_buf + size, conn->ib_size);
342 }
343 tm_last = mstime64();
344 }
345 }
257 client_close(conn->client); 346 client_close(conn->client);
258 } 347 }
259 } 348 }
260 if(conn->ib_buf) 349 free(conn->ib_buf);
261 free(conn->ib_buf); 350 free(conn->ob_buf);
262 if(conn->ob_buf)
263 free(conn->ob_buf);
264 collection_remove(&conn->dev->connections, conn); 351 collection_remove(&conn->dev->connections, conn);
265 free(conn); 352 free(conn);
266} 353}
267 354
268int device_start_connect(int device_id, uint16_t dport, struct mux_client *client) 355int device_start_connect(int device_id, uint16_t dport, struct mux_client *client)
269{ 356{
270 struct mux_device *dev = NULL; 357 struct mux_device *dev = get_mux_device_for_id(device_id);
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) { 358 if(!dev) {
278 usbmuxd_log(LL_WARNING, "Attempted to connect to nonexistent device %d", device_id); 359 usbmuxd_log(LL_WARNING, "Attempted to connect to nonexistent device %d", device_id);
279 return -RESULT_BADDEV; 360 return -RESULT_BADDEV;
@@ -313,6 +394,8 @@ int device_start_connect(int device_id, uint16_t dport, struct mux_client *clien
313 res = send_tcp(conn, TH_SYN, NULL, 0); 394 res = send_tcp(conn, TH_SYN, NULL, 0);
314 if(res < 0) { 395 if(res < 0) {
315 usbmuxd_log(LL_ERROR, "Error sending TCP SYN to device %d (%d->%d)", dev->id, sport, dport); 396 usbmuxd_log(LL_ERROR, "Error sending TCP SYN to device %d (%d->%d)", dev->id, sport, dport);
397 free(conn->ib_buf);
398 free(conn->ob_buf);
316 free(conn); 399 free(conn);
317 return -RESULT_CONNREFUSED; //bleh 400 return -RESULT_CONNREFUSED; //bleh
318 } 401 }
@@ -320,6 +403,13 @@ int device_start_connect(int device_id, uint16_t dport, struct mux_client *clien
320 return 0; 403 return 0;
321} 404}
322 405
406/**
407 * Examine the state of a connection's buffers and
408 * update all connection flags and masks accordingly.
409 * Does not do I/O.
410 *
411 * @param conn The connection to update.
412 */
323static void update_connection(struct mux_connection *conn) 413static void update_connection(struct mux_connection *conn)
324{ 414{
325 uint32_t sent = conn->tx_seq - conn->rx_ack; 415 uint32_t sent = conn->tx_seq - conn->rx_ack;
@@ -353,21 +443,33 @@ static void update_connection(struct mux_connection *conn)
353 client_set_events(conn->client, conn->events); 443 client_set_events(conn->client, conn->events);
354} 444}
355 445
356void device_client_process(int device_id, struct mux_client *client, short events) 446static int send_tcp_ack(struct mux_connection *conn)
357{ 447{
358 struct mux_connection *conn = NULL; 448 if(send_tcp(conn, TH_ACK, NULL, 0) < 0) {
359 FOREACH(struct mux_device *dev, &device_list) { 449 usbmuxd_log(LL_ERROR, "Error sending TCP ACK (%d->%d)", conn->sport, conn->dport);
360 if(dev->id == device_id) { 450 connection_teardown(conn);
361 FOREACH(struct mux_connection *lconn, &dev->connections) { 451 return -1;
362 if(lconn->client == client) { 452 }
363 conn = lconn; 453
364 break; 454 update_connection(conn);
365 }
366 } ENDFOREACH
367 break;
368 }
369 } ENDFOREACH
370 455
456 return 0;
457}
458
459/**
460 * Flush input and output buffers for a client connection.
461 *
462 * @param device_id Numeric id for the device.
463 * @param client The client to flush buffers for.
464 * @param events event mask for the client. POLLOUT means that
465 * the client is ready to receive data, POLLIN that it has
466 * data to be read (and send along to the device).
467 */
468void device_client_process(int device_id, struct mux_client *client, short events)
469{
470 mutex_lock(&device_list_mutex);
471 struct mux_connection *conn = get_mux_connection(device_id, client);
472 mutex_unlock(&device_list_mutex);
371 if(!conn) { 473 if(!conn) {
372 usbmuxd_log(LL_WARNING, "Could not find connection for device %d client %p", device_id, client); 474 usbmuxd_log(LL_WARNING, "Could not find connection for device %d client %p", device_id, client);
373 return; 475 return;
@@ -376,7 +478,9 @@ void device_client_process(int device_id, struct mux_client *client, short event
376 478
377 int res; 479 int res;
378 int size; 480 int size;
379 if(events & POLLOUT) { 481 if((events & POLLOUT) && conn->ib_size > 0) {
482 // Client is ready to receive data, send what we have
483 // in the client's connection buffer (if there is any)
380 size = client_write(conn->client, conn->ib_buf, conn->ib_size); 484 size = client_write(conn->client, conn->ib_buf, conn->ib_size);
381 if(size <= 0) { 485 if(size <= 0) {
382 usbmuxd_log(LL_DEBUG, "error writing to client (%d)", size); 486 usbmuxd_log(LL_DEBUG, "error writing to client (%d)", size);
@@ -384,17 +488,22 @@ void device_client_process(int device_id, struct mux_client *client, short event
384 return; 488 return;
385 } 489 }
386 conn->tx_ack += size; 490 conn->tx_ack += size;
387 if(size == conn->ib_size) { 491 if(size == (int)conn->ib_size) {
388 conn->ib_size = 0; 492 conn->ib_size = 0;
389 } else { 493 } else {
390 conn->ib_size -= size; 494 conn->ib_size -= size;
391 memmove(conn->ib_buf, conn->ib_buf + size, conn->ib_size); 495 memmove(conn->ib_buf, conn->ib_buf + size, conn->ib_size);
392 } 496 }
393 } 497 }
394 if(events & POLLIN) { 498 if((events & POLLIN) && conn->sendable > 0) {
499 // There is inbound trafic on the client socket,
500 // convert it to tcp and send to the device
501 // (if the device's input buffer is not full)
395 size = client_read(conn->client, conn->ob_buf, conn->sendable); 502 size = client_read(conn->client, conn->ob_buf, conn->sendable);
396 if(size <= 0) { 503 if(size <= 0) {
397 usbmuxd_log(LL_DEBUG, "error reading from client (%d)", size); 504 if (size < 0) {
505 usbmuxd_log(LL_DEBUG, "error reading from client (%d)", size);
506 }
398 connection_teardown(conn); 507 connection_teardown(conn);
399 return; 508 return;
400 } 509 }
@@ -409,6 +518,23 @@ void device_client_process(int device_id, struct mux_client *client, short event
409 update_connection(conn); 518 update_connection(conn);
410} 519}
411 520
521/**
522 * Copy a payload to a connection's in-buffer and
523 * set the POLLOUT event mask on the connection so
524 * the next main_loop iteration will dispatch the
525 * buffer if the connection socket is writable.
526 *
527 * Connection buffers are flushed in the
528 * device_client_process() function.
529 *
530 * @param conn The connection to add incoming data to.
531 * @param payload Payload to prepare for writing.
532 * The payload will be copied immediately so you are
533 * free to alter or free the payload buffer when this
534 * function returns.
535 * @param payload_length number of bytes to copy from from
536 * the payload.
537 */
412static void connection_device_input(struct mux_connection *conn, unsigned char *payload, uint32_t payload_length) 538static void connection_device_input(struct mux_connection *conn, unsigned char *payload, uint32_t payload_length)
413{ 539{
414 if((conn->ib_size + payload_length) > conn->ib_capacity) { 540 if((conn->ib_size + payload_length) > conn->ib_capacity) {
@@ -424,19 +550,13 @@ static void connection_device_input(struct mux_connection *conn, unsigned char *
424 550
425void device_abort_connect(int device_id, struct mux_client *client) 551void device_abort_connect(int device_id, struct mux_client *client)
426{ 552{
427 FOREACH(struct mux_device *dev, &device_list) { 553 struct mux_connection *conn = get_mux_connection(device_id, client);
428 if(dev->id == device_id) { 554 if (conn) {
429 FOREACH(struct mux_connection *conn, &dev->connections) { 555 conn->client = NULL;
430 if(conn->client == client) { 556 connection_teardown(conn);
431 connection_teardown(conn); 557 } else {
432 return; 558 usbmuxd_log(LL_WARNING, "Attempted to abort for nonexistent connection for device %d", device_id);
433 } 559 }
434 } ENDFOREACH
435 usbmuxd_log(LL_WARNING, "Attempted to abort for nonexistent connection for device %d", device_id);
436 return;
437 }
438 } ENDFOREACH
439 usbmuxd_log(LL_WARNING, "Attempted to abort connection for nonexistent device %d", device_id);
440} 560}
441 561
442static void device_version_input(struct mux_device *dev, struct version_header *vh) 562static void device_version_input(struct mux_device *dev, struct version_header *vh)
@@ -447,13 +567,21 @@ static void device_version_input(struct mux_device *dev, struct version_header *
447 } 567 }
448 vh->major = ntohl(vh->major); 568 vh->major = ntohl(vh->major);
449 vh->minor = ntohl(vh->minor); 569 vh->minor = ntohl(vh->minor);
450 if(vh->major != 1 || vh->minor != 0) { 570 if(vh->major != 2 && vh->major != 1) {
451 usbmuxd_log(LL_ERROR, "Device %d has unknown version %d.%d\n", dev->id, vh->major, vh->minor); 571 usbmuxd_log(LL_ERROR, "Device %d has unknown version %d.%d", dev->id, vh->major, vh->minor);
572 mutex_lock(&device_list_mutex);
452 collection_remove(&device_list, dev); 573 collection_remove(&device_list, dev);
574 mutex_unlock(&device_list_mutex);
453 free(dev); 575 free(dev);
454 return; 576 return;
455 } 577 }
456 usbmuxd_log(LL_NOTICE, "Connected to v%d.%d device %d on location 0x%x with serial number %s", vh->major, vh->minor, dev->id, usb_get_location(dev->usbdev), usb_get_serial(dev->usbdev)); 578 dev->version = vh->major;
579
580 if (dev->version >= 2) {
581 send_packet(dev, MUX_PROTO_SETUP, NULL, "\x07", 1);
582 }
583
584 usbmuxd_log(LL_NOTICE, "Connected to v%d.%d device %d on location 0x%x with serial number %s", dev->version, vh->minor, dev->id, usb_get_location(dev->usbdev), usb_get_serial(dev->usbdev));
457 dev->state = MUXDEV_ACTIVE; 585 dev->state = MUXDEV_ACTIVE;
458 collection_init(&dev->connections); 586 collection_init(&dev->connections);
459 struct device_info info; 587 struct device_info info;
@@ -461,9 +589,52 @@ static void device_version_input(struct mux_device *dev, struct version_header *
461 info.location = usb_get_location(dev->usbdev); 589 info.location = usb_get_location(dev->usbdev);
462 info.serial = usb_get_serial(dev->usbdev); 590 info.serial = usb_get_serial(dev->usbdev);
463 info.pid = usb_get_pid(dev->usbdev); 591 info.pid = usb_get_pid(dev->usbdev);
464 client_device_add(&info); 592 info.speed = usb_get_speed(dev->usbdev);
593 preflight_worker_device_add(&info);
465} 594}
466 595
596static void device_control_input(struct mux_device *dev, unsigned char *payload, uint32_t payload_length)
597{
598 if (payload_length > 0) {
599 switch (payload[0]) {
600 case 3:
601 if (payload_length > 1) {
602 usbmuxd_log(LL_ERROR, "Device %d: ERROR: %.*s", dev->id, payload_length-1, payload+1);
603 } else {
604 usbmuxd_log(LL_ERROR, "%s: Device %d: Got device error payload with empty message", __func__, dev->id);
605 }
606 break;
607 case 5:
608 if (payload_length > 1) {
609 usbmuxd_log(LL_WARNING, "Device %d: WARNING: %.*s", dev->id, payload_length-1, payload+1);
610 } else {
611 usbmuxd_log(LL_WARNING, "%s: Device %d: Got payload type %d with empty message", __func__, dev->id, payload[0]);
612 }
613 break;
614 case 7:
615 if (payload_length > 1) {
616 usbmuxd_log(LL_INFO, "Device %d: %.*s", dev->id, payload_length-1, payload+1);
617 } else {
618 usbmuxd_log(LL_WARNING, "%s: Device %d: Got payload type %d with empty message", __func__, dev->id, payload[0]);
619 }
620 break;
621 default:
622 usbmuxd_log(LL_WARNING, "%s: Device %d: Got unhandled payload type %d: %.*s", __func__, dev->id, payload[0], payload_length-1, payload+1);
623 break;
624 }
625 } else {
626 usbmuxd_log(LL_WARNING, "%s: Got a type 1 packet without payload for device %d", __func__, dev->id);
627 }
628}
629
630/**
631 * Handle an incoming TCP packet from the device.
632 *
633 * @param dev The device handle TCP input on.
634 * @param th Pointer to the TCP header struct.
635 * @param payload Payload data.
636 * @param payload_length Number of bytes in payload.
637 */
467static void device_tcp_input(struct mux_device *dev, struct tcphdr *th, unsigned char *payload, uint32_t payload_length) 638static void device_tcp_input(struct mux_device *dev, struct tcphdr *th, unsigned char *payload, uint32_t payload_length)
468{ 639{
469 uint16_t sport = ntohs(th->th_dport); 640 uint16_t sport = ntohs(th->th_dport);
@@ -474,10 +645,11 @@ static void device_tcp_input(struct mux_device *dev, struct tcphdr *th, unsigned
474 dev->id, dport, sport, ntohl(th->th_seq), ntohl(th->th_ack), th->th_flags, ntohs(th->th_win) << 8, ntohs(th->th_win), payload_length); 645 dev->id, dport, sport, ntohl(th->th_seq), ntohl(th->th_ack), th->th_flags, ntohs(th->th_win) << 8, ntohs(th->th_win), payload_length);
475 646
476 if(dev->state != MUXDEV_ACTIVE) { 647 if(dev->state != MUXDEV_ACTIVE) {
477 usbmuxd_log(LL_ERROR, "Received TCP packet from device %d but the device isn't active yet, discarding\n", dev->id); 648 usbmuxd_log(LL_ERROR, "Received TCP packet from device %d but the device isn't active yet, discarding", dev->id);
478 return; 649 return;
479 } 650 }
480 651
652 // Find the connection on this device that has the right sport and dport
481 FOREACH(struct mux_connection *lconn, &dev->connections) { 653 FOREACH(struct mux_connection *lconn, &dev->connections) {
482 if(lconn->sport == sport && lconn->dport == dport) { 654 if(lconn->sport == sport && lconn->dport == dport) {
483 conn = lconn; 655 conn = lconn;
@@ -486,10 +658,10 @@ static void device_tcp_input(struct mux_device *dev, struct tcphdr *th, unsigned
486 } ENDFOREACH 658 } ENDFOREACH
487 659
488 if(!conn) { 660 if(!conn) {
489 usbmuxd_log(LL_INFO, "No connection for device %d incoming packet %d->%d", dev->id, dport, sport);
490 if(!(th->th_flags & TH_RST)) { 661 if(!(th->th_flags & TH_RST)) {
662 usbmuxd_log(LL_INFO, "No connection for device %d incoming packet %d->%d", dev->id, dport, sport);
491 if(send_anon_rst(dev, sport, dport, ntohl(th->th_seq)) < 0) 663 if(send_anon_rst(dev, sport, dport, ntohl(th->th_seq)) < 0)
492 usbmuxd_log(LL_ERROR, "Error sending TCP RST to device %d (%d->%d)", conn->dev->id, sport, dport); 664 usbmuxd_log(LL_ERROR, "Error sending TCP RST to device %d (%d->%d)", dev->id, sport, dport);
493 } 665 }
494 return; 666 return;
495 } 667 }
@@ -524,6 +696,7 @@ static void device_tcp_input(struct mux_device *dev, struct tcphdr *th, unsigned
524 return; 696 return;
525 } 697 }
526 conn->state = CONN_CONNECTED; 698 conn->state = CONN_CONNECTED;
699 usbmuxd_log(LL_INFO, "Client connected to device %d (%d->%d)", dev->id, sport, dport);
527 if(client_notify_connect(conn->client, RESULT_OK) < 0) { 700 if(client_notify_connect(conn->client, RESULT_OK) < 0) {
528 conn->client = NULL; 701 conn->client = NULL;
529 connection_teardown(conn); 702 connection_teardown(conn);
@@ -538,19 +711,32 @@ static void device_tcp_input(struct mux_device *dev, struct tcphdr *th, unsigned
538 connection_teardown(conn); 711 connection_teardown(conn);
539 } else { 712 } else {
540 connection_device_input(conn, payload, payload_length); 713 connection_device_input(conn, payload, payload_length);
714
715 // Device likes it best when we are prompty ACKing data
716 send_tcp_ack(conn);
541 } 717 }
542 } 718 }
543} 719}
544 720
721/**
722 * Take input data from the device that has been read into a buffer
723 * and dispatch it to the right protocol backend (eg. TCP).
724 *
725 * @param usbdev
726 * @param buffer
727 * @param length
728 */
545void device_data_input(struct usb_device *usbdev, unsigned char *buffer, uint32_t length) 729void device_data_input(struct usb_device *usbdev, unsigned char *buffer, uint32_t length)
546{ 730{
547 struct mux_device *dev = NULL; 731 struct mux_device *dev = NULL;
732 mutex_lock(&device_list_mutex);
548 FOREACH(struct mux_device *tdev, &device_list) { 733 FOREACH(struct mux_device *tdev, &device_list) {
549 if(tdev->usbdev == usbdev) { 734 if(tdev->usbdev == usbdev) {
550 dev = tdev; 735 dev = tdev;
551 break; 736 break;
552 } 737 }
553 } ENDFOREACH 738 } ENDFOREACH
739 mutex_unlock(&device_list_mutex);
554 if(!dev) { 740 if(!dev) {
555 usbmuxd_log(LL_WARNING, "Cannot find device entry for RX input from USB device %p on location 0x%x", usbdev, usb_get_location(usbdev)); 741 usbmuxd_log(LL_WARNING, "Cannot find device entry for RX input from USB device %p on location 0x%x", usbdev, usb_get_location(usbdev));
556 return; 742 return;
@@ -576,7 +762,7 @@ void device_data_input(struct usb_device *usbdev, unsigned char *buffer, uint32_
576 } 762 }
577 memcpy(dev->pktbuf + dev->pktlen, buffer, length); 763 memcpy(dev->pktbuf + dev->pktlen, buffer, length);
578 struct mux_header *mhdr = (struct mux_header *)dev->pktbuf; 764 struct mux_header *mhdr = (struct mux_header *)dev->pktbuf;
579 if((length < USB_MRU) || (ntohl(mhdr->length) == length)) { 765 if((length < USB_MRU) || (ntohl(mhdr->length) == (length + dev->pktlen))) {
580 buffer = dev->pktbuf; 766 buffer = dev->pktbuf;
581 length += dev->pktlen; 767 length += dev->pktlen;
582 dev->pktlen = 0; 768 dev->pktlen = 0;
@@ -597,7 +783,7 @@ void device_data_input(struct usb_device *usbdev, unsigned char *buffer, uint32_
597 } 783 }
598 784
599 struct mux_header *mhdr = (struct mux_header *)buffer; 785 struct mux_header *mhdr = (struct mux_header *)buffer;
600 786 int mux_header_size = ((dev->version < 2) ? 8 : sizeof(struct mux_header));
601 if(ntohl(mhdr->length) != length) { 787 if(ntohl(mhdr->length) != length) {
602 usbmuxd_log(LL_ERROR, "Incoming packet size mismatch (dev %d, expected %d, got %d)", dev->id, ntohl(mhdr->length), length); 788 usbmuxd_log(LL_ERROR, "Incoming packet size mismatch (dev %d, expected %d, got %d)", dev->id, ntohl(mhdr->length), length);
603 return; 789 return;
@@ -607,23 +793,32 @@ void device_data_input(struct usb_device *usbdev, unsigned char *buffer, uint32_
607 unsigned char *payload; 793 unsigned char *payload;
608 uint32_t payload_length; 794 uint32_t payload_length;
609 795
796 if (dev->version >= 2) {
797 dev->rx_seq = ntohs(mhdr->rx_seq);
798 }
799
610 switch(ntohl(mhdr->protocol)) { 800 switch(ntohl(mhdr->protocol)) {
611 case MUX_PROTO_VERSION: 801 case MUX_PROTO_VERSION:
612 if(length < (sizeof(struct mux_header) + sizeof(struct version_header))) { 802 if(length < (mux_header_size + sizeof(struct version_header))) {
613 usbmuxd_log(LL_ERROR, "Incoming version packet is too small (%d)", length); 803 usbmuxd_log(LL_ERROR, "Incoming version packet is too small (%d)", length);
614 return; 804 return;
615 } 805 }
616 device_version_input(dev, (struct version_header *)(mhdr+1)); 806 device_version_input(dev, (struct version_header *)((char*)mhdr+mux_header_size));
807 break;
808 case MUX_PROTO_CONTROL:
809 payload = (unsigned char *)(mhdr+1);
810 payload_length = length - mux_header_size;
811 device_control_input(dev, payload, payload_length);
617 break; 812 break;
618 case MUX_PROTO_TCP: 813 case MUX_PROTO_TCP:
619 if(length < (sizeof(struct mux_header) + sizeof(struct tcphdr))) { 814 if(length < (mux_header_size + sizeof(struct tcphdr))) {
620 usbmuxd_log(LL_ERROR, "Incoming TCP packet is too small (%d)", length); 815 usbmuxd_log(LL_ERROR, "Incoming TCP packet is too small (%d)", length);
621 return; 816 return;
622 } 817 }
623 th = (struct tcphdr *)(mhdr+1); 818 th = (struct tcphdr *)((char*)mhdr+mux_header_size);
624 payload = (unsigned char *)(th+1); 819 payload = (unsigned char *)(th+1);
625 payload_length = length - sizeof(struct tcphdr) - sizeof(struct mux_header); 820 payload_length = length - sizeof(struct tcphdr) - mux_header_size;
626 device_tcp_input(dev, (struct tcphdr *)(mhdr+1), payload, payload_length); 821 device_tcp_input(dev, th, payload, payload_length);
627 break; 822 break;
628 default: 823 default:
629 usbmuxd_log(LL_ERROR, "Incoming packet for device %d has unknown protocol 0x%x)", dev->id, ntohl(mhdr->protocol)); 824 usbmuxd_log(LL_ERROR, "Incoming packet for device %d has unknown protocol 0x%x)", dev->id, ntohl(mhdr->protocol));
@@ -642,24 +837,31 @@ int device_add(struct usb_device *usbdev)
642 dev->id = id; 837 dev->id = id;
643 dev->usbdev = usbdev; 838 dev->usbdev = usbdev;
644 dev->state = MUXDEV_INIT; 839 dev->state = MUXDEV_INIT;
840 dev->visible = 0;
645 dev->next_sport = 1; 841 dev->next_sport = 1;
646 dev->pktbuf = malloc(DEV_MRU); 842 dev->pktbuf = malloc(DEV_MRU);
647 dev->pktlen = 0; 843 dev->pktlen = 0;
844 dev->preflight_cb_data = NULL;
845 dev->version = 0;
648 struct version_header vh; 846 struct version_header vh;
649 vh.major = htonl(1); 847 vh.major = htonl(2);
650 vh.minor = htonl(0); 848 vh.minor = htonl(0);
651 vh.padding = 0; 849 vh.padding = 0;
652 if((res = send_packet(dev, MUX_PROTO_VERSION, &vh, NULL, 0)) < 0) { 850 if((res = send_packet(dev, MUX_PROTO_VERSION, &vh, NULL, 0)) < 0) {
653 usbmuxd_log(LL_ERROR, "Error sending version request packet to device %d", id); 851 usbmuxd_log(LL_ERROR, "Error sending version request packet to device %d", id);
852 free(dev->pktbuf);
654 free(dev); 853 free(dev);
655 return res; 854 return res;
656 } 855 }
856 mutex_lock(&device_list_mutex);
657 collection_add(&device_list, dev); 857 collection_add(&device_list, dev);
858 mutex_unlock(&device_list_mutex);
658 return 0; 859 return 0;
659} 860}
660 861
661void device_remove(struct usb_device *usbdev) 862void device_remove(struct usb_device *usbdev)
662{ 863{
864 mutex_lock(&device_list_mutex);
663 FOREACH(struct mux_device *dev, &device_list) { 865 FOREACH(struct mux_device *dev, &device_list) {
664 if(dev->usbdev == usbdev) { 866 if(dev->usbdev == usbdev) {
665 usbmuxd_log(LL_NOTICE, "Removed device %d on location 0x%x", dev->id, usb_get_location(usbdev)); 867 usbmuxd_log(LL_NOTICE, "Removed device %d on location 0x%x", dev->id, usb_get_location(usbdev));
@@ -671,44 +873,94 @@ void device_remove(struct usb_device *usbdev)
671 client_device_remove(dev->id); 873 client_device_remove(dev->id);
672 collection_free(&dev->connections); 874 collection_free(&dev->connections);
673 } 875 }
876 if (dev->preflight_cb_data) {
877 preflight_device_remove_cb(dev->preflight_cb_data);
878 }
674 collection_remove(&device_list, dev); 879 collection_remove(&device_list, dev);
880 mutex_unlock(&device_list_mutex);
675 free(dev->pktbuf); 881 free(dev->pktbuf);
676 free(dev); 882 free(dev);
677 return; 883 return;
678 } 884 }
679 } ENDFOREACH 885 } ENDFOREACH
886 mutex_unlock(&device_list_mutex);
887
680 usbmuxd_log(LL_WARNING, "Cannot find device entry while removing USB device %p on location 0x%x", usbdev, usb_get_location(usbdev)); 888 usbmuxd_log(LL_WARNING, "Cannot find device entry while removing USB device %p on location 0x%x", usbdev, usb_get_location(usbdev));
681} 889}
682 890
683int device_get_count(void) 891void device_set_visible(int device_id)
684{ 892{
685 int count = 0; 893 mutex_lock(&device_list_mutex);
894 FOREACH(struct mux_device *dev, &device_list) {
895 if(dev->id == device_id) {
896 dev->visible = 1;
897 break;
898 }
899 } ENDFOREACH
900 mutex_unlock(&device_list_mutex);
901}
902
903void device_set_preflight_cb_data(int device_id, void* data)
904{
905 mutex_lock(&device_list_mutex);
686 FOREACH(struct mux_device *dev, &device_list) { 906 FOREACH(struct mux_device *dev, &device_list) {
687 if(dev->state == MUXDEV_ACTIVE) 907 if(dev->id == device_id) {
908 dev->preflight_cb_data = data;
909 break;
910 }
911 } ENDFOREACH
912 mutex_unlock(&device_list_mutex);
913}
914
915int device_get_count(int include_hidden)
916{
917 int count = 0;
918 struct collection dev_list = {NULL, 0};
919 mutex_lock(&device_list_mutex);
920 collection_copy(&dev_list, &device_list);
921 mutex_unlock(&device_list_mutex);
922
923 FOREACH(struct mux_device *dev, &dev_list) {
924 if((dev->state == MUXDEV_ACTIVE) && (include_hidden || dev->visible))
688 count++; 925 count++;
689 } ENDFOREACH 926 } ENDFOREACH
927
928 collection_free(&dev_list);
690 return count; 929 return count;
691} 930}
692 931
693int device_get_list(struct device_info *p) 932int device_get_list(int include_hidden, struct device_info **devices)
694{ 933{
695 int count = 0; 934 int count = 0;
696 FOREACH(struct mux_device *dev, &device_list) { 935 struct collection dev_list = {NULL, 0};
697 if(dev->state == MUXDEV_ACTIVE) { 936 mutex_lock(&device_list_mutex);
937 collection_copy(&dev_list, &device_list);
938 mutex_unlock(&device_list_mutex);
939
940 *devices = malloc(sizeof(struct device_info) * dev_list.capacity);
941 struct device_info *p = *devices;
942
943 FOREACH(struct mux_device *dev, &dev_list) {
944 if((dev->state == MUXDEV_ACTIVE) && (include_hidden || dev->visible)) {
698 p->id = dev->id; 945 p->id = dev->id;
699 p->serial = usb_get_serial(dev->usbdev); 946 p->serial = usb_get_serial(dev->usbdev);
700 p->location = usb_get_location(dev->usbdev); 947 p->location = usb_get_location(dev->usbdev);
701 p->pid = usb_get_pid(dev->usbdev); 948 p->pid = usb_get_pid(dev->usbdev);
949 p->speed = usb_get_speed(dev->usbdev);
702 count++; 950 count++;
703 p++; 951 p++;
704 } 952 }
705 } ENDFOREACH 953 } ENDFOREACH
954
955 collection_free(&dev_list);
956
706 return count; 957 return count;
707} 958}
708 959
709int device_get_timeout(void) 960int device_get_timeout(void)
710{ 961{
711 uint64_t oldest = (uint64_t)-1; 962 uint64_t oldest = (uint64_t)-1LL;
963 mutex_lock(&device_list_mutex);
712 FOREACH(struct mux_device *dev, &device_list) { 964 FOREACH(struct mux_device *dev, &device_list) {
713 if(dev->state == MUXDEV_ACTIVE) { 965 if(dev->state == MUXDEV_ACTIVE) {
714 FOREACH(struct mux_connection *conn, &dev->connections) { 966 FOREACH(struct mux_connection *conn, &dev->connections) {
@@ -717,8 +969,9 @@ int device_get_timeout(void)
717 } ENDFOREACH 969 } ENDFOREACH
718 } 970 }
719 } ENDFOREACH 971 } ENDFOREACH
972 mutex_unlock(&device_list_mutex);
720 uint64_t ct = mstime64(); 973 uint64_t ct = mstime64();
721 if(oldest == -1) 974 if((int64_t)oldest == -1LL)
722 return 100000; //meh 975 return 100000; //meh
723 if((ct - oldest) > ACK_TIMEOUT) 976 if((ct - oldest) > ACK_TIMEOUT)
724 return 0; 977 return 0;
@@ -728,27 +981,27 @@ int device_get_timeout(void)
728void device_check_timeouts(void) 981void device_check_timeouts(void)
729{ 982{
730 uint64_t ct = mstime64(); 983 uint64_t ct = mstime64();
984 mutex_lock(&device_list_mutex);
731 FOREACH(struct mux_device *dev, &device_list) { 985 FOREACH(struct mux_device *dev, &device_list) {
732 if(dev->state == MUXDEV_ACTIVE) { 986 if(dev->state == MUXDEV_ACTIVE) {
733 FOREACH(struct mux_connection *conn, &dev->connections) { 987 FOREACH(struct mux_connection *conn, &dev->connections) {
734 if((conn->state == CONN_CONNECTED) && 988 if((conn->state == CONN_CONNECTED) &&
735 (conn->flags & CONN_ACK_PENDING) && 989 (conn->flags & CONN_ACK_PENDING) &&
736 (ct - conn->last_ack_time) > ACK_TIMEOUT) { 990 (ct - conn->last_ack_time) > ACK_TIMEOUT) {
737 usbmuxd_log(LL_DEBUG, "Sending ACK due to expired timeout (%" PRIu64 " -> %" PRIu64 ")", conn->last_ack_time, ct); 991 usbmuxd_log(LL_DEBUG, "Sending ACK due to expired timeout (%" PRIu64 " -> %" PRIu64 ")", conn->last_ack_time, ct);
738 if(send_tcp(conn, TH_ACK, NULL, 0) < 0) { 992 send_tcp_ack(conn);
739 usbmuxd_log(LL_ERROR, "Error sending TCP ACK to device %d (%d->%d)", dev->id, conn->sport, conn->dport);
740 connection_teardown(conn);
741 }
742 } 993 }
743 } ENDFOREACH 994 } ENDFOREACH
744 } 995 }
745 } ENDFOREACH 996 } ENDFOREACH
997 mutex_unlock(&device_list_mutex);
746} 998}
747 999
748void device_init(void) 1000void device_init(void)
749{ 1001{
750 usbmuxd_log(LL_DEBUG, "device_init"); 1002 usbmuxd_log(LL_DEBUG, "device_init");
751 collection_init(&device_list); 1003 collection_init(&device_list);
1004 mutex_init(&device_list_mutex);
752 next_device_id = 1; 1005 next_device_id = 1;
753} 1006}
754 1007
@@ -769,6 +1022,7 @@ void device_kill_connections(void)
769void device_shutdown(void) 1022void device_shutdown(void)
770{ 1023{
771 usbmuxd_log(LL_DEBUG, "device_shutdown"); 1024 usbmuxd_log(LL_DEBUG, "device_shutdown");
1025 mutex_lock(&device_list_mutex);
772 FOREACH(struct mux_device *dev, &device_list) { 1026 FOREACH(struct mux_device *dev, &device_list) {
773 FOREACH(struct mux_connection *conn, &dev->connections) { 1027 FOREACH(struct mux_connection *conn, &dev->connections) {
774 connection_teardown(conn); 1028 connection_teardown(conn);
@@ -777,5 +1031,7 @@ void device_shutdown(void)
777 collection_remove(&device_list, dev); 1031 collection_remove(&device_list, dev);
778 free(dev); 1032 free(dev);
779 } ENDFOREACH 1033 } ENDFOREACH
1034 mutex_unlock(&device_list_mutex);
1035 mutex_destroy(&device_list_mutex);
780 collection_free(&device_list); 1036 collection_free(&device_list);
781} 1037}
diff --git a/src/device.h b/src/device.h
new file mode 100644
index 0000000..85703e4
--- /dev/null
+++ b/src/device.h
@@ -0,0 +1,56 @@
1/*
2 * device.h
3 *
4 * Copyright (C) 2009 Hector Martin <hector@marcansoft.com>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 2 or version 3.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18 */
19
20#ifndef DEVICE_H
21#define DEVICE_H
22
23#include "usb.h"
24#include "client.h"
25
26struct device_info {
27 int id;
28 const char *serial;
29 uint32_t location;
30 uint16_t pid;
31 uint64_t speed;
32};
33
34void device_data_input(struct usb_device *dev, unsigned char *buf, uint32_t length);
35
36int device_add(struct usb_device *dev);
37void device_remove(struct usb_device *dev);
38
39int device_start_connect(int device_id, uint16_t port, struct mux_client *client);
40void device_client_process(int device_id, struct mux_client *client, short events);
41void device_abort_connect(int device_id, struct mux_client *client);
42
43void device_set_visible(int device_id);
44void device_set_preflight_cb_data(int device_id, void* data);
45
46int device_get_count(int include_hidden);
47int device_get_list(int include_hidden, struct device_info **devices);
48
49int device_get_timeout(void);
50void device_check_timeouts(void);
51
52void device_init(void);
53void device_kill_connections(void);
54void device_shutdown(void);
55
56#endif
diff --git a/daemon/log.c b/src/log.c
index 1973257..cd7c2d5 100644
--- a/daemon/log.c
+++ b/src/log.c
@@ -1,23 +1,22 @@
1/* 1/*
2 usbmuxd - iPhone/iPod Touch USB multiplex server daemon 2 * log.c
3 3 *
4Copyright (C) 2009 Hector Martin "marcan" <hector@marcansoft.com> 4 * Copyright (C) 2009 Hector Martin <hector@marcansoft.com>
5Copyright (C) 2009 Nikias Bassen <nikias@gmx.li> 5 * Copyright (C) 2009 Nikias Bassen <nikias@gmx.li>
6 6 *
7This program is free software; you can redistribute it and/or modify 7 * This program is free software; you can redistribute it and/or modify
8it under the terms of the GNU General Public License as published by 8 * it under the terms of the GNU General Public License as published by
9the Free Software Foundation, either version 2 or version 3. 9 * the Free Software Foundation, either version 2 or version 3.
10 10 *
11This program is distributed in the hope that it will be useful, 11 * This program is distributed in the hope that it will be useful,
12but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14GNU General Public License for more details. 14 * GNU General Public License for more details.
15 15 *
16You should have received a copy of the GNU General Public License 16 * You should have received a copy of the GNU General Public License
17along with this program; if not, write to the Free Software 17 * along with this program; if not, write to the Free Software
18Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 19 */
20*/
21 20
22#ifdef HAVE_CONFIG_H 21#ifdef HAVE_CONFIG_H
23#include <config.h> 22#include <config.h>
@@ -32,8 +31,9 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
32#include <syslog.h> 31#include <syslog.h>
33 32
34#include "log.h" 33#include "log.h"
34#include "utils.h"
35 35
36int log_level = LL_WARNING; 36unsigned int log_level = LL_WARNING;
37 37
38int log_syslog = 0; 38int log_syslog = 0;
39 39
@@ -65,20 +65,26 @@ void usbmuxd_log(enum loglevel level, const char *fmt, ...)
65{ 65{
66 va_list ap; 66 va_list ap;
67 char *fs; 67 char *fs;
68 struct timeval ts;
69 struct tm *tp;
70 68
71 if(level > log_level) 69 if(level > log_level)
72 return; 70 return;
73 71
74 gettimeofday(&ts, NULL);
75 tp = localtime(&ts.tv_sec);
76
77 fs = malloc(20 + strlen(fmt)); 72 fs = malloc(20 + strlen(fmt));
78 73
79 if(log_syslog) { 74 if(log_syslog) {
80 sprintf(fs, "[%d] %s\n", level, fmt); 75 sprintf(fs, "[%d] %s\n", level, fmt);
81 } else { 76 } else {
77 struct timeval ts;
78 struct tm tp_;
79 struct tm *tp;
80
81 gettimeofday(&ts, NULL);
82#ifdef HAVE_LOCALTIME_R
83 tp = localtime_r(&ts.tv_sec, &tp_);
84#else
85 tp = localtime(&ts.tv_sec);
86#endif
87
82 strftime(fs, 10, "[%H:%M:%S", tp); 88 strftime(fs, 10, "[%H:%M:%S", tp);
83 sprintf(fs+9, ".%03d][%d] %s\n", (int)(ts.tv_usec / 1000), level, fmt); 89 sprintf(fs+9, ".%03d][%d] %s\n", (int)(ts.tv_usec / 1000), level, fmt);
84 } 90 }
diff --git a/src/log.h b/src/log.h
new file mode 100644
index 0000000..858e7d0
--- /dev/null
+++ b/src/log.h
@@ -0,0 +1,42 @@
1/*
2 * log.h
3 *
4 * Copyright (C) 2009 Hector Martin "marcan" <hector@marcansoft.com>
5 * Copyright (C) 2009 Nikias Bassen <nikias@gmx.li>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 2 or version 3.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
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 unsigned 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#endif
diff --git a/src/main.c b/src/main.c
new file mode 100644
index 0000000..b09a187
--- /dev/null
+++ b/src/main.c
@@ -0,0 +1,931 @@
1/*
2 * main.c
3 *
4 * Copyright (C) 2009-2021 Nikias Bassen <nikias@gmx.li>
5 * Copyright (C) 2013-2014 Martin Szulecki <m.szulecki@libimobiledevice.org>
6 * Copyright (C) 2009 Hector Martin <hector@marcansoft.com>
7 * Copyright (C) 2009 Paul Sladen <libiphone@paul.sladen.org>
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation, either version 2 or version 3.
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
23#define _DEFAULT_SOURCE
24#define _BSD_SOURCE
25#define _GNU_SOURCE
26
27#ifdef HAVE_CONFIG_H
28#include <config.h>
29#endif
30
31#include <stdio.h>
32#include <errno.h>
33#include <string.h>
34#include <stdlib.h>
35#include <signal.h>
36#include <unistd.h>
37#include <sys/socket.h>
38#include <sys/un.h>
39#include <netinet/in.h>
40#include <netdb.h>
41#include <arpa/inet.h>
42#include <sys/stat.h>
43#include <sys/types.h>
44#include <sys/resource.h>
45#include <fcntl.h>
46#include <getopt.h>
47#include <pwd.h>
48#include <grp.h>
49
50#include "log.h"
51#include "usb.h"
52#include "device.h"
53#include "client.h"
54#include "conf.h"
55
56static const char *socket_path = "/var/run/usbmuxd";
57#define DEFAULT_LOCKFILE "/var/run/usbmuxd.pid"
58static const char *lockfile = DEFAULT_LOCKFILE;
59
60// Global state used in other files
61int should_exit;
62int should_discover;
63int use_logfile = 0;
64int no_preflight = 0;
65
66// Global state for main.c
67static int verbose = 0;
68static int foreground = 0;
69static int drop_privileges = 0;
70static const char *drop_user = NULL;
71static int opt_disable_hotplug = 0;
72static int opt_enable_exit = 0;
73static int opt_exit = 0;
74static int exit_signal = 0;
75static int daemon_pipe;
76static const char *listen_addr = NULL;
77
78static int report_to_parent = 0;
79
80static int create_socket(void)
81{
82 int listenfd;
83 const char* socket_addr = socket_path;
84 const char* tcp_port;
85 char listen_addr_str[256];
86
87 if (listen_addr) {
88 socket_addr = listen_addr;
89 }
90 tcp_port = strrchr(socket_addr, ':');
91 if (tcp_port) {
92 tcp_port++;
93 size_t nlen = tcp_port - socket_addr;
94 char* hostname = malloc(nlen);
95 struct addrinfo hints;
96 struct addrinfo *result, *rp;
97 int yes = 1;
98 int res;
99
100 strncpy(hostname, socket_addr, nlen-1);
101 hostname[nlen-1] = '\0';
102
103 memset(&hints, '\0', sizeof(struct addrinfo));
104 hints.ai_family = AF_UNSPEC;
105 hints.ai_socktype = SOCK_STREAM;
106 hints.ai_flags = AI_PASSIVE | AI_NUMERICSERV;
107 hints.ai_protocol = IPPROTO_TCP;
108
109 res = getaddrinfo(hostname, tcp_port, &hints, &result);
110 free(hostname);
111 if (res != 0) {
112 usbmuxd_log(LL_FATAL, "%s: getaddrinfo() failed: %s\n", __func__, gai_strerror(res));
113 return -1;
114 }
115
116 for (rp = result; rp != NULL; rp = rp->ai_next) {
117 listenfd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
118 if (listenfd == -1) {
119 listenfd = -1;
120 continue;
121 }
122
123 if (setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, (void*)&yes, sizeof(int)) == -1) {
124 usbmuxd_log(LL_ERROR, "%s: setsockopt(): %s", __func__, strerror(errno));
125 close(listenfd);
126 listenfd = -1;
127 continue;
128 }
129
130#ifdef SO_NOSIGPIPE
131 if (setsockopt(listenfd, SOL_SOCKET, SO_NOSIGPIPE, (void*)&yes, sizeof(int)) == -1) {
132 usbmuxd_log(LL_ERROR, "%s: setsockopt(): %s", __func__, strerror(errno));
133 close(listenfd);
134 listenfd = -1;
135 continue;
136 }
137#endif
138
139#if defined(AF_INET6) && defined(IPV6_V6ONLY)
140 if (rp->ai_family == AF_INET6) {
141 if (setsockopt(listenfd, IPPROTO_IPV6, IPV6_V6ONLY, (void*)&yes, sizeof(int)) == -1) {
142 usbmuxd_log(LL_ERROR, "%s: setsockopt() IPV6_V6ONLY: %s", __func__, strerror(errno));
143 }
144 }
145#endif
146
147 if (bind(listenfd, rp->ai_addr, rp->ai_addrlen) < 0) {
148 usbmuxd_log(LL_FATAL, "%s: bind() failed: %s", __func__, strerror(errno));
149 close(listenfd);
150 listenfd = -1;
151 continue;
152 }
153
154 const void *addrdata = NULL;
155 if (rp->ai_family == AF_INET) {
156 addrdata = &((struct sockaddr_in*)rp->ai_addr)->sin_addr;
157 }
158#ifdef AF_INET6
159 else if (rp->ai_family == AF_INET6) {
160 addrdata = &((struct sockaddr_in6*)rp->ai_addr)->sin6_addr;
161 }
162#endif
163 if (addrdata) {
164 char* endp = NULL;
165 uint16_t listen_port = 0;
166 if (rp->ai_family == AF_INET) {
167 listen_port = ntohs(((struct sockaddr_in*)rp->ai_addr)->sin_port);
168 if (inet_ntop(AF_INET, addrdata, listen_addr_str, sizeof(listen_addr_str)-6)) {
169 endp = &listen_addr_str[0] + strlen(listen_addr_str);
170 }
171 }
172#ifdef AF_INET6
173 else if (rp->ai_family == AF_INET6) {
174 listen_port = ntohs(((struct sockaddr_in6*)rp->ai_addr)->sin6_port);
175 listen_addr_str[0] = '[';
176 if (inet_ntop(AF_INET6, addrdata, listen_addr_str+1, sizeof(listen_addr_str)-8)) {
177 endp = &listen_addr_str[0] + strlen(listen_addr_str);
178 }
179 if (endp) {
180 *endp = ']';
181 endp++;
182 }
183 }
184#endif
185 if (endp) {
186 sprintf(endp, ":%u", listen_port);
187 }
188 }
189 break;
190 }
191 freeaddrinfo(result);
192 if (listenfd == -1) {
193 usbmuxd_log(LL_FATAL, "%s: Failed to create listening socket", __func__);
194 return -1;
195 }
196 } else {
197 struct sockaddr_un bind_addr;
198
199 if (strcmp(socket_addr, socket_path) != 0) {
200 struct stat fst;
201 if (stat(socket_addr, &fst) == 0) {
202 if (!S_ISSOCK(fst.st_mode)) {
203 usbmuxd_log(LL_FATAL, "FATAL: File '%s' already exists and is not a socket file. Refusing to continue.", socket_addr);
204 return -1;
205 }
206 }
207 }
208
209 if (unlink(socket_addr) == -1 && errno != ENOENT) {
210 usbmuxd_log(LL_FATAL, "%s: unlink(%s) failed: %s", __func__, socket_addr, strerror(errno));
211 return -1;
212 }
213
214 listenfd = socket(AF_UNIX, SOCK_STREAM, 0);
215 if (listenfd == -1) {
216 usbmuxd_log(LL_FATAL, "socket() failed: %s", strerror(errno));
217 return -1;
218 }
219
220 bzero(&bind_addr, sizeof(bind_addr));
221 bind_addr.sun_family = AF_UNIX;
222 strncpy(bind_addr.sun_path, socket_addr, sizeof(bind_addr.sun_path));
223 bind_addr.sun_path[sizeof(bind_addr.sun_path) - 1] = '\0';
224
225 if (bind(listenfd, (struct sockaddr*)&bind_addr, sizeof(bind_addr)) != 0) {
226 usbmuxd_log(LL_FATAL, "bind() failed: %s", strerror(errno));
227 return -1;
228 }
229 chmod(socket_addr, 0666);
230
231 snprintf(listen_addr_str, sizeof(listen_addr_str), "%s", socket_addr);
232 }
233
234 int flags = fcntl(listenfd, F_GETFL, 0);
235 if (flags < 0) {
236 usbmuxd_log(LL_FATAL, "ERROR: Could not get flags for socket");
237 } else {
238 if (fcntl(listenfd, F_SETFL, flags | O_NONBLOCK) < 0) {
239 usbmuxd_log(LL_FATAL, "ERROR: Could not set socket to non-blocking");
240 }
241 }
242
243 // Start listening
244 if (listen(listenfd, 256) != 0) {
245 usbmuxd_log(LL_FATAL, "listen() failed: %s", strerror(errno));
246 return -1;
247 }
248
249 usbmuxd_log(LL_INFO, "Listening on %s", listen_addr_str);
250
251 return listenfd;
252}
253
254static void handle_signal(int sig)
255{
256 if (sig != SIGUSR1 && sig != SIGUSR2) {
257 usbmuxd_log(LL_NOTICE,"Caught signal %d, exiting", sig);
258 should_exit = 1;
259 } else {
260 if(opt_enable_exit) {
261 if (sig == SIGUSR1) {
262 usbmuxd_log(LL_INFO, "Caught SIGUSR1, checking if we can terminate (no more devices attached)...");
263 if (device_get_count(1) > 0) {
264 // we can't quit, there are still devices attached.
265 usbmuxd_log(LL_NOTICE, "Refusing to terminate, there are still devices attached. Kill me with signal 15 (TERM) to force quit.");
266 } else {
267 // it's safe to quit
268 should_exit = 1;
269 }
270 } else if (sig == SIGUSR2) {
271 usbmuxd_log(LL_INFO, "Caught SIGUSR2, scheduling device discovery");
272 should_discover = 1;
273 }
274 } else {
275 usbmuxd_log(LL_INFO, "Caught SIGUSR1/2 but this instance was not started with \"--enable-exit\", ignoring.");
276 }
277 }
278}
279
280static void set_signal_handlers(void)
281{
282 struct sigaction sa;
283 sigset_t set;
284
285 // Mask all signals we handle. They will be unmasked by ppoll().
286 sigemptyset(&set);
287 sigaddset(&set, SIGINT);
288 sigaddset(&set, SIGQUIT);
289 sigaddset(&set, SIGTERM);
290 sigaddset(&set, SIGUSR1);
291 sigaddset(&set, SIGUSR2);
292 sigprocmask(SIG_SETMASK, &set, NULL);
293
294 memset(&sa, 0, sizeof(struct sigaction));
295 sa.sa_handler = handle_signal;
296 sigaction(SIGINT, &sa, NULL);
297 sigaction(SIGQUIT, &sa, NULL);
298 sigaction(SIGTERM, &sa, NULL);
299 sigaction(SIGUSR1, &sa, NULL);
300 sigaction(SIGUSR2, &sa, NULL);
301}
302
303#ifndef HAVE_PPOLL
304static int ppoll(struct pollfd *fds, nfds_t nfds, const struct timespec *timeout, const sigset_t *sigmask)
305{
306 int ready;
307 sigset_t origmask;
308 int to = timeout->tv_sec*1000 + timeout->tv_nsec/1000000;
309
310 sigprocmask(SIG_SETMASK, sigmask, &origmask);
311 ready = poll(fds, nfds, to);
312 sigprocmask(SIG_SETMASK, &origmask, NULL);
313
314 return ready;
315}
316#endif
317
318static int main_loop(int listenfd)
319{
320 int to, cnt, i, dto;
321 struct fdlist pollfds;
322 struct timespec tspec;
323
324 sigset_t empty_sigset;
325 sigemptyset(&empty_sigset); // unmask all signals
326
327 fdlist_create(&pollfds);
328 while(!should_exit) {
329 usbmuxd_log(LL_FLOOD, "main_loop iteration");
330 to = usb_get_timeout();
331 usbmuxd_log(LL_FLOOD, "USB timeout is %d ms", to);
332 dto = device_get_timeout();
333 usbmuxd_log(LL_FLOOD, "Device timeout is %d ms", dto);
334 if(dto < to)
335 to = dto;
336
337 fdlist_reset(&pollfds);
338 fdlist_add(&pollfds, FD_LISTEN, listenfd, POLLIN);
339 usb_get_fds(&pollfds);
340 client_get_fds(&pollfds);
341 usbmuxd_log(LL_FLOOD, "fd count is %d", pollfds.count);
342
343 tspec.tv_sec = to / 1000;
344 tspec.tv_nsec = (to % 1000) * 1000000;
345 cnt = ppoll(pollfds.fds, pollfds.count, &tspec, &empty_sigset);
346 usbmuxd_log(LL_FLOOD, "poll() returned %d", cnt);
347 if(cnt == -1) {
348 if(errno == EINTR) {
349 if(should_exit) {
350 usbmuxd_log(LL_INFO, "Event processing interrupted");
351 break;
352 }
353 if(should_discover) {
354 should_discover = 0;
355 usbmuxd_log(LL_INFO, "Device discovery triggered");
356 usb_discover();
357 }
358 }
359 } else if(cnt == 0) {
360 if(usb_process() < 0) {
361 usbmuxd_log(LL_FATAL, "usb_process() failed");
362 fdlist_free(&pollfds);
363 return -1;
364 }
365 device_check_timeouts();
366 } else {
367 int done_usb = 0;
368 for(i=0; i<pollfds.count; i++) {
369 if(pollfds.fds[i].revents) {
370 if(!done_usb && pollfds.owners[i] == FD_USB) {
371 if(usb_process() < 0) {
372 usbmuxd_log(LL_FATAL, "usb_process() failed");
373 fdlist_free(&pollfds);
374 return -1;
375 }
376 done_usb = 1;
377 }
378 if(pollfds.owners[i] == FD_LISTEN) {
379 if(client_accept(listenfd) < 0) {
380 usbmuxd_log(LL_FATAL, "client_accept() failed");
381 fdlist_free(&pollfds);
382 return -1;
383 }
384 }
385 if(pollfds.owners[i] == FD_CLIENT) {
386 client_process(pollfds.fds[i].fd, pollfds.fds[i].revents);
387 }
388 }
389 }
390 }
391 }
392 fdlist_free(&pollfds);
393 return 0;
394}
395
396/**
397 * make this program run detached from the current console
398 */
399static int daemonize(void)
400{
401 pid_t pid;
402 pid_t sid;
403 int pfd[2];
404 int res;
405
406 // already a daemon
407 if (getppid() == 1)
408 return 0;
409
410 if((res = pipe(pfd)) < 0) {
411 usbmuxd_log(LL_FATAL, "pipe() failed.");
412 return res;
413 }
414
415 pid = fork();
416 if (pid < 0) {
417 usbmuxd_log(LL_FATAL, "fork() failed.");
418 return pid;
419 }
420
421 if (pid > 0) {
422 // exit parent process
423 int status;
424 close(pfd[1]);
425
426 if((res = read(pfd[0],&status,sizeof(int))) != sizeof(int)) {
427 fprintf(stderr, "usbmuxd: ERROR: Failed to get init status from child, check syslog for messages.\n");
428 exit(1);
429 }
430 if(status != 0)
431 fprintf(stderr, "usbmuxd: ERROR: Child process exited with error %d, check syslog for messages.\n", status);
432 exit(status);
433 }
434 // At this point we are executing as the child process
435 // but we need to do one more fork
436
437 daemon_pipe = pfd[1];
438 close(pfd[0]);
439 report_to_parent = 1;
440
441 // Create a new SID for the child process
442 sid = setsid();
443 if (sid < 0) {
444 usbmuxd_log(LL_FATAL, "setsid() failed.");
445 return -1;
446 }
447
448 pid = fork();
449 if (pid < 0) {
450 usbmuxd_log(LL_FATAL, "fork() failed (second).");
451 return pid;
452 }
453
454 if (pid > 0) {
455 // exit parent process
456 close(daemon_pipe);
457 exit(0);
458 }
459
460 // Change the current working directory.
461 if ((chdir("/")) < 0) {
462 usbmuxd_log(LL_FATAL, "chdir() failed");
463 return -2;
464 }
465 // Redirect standard files to /dev/null
466 if (!freopen("/dev/null", "r", stdin)) {
467 usbmuxd_log(LL_FATAL, "Redirection of stdin failed.");
468 return -3;
469 }
470 if (!freopen("/dev/null", "w", stdout)) {
471 usbmuxd_log(LL_FATAL, "Redirection of stdout failed.");
472 return -3;
473 }
474
475 return 0;
476}
477
478static int notify_parent(int status)
479{
480 int res;
481
482 report_to_parent = 0;
483 if ((res = write(daemon_pipe, &status, sizeof(int))) != sizeof(int)) {
484 usbmuxd_log(LL_FATAL, "Could not notify parent!");
485 if(res >= 0)
486 return -2;
487 else
488 return res;
489 }
490 close(daemon_pipe);
491 if (!freopen("/dev/null", "w", stderr)) {
492 usbmuxd_log(LL_FATAL, "Redirection of stderr failed.");
493 return -1;
494 }
495 return 0;
496}
497
498static void usage()
499{
500 printf("Usage: %s [OPTIONS]\n", PACKAGE_NAME);
501 printf("\n");
502 printf("Expose a socket to multiplex connections from and to iOS devices.\n");
503 printf("\n");
504 printf("OPTIONS:\n");
505 printf(" -h, --help\t\tPrint this message.\n");
506 printf(" -v, --verbose\t\tBe verbose (use twice or more to increase).\n");
507 printf(" -f, --foreground\tDo not daemonize (implies one -v).\n");
508 printf(" -U, --user USER\tChange to this user after startup (needs USB privileges).\n");
509 printf(" -n, --disable-hotplug\tDisables automatic discovery of devices on hotplug.\n");
510 printf(" \tStarting another instance will trigger discovery instead.\n");
511 printf(" -z, --enable-exit\tEnable \"--exit\" request from other instances and exit\n");
512 printf(" \tautomatically if no device is attached.\n");
513 printf(" -p, --no-preflight\tDisable lockdownd preflight on new device.\n");
514#ifdef HAVE_UDEV
515 printf(" -u, --udev\t\tRun in udev operation mode (implies -n and -z).\n");
516#endif
517#ifdef HAVE_SYSTEMD
518 printf(" -s, --systemd\t\tRun in systemd operation mode (implies -z and -f).\n");
519#endif
520 printf(" -C, --config-dir PATH\tSpecify different configuration directory.\n");
521 printf(" -S, --socket ADDR:PORT | PATH Specify source ADDR and PORT or a UNIX\n");
522 printf(" \t\tsocket PATH to use for the listening socket.\n");
523 printf(" \t\tDefault: %s\n", socket_path);
524 printf(" -P, --pidfile PATH\tSpecify a different location for the pid file, or pass\n");
525 printf(" \t\tNONE to disable. Default: %s\n", DEFAULT_LOCKFILE);
526 printf(" -x, --exit\t\tNotify a running instance to exit if there are no devices\n");
527 printf(" \t\tconnected (sends SIGUSR1 to running instance) and exit.\n");
528 printf(" -X, --force-exit\tNotify a running instance to exit even if there are still\n");
529 printf(" \tdevices connected (always works) and exit.\n");
530 printf(" -l, --logfile=LOGFILE\tLog (append) to LOGFILE instead of stderr or syslog.\n");
531 printf(" -V, --version\t\tPrint version information and exit.\n");
532 printf("\n");
533 printf("Homepage: <" PACKAGE_URL ">\n");
534 printf("Bug Reports: <" PACKAGE_BUGREPORT ">\n");
535}
536
537static void parse_opts(int argc, char **argv)
538{
539 static struct option longopts[] = {
540 {"help", no_argument, NULL, 'h'},
541 {"foreground", no_argument, NULL, 'f'},
542 {"verbose", no_argument, NULL, 'v'},
543 {"user", required_argument, NULL, 'U'},
544 {"disable-hotplug", no_argument, NULL, 'n'},
545 {"enable-exit", no_argument, NULL, 'z'},
546 {"no-preflight", no_argument, NULL, 'p'},
547#ifdef HAVE_UDEV
548 {"udev", no_argument, NULL, 'u'},
549#endif
550#ifdef HAVE_SYSTEMD
551 {"systemd", no_argument, NULL, 's'},
552#endif
553 {"config-dir", required_argument, NULL, 'C'},
554 {"socket", required_argument, NULL, 'S'},
555 {"pidfile", required_argument, NULL, 'P'},
556 {"exit", no_argument, NULL, 'x'},
557 {"force-exit", no_argument, NULL, 'X'},
558 {"logfile", required_argument, NULL, 'l'},
559 {"version", no_argument, NULL, 'V'},
560 {NULL, 0, NULL, 0}
561 };
562 int c;
563
564#ifdef HAVE_SYSTEMD
565 const char* opts_spec = "hfvVuU:xXsnzl:pC:S:P:";
566#elif HAVE_UDEV
567 const char* opts_spec = "hfvVuU:xXnzl:pC:S:P:";
568#else
569 const char* opts_spec = "hfvVU:xXnzl:pC:S:P:";
570#endif
571
572 while (1) {
573 c = getopt_long(argc, argv, opts_spec, longopts, (int *) 0);
574 if (c == -1) {
575 break;
576 }
577
578 switch (c) {
579 case 'h':
580 usage();
581 exit(0);
582 case 'f':
583 foreground = 1;
584 break;
585 case 'v':
586 ++verbose;
587 break;
588 case 'V':
589 printf("%s %s\n", PACKAGE_NAME, PACKAGE_VERSION);
590 exit(0);
591 case 'U':
592 drop_privileges = 1;
593 drop_user = optarg;
594 break;
595 case 'p':
596 no_preflight = 1;
597 break;
598#ifdef HAVE_UDEV
599 case 'u':
600 opt_disable_hotplug = 1;
601 opt_enable_exit = 1;
602 break;
603#endif
604#ifdef HAVE_SYSTEMD
605 case 's':
606 opt_enable_exit = 1;
607 foreground = 1;
608 break;
609#endif
610 case 'n':
611 opt_disable_hotplug = 1;
612 break;
613 case 'z':
614 opt_enable_exit = 1;
615 break;
616 case 'C':
617 if (!*optarg) {
618 usbmuxd_log(LL_FATAL, "ERROR: --config-dir requires an argument");
619 usage();
620 exit(2);
621 }
622 config_set_config_dir(optarg);
623 break;
624 case 'S':
625 if (!*optarg || *optarg == '-') {
626 usbmuxd_log(LL_FATAL, "ERROR: --socket requires an argument");
627 usage();
628 exit(2);
629 }
630 listen_addr = optarg;
631 break;
632 case 'P':
633 if (!*optarg || *optarg == '-') {
634 usbmuxd_log(LL_FATAL, "ERROR: --pidfile requires an argument");
635 usage();
636 exit(2);
637 }
638 if (!strcmp(optarg, "NONE")) {
639 lockfile = NULL;
640 } else {
641 lockfile = optarg;
642 }
643 break;
644 case 'x':
645 opt_exit = 1;
646 exit_signal = SIGUSR1;
647 break;
648 case 'X':
649 opt_exit = 1;
650 exit_signal = SIGTERM;
651 break;
652 case 'l':
653 if (!*optarg) {
654 usbmuxd_log(LL_FATAL, "ERROR: --logfile requires a non-empty filename");
655 usage();
656 exit(2);
657 }
658 if (use_logfile) {
659 usbmuxd_log(LL_FATAL, "ERROR: --logfile cannot be used multiple times");
660 exit(2);
661 }
662 if (!freopen(optarg, "a", stderr)) {
663 usbmuxd_log(LL_FATAL, "ERROR: fdreopen: %s", strerror(errno));
664 } else {
665 use_logfile = 1;
666 }
667 break;
668 default:
669 usage();
670 exit(2);
671 }
672 }
673}
674
675int main(int argc, char *argv[])
676{
677 int listenfd;
678 int res = 0;
679 int lfd;
680 struct flock lock;
681 char pids[10];
682
683 parse_opts(argc, argv);
684
685 argc -= optind;
686 argv += optind;
687
688 if (!foreground && !use_logfile) {
689 verbose += LL_WARNING;
690 log_enable_syslog();
691 } else {
692 verbose += LL_NOTICE;
693 }
694
695 /* set log level to specified verbosity */
696 log_level = verbose;
697
698 usbmuxd_log(LL_NOTICE, "usbmuxd v%s starting up", PACKAGE_VERSION);
699 should_exit = 0;
700 should_discover = 0;
701
702 set_signal_handlers();
703 signal(SIGPIPE, SIG_IGN);
704
705 if (lockfile) {
706 res = lfd = open(lockfile, O_WRONLY|O_CREAT, 0644);
707 if(res == -1) {
708 usbmuxd_log(LL_FATAL, "Could not open lockfile");
709 goto terminate;
710 }
711 lock.l_type = F_WRLCK;
712 lock.l_whence = SEEK_SET;
713 lock.l_start = 0;
714 lock.l_len = 0;
715 lock.l_pid = 0;
716 fcntl(lfd, F_GETLK, &lock);
717 close(lfd);
718 }
719 if (lockfile && lock.l_type != F_UNLCK) {
720 if (opt_exit) {
721 if (lock.l_pid && !kill(lock.l_pid, 0)) {
722 usbmuxd_log(LL_NOTICE, "Sending signal %d to instance with pid %d", exit_signal, lock.l_pid);
723 res = 0;
724 if (kill(lock.l_pid, exit_signal) < 0) {
725 usbmuxd_log(LL_FATAL, "Could not deliver signal %d to pid %d", exit_signal, lock.l_pid);
726 res = -1;
727 }
728 goto terminate;
729 } else {
730 usbmuxd_log(LL_ERROR, "Could not determine pid of the other running instance!");
731 res = -1;
732 goto terminate;
733 }
734 } else {
735 if (!opt_disable_hotplug) {
736 usbmuxd_log(LL_ERROR, "Another instance is already running (pid %d). exiting.", lock.l_pid);
737 res = -1;
738 } else {
739 usbmuxd_log(LL_NOTICE, "Another instance is already running (pid %d). Telling it to check for devices.", lock.l_pid);
740 if (lock.l_pid && !kill(lock.l_pid, 0)) {
741 usbmuxd_log(LL_NOTICE, "Sending signal SIGUSR2 to instance with pid %d", lock.l_pid);
742 res = 0;
743 if (kill(lock.l_pid, SIGUSR2) < 0) {
744 usbmuxd_log(LL_FATAL, "Could not deliver SIGUSR2 to pid %d", lock.l_pid);
745 res = -1;
746 }
747 } else {
748 usbmuxd_log(LL_ERROR, "Could not determine pid of the other running instance!");
749 res = -1;
750 }
751 }
752 goto terminate;
753 }
754 }
755 if (lockfile) {
756 unlink(lockfile);
757 }
758
759 if (opt_exit) {
760 usbmuxd_log(LL_NOTICE, "No running instance found, none killed. Exiting.");
761 goto terminate;
762 }
763
764 if (!foreground) {
765 if ((res = daemonize()) < 0) {
766 fprintf(stderr, "usbmuxd: FATAL: Could not daemonize!\n");
767 usbmuxd_log(LL_FATAL, "Could not daemonize!");
768 goto terminate;
769 }
770 }
771
772 if (lockfile) {
773 // now open the lockfile and place the lock
774 res = lfd = open(lockfile, O_WRONLY|O_CREAT|O_TRUNC|O_EXCL, 0644);
775 if(res < 0) {
776 usbmuxd_log(LL_FATAL, "Could not open pidfile '%s'", lockfile);
777 goto terminate;
778 }
779 lock.l_type = F_WRLCK;
780 lock.l_whence = SEEK_SET;
781 lock.l_start = 0;
782 lock.l_len = 0;
783 if ((res = fcntl(lfd, F_SETLK, &lock)) < 0) {
784 usbmuxd_log(LL_FATAL, "Locking pidfile '%s' failed!", lockfile);
785 goto terminate;
786 }
787 sprintf(pids, "%d", getpid());
788 if ((size_t)(res = write(lfd, pids, strlen(pids))) != strlen(pids)) {
789 usbmuxd_log(LL_FATAL, "Could not write pidfile!");
790 if(res >= 0)
791 res = -2;
792 goto terminate;
793 }
794 }
795
796 // set number of file descriptors to higher value
797 struct rlimit rlim;
798 getrlimit(RLIMIT_NOFILE, &rlim);
799 rlim.rlim_max = 65536;
800 setrlimit(RLIMIT_NOFILE, (const struct rlimit*)&rlim);
801
802 usbmuxd_log(LL_INFO, "Creating socket");
803 res = listenfd = create_socket();
804 if(listenfd < 0)
805 goto terminate;
806
807#ifdef HAVE_LIBIMOBILEDEVICE
808 const char* userprefdir = config_get_config_dir();
809 usbmuxd_log(LL_NOTICE, "Configuration directory: %s", userprefdir);
810 struct stat fst;
811 memset(&fst, '\0', sizeof(struct stat));
812 if (stat(userprefdir, &fst) < 0) {
813 if (mkdir(userprefdir, 0775) < 0) {
814 usbmuxd_log(LL_FATAL, "Failed to create configuration directory '%s': %s", userprefdir, strerror(errno));
815 res = -1;
816 goto terminate;
817 }
818 if (stat(userprefdir, &fst) < 0) {
819 usbmuxd_log(LL_FATAL, "stat() failed after creating directory '%s': %s", userprefdir, strerror(errno));
820 res = -1;
821 goto terminate;
822 }
823 }
824
825 // make sure permission bits are set correctly
826 if (fst.st_mode != 02775) {
827 if (chmod(userprefdir, 02775) < 0) {
828 usbmuxd_log(LL_WARNING, "chmod(%s, 02775) failed: %s", userprefdir, strerror(errno));
829 }
830 }
831#endif
832
833 // drop elevated privileges
834 if (drop_privileges && (getuid() == 0 || geteuid() == 0)) {
835 struct passwd *pw;
836 if (!drop_user) {
837 usbmuxd_log(LL_FATAL, "No user to drop privileges to?");
838 res = -1;
839 goto terminate;
840 }
841 pw = getpwnam(drop_user);
842 if (!pw) {
843 usbmuxd_log(LL_FATAL, "Dropping privileges failed, check if user '%s' exists!", drop_user);
844 res = -1;
845 goto terminate;
846 }
847 if (pw->pw_uid == 0) {
848 usbmuxd_log(LL_INFO, "Not dropping privileges to root");
849 } else {
850#ifdef HAVE_LIBIMOBILEDEVICE
851 /* make sure the non-privileged user has proper access to the config directory */
852 if ((fst.st_uid != pw->pw_uid) || (fst.st_gid != pw->pw_gid)) {
853 if (chown(userprefdir, pw->pw_uid, pw->pw_gid) < 0) {
854 usbmuxd_log(LL_WARNING, "chown(%s, %d, %d) failed: %s", userprefdir, pw->pw_uid, pw->pw_gid, strerror(errno));
855 }
856 }
857#endif
858
859 if ((res = initgroups(drop_user, pw->pw_gid)) < 0) {
860 usbmuxd_log(LL_FATAL, "Failed to drop privileges (cannot set supplementary groups)");
861 goto terminate;
862 }
863 if ((res = setgid(pw->pw_gid)) < 0) {
864 usbmuxd_log(LL_FATAL, "Failed to drop privileges (cannot set group ID to %d)", pw->pw_gid);
865 goto terminate;
866 }
867 if ((res = setuid(pw->pw_uid)) < 0) {
868 usbmuxd_log(LL_FATAL, "Failed to drop privileges (cannot set user ID to %d)", pw->pw_uid);
869 goto terminate;
870 }
871
872 // security check
873 if (setuid(0) != -1) {
874 usbmuxd_log(LL_FATAL, "Failed to drop privileges properly!");
875 res = -1;
876 goto terminate;
877 }
878 if (getuid() != pw->pw_uid || getgid() != pw->pw_gid) {
879 usbmuxd_log(LL_FATAL, "Failed to drop privileges properly!");
880 res = -1;
881 goto terminate;
882 }
883 usbmuxd_log(LL_NOTICE, "Successfully dropped privileges to '%s'", drop_user);
884 }
885 }
886
887 client_init();
888 device_init();
889 usbmuxd_log(LL_INFO, "Initializing USB");
890 if((res = usb_init()) < 0)
891 goto terminate;
892
893 usbmuxd_log(LL_INFO, "%d device%s detected", res, (res==1)?"":"s");
894
895 usbmuxd_log(LL_NOTICE, "Initialization complete");
896
897 if (report_to_parent)
898 if((res = notify_parent(0)) < 0)
899 goto terminate;
900
901 if(opt_disable_hotplug) {
902 usbmuxd_log(LL_NOTICE, "Automatic device discovery on hotplug disabled.");
903 usb_autodiscover(0); // discovery to be triggered by new instance
904 }
905 if (opt_enable_exit) {
906 usbmuxd_log(LL_NOTICE, "Enabled exit on SIGUSR1 if no devices are attached. Start a new instance with \"--exit\" to trigger.");
907 }
908
909 res = main_loop(listenfd);
910 if(res < 0)
911 usbmuxd_log(LL_FATAL, "main_loop failed");
912
913 usbmuxd_log(LL_NOTICE, "usbmuxd shutting down");
914 device_kill_connections();
915 usb_shutdown();
916 device_shutdown();
917 client_shutdown();
918 usbmuxd_log(LL_NOTICE, "Shutdown complete");
919
920terminate:
921 log_disable_syslog();
922
923 if (res < 0)
924 res = -res;
925 else
926 res = 0;
927 if (report_to_parent)
928 notify_parent(res);
929
930 return res;
931}
diff --git a/src/preflight.c b/src/preflight.c
new file mode 100644
index 0000000..9c57e98
--- /dev/null
+++ b/src/preflight.c
@@ -0,0 +1,406 @@
1/*
2 * preflight.c
3 *
4 * Copyright (C) 2013 Nikias Bassen <nikias@gmx.li>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 2 or version 3.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18 */
19
20#ifdef HAVE_CONFIG_H
21#include <config.h>
22#endif
23
24#include <stdlib.h>
25#include <string.h>
26#include <unistd.h>
27#include <errno.h>
28
29#include <sys/time.h>
30
31#ifdef HAVE_LIBIMOBILEDEVICE
32#include <libimobiledevice/libimobiledevice.h>
33#include <libimobiledevice/lockdown.h>
34#include <libimobiledevice/notification_proxy.h>
35#endif
36
37#include <libimobiledevice-glue/thread.h>
38
39#include "preflight.h"
40#include "device.h"
41#include "client.h"
42#include "conf.h"
43#include "log.h"
44#include "usb.h"
45
46extern int no_preflight;
47
48#ifdef HAVE_LIBIMOBILEDEVICE
49#ifndef HAVE_ENUM_IDEVICE_CONNECTION_TYPE
50enum idevice_connection_type {
51 CONNECTION_USBMUXD = 1,
52 CONNECTION_NETWORK
53};
54#endif
55
56struct idevice_private {
57 char *udid;
58 uint32_t mux_id;
59 enum idevice_connection_type conn_type;
60 void *conn_data;
61 int version;
62 int device_class;
63};
64
65struct cb_data {
66 idevice_t dev;
67 np_client_t np;
68 int is_device_connected;
69 int is_finished;
70};
71
72static void lockdownd_set_untrusted_host_buid(lockdownd_client_t lockdown)
73{
74 char* system_buid = NULL;
75 config_get_system_buid(&system_buid);
76 usbmuxd_log(LL_DEBUG, "%s: Setting UntrustedHostBUID to %s", __func__, system_buid);
77 lockdownd_set_value(lockdown, NULL, "UntrustedHostBUID", plist_new_string(system_buid));
78 free(system_buid);
79}
80
81void preflight_device_remove_cb(void *data)
82{
83 if (!data)
84 return;
85 struct cb_data *cbdata = (struct cb_data*)data;
86 cbdata->is_device_connected = 0;
87}
88
89static void np_callback(const char* notification, void* userdata)
90{
91 struct cb_data *cbdata = (struct cb_data*)userdata;
92 idevice_t dev = cbdata->dev;
93 struct idevice_private *_dev = (struct idevice_private*)dev;
94
95 lockdownd_client_t lockdown = NULL;
96 lockdownd_error_t lerr;
97
98 if (strlen(notification) == 0) {
99 cbdata->np = NULL;
100 return;
101 }
102
103 if (strcmp(notification, "com.apple.mobile.lockdown.request_pair") == 0) {
104 usbmuxd_log(LL_INFO, "%s: user trusted this computer on device %s, pairing now", __func__, _dev->udid);
105 lerr = lockdownd_client_new(dev, &lockdown, "usbmuxd");
106 if (lerr != LOCKDOWN_E_SUCCESS) {
107 usbmuxd_log(LL_ERROR, "%s: ERROR: Could not connect to lockdownd on device %s, lockdown error %d", __func__, _dev->udid, lerr);
108 cbdata->is_finished = 1;
109 return;
110 }
111
112 lerr = lockdownd_pair(lockdown, NULL);
113 if (lerr != LOCKDOWN_E_SUCCESS) {
114 usbmuxd_log(LL_ERROR, "%s: ERROR: Pair failed for device %s, lockdown error %d", __func__, _dev->udid, lerr);
115 lockdownd_client_free(lockdown);
116 cbdata->is_finished = 1;
117 return;
118 }
119 lockdownd_client_free(lockdown);
120 cbdata->is_finished = 1;
121
122 } else if (strcmp(notification, "com.apple.mobile.lockdown.request_host_buid") == 0) {
123 lerr = lockdownd_client_new(cbdata->dev, &lockdown, "usbmuxd");
124 if (lerr != LOCKDOWN_E_SUCCESS) {
125 usbmuxd_log(LL_ERROR, "%s: ERROR: Could not connect to lockdownd on device %s, lockdown error %d", __func__, _dev->udid, lerr);
126 } else {
127 lockdownd_set_untrusted_host_buid(lockdown);
128 lockdownd_client_free(lockdown);
129 }
130 }
131}
132
133static void* preflight_worker_handle_device_add(void* userdata)
134{
135 struct device_info *info = (struct device_info*)userdata;
136 struct idevice_private *_dev = (struct idevice_private*)malloc(sizeof(struct idevice_private));
137 _dev->udid = strdup(info->serial);
138 _dev->mux_id = info->id;
139 _dev->conn_type = CONNECTION_USBMUXD;
140 _dev->conn_data = NULL;
141 _dev->version = 0;
142 _dev->device_class = 0;
143
144 idevice_t dev = (idevice_t)_dev;
145
146 lockdownd_client_t lockdown = NULL;
147 lockdownd_error_t lerr;
148
149 plist_t value = NULL;
150 char* version_str = NULL;
151 char* deviceclass_str = NULL;
152
153 usbmuxd_log(LL_INFO, "%s: Starting preflight on device %s...", __func__, _dev->udid);
154
155retry:
156 lerr = lockdownd_client_new(dev, &lockdown, "usbmuxd");
157 if (lerr != LOCKDOWN_E_SUCCESS) {
158 usbmuxd_log(LL_ERROR, "%s: ERROR: Could not connect to lockdownd on device %s, lockdown error %d", __func__, _dev->udid, lerr);
159 goto leave;
160 }
161
162 char *type = NULL;
163 lerr = lockdownd_query_type(lockdown, &type);
164 if (!type) {
165 usbmuxd_log(LL_ERROR, "%s: ERROR: Could not get lockdownd type from device %s, lockdown error %d", __func__, _dev->udid, lerr);
166 goto leave;
167 }
168
169 if (strcmp(type, "com.apple.mobile.lockdown") != 0) {
170 // make restore mode devices visible
171 free(type);
172 usbmuxd_log(LL_INFO, "%s: Finished preflight on device %s", __func__, _dev->udid);
173 client_device_add(info);
174 goto leave;
175 }
176 free(type);
177
178 int is_device_paired = 0;
179 char *host_id = NULL;
180 if (config_has_device_record(dev->udid)) {
181 config_device_record_get_host_id(dev->udid, &host_id);
182 lerr = lockdownd_start_session(lockdown, host_id, NULL, NULL);
183 if (host_id)
184 free(host_id);
185 if (lerr == LOCKDOWN_E_SUCCESS) {
186 usbmuxd_log(LL_INFO, "%s: StartSession success for device %s", __func__, _dev->udid);
187 usbmuxd_log(LL_INFO, "%s: Finished preflight on device %s", __func__, _dev->udid);
188 client_device_add(info);
189 goto leave;
190 }
191
192 usbmuxd_log(LL_INFO, "%s: StartSession failed on device %s, lockdown error %d", __func__, _dev->udid, lerr);
193 } else {
194 lerr = LOCKDOWN_E_INVALID_HOST_ID;
195 }
196 switch (lerr) {
197 case LOCKDOWN_E_INVALID_HOST_ID:
198 usbmuxd_log(LL_INFO, "%s: Device %s is not paired with this host.", __func__, _dev->udid);
199 break;
200 case LOCKDOWN_E_SSL_ERROR:
201 usbmuxd_log(LL_ERROR, "%s: The stored pair record for device %s is invalid. Removing.", __func__, _dev->udid);
202 if (config_remove_device_record(_dev->udid) == 0) {
203 lockdownd_client_free(lockdown);
204 lockdown = NULL;
205 goto retry;
206 } else {
207 usbmuxd_log(LL_ERROR, "%s: Could not remove pair record for device %s", __func__, _dev->udid);
208 }
209 break;
210 default:
211 is_device_paired = 1;
212 break;
213 }
214
215 lerr = lockdownd_get_value(lockdown, NULL, "ProductVersion", &value);
216 if (lerr != LOCKDOWN_E_SUCCESS) {
217 usbmuxd_log(LL_WARNING, "%s: Could not get ProductVersion from device %s, lockdown error %d", __func__, _dev->udid, lerr);
218 /* assume old iOS version */
219 version_str = strdup("1.0");
220 } else {
221 if (value && plist_get_node_type(value) == PLIST_STRING) {
222 plist_get_string_val(value, &version_str);
223 }
224 plist_free(value);
225
226 if (!version_str) {
227 usbmuxd_log(LL_ERROR, "%s: Could not get ProductVersion string from device %s handle %d", __func__, _dev->udid, (int)(long)_dev->conn_data);
228 goto leave;
229 }
230 }
231
232 lerr = lockdownd_get_value(lockdown, NULL, "DeviceClass", &value);
233 if (lerr != LOCKDOWN_E_SUCCESS) {
234 usbmuxd_log(LL_ERROR, "%s: ERROR: Could not get DeviceClass from device %s, lockdown error %d", __func__, _dev->udid, lerr);
235 goto leave;
236 }
237 if (value && plist_get_node_type(value) == PLIST_STRING) {
238 plist_get_string_val(value, &deviceclass_str);
239 }
240 plist_free(value);
241
242 if (!deviceclass_str) {
243 usbmuxd_log(LL_ERROR, "%s: Could not get DeviceClass string from device %s handle %d", __func__, _dev->udid, (int)(long)_dev->conn_data);
244 goto leave;
245 }
246
247 int version_major = strtol(version_str, NULL, 10);
248 if (((!strcmp(deviceclass_str, "iPhone") || !strcmp(deviceclass_str, "iPad")) && version_major >= 7)
249 || (!strcmp(deviceclass_str, "Watch") && version_major >= 2)
250 || (!strcmp(deviceclass_str, "AppleTV") && version_major >= 9)
251 ) {
252 /* iOS 7.0 / watchOS 2.0 / tvOS 9.0 and later */
253 usbmuxd_log(LL_INFO, "%s: Found %s %s device %s", __func__, deviceclass_str, version_str, _dev->udid);
254
255 lockdownd_set_untrusted_host_buid(lockdown);
256
257 /* if not paired, trigger the trust dialog to make sure it appears */
258 if (!is_device_paired) {
259 if (lockdownd_pair(lockdown, NULL) == LOCKDOWN_E_SUCCESS) {
260 /* if device is still showing the setup screen it will pair even without trust dialog */
261 usbmuxd_log(LL_INFO, "%s: Pair success for device %s", __func__, _dev->udid);
262 usbmuxd_log(LL_INFO, "%s: Finished preflight on device %s", __func__, _dev->udid);
263 client_device_add(info);
264 goto leave;
265 }
266 }
267
268 lockdownd_service_descriptor_t service = NULL;
269 lerr = lockdownd_start_service(lockdown, "com.apple.mobile.insecure_notification_proxy", &service);
270 if (lerr != LOCKDOWN_E_SUCCESS) {
271 /* even though we failed, simple mode should still work, so only warn of an error */
272 usbmuxd_log(LL_INFO, "%s: ERROR: Could not start insecure_notification_proxy on %s, lockdown error %d", __func__, _dev->udid, lerr);
273 client_device_add(info);
274 goto leave;
275 }
276
277 np_client_t np = NULL;
278 np_client_new(dev, service, &np);
279
280 lockdownd_service_descriptor_free(service);
281 service = NULL;
282
283 lockdownd_client_free(lockdown);
284 lockdown = NULL;
285
286 struct cb_data cbdata;
287 cbdata.dev = dev;
288 cbdata.np = np;
289 cbdata.is_device_connected = 1;
290 cbdata.is_finished = 0;
291
292 np_set_notify_callback(np, np_callback, (void*)&cbdata);
293 device_set_preflight_cb_data(info->id, (void*)&cbdata);
294
295 const char* spec[] = {
296 "com.apple.mobile.lockdown.request_pair",
297 "com.apple.mobile.lockdown.request_host_buid",
298 NULL
299 };
300 np_observe_notifications(np, spec);
301
302 /* TODO send notification to user's desktop */
303
304 usbmuxd_log(LL_INFO, "%s: Waiting for user to trust this computer on device %s", __func__, _dev->udid);
305
306 /* make device visible anyways */
307 client_device_add(info);
308
309 while (cbdata.np && cbdata.is_device_connected && !cbdata.is_finished) {
310 sleep(1);
311 }
312 device_set_preflight_cb_data(info->id, NULL);
313
314 usbmuxd_log(LL_INFO, "%s: Finished waiting for notification from device %s, is_device_connected %d", __func__, _dev->udid, cbdata.is_device_connected);
315
316 if (cbdata.np) {
317 np_client_free(cbdata.np);
318 }
319 } else {
320 /* iOS 6.x and earlier */
321 lerr = lockdownd_pair(lockdown, NULL);
322 if (lerr != LOCKDOWN_E_SUCCESS) {
323 if (lerr == LOCKDOWN_E_PASSWORD_PROTECTED) {
324 usbmuxd_log(LL_INFO, "%s: Device %s is locked with a passcode. Cannot pair.", __func__, _dev->udid);
325 /* TODO send notification to user's desktop */
326 } else {
327 usbmuxd_log(LL_ERROR, "%s: ERROR: Pair failed for device %s, lockdown error %d", __func__, _dev->udid, lerr);
328 }
329
330 usbmuxd_log(LL_INFO, "%s: Finished preflight on device %s", __func__, _dev->udid);
331
332 /* make device visible anyways */
333 client_device_add(info);
334
335 goto leave;
336 }
337
338 host_id = NULL;
339 config_device_record_get_host_id(dev->udid, &host_id);
340 lerr = lockdownd_start_session(lockdown, host_id, NULL, NULL);
341 free(host_id);
342 if (lerr != LOCKDOWN_E_SUCCESS) {
343 usbmuxd_log(LL_ERROR, "%s: ERROR StartSession failed on device %s, lockdown error %d", __func__, _dev->udid, lerr);
344 goto leave;
345 }
346
347 lerr = lockdownd_validate_pair(lockdown, NULL);
348 if (lerr != LOCKDOWN_E_SUCCESS) {
349 usbmuxd_log(LL_ERROR, "%s: ERROR: ValidatePair failed for device %s, lockdown error %d", __func__, _dev->udid, lerr);
350 goto leave;
351 }
352
353 usbmuxd_log(LL_INFO, "%s: Finished preflight on device %s", __func__, _dev->udid);
354
355 /* emit device added event and thus make device visible to clients */
356 client_device_add(info);
357 }
358
359leave:
360 free(deviceclass_str);
361 free(version_str);
362 if (lockdown)
363 lockdownd_client_free(lockdown);
364 if (dev)
365 idevice_free(dev);
366
367 free((char*)info->serial);
368 free(info);
369
370 return NULL;
371}
372#else
373void preflight_device_remove_cb(void *data)
374{
375}
376#endif
377
378void preflight_worker_device_add(struct device_info* info)
379{
380 if (info->pid == PID_APPLE_T2_COPROCESSOR || no_preflight == 1) {
381 client_device_add(info);
382 return;
383 }
384
385#ifdef HAVE_LIBIMOBILEDEVICE
386 struct device_info *infocopy = (struct device_info*)malloc(sizeof(struct device_info));
387
388 memcpy(infocopy, info, sizeof(struct device_info));
389 if (info->serial) {
390 infocopy->serial = strdup(info->serial);
391 }
392
393 THREAD_T th;
394 int perr = thread_new(&th, preflight_worker_handle_device_add, infocopy);
395 if (perr != 0) {
396 free((char*)infocopy->serial);
397 free(infocopy);
398 usbmuxd_log(LL_ERROR, "ERROR: failed to start preflight worker thread for device %s: %s (%d). Invoking client_device_add() directly but things might not work as expected.", info->serial, strerror(perr), perr);
399 client_device_add(info);
400 } else {
401 thread_detach(th);
402 }
403#else
404 client_device_add(info);
405#endif
406}
diff --git a/src/preflight.h b/src/preflight.h
new file mode 100644
index 0000000..dd8647e
--- /dev/null
+++ b/src/preflight.h
@@ -0,0 +1,28 @@
1/*
2 * preflight.h
3 *
4 * Copyright (C) 2013 Nikias Bassen <nikias@gmx.li>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 2 or version 3.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18 */
19
20#ifndef PREFLIGHT_H
21#define PREFLIGHT_H
22
23#include "device.h"
24
25void preflight_device_remove_cb(void *data);
26void preflight_worker_device_add(struct device_info* info);
27
28#endif
diff --git a/src/usb.c b/src/usb.c
new file mode 100644
index 0000000..d0e9afc
--- /dev/null
+++ b/src/usb.c
@@ -0,0 +1,1097 @@
1/*
2 * usb.c
3 *
4 * Copyright (C) 2009 Hector Martin <hector@marcansoft.com>
5 * Copyright (C) 2009 Nikias Bassen <nikias@gmx.li>
6 * Copyright (C) 2009-2020 Martin Szulecki <martin.szulecki@libimobiledevice.org>
7 * Copyright (C) 2014 Mikkel Kamstrup Erlandsen <mikkel.kamstrup@xamarin.com>
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation, either version 2 or version 3.
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
23#ifdef HAVE_CONFIG_H
24#include <config.h>
25#endif
26
27#include <stdio.h>
28#include <stdlib.h>
29#include <stdint.h>
30#include <string.h>
31
32#include <libusb.h>
33
34#include <libimobiledevice-glue/collection.h>
35
36#include "usb.h"
37#include "log.h"
38#include "device.h"
39#include "utils.h"
40
41#if (defined(LIBUSB_API_VERSION) && (LIBUSB_API_VERSION >= 0x01000102)) || (defined(LIBUSBX_API_VERSION) && (LIBUSBX_API_VERSION >= 0x01000102))
42#define HAVE_LIBUSB_HOTPLUG_API 1
43#endif
44
45// interval for device connection/disconnection polling, in milliseconds
46// we need this because there is currently no asynchronous device discovery mechanism in libusb
47#define DEVICE_POLL_TIME 1000
48
49// Number of parallel bulk transfers we have running for reading data from the device.
50// Older versions of usbmuxd kept only 1, which leads to a mostly dormant USB port.
51// 3 seems to be an all round sensible number - giving better read perf than
52// Apples usbmuxd, at least.
53#define NUM_RX_LOOPS 3
54
55struct usb_device {
56 libusb_device_handle *handle;
57 uint8_t bus, address;
58 char serial[256];
59 int alive;
60 uint8_t interface, ep_in, ep_out;
61 struct collection rx_xfers;
62 struct collection tx_xfers;
63 int wMaxPacketSize;
64 uint64_t speed;
65 struct libusb_device_descriptor devdesc;
66};
67
68struct mode_context {
69 struct libusb_device* dev;
70 uint8_t bus, address;
71 uint8_t bRequest;
72 uint16_t wValue, wIndex, wLength;
73 unsigned int timeout;
74};
75
76static struct collection device_list;
77
78static struct timeval next_dev_poll_time;
79
80static int devlist_failures;
81static int device_polling;
82static int device_hotplug = 1;
83
84static void usb_disconnect(struct usb_device *dev)
85{
86 if(!dev->handle) {
87 return;
88 }
89
90 // kill the rx xfer and tx xfers and try to make sure the callbacks
91 // get called before we free the device
92 FOREACH(struct libusb_transfer *xfer, &dev->rx_xfers) {
93 usbmuxd_log(LL_DEBUG, "usb_disconnect: cancelling RX xfer %p", xfer);
94 libusb_cancel_transfer(xfer);
95 } ENDFOREACH
96
97 FOREACH(struct libusb_transfer *xfer, &dev->tx_xfers) {
98 usbmuxd_log(LL_DEBUG, "usb_disconnect: cancelling TX xfer %p", xfer);
99 libusb_cancel_transfer(xfer);
100 } ENDFOREACH
101
102 // Busy-wait until all xfers are closed
103 while(collection_count(&dev->rx_xfers) || collection_count(&dev->tx_xfers)) {
104 struct timeval tv;
105 int res;
106
107 tv.tv_sec = 0;
108 tv.tv_usec = 1000;
109 if((res = libusb_handle_events_timeout(NULL, &tv)) < 0) {
110 usbmuxd_log(LL_ERROR, "libusb_handle_events_timeout for usb_disconnect failed: %s", libusb_error_name(res));
111 break;
112 }
113 }
114
115 collection_free(&dev->tx_xfers);
116 collection_free(&dev->rx_xfers);
117 libusb_release_interface(dev->handle, dev->interface);
118 libusb_close(dev->handle);
119 dev->handle = NULL;
120 collection_remove(&device_list, dev);
121 free(dev);
122}
123
124static void reap_dead_devices(void) {
125 FOREACH(struct usb_device *usbdev, &device_list) {
126 if(!usbdev->alive) {
127 device_remove(usbdev);
128 usb_disconnect(usbdev);
129 }
130 } ENDFOREACH
131}
132
133// Callback from write operation
134static void tx_callback(struct libusb_transfer *xfer)
135{
136 struct usb_device *dev = xfer->user_data;
137 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);
138 if(xfer->status != LIBUSB_TRANSFER_COMPLETED) {
139 switch(xfer->status) {
140 case LIBUSB_TRANSFER_COMPLETED: //shut up compiler
141 case LIBUSB_TRANSFER_ERROR:
142 // funny, this happens when we disconnect the device while waiting for a transfer, sometimes
143 usbmuxd_log(LL_INFO, "Device %d-%d TX aborted due to error or disconnect", dev->bus, dev->address);
144 break;
145 case LIBUSB_TRANSFER_TIMED_OUT:
146 usbmuxd_log(LL_ERROR, "TX transfer timed out for device %d-%d", dev->bus, dev->address);
147 break;
148 case LIBUSB_TRANSFER_CANCELLED:
149 usbmuxd_log(LL_DEBUG, "Device %d-%d TX transfer cancelled", dev->bus, dev->address);
150 break;
151 case LIBUSB_TRANSFER_STALL:
152 usbmuxd_log(LL_ERROR, "TX transfer stalled for device %d-%d", dev->bus, dev->address);
153 break;
154 case LIBUSB_TRANSFER_NO_DEVICE:
155 // other times, this happens, and also even when we abort the transfer after device removal
156 usbmuxd_log(LL_INFO, "Device %d-%d TX aborted due to disconnect", dev->bus, dev->address);
157 break;
158 case LIBUSB_TRANSFER_OVERFLOW:
159 usbmuxd_log(LL_ERROR, "TX transfer overflow for device %d-%d", dev->bus, dev->address);
160 break;
161 // and nothing happens (this never gets called) if the device is freed after a disconnect! (bad)
162 default:
163 // this should never be reached.
164 break;
165 }
166 // we can't usb_disconnect here due to a deadlock, so instead mark it as dead and reap it after processing events
167 // we'll do device_remove there too
168 dev->alive = 0;
169 }
170 if(xfer->buffer)
171 free(xfer->buffer);
172 collection_remove(&dev->tx_xfers, xfer);
173 libusb_free_transfer(xfer);
174}
175
176int usb_send(struct usb_device *dev, const unsigned char *buf, int length)
177{
178 int res;
179 struct libusb_transfer *xfer = libusb_alloc_transfer(0);
180 libusb_fill_bulk_transfer(xfer, dev->handle, dev->ep_out, (void*)buf, length, tx_callback, dev, 0);
181 if((res = libusb_submit_transfer(xfer)) < 0) {
182 usbmuxd_log(LL_ERROR, "Failed to submit TX transfer %p len %d to device %d-%d: %s", buf, length, dev->bus, dev->address, libusb_error_name(res));
183 libusb_free_transfer(xfer);
184 return res;
185 }
186 collection_add(&dev->tx_xfers, xfer);
187 if (length % dev->wMaxPacketSize == 0) {
188 usbmuxd_log(LL_DEBUG, "Send ZLP");
189 // Send Zero Length Packet
190 xfer = libusb_alloc_transfer(0);
191 void *buffer = malloc(1);
192 libusb_fill_bulk_transfer(xfer, dev->handle, dev->ep_out, buffer, 0, tx_callback, dev, 0);
193 if((res = libusb_submit_transfer(xfer)) < 0) {
194 usbmuxd_log(LL_ERROR, "Failed to submit TX ZLP transfer to device %d-%d: %s", dev->bus, dev->address, libusb_error_name(res));
195 libusb_free_transfer(xfer);
196 return res;
197 }
198 collection_add(&dev->tx_xfers, xfer);
199 }
200 return 0;
201}
202
203// Callback from read operation
204// Under normal operation this issues a new read transfer request immediately,
205// doing a kind of read-callback loop
206static void rx_callback(struct libusb_transfer *xfer)
207{
208 struct usb_device *dev = xfer->user_data;
209 usbmuxd_log(LL_SPEW, "RX callback dev %d-%d len %d status %d", dev->bus, dev->address, xfer->actual_length, xfer->status);
210 if(xfer->status == LIBUSB_TRANSFER_COMPLETED) {
211 device_data_input(dev, xfer->buffer, xfer->actual_length);
212 libusb_submit_transfer(xfer);
213 } else {
214 switch(xfer->status) {
215 case LIBUSB_TRANSFER_COMPLETED: //shut up compiler
216 case LIBUSB_TRANSFER_ERROR:
217 // funny, this happens when we disconnect the device while waiting for a transfer, sometimes
218 usbmuxd_log(LL_INFO, "Device %d-%d RX aborted due to error or disconnect", dev->bus, dev->address);
219 break;
220 case LIBUSB_TRANSFER_TIMED_OUT:
221 usbmuxd_log(LL_ERROR, "RX transfer timed out for device %d-%d", dev->bus, dev->address);
222 break;
223 case LIBUSB_TRANSFER_CANCELLED:
224 usbmuxd_log(LL_DEBUG, "Device %d-%d RX transfer cancelled", dev->bus, dev->address);
225 break;
226 case LIBUSB_TRANSFER_STALL:
227 usbmuxd_log(LL_ERROR, "RX transfer stalled for device %d-%d", dev->bus, dev->address);
228 break;
229 case LIBUSB_TRANSFER_NO_DEVICE:
230 // other times, this happens, and also even when we abort the transfer after device removal
231 usbmuxd_log(LL_INFO, "Device %d-%d RX aborted due to disconnect", dev->bus, dev->address);
232 break;
233 case LIBUSB_TRANSFER_OVERFLOW:
234 usbmuxd_log(LL_ERROR, "RX transfer overflow for device %d-%d", dev->bus, dev->address);
235 break;
236 // and nothing happens (this never gets called) if the device is freed after a disconnect! (bad)
237 default:
238 // this should never be reached.
239 break;
240 }
241
242 free(xfer->buffer);
243 collection_remove(&dev->rx_xfers, xfer);
244 libusb_free_transfer(xfer);
245
246 // we can't usb_disconnect here due to a deadlock, so instead mark it as dead and reap it after processing events
247 // we'll do device_remove there too
248 dev->alive = 0;
249 }
250}
251
252// Start a read-callback loop for this device
253static int start_rx_loop(struct usb_device *dev)
254{
255 int res;
256 void *buf;
257 struct libusb_transfer *xfer = libusb_alloc_transfer(0);
258 buf = malloc(USB_MRU);
259 libusb_fill_bulk_transfer(xfer, dev->handle, dev->ep_in, buf, USB_MRU, rx_callback, dev, 0);
260 if((res = libusb_submit_transfer(xfer)) != 0) {
261 usbmuxd_log(LL_ERROR, "Failed to submit RX transfer to device %d-%d: %s", dev->bus, dev->address, libusb_error_name(res));
262 libusb_free_transfer(xfer);
263 return res;
264 }
265
266 collection_add(&dev->rx_xfers, xfer);
267
268 return 0;
269}
270
271static void get_serial_callback(struct libusb_transfer *transfer)
272{
273 unsigned int di, si;
274 struct usb_device *usbdev = transfer->user_data;
275
276 if(transfer->status != LIBUSB_TRANSFER_COMPLETED) {
277 usbmuxd_log(LL_ERROR, "Failed to request serial for device %d-%d (%i)", usbdev->bus, usbdev->address, transfer->status);
278 libusb_free_transfer(transfer);
279 return;
280 }
281
282 /* De-unicode, taken from libusb */
283 unsigned char *data = libusb_control_transfer_get_data(transfer);
284 for (di = 0, si = 2; si < data[0] && di < sizeof(usbdev->serial)-1; si += 2) {
285 if ((data[si] & 0x80) || (data[si + 1])) /* non-ASCII */
286 usbdev->serial[di++] = '?';
287 else if (data[si] == '\0')
288 break;
289 else
290 usbdev->serial[di++] = data[si];
291 }
292 usbdev->serial[di] = '\0';
293
294 usbmuxd_log(LL_INFO, "Got serial '%s' for device %d-%d", usbdev->serial, usbdev->bus, usbdev->address);
295
296 libusb_free_transfer(transfer);
297
298 /* new style UDID: add hyphen between first 8 and following 16 digits */
299 if (di == 24) {
300 memmove(&usbdev->serial[9], &usbdev->serial[8], 16);
301 usbdev->serial[8] = '-';
302 usbdev->serial[di+1] = '\0';
303 }
304
305 /* Finish setup now */
306 if(device_add(usbdev) < 0) {
307 usb_disconnect(usbdev);
308 return;
309 }
310
311 // Spin up NUM_RX_LOOPS parallel usb data retrieval loops
312 // Old usbmuxds used only 1 rx loop, but that leaves the
313 // USB port sleeping most of the time
314 int rx_loops = NUM_RX_LOOPS;
315 for (rx_loops = NUM_RX_LOOPS; rx_loops > 0; rx_loops--) {
316 if(start_rx_loop(usbdev) < 0) {
317 usbmuxd_log(LL_WARNING, "Failed to start RX loop number %d", NUM_RX_LOOPS - rx_loops);
318 break;
319 }
320 }
321
322 // Ensure we have at least 1 RX loop going
323 if (rx_loops == NUM_RX_LOOPS) {
324 usbmuxd_log(LL_FATAL, "Failed to start any RX loop for device %d-%d",
325 usbdev->bus, usbdev->address);
326 device_remove(usbdev);
327 usb_disconnect(usbdev);
328 return;
329 } else if (rx_loops > 0) {
330 usbmuxd_log(LL_WARNING, "Failed to start all %d RX loops. Going on with %d loops. "
331 "This may have negative impact on device read speed.",
332 NUM_RX_LOOPS, NUM_RX_LOOPS - rx_loops);
333 } else {
334 usbmuxd_log(LL_DEBUG, "All %d RX loops started successfully", NUM_RX_LOOPS);
335 }
336}
337
338static void get_langid_callback(struct libusb_transfer *transfer)
339{
340 int res;
341 struct usb_device *usbdev = transfer->user_data;
342
343 transfer->flags |= LIBUSB_TRANSFER_FREE_BUFFER;
344
345 if(transfer->status != LIBUSB_TRANSFER_COMPLETED) {
346 usbmuxd_log(LL_ERROR, "Failed to request lang ID for device %d-%d (%i)", usbdev->bus,
347 usbdev->address, transfer->status);
348 libusb_free_transfer(transfer);
349 return;
350 }
351
352 unsigned char *data = libusb_control_transfer_get_data(transfer);
353 uint16_t langid = (uint16_t)(data[2] | (data[3] << 8));
354 usbmuxd_log(LL_INFO, "Got lang ID %u for device %d-%d", langid, usbdev->bus, usbdev->address);
355
356 /* re-use the same transfer */
357 libusb_fill_control_setup(transfer->buffer, LIBUSB_ENDPOINT_IN, LIBUSB_REQUEST_GET_DESCRIPTOR,
358 (uint16_t)((LIBUSB_DT_STRING << 8) | usbdev->devdesc.iSerialNumber),
359 langid, 1024 + LIBUSB_CONTROL_SETUP_SIZE);
360 libusb_fill_control_transfer(transfer, usbdev->handle, transfer->buffer, get_serial_callback, usbdev, 1000);
361
362 if((res = libusb_submit_transfer(transfer)) < 0) {
363 usbmuxd_log(LL_ERROR, "Could not request transfer for device %d-%d: %s", usbdev->bus, usbdev->address, libusb_error_name(res));
364 libusb_free_transfer(transfer);
365 }
366}
367
368static int submit_vendor_specific(struct libusb_device_handle *handle, struct mode_context *context, libusb_transfer_cb_fn callback)
369{
370 struct libusb_transfer* ctrl_transfer = libusb_alloc_transfer(0);
371 int ret = 0;
372 unsigned char* buffer = calloc(LIBUSB_CONTROL_SETUP_SIZE + context->wLength, 1);
373 uint8_t bRequestType = LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_ENDPOINT_IN | LIBUSB_RECIPIENT_DEVICE;
374 libusb_fill_control_setup(buffer, bRequestType, context->bRequest, context->wValue, context->wIndex, context->wLength);
375
376 ctrl_transfer->flags = LIBUSB_TRANSFER_FREE_TRANSFER;
377 libusb_fill_control_transfer(ctrl_transfer, handle, buffer, callback, context, context->timeout);
378
379 ret = libusb_submit_transfer(ctrl_transfer);
380 return ret;
381}
382
383static struct usb_device* find_device(int bus, int address)
384{
385 FOREACH(struct usb_device *usbdev, &device_list) {
386 if(usbdev->bus == bus && usbdev->address == address) {
387 return usbdev;
388 }
389 } ENDFOREACH
390 return NULL;
391}
392
393/// @brief guess the current mode
394/// @param dev
395/// @param usbdev
396/// @param handle
397/// @return 0 - undetermined, 1 - initial, 2 - valeria, 3 - cdc_ncm, 4 - usbeth+cdc_ncm, 5 - cdc_ncm direct
398static int guess_mode(struct libusb_device* dev, struct usb_device *usbdev)
399{
400 int res, j;
401 int has_valeria = 0, has_cdc_ncm = 0, has_usbmux = 0;
402 struct libusb_device_descriptor devdesc = usbdev->devdesc;
403 struct libusb_config_descriptor *config;
404 int bus = usbdev->bus;
405 int address = usbdev->address;
406
407 if(devdesc.bNumConfigurations == 1) {
408 // CDC-NCM Direct
409 return 5;
410 }
411
412 if(devdesc.bNumConfigurations <= 4) {
413 // Assume this is initial mode
414 return 1;
415 }
416
417 if(devdesc.bNumConfigurations == 6) {
418 // USB Ethernet + CDC-NCM
419 return 4;
420 }
421
422 if(devdesc.bNumConfigurations != 5) {
423 // No known modes with more then 5 configurations
424 return 0;
425 }
426
427 if((res = libusb_get_config_descriptor_by_value(dev, 5, &config)) != 0) {
428 usbmuxd_log(LL_NOTICE, "Could not get configuration 5 descriptor for device %i-%i: %s", bus, address, libusb_error_name(res));
429 return 0;
430 }
431
432 // Require both usbmux and one of the other interfaces to determine this is a valid configuration
433 for(j = 0 ; j < config->bNumInterfaces ; j++) {
434 const struct libusb_interface_descriptor *intf = &config->interface[j].altsetting[0];
435 if(intf->bInterfaceClass == INTERFACE_CLASS &&
436 intf->bInterfaceSubClass == 42 &&
437 intf->bInterfaceProtocol == 255) {
438 has_valeria = 1;
439 }
440 // https://github.com/torvalds/linux/blob/72a85e2b0a1e1e6fb4ee51ae902730212b2de25c/include/uapi/linux/usb/cdc.h#L22
441 // 2 for Communication class, 0xd for CDC NCM subclass
442 if(intf->bInterfaceClass == 2 &&
443 intf->bInterfaceSubClass == 0xd) {
444 has_cdc_ncm = 1;
445 }
446 if(intf->bInterfaceClass == INTERFACE_CLASS &&
447 intf->bInterfaceSubClass == INTERFACE_SUBCLASS &&
448 intf->bInterfaceProtocol == INTERFACE_PROTOCOL) {
449 has_usbmux = 1;
450 }
451 }
452
453 libusb_free_config_descriptor(config);
454
455 if(has_valeria && has_usbmux) {
456 usbmuxd_log(LL_NOTICE, "Found Valeria and Apple USB Multiplexor in device %i-%i configuration 5", bus, address);
457 return 2;
458 }
459
460 if(has_cdc_ncm && has_usbmux) {
461 usbmuxd_log(LL_NOTICE, "Found CDC-NCM and Apple USB Multiplexor in device %i-%i configuration 5", bus, address);
462 return 3;
463 }
464
465 return 0;
466}
467
468/// @brief Finds and sets the valid configuration, interface and endpoints on the usb_device
469static int set_valid_configuration(struct libusb_device* dev, struct usb_device *usbdev, struct libusb_device_handle *handle)
470{
471 int j, k, res, found = 0;
472 struct libusb_config_descriptor *config;
473 const struct libusb_interface_descriptor *intf;
474 struct libusb_device_descriptor devdesc = usbdev->devdesc;
475 int bus = usbdev->bus;
476 int address = usbdev->address;
477 int current_config = 0;
478
479 if((res = libusb_get_configuration(handle, &current_config)) != 0) {
480 usbmuxd_log(LL_WARNING, "Could not get current configuration for device %d-%d: %s", bus, address, libusb_error_name(res));
481 return -1;
482 }
483
484 for(j = devdesc.bNumConfigurations ; j > 0 ; j--) {
485 if((res = libusb_get_config_descriptor_by_value(dev, j, &config)) != 0) {
486 usbmuxd_log(LL_NOTICE, "Could not get configuration %i descriptor for device %i-%i: %s", j, bus, address, libusb_error_name(res));
487 continue;
488 }
489 for(k = 0 ; k < config->bNumInterfaces ; k++) {
490 intf = &config->interface[k].altsetting[0];
491 if(intf->bInterfaceClass == INTERFACE_CLASS ||
492 intf->bInterfaceSubClass == INTERFACE_SUBCLASS ||
493 intf->bInterfaceProtocol == INTERFACE_PROTOCOL) {
494 usbmuxd_log(LL_NOTICE, "Found usbmux interface for device %i-%i: %i", bus, address, intf->bInterfaceNumber);
495 if(intf->bNumEndpoints != 2) {
496 usbmuxd_log(LL_WARNING, "Endpoint count mismatch for interface %i of device %i-%i", intf->bInterfaceNumber, bus, address);
497 continue;
498 }
499 if((intf->endpoint[0].bEndpointAddress & 0x80) == LIBUSB_ENDPOINT_OUT &&
500 (intf->endpoint[1].bEndpointAddress & 0x80) == LIBUSB_ENDPOINT_IN) {
501 usbdev->interface = intf->bInterfaceNumber;
502 usbdev->ep_out = intf->endpoint[0].bEndpointAddress;
503 usbdev->ep_in = intf->endpoint[1].bEndpointAddress;
504 usbmuxd_log(LL_INFO, "Found interface %i with endpoints %02x/%02x for device %i-%i", usbdev->interface, usbdev->ep_out, usbdev->ep_in, bus, address);
505 found = 1;
506 break;
507 } else if((intf->endpoint[1].bEndpointAddress & 0x80) == LIBUSB_ENDPOINT_OUT &&
508 (intf->endpoint[0].bEndpointAddress & 0x80) == LIBUSB_ENDPOINT_IN) {
509 usbdev->interface = intf->bInterfaceNumber;
510 usbdev->ep_out = intf->endpoint[1].bEndpointAddress;
511 usbdev->ep_in = intf->endpoint[0].bEndpointAddress;
512 usbmuxd_log(LL_INFO, "Found interface %i with swapped endpoints %02x/%02x for device %i-%i", usbdev->interface, usbdev->ep_out, usbdev->ep_in, bus, address);
513 found = 1;
514 break;
515 } else {
516 usbmuxd_log(LL_WARNING, "Endpoint type mismatch for interface %i of device %i-%i", intf->bInterfaceNumber, bus, address);
517 }
518 }
519 }
520 if(!found) {
521 libusb_free_config_descriptor(config);
522 continue;
523 }
524 // If set configuration is required, try to first detach all kernel drivers
525 if (current_config == 0) {
526 usbmuxd_log(LL_DEBUG, "Device %d-%d is unconfigured", bus, address);
527 }
528 if(current_config == 0 || config->bConfigurationValue != current_config) {
529 usbmuxd_log(LL_NOTICE, "Changing configuration of device %i-%i: %i -> %i", bus, address, current_config, config->bConfigurationValue);
530 for(k=0 ; k < config->bNumInterfaces ; k++) {
531 const struct libusb_interface_descriptor *intf1 = &config->interface[k].altsetting[0];
532 if((res = libusb_kernel_driver_active(handle, intf1->bInterfaceNumber)) < 0) {
533 usbmuxd_log(LL_NOTICE, "Could not check kernel ownership of interface %d for device %d-%d: %s", intf1->bInterfaceNumber, bus, address, libusb_error_name(res));
534 continue;
535 }
536 if(res == 1) {
537 usbmuxd_log(LL_INFO, "Detaching kernel driver for device %d-%d, interface %d", bus, address, intf1->bInterfaceNumber);
538 if((res = libusb_detach_kernel_driver(handle, intf1->bInterfaceNumber)) < 0) {
539 usbmuxd_log(LL_WARNING, "Could not detach kernel driver, configuration change will probably fail! %s", libusb_error_name(res));
540 continue;
541 }
542 }
543 }
544 if((res = libusb_set_configuration(handle, j)) != 0) {
545 usbmuxd_log(LL_WARNING, "Could not set configuration %d for device %d-%d: %s", j, bus, address, libusb_error_name(res));
546 libusb_free_config_descriptor(config);
547 continue;
548 }
549 }
550
551 libusb_free_config_descriptor(config);
552 break;
553 }
554
555 if(!found) {
556 usbmuxd_log(LL_WARNING, "Could not find a suitable USB interface for device %i-%i", bus, address);
557 return -1;
558 }
559
560 return 0;
561}
562
563static void device_complete_initialization(struct mode_context *context, struct libusb_device_handle *handle)
564{
565 struct usb_device *usbdev = find_device(context->bus, context->address);
566 if(!usbdev) {
567 usbmuxd_log(LL_ERROR, "Device %d-%d is missing from device list, aborting initialization", context->bus, context->address);
568 return;
569 }
570 struct libusb_device *dev = context->dev;
571 struct libusb_device_descriptor devdesc = usbdev->devdesc;
572 int bus = context->bus;
573 int address = context->address;
574 int res;
575 struct libusb_transfer *transfer;
576
577 if((res = set_valid_configuration(dev, usbdev, handle)) != 0) {
578 usbdev->alive = 0;
579 return;
580 }
581
582 if((res = libusb_claim_interface(handle, usbdev->interface)) != 0) {
583 usbmuxd_log(LL_WARNING, "Could not claim interface %d for device %d-%d: %s", usbdev->interface, bus, address, libusb_error_name(res));
584 usbdev->alive = 0;
585 return;
586 }
587
588 transfer = libusb_alloc_transfer(0);
589 if(!transfer) {
590 usbmuxd_log(LL_WARNING, "Failed to allocate transfer for device %d-%d: %s", bus, address, libusb_error_name(res));
591 usbdev->alive = 0;
592 return;
593 }
594
595 unsigned char *transfer_buffer = malloc(1024 + LIBUSB_CONTROL_SETUP_SIZE + 8);
596 if (!transfer_buffer) {
597 usbmuxd_log(LL_WARNING, "Failed to allocate transfer buffer for device %d-%d: %s", bus, address, libusb_error_name(res));
598 usbdev->alive = 0;
599 return;
600 }
601 memset(transfer_buffer, '\0', 1024 + LIBUSB_CONTROL_SETUP_SIZE + 8);
602
603 usbdev->serial[0] = 0;
604 usbdev->bus = bus;
605 usbdev->address = address;
606 usbdev->devdesc = devdesc;
607 usbdev->speed = 480000000;
608 usbdev->handle = handle;
609 usbdev->alive = 1;
610 usbdev->wMaxPacketSize = libusb_get_max_packet_size(dev, usbdev->ep_out);
611 if (usbdev->wMaxPacketSize <= 0) {
612 usbmuxd_log(LL_ERROR, "Could not determine wMaxPacketSize for device %d-%d, setting to 64", usbdev->bus, usbdev->address);
613 usbdev->wMaxPacketSize = 64;
614 } else {
615 usbmuxd_log(LL_INFO, "Using wMaxPacketSize=%d for device %d-%d", usbdev->wMaxPacketSize, usbdev->bus, usbdev->address);
616 }
617
618 switch (libusb_get_device_speed(dev)) {
619 case LIBUSB_SPEED_LOW:
620 usbdev->speed = 1500000;
621 break;
622 case LIBUSB_SPEED_FULL:
623 usbdev->speed = 12000000;
624 break;
625 case LIBUSB_SPEED_SUPER:
626 usbdev->speed = 5000000000;
627 break;
628 case LIBUSB_SPEED_SUPER_PLUS:
629 usbdev->speed = 10000000000;
630 break;
631 case LIBUSB_SPEED_HIGH:
632 case LIBUSB_SPEED_UNKNOWN:
633 default:
634 usbdev->speed = 480000000;
635 break;
636 }
637
638 usbmuxd_log(LL_INFO, "USB Speed is %g MBit/s for device %d-%d", (double)(usbdev->speed / 1000000.0), usbdev->bus, usbdev->address);
639
640 /**
641 * From libusb:
642 * Asking for the zero'th index is special - it returns a string
643 * descriptor that contains all the language IDs supported by the
644 * device.
645 **/
646 libusb_fill_control_setup(transfer_buffer, LIBUSB_ENDPOINT_IN, LIBUSB_REQUEST_GET_DESCRIPTOR, LIBUSB_DT_STRING << 8, 0, 1024 + LIBUSB_CONTROL_SETUP_SIZE);
647 libusb_fill_control_transfer(transfer, handle, transfer_buffer, get_langid_callback, usbdev, 1000);
648
649 if((res = libusb_submit_transfer(transfer)) < 0) {
650 usbmuxd_log(LL_ERROR, "Could not request transfer for device %d-%d: %s", usbdev->bus, usbdev->address, libusb_error_name(res));
651 libusb_free_transfer(transfer);
652 free(transfer_buffer);
653 usbdev->alive = 0;
654 return;
655 }
656}
657
658static void switch_mode_cb(struct libusb_transfer* transfer)
659{
660 // For old devices not supporting mode swtich, if anything goes wrong - continue in current mode
661 struct mode_context* context = transfer->user_data;
662 struct usb_device *dev = find_device(context->bus, context->address);
663 if(!dev) {
664 usbmuxd_log(LL_WARNING, "Device %d-%d is missing from device list", context->bus, context->address);
665 }
666 if(transfer->status != LIBUSB_TRANSFER_COMPLETED) {
667 usbmuxd_log(LL_ERROR, "Failed to request mode switch for device %i-%i (%i). Completing initialization in current mode",
668 context->bus, context->address, transfer->status);
669 device_complete_initialization(context, transfer->dev_handle);
670 }
671 else {
672 unsigned char *data = libusb_control_transfer_get_data(transfer);
673 if(data[0] != 0) {
674 usbmuxd_log(LL_INFO, "Received unexpected response for device %i-%i mode switch (%i). Completing initialization in current mode",
675 context->bus, context->address, data[0]);
676 device_complete_initialization(context, transfer->dev_handle);
677 }
678 }
679 free(context);
680 if(transfer->buffer)
681 free(transfer->buffer);
682}
683
684static void get_mode_cb(struct libusb_transfer* transfer)
685{
686 // For old devices not supporting mode swtich, if anything goes wrong - continue in current mode
687 int res;
688 struct mode_context* context = transfer->user_data;
689 struct usb_device *dev = find_device(context->bus, context->address);
690 if(!dev) {
691 usbmuxd_log(LL_ERROR, "Device %d-%d is missing from device list, aborting mode switch", context->bus, context->address);
692 free(context);
693 return;
694 }
695
696 if(transfer->status != LIBUSB_TRANSFER_COMPLETED) {
697 usbmuxd_log(LL_ERROR, "Failed to request get mode for device %i-%i (%i). Completing initialization in current mode",
698 context->bus, context->address, transfer->status);
699 device_complete_initialization(context, transfer->dev_handle);
700 free(context);
701 return;
702 }
703
704 unsigned char *data = libusb_control_transfer_get_data(transfer);
705
706 char* desired_mode_char = getenv(ENV_DEVICE_MODE);
707 int desired_mode = desired_mode_char ? atoi(desired_mode_char) : 1;
708 int guessed_mode = guess_mode(context->dev, dev);
709
710 // Response is 3:3:3:0 for initial mode, 5:3:3:0 otherwise.
711 usbmuxd_log(LL_INFO, "Received response %i:%i:%i:%i for get_mode request for device %i-%i", data[0], data[1], data[2], data[3], context->bus, context->address);
712 if(desired_mode >= 1 && desired_mode <= 5 &&
713 guessed_mode > 0 && // do not switch mode if guess failed
714 guessed_mode != desired_mode) {
715 usbmuxd_log(LL_WARNING, "Switching device %i-%i mode to %i", context->bus, context->address, desired_mode);
716
717 context->bRequest = APPLE_VEND_SPECIFIC_SET_MODE;
718 context->wValue = 0;
719 context->wIndex = desired_mode;
720 context->wLength = 1;
721
722 if((res = submit_vendor_specific(transfer->dev_handle, context, switch_mode_cb)) != 0) {
723 usbmuxd_log(LL_WARNING, "Could not request to switch mode %i for device %i-%i (%i)", context->wIndex, context->bus, context->address, res);
724 dev->alive = 0;
725 free(context);
726 }
727 }
728 else {
729 usbmuxd_log(LL_WARNING, "Skipping switch device %i-%i mode from %i to %i", context->bus, context->address, guessed_mode, desired_mode);
730 device_complete_initialization(context, transfer->dev_handle);
731 free(context);
732 }
733 if(transfer->buffer)
734 free(transfer->buffer);
735}
736
737static int usb_device_add(libusb_device* dev)
738{
739 int res;
740 // the following are non-blocking operations on the device list
741 uint8_t bus = libusb_get_bus_number(dev);
742 uint8_t address = libusb_get_device_address(dev);
743 struct libusb_device_descriptor devdesc;
744 struct usb_device *usbdev = find_device(bus, address);
745 if(usbdev) {
746 usbdev->alive = 1;
747 return 0; //device already found
748 }
749
750 if((res = libusb_get_device_descriptor(dev, &devdesc)) != 0) {
751 usbmuxd_log(LL_WARNING, "Could not get device descriptor for device %d-%d: %s", bus, address, libusb_error_name(res));
752 return -1;
753 }
754 if(devdesc.idVendor != VID_APPLE)
755 return -1;
756 if((devdesc.idProduct != PID_APPLE_T2_COPROCESSOR) &&
757 ((devdesc.idProduct < PID_APPLE_SILICON_RESTORE_LOW) ||
758 (devdesc.idProduct > PID_APPLE_SILICON_RESTORE_MAX)) &&
759 ((devdesc.idProduct < PID_RANGE_LOW) ||
760 (devdesc.idProduct > PID_RANGE_MAX)))
761 return -1;
762 libusb_device_handle *handle;
763 usbmuxd_log(LL_INFO, "Found new device with v/p %04x:%04x at %d-%d", devdesc.idVendor, devdesc.idProduct, bus, address);
764 // No blocking operation can follow: it may be run in the libusb hotplug callback and libusb will refuse any
765 // blocking call
766 if((res = libusb_open(dev, &handle)) != 0) {
767 usbmuxd_log(LL_WARNING, "Could not open device %d-%d: %s", bus, address, libusb_error_name(res));
768 return -1;
769 }
770
771 // Add the created handle to the device list, so we can close it in case of failure/disconnection
772 usbdev = malloc(sizeof(struct usb_device));
773 memset(usbdev, 0, sizeof(*usbdev));
774
775 usbdev->serial[0] = 0;
776 usbdev->bus = bus;
777 usbdev->address = address;
778 usbdev->devdesc = devdesc;
779 usbdev->speed = 0;
780 usbdev->handle = handle;
781 usbdev->alive = 1;
782
783 collection_init(&usbdev->tx_xfers);
784 collection_init(&usbdev->rx_xfers);
785
786 collection_add(&device_list, usbdev);
787
788 // On top of configurations, Apple have multiple "modes" for devices, namely:
789 // 1: An "initial" mode with 4 configurations
790 // 2: "Valeria" mode, where configuration 5 is included with interface for H.265 video capture (activated when recording screen with QuickTime in macOS)
791 // 3: "CDC NCM" mode, where configuration 5 is included with interface for Ethernet/USB (activated using internet-sharing feature in macOS)
792 // Request current mode asynchroniously, so it can be changed in callback if needed
793 usbmuxd_log(LL_INFO, "Requesting current mode from device %i-%i", bus, address);
794 struct mode_context* context = malloc(sizeof(struct mode_context));
795 context->dev = dev;
796 context->bus = bus;
797 context->address = address;
798 context->bRequest = APPLE_VEND_SPECIFIC_GET_MODE;
799 context->wValue = 0;
800 context->wIndex = 0;
801 context->wLength = 4;
802 context->timeout = 1000;
803
804 if(submit_vendor_specific(handle, context, get_mode_cb) != 0) {
805 usbmuxd_log(LL_WARNING, "Could not request current mode from device %d-%d", bus, address);
806 // Schedule device for close and cleanup
807 usbdev->alive = 0;
808 return -1;
809 }
810 return 0;
811}
812
813int usb_discover(void)
814{
815 int cnt, i;
816 int valid_count = 0;
817 libusb_device **devs;
818
819 cnt = libusb_get_device_list(NULL, &devs);
820 if(cnt < 0) {
821 usbmuxd_log(LL_WARNING, "Could not get device list: %d", cnt);
822 devlist_failures++;
823 // sometimes libusb fails getting the device list if you've just removed something
824 if(devlist_failures > 5) {
825 usbmuxd_log(LL_FATAL, "Too many errors getting device list");
826 return cnt;
827 } else {
828 get_tick_count(&next_dev_poll_time);
829 next_dev_poll_time.tv_usec += DEVICE_POLL_TIME * 1000;
830 next_dev_poll_time.tv_sec += next_dev_poll_time.tv_usec / 1000000;
831 next_dev_poll_time.tv_usec = next_dev_poll_time.tv_usec % 1000000;
832 return 0;
833 }
834 }
835 devlist_failures = 0;
836
837 usbmuxd_log(LL_SPEW, "usb_discover: scanning %d devices", cnt);
838
839 // Mark all devices as dead, and do a mark-sweep like
840 // collection of dead devices
841 FOREACH(struct usb_device *usbdev, &device_list) {
842 usbdev->alive = 0;
843 } ENDFOREACH
844
845 // Enumerate all USB devices and mark the ones we already know
846 // about as live, again
847 for(i=0; i<cnt; i++) {
848 libusb_device *dev = devs[i];
849 if (usb_device_add(dev) < 0) {
850 continue;
851 }
852 valid_count++;
853 }
854
855 // Clean out any device we didn't mark back as live
856 reap_dead_devices();
857
858 libusb_free_device_list(devs, 1);
859
860 get_tick_count(&next_dev_poll_time);
861 next_dev_poll_time.tv_usec += DEVICE_POLL_TIME * 1000;
862 next_dev_poll_time.tv_sec += next_dev_poll_time.tv_usec / 1000000;
863 next_dev_poll_time.tv_usec = next_dev_poll_time.tv_usec % 1000000;
864
865 return valid_count;
866}
867
868const char *usb_get_serial(struct usb_device *dev)
869{
870 if(!dev->handle) {
871 return NULL;
872 }
873 return dev->serial;
874}
875
876uint32_t usb_get_location(struct usb_device *dev)
877{
878 if(!dev->handle) {
879 return 0;
880 }
881 return (dev->bus << 16) | dev->address;
882}
883
884uint16_t usb_get_pid(struct usb_device *dev)
885{
886 if(!dev->handle) {
887 return 0;
888 }
889 return dev->devdesc.idProduct;
890}
891
892uint64_t usb_get_speed(struct usb_device *dev)
893{
894 if (!dev->handle) {
895 return 0;
896 }
897 return dev->speed;
898}
899
900void usb_get_fds(struct fdlist *list)
901{
902 const struct libusb_pollfd **usbfds;
903 const struct libusb_pollfd **p;
904 usbfds = libusb_get_pollfds(NULL);
905 if(!usbfds) {
906 usbmuxd_log(LL_ERROR, "libusb_get_pollfds failed");
907 return;
908 }
909 p = usbfds;
910 while(*p) {
911 fdlist_add(list, FD_USB, (*p)->fd, (*p)->events);
912 p++;
913 }
914 free(usbfds);
915}
916
917void usb_autodiscover(int enable)
918{
919 usbmuxd_log(LL_DEBUG, "usb polling enable: %d", enable);
920 device_polling = enable;
921 device_hotplug = enable;
922}
923
924static int dev_poll_remain_ms(void)
925{
926 int msecs;
927 struct timeval tv;
928 if(!device_polling)
929 return 100000; // devices will never be polled if this is > 0
930 get_tick_count(&tv);
931 msecs = (next_dev_poll_time.tv_sec - tv.tv_sec) * 1000;
932 msecs += (next_dev_poll_time.tv_usec - tv.tv_usec) / 1000;
933 if(msecs < 0)
934 return 0;
935 return msecs;
936}
937
938int usb_get_timeout(void)
939{
940 struct timeval tv;
941 int msec;
942 int res;
943 int pollrem;
944 pollrem = dev_poll_remain_ms();
945 res = libusb_get_next_timeout(NULL, &tv);
946 if(res == 0)
947 return pollrem;
948 if(res < 0) {
949 usbmuxd_log(LL_ERROR, "libusb_get_next_timeout failed: %s", libusb_error_name(res));
950 return pollrem;
951 }
952 msec = tv.tv_sec * 1000;
953 msec += tv.tv_usec / 1000;
954 if(msec > pollrem)
955 return pollrem;
956 return msec;
957}
958
959int usb_process(void)
960{
961 int res;
962 struct timeval tv;
963 tv.tv_sec = tv.tv_usec = 0;
964 res = libusb_handle_events_timeout(NULL, &tv);
965 if(res < 0) {
966 usbmuxd_log(LL_ERROR, "libusb_handle_events_timeout failed: %s", libusb_error_name(res));
967 return res;
968 }
969
970 // reap devices marked dead due to an RX error
971 reap_dead_devices();
972
973 if(dev_poll_remain_ms() <= 0) {
974 res = usb_discover();
975 if(res < 0) {
976 usbmuxd_log(LL_ERROR, "usb_discover failed: %s", libusb_error_name(res));
977 return res;
978 }
979 }
980 return 0;
981}
982
983int usb_process_timeout(int msec)
984{
985 int res;
986 struct timeval tleft, tcur, tfin;
987 get_tick_count(&tcur);
988 tfin.tv_sec = tcur.tv_sec + (msec / 1000);
989 tfin.tv_usec = tcur.tv_usec + (msec % 1000) * 1000;
990 tfin.tv_sec += tfin.tv_usec / 1000000;
991 tfin.tv_usec %= 1000000;
992 while((tfin.tv_sec > tcur.tv_sec) || ((tfin.tv_sec == tcur.tv_sec) && (tfin.tv_usec > tcur.tv_usec))) {
993 tleft.tv_sec = tfin.tv_sec - tcur.tv_sec;
994 tleft.tv_usec = tfin.tv_usec - tcur.tv_usec;
995 if(tleft.tv_usec < 0) {
996 tleft.tv_usec += 1000000;
997 tleft.tv_sec -= 1;
998 }
999 res = libusb_handle_events_timeout(NULL, &tleft);
1000 if(res < 0) {
1001 usbmuxd_log(LL_ERROR, "libusb_handle_events_timeout failed: %s", libusb_error_name(res));
1002 return res;
1003 }
1004 // reap devices marked dead due to an RX error
1005 reap_dead_devices();
1006 get_tick_count(&tcur);
1007 }
1008 return 0;
1009}
1010
1011#ifdef HAVE_LIBUSB_HOTPLUG_API
1012static libusb_hotplug_callback_handle usb_hotplug_cb_handle;
1013
1014static int usb_hotplug_cb(libusb_context *ctx, libusb_device *device, libusb_hotplug_event event, void *user_data)
1015{
1016 if (LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED == event) {
1017 if (device_hotplug) {
1018 usb_device_add(device);
1019 }
1020 } else if (LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT == event) {
1021 uint8_t bus = libusb_get_bus_number(device);
1022 uint8_t address = libusb_get_device_address(device);
1023 FOREACH(struct usb_device *usbdev, &device_list) {
1024 if(usbdev->bus == bus && usbdev->address == address) {
1025 usbdev->alive = 0;
1026 device_remove(usbdev);
1027 break;
1028 }
1029 } ENDFOREACH
1030 } else {
1031 usbmuxd_log(LL_ERROR, "Unhandled event %d", event);
1032 }
1033 return 0;
1034}
1035#endif
1036
1037int usb_init(void)
1038{
1039 int res;
1040 const struct libusb_version* libusb_version_info = libusb_get_version();
1041 usbmuxd_log(LL_NOTICE, "Using libusb %d.%d.%d", libusb_version_info->major, libusb_version_info->minor, libusb_version_info->micro);
1042
1043 devlist_failures = 0;
1044 device_polling = 1;
1045 res = libusb_init(NULL);
1046
1047 if (res != 0) {
1048 usbmuxd_log(LL_FATAL, "libusb_init failed: %s", libusb_error_name(res));
1049 return -1;
1050 }
1051
1052#if LIBUSB_API_VERSION >= 0x01000106
1053 libusb_set_option(NULL, LIBUSB_OPTION_LOG_LEVEL, (log_level >= LL_DEBUG ? LIBUSB_LOG_LEVEL_DEBUG: (log_level >= LL_WARNING ? LIBUSB_LOG_LEVEL_WARNING: LIBUSB_LOG_LEVEL_NONE)));
1054#else
1055 libusb_set_debug(NULL, (log_level >= LL_DEBUG ? LIBUSB_LOG_LEVEL_DEBUG: (log_level >= LL_WARNING ? LIBUSB_LOG_LEVEL_WARNING: LIBUSB_LOG_LEVEL_NONE)));
1056#endif
1057
1058 collection_init(&device_list);
1059
1060#ifdef HAVE_LIBUSB_HOTPLUG_API
1061 if (libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG)) {
1062 usbmuxd_log(LL_INFO, "Registering for libusb hotplug events");
1063 res = libusb_hotplug_register_callback(NULL, LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED | LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT, LIBUSB_HOTPLUG_ENUMERATE, VID_APPLE, LIBUSB_HOTPLUG_MATCH_ANY, 0, usb_hotplug_cb, NULL, &usb_hotplug_cb_handle);
1064 if (res == LIBUSB_SUCCESS) {
1065 device_polling = 0;
1066 } else {
1067 usbmuxd_log(LL_ERROR, "ERROR: Could not register for libusb hotplug events. %s", libusb_error_name(res));
1068 }
1069 } else {
1070 usbmuxd_log(LL_ERROR, "libusb does not support hotplug events");
1071 }
1072#endif
1073 if (device_polling) {
1074 res = usb_discover();
1075 if (res >= 0) {
1076 }
1077 } else {
1078 res = collection_count(&device_list);
1079 }
1080 return res;
1081}
1082
1083void usb_shutdown(void)
1084{
1085 usbmuxd_log(LL_DEBUG, "usb_shutdown");
1086
1087#ifdef HAVE_LIBUSB_HOTPLUG_API
1088 libusb_hotplug_deregister_callback(NULL, usb_hotplug_cb_handle);
1089#endif
1090
1091 FOREACH(struct usb_device *usbdev, &device_list) {
1092 device_remove(usbdev);
1093 usb_disconnect(usbdev);
1094 } ENDFOREACH
1095 collection_free(&device_list);
1096 libusb_exit(NULL);
1097}
diff --git a/src/usb.h b/src/usb.h
new file mode 100644
index 0000000..4e44cce
--- /dev/null
+++ b/src/usb.h
@@ -0,0 +1,73 @@
1/*
2 * usb.h
3 *
4 * Copyright (C) 2009 Hector Martin <hector@marcansoft.com>
5 * Copyright (C) 2009 Nikias Bassen <nikias@gmx.li>
6 * Copyright (C) 2009 Martin Szulecki <opensuse@sukimashita.com>
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 or version 3.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 */
21
22#ifndef USB_H
23#define USB_H
24
25#include <stdint.h>
26#include "utils.h"
27
28#define INTERFACE_CLASS 255
29#define INTERFACE_SUBCLASS 254
30#define INTERFACE_PROTOCOL 2
31
32// libusb fragments packets larger than this (usbfs limitation)
33// on input, this creates race conditions and other issues
34#define USB_MRU 16384
35
36// max transmission packet size
37// libusb fragments these too, but doesn't send ZLPs so we're safe
38// but we need to send a ZLP ourselves at the end (see usb-linux.c)
39// we're using 3 * 16384 to optimize for the fragmentation
40// this results in three URBs per full transfer, 32 USB packets each
41// if there are ZLP issues this should make them show up easily too
42#define USB_MTU (3 * 16384)
43
44#define USB_PACKET_SIZE 512
45
46#define VID_APPLE 0x5ac
47#define PID_RANGE_LOW 0x1290
48#define PID_RANGE_MAX 0x12af
49#define PID_APPLE_T2_COPROCESSOR 0x8600
50#define PID_APPLE_SILICON_RESTORE_LOW 0x1901
51#define PID_APPLE_SILICON_RESTORE_MAX 0x1905
52
53#define ENV_DEVICE_MODE "USBMUXD_DEFAULT_DEVICE_MODE"
54#define APPLE_VEND_SPECIFIC_GET_MODE 0x45
55#define APPLE_VEND_SPECIFIC_SET_MODE 0x52
56
57struct usb_device;
58
59int usb_init(void);
60void usb_shutdown(void);
61const char *usb_get_serial(struct usb_device *dev);
62uint32_t usb_get_location(struct usb_device *dev);
63uint16_t usb_get_pid(struct usb_device *dev);
64uint64_t usb_get_speed(struct usb_device *dev);
65void usb_get_fds(struct fdlist *list);
66int usb_get_timeout(void);
67int usb_send(struct usb_device *dev, const unsigned char *buf, int length);
68int usb_discover(void);
69void usb_autodiscover(int enable);
70int usb_process(void);
71int usb_process_timeout(int msec);
72
73#endif
diff --git a/libusbmuxd/usbmuxd-proto.h b/src/usbmuxd-proto.h
index be9e709..93df00e 100644
--- a/libusbmuxd/usbmuxd-proto.h
+++ b/src/usbmuxd-proto.h
@@ -1,29 +1,27 @@
1/* 1/*
2 libusbmuxd - client library to talk to usbmuxd 2 * usbmuxd-proto.h
3 3 *
4Copyright (C) 2009 Paul Sladen <libiphone@paul.sladen.org> 4 * Copyright (C) 2009 Paul Sladen <libiphone@paul.sladen.org>
5Copyright (C) 2009 Nikias Bassen <nikias@gmx.li> 5 * Copyright (C) 2009 Nikias Bassen <nikias@gmx.li>
6Copyright (C) 2009 Hector Martin "marcan" <hector@marcansoft.com> 6 * Copyright (C) 2009 Hector Martin <hector@marcansoft.com>
7 7 *
8This library is free software; you can redistribute it and/or modify 8 * This program is free software; you can redistribute it and/or modify
9it under the terms of the GNU Lesser General Public License as 9 * it under the terms of the GNU General Public License as published by
10published by the Free Software Foundation, either version 2.1 of the 10 * the Free Software Foundation, either version 2 or version 3.
11License, or (at your option) any later version. 11 *
12 12 * This program is distributed in the hope that it will be useful,
13This library is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 * GNU General Public License for more details.
16GNU General Public License for more details. 16 *
17 17 * You should have received a copy of the GNU General Public License
18You should have received a copy of the GNU Lesser General Public 18 * along with this program; if not, write to the Free Software
19License along with this program; if not, write to the Free Software 19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 20 */
21 21
22*/ 22/* Protocol definition for usbmuxd proxy protocol */
23 23#ifndef USBMUXD_PROTO_H
24/* Protocol defintion for usbmuxd proxy protocol */ 24#define USBMUXD_PROTO_H
25#ifndef __USBMUXD_PROTO_H
26#define __USBMUXD_PROTO_H
27 25
28#include <stdint.h> 26#include <stdint.h>
29#define USBMUXD_PROTOCOL_VERSION 0 27#define USBMUXD_PROTOCOL_VERSION 0
@@ -54,7 +52,7 @@ enum usbmuxd_msgtype {
54 MESSAGE_LISTEN = 3, 52 MESSAGE_LISTEN = 3,
55 MESSAGE_DEVICE_ADD = 4, 53 MESSAGE_DEVICE_ADD = 4,
56 MESSAGE_DEVICE_REMOVE = 5, 54 MESSAGE_DEVICE_REMOVE = 5,
57 //??? 55 MESSAGE_DEVICE_PAIRED = 6,
58 //??? 56 //???
59 MESSAGE_PLIST = 8, 57 MESSAGE_PLIST = 8,
60}; 58};
@@ -94,4 +92,4 @@ struct usbmuxd_device_record {
94} 92}
95#endif 93#endif
96 94
97#endif /* __USBMUXD_PROTO_H */ 95#endif /* USBMUXD_PROTO_H */
diff --git a/src/utils.c b/src/utils.c
new file mode 100644
index 0000000..2cc5675
--- /dev/null
+++ b/src/utils.c
@@ -0,0 +1,131 @@
1/*
2 * utils.c
3 *
4 * Copyright (C) 2009 Hector Martin <hector@marcansoft.com>
5 * Copyright (C) 2009 Nikias Bassen <nikias@gmx.li>
6 * Copyright (c) 2013 Federico Mena Quintero
7 *
8 * This library is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU Lesser General Public License as
10 * published by the Free Software Foundation, either version 2.1 of the
11 * License, or (at your option) any later version.
12 *
13 * This library 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 Lesser General Public
19 * License 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
23#ifdef HAVE_CONFIG_H
24#include <config.h>
25#endif
26
27#include <stdlib.h>
28#include <string.h>
29#include <stdio.h>
30#include <stdarg.h>
31#include <time.h>
32#include <sys/time.h>
33#include <errno.h>
34#ifdef __APPLE__
35#include <mach/mach_time.h>
36#endif
37
38#include "utils.h"
39
40#include "log.h"
41#define util_error(...) usbmuxd_log(LL_ERROR, __VA_ARGS__)
42
43void fdlist_create(struct fdlist *list)
44{
45 list->count = 0;
46 list->capacity = 4;
47 list->owners = malloc(sizeof(*list->owners) * list->capacity);
48 list->fds = malloc(sizeof(*list->fds) * list->capacity);
49}
50void fdlist_add(struct fdlist *list, enum fdowner owner, int fd, short events)
51{
52 if(list->count == list->capacity) {
53 list->capacity *= 2;
54 list->owners = realloc(list->owners, sizeof(*list->owners) * list->capacity);
55 list->fds = realloc(list->fds, sizeof(*list->fds) * list->capacity);
56 }
57 list->owners[list->count] = owner;
58 list->fds[list->count].fd = fd;
59 list->fds[list->count].events = events;
60 list->fds[list->count].revents = 0;
61 list->count++;
62}
63
64void fdlist_free(struct fdlist *list)
65{
66 list->count = 0;
67 list->capacity = 0;
68 free(list->owners);
69 list->owners = NULL;
70 free(list->fds);
71 list->fds = NULL;
72}
73
74void fdlist_reset(struct fdlist *list)
75{
76 list->count = 0;
77}
78
79#ifndef HAVE_CLOCK_GETTIME
80typedef int clockid_t;
81#define CLOCK_MONOTONIC 1
82
83static int clock_gettime(clockid_t clk_id, struct timespec *ts)
84{
85 // See http://developer.apple.com/library/mac/qa/qa1398
86
87 uint64_t mach_time, nano_sec;
88
89 static mach_timebase_info_data_t base_info;
90
91 mach_time = mach_absolute_time();
92
93 if (base_info.denom == 0) {
94 (void) mach_timebase_info(&base_info);
95 }
96
97 if (base_info.numer == 1 && base_info.denom == 1)
98 nano_sec = mach_time;
99 else
100 nano_sec = mach_time * base_info.numer / base_info.denom;
101
102 ts->tv_sec = nano_sec / 1000000000;
103 ts->tv_nsec = nano_sec % 1000000000;
104
105 return 0;
106}
107#endif
108
109void get_tick_count(struct timeval * tv)
110{
111 struct timespec ts;
112 if(0 == clock_gettime(CLOCK_MONOTONIC, &ts)) {
113 tv->tv_sec = ts.tv_sec;
114 tv->tv_usec = ts.tv_nsec / 1000;
115 } else {
116 gettimeofday(tv, NULL);
117 }
118}
119
120/**
121 * Get number of milliseconds since the epoch.
122 */
123uint64_t mstime64(void)
124{
125 struct timeval tv;
126 get_tick_count(&tv);
127
128 // Careful, avoid overflow on 32 bit systems
129 // time_t could be 4 bytes
130 return ((long long)tv.tv_sec) * 1000LL + ((long long)tv.tv_usec) / 1000LL;
131}
diff --git a/src/utils.h b/src/utils.h
new file mode 100644
index 0000000..ce3b2e0
--- /dev/null
+++ b/src/utils.h
@@ -0,0 +1,49 @@
1/*
2 * utils.h
3 *
4 * Copyright (C) 2009 Hector Martin <hector@marcansoft.com>
5 * Copyright (C) 2009 Nikias Bassen <nikias@gmx.li>
6 *
7 * This library is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU Lesser General Public License as
9 * published by the Free Software Foundation, either version 2.1 of the
10 * License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 */
21
22#ifndef UTILS_H
23#define UTILS_H
24
25#include <poll.h>
26#include <plist/plist.h>
27
28enum fdowner {
29 FD_LISTEN,
30 FD_CLIENT,
31 FD_USB
32};
33
34struct fdlist {
35 int count;
36 int capacity;
37 enum fdowner *owners;
38 struct pollfd *fds;
39};
40
41void fdlist_create(struct fdlist *list);
42void fdlist_add(struct fdlist *list, enum fdowner owner, int fd, short events);
43void fdlist_free(struct fdlist *list);
44void fdlist_reset(struct fdlist *list);
45
46uint64_t mstime64(void);
47void get_tick_count(struct timeval * tv);
48
49#endif
diff --git a/stuff/README b/stuff/README
deleted file mode 100644
index 290285d..0000000
--- a/stuff/README
+++ /dev/null
@@ -1,25 +0,0 @@
1*** NOTE:
2*** Doing this is mostly obsolete. The preferred method to sync music to an
3*** iPhone now is to use ifuse or some other afc-based client, which does not
4*** require jailbreaking or adding this service. Please take a look at the
5*** libiphone, ifuse, and libgpod projects.
6
7com.openssh.sft.plist is a launchd configuration to set up a bare SFTP server
8on TCP port 2299 localhost-only for USB use. It's nice for relatively fast music
9syncing and file transfer under Linux (and it avoids encryption). Con: it gives
10anyone with usb access root FS access on the phone, as well as anything running
11on the phone itself.
12
13Use it with a command like this:
14
15IPATH=/var/mobile/Media
16MOUNTPOINT=$HOME/media/iphone
17$ sshfs localhost:$IPATH $MOUNTPOINT -o workaround=rename -o directport=2299 \
18 -o kernel_cache -o entry_timeout=30 -o attr_timeout=30
19
20Make sure you run tcprelay.py:
21$ python tcprelay.py -t 2299
22
23Remember that to bypass the stupid new iTunesDB hash you need to edit
24/System/Library/Lockdown/Checkpoint.xml and change DBVersion to 2.
25
diff --git a/stuff/com.openssh.sftp.plist b/stuff/com.openssh.sftp.plist
deleted file mode 100644
index 569fabc..0000000
--- a/stuff/com.openssh.sftp.plist
+++ /dev/null
@@ -1,41 +0,0 @@
1<?xml version="1.0" encoding="UTF-8"?>
2<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3<plist version="1.0">
4
5<dict>
6 <key>Label</key>
7 <string>com.openssh.sftpd</string>
8
9 <key>Program</key>
10 <string>/usr/libexec/sftp-server</string>
11
12 <key>ProgramArguments</key>
13 <array>
14 <string>/usr/libexec/sftp-server</string>
15 </array>
16
17 <key>SessionCreate</key>
18 <true/>
19
20 <key>Sockets</key>
21 <dict>
22 <key>Listeners</key>
23 <dict>
24 <key>SockServiceName</key>
25 <string>2299</string>
26 <key>SockNodeName</key>
27 <string>127.0.0.1</string>
28 </dict>
29 </dict>
30
31 <key>StandardErrorPath</key>
32 <string>/dev/null</string>
33
34 <key>inetdCompatibility</key>
35 <dict>
36 <key>Wait</key>
37 <false/>
38 </dict>
39</dict>
40
41</plist>
diff --git a/systemd/Makefile.am b/systemd/Makefile.am
new file mode 100644
index 0000000..1d40c25
--- /dev/null
+++ b/systemd/Makefile.am
@@ -0,0 +1,21 @@
1edit = \
2 $(SED) -r \
3 -e 's|@sbindir[@]|$(sbindir)|g' \
4 -e 's|@runstatedir[@]|$(runstatedir)|g' \
5 < $< > $@ || rm $@
6
7if WANT_SYSTEMD
8
9systemdsystemunit_DATA = \
10 usbmuxd.service
11
12usbmuxd.service: usbmuxd.service.in
13 $(edit)
14
15EXTRA_DIST = \
16 usbmuxd.service.in
17
18CLEANFILES = \
19 usbmuxd.service
20
21endif
diff --git a/systemd/usbmuxd.service.in b/systemd/usbmuxd.service.in
new file mode 100644
index 0000000..3a27aee
--- /dev/null
+++ b/systemd/usbmuxd.service.in
@@ -0,0 +1,7 @@
1[Unit]
2Description=Socket daemon for the usbmux protocol used by Apple devices
3Documentation=man:usbmuxd(8)
4
5[Service]
6ExecStart=@sbindir@/usbmuxd --user usbmux --systemd
7PIDFile=@runstatedir@/usbmuxd.pid
diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt
deleted file mode 100644
index 64d0d0e..0000000
--- a/tools/CMakeLists.txt
+++ /dev/null
@@ -1,11 +0,0 @@
1include_directories (${CMAKE_SOURCE_DIR}/libusbmuxd)
2link_directories (${CMAKE_BINARY_DIR}/libusbmuxd)
3
4add_executable(iproxy iproxy.c)
5if(WIN32)
6 target_link_libraries(iproxy libusbmuxd pthread ws2_32)
7else()
8 target_link_libraries(iproxy libusbmuxd pthread)
9endif()
10
11install(TARGETS iproxy RUNTIME DESTINATION bin)
diff --git a/tools/iproxy.c b/tools/iproxy.c
deleted file mode 100644
index 4469c48..0000000
--- a/tools/iproxy.c
+++ /dev/null
@@ -1,251 +0,0 @@
1/*
2 iproxy -- proxy that enables tcp service access to iPhone/iPod
3
4Copyright (C) 2009 Nikias Bassen <nikias@gmx.li>
5Copyright (C) 2009 Paul Sladen <libiphone@paul.sladen.org>
6
7Based upon iTunnel source code, Copyright (c) 2008 Jing Su.
8http://www.cs.toronto.edu/~jingsu/itunnel/
9
10This program is free software; you can redistribute it and/or modify
11it under the terms of the GNU General Public License as published by
12the Free Software Foundation; either version 2 of the License, or
13(at your option) any later version.
14
15This program is distributed in the hope that it will be useful,
16but WITHOUT ANY WARRANTY; without even the implied warranty of
17MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18GNU General Public License for more details.
19
20You should have received a copy of the GNU General Public License
21along with this program; if not, write to the Free Software
22Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
23
24TODO: improve code...
25
26*/
27
28#include <stdio.h>
29#include <stdlib.h>
30#include <string.h>
31#include <fcntl.h>
32#include <stddef.h>
33#include <unistd.h>
34#include <errno.h>
35#ifdef WIN32
36#include <windows.h>
37#include <winsock2.h>
38typedef unsigned int socklen_t;
39#else
40#include <sys/socket.h>
41#include <sys/un.h>
42#include <arpa/inet.h>
43#endif
44#include <pthread.h>
45#include <netinet/in.h>
46#include "sock_stuff.h"
47#include "usbmuxd.h"
48
49static uint16_t listen_port = 0;
50static uint16_t device_port = 0;
51
52struct client_data {
53 int fd;
54 int sfd;
55 volatile int stop_ctos;
56 volatile int stop_stoc;
57};
58
59void *run_stoc_loop(void *arg)
60{
61 struct client_data *cdata = (struct client_data*)arg;
62 int recv_len;
63 int sent;
64 char buffer[131072];
65
66 printf("%s: fd = %d\n", __func__, cdata->fd);
67
68 while (!cdata->stop_stoc && cdata->fd>0 && cdata->sfd>0) {
69 recv_len = recv_buf_timeout(cdata->sfd, buffer, sizeof(buffer), 0, 5000);
70 if (recv_len <= 0) {
71 if (recv_len == 0) {
72 // try again
73 continue;
74 } else {
75 fprintf(stderr, "recv failed: %s\n", strerror(errno));
76 break;
77 }
78 } else {
79// printf("received %d bytes from server\n", recv_len);
80 // send to socket
81 sent = send_buf(cdata->fd, buffer, recv_len);
82 if (sent < recv_len) {
83 if (sent <= 0) {
84 fprintf(stderr, "send failed: %s\n", strerror(errno));
85 break;
86 } else {
87 fprintf(stderr, "only sent %d from %d bytes\n", sent, recv_len);
88 }
89 } else {
90 // sending succeeded, receive from device
91// printf("pushed %d bytes to client\n", sent);
92 }
93 }
94 }
95 close(cdata->fd);
96 cdata->fd = -1;
97 cdata->stop_ctos = 1;
98
99 return NULL;
100}
101
102void *run_ctos_loop(void *arg)
103{
104 struct client_data *cdata = (struct client_data*)arg;
105 int recv_len;
106 int sent;
107 char buffer[131072];
108 pthread_t stoc;
109
110 printf("%s: fd = %d\n", __func__, cdata->fd);
111
112 cdata->stop_stoc = 0;
113 pthread_create(&stoc, NULL, run_stoc_loop, cdata);
114
115 while (!cdata->stop_ctos && cdata->fd>0 && cdata->sfd>0) {
116 recv_len = recv_buf_timeout(cdata->fd, buffer, sizeof(buffer), 0, 5000);
117 if (recv_len <= 0) {
118 if (recv_len == 0) {
119 // try again
120 continue;
121 } else {
122 fprintf(stderr, "recv failed: %s\n", strerror(errno));
123 break;
124 }
125 } else {
126// printf("pulled %d bytes from client\n", recv_len);
127 // send to local socket
128 sent = send_buf(cdata->sfd, buffer, recv_len);
129 if (sent < recv_len) {
130 if (sent <= 0) {
131 fprintf(stderr, "send failed: %s\n", strerror(errno));
132 break;
133 } else {
134 fprintf(stderr, "only sent %d from %d bytes\n", sent, recv_len);
135 }
136 } else {
137 // sending succeeded, receive from device
138// printf("sent %d bytes to server\n", sent);
139 }
140 }
141 }
142 close(cdata->fd);
143 cdata->fd = -1;
144 cdata->stop_stoc = 1;
145
146 pthread_join(stoc, NULL);
147
148 return NULL;
149}
150
151void *acceptor_thread(void *arg)
152{
153 struct client_data *cdata;
154 usbmuxd_device_info_t *dev_list = NULL;
155 pthread_t ctos;
156 int count;
157
158 if (!arg) {
159 fprintf(stderr, "invalid client_data provided!\n");
160 return NULL;
161 }
162
163 cdata = (struct client_data*)arg;
164
165 if ((count = usbmuxd_get_device_list(&dev_list)) < 0) {
166 printf("Connecting to usbmuxd failed, terminating.\n");
167 free(dev_list);
168 return NULL;
169 }
170
171 fprintf(stdout, "Number of available devices == %d\n", count);
172
173 if (dev_list == NULL || dev_list[0].handle == 0) {
174 printf("No connected device found, terminating.\n");
175 free(dev_list);
176 return NULL;
177 }
178
179 fprintf(stdout, "Requesting connecion to device handle == %d (serial: %s), port %d\n", dev_list[0].handle, dev_list[0].uuid, device_port);
180
181 cdata->sfd = usbmuxd_connect(dev_list[0].handle, device_port);
182 free(dev_list);
183 if (cdata->sfd < 0) {
184 fprintf(stderr, "Error connecting to device!\n");
185 } else {
186 cdata->stop_ctos = 0;
187 pthread_create(&ctos, NULL, run_ctos_loop, cdata);
188 pthread_join(ctos, NULL);
189 }
190
191 if (cdata->fd > 0) {
192 close(cdata->fd);
193 }
194 if (cdata->sfd > 0) {
195 close(cdata->sfd);
196 }
197
198 return NULL;
199}
200
201int main(int argc, char **argv)
202{
203 int mysock = -1;
204
205 if (argc != 3) {
206 printf("usage: %s LOCAL_TCP_PORT DEVICE_TCP_PORT\n", argv[0]);
207 return 0;
208 }
209
210 listen_port = atoi(argv[1]);
211 device_port = atoi(argv[2]);
212
213 if (!listen_port) {
214 fprintf(stderr, "Invalid listen_port specified!\n");
215 return -EINVAL;
216 }
217
218 if (!device_port) {
219 fprintf(stderr, "Invalid device_port specified!\n");
220 return -EINVAL;
221 }
222
223 // first create the listening socket endpoint waiting for connections.
224 mysock = create_socket(listen_port);
225 if (mysock < 0) {
226 fprintf(stderr, "Error creating socket: %s\n", strerror(errno));
227 return -errno;
228 } else {
229 pthread_t acceptor;
230 struct sockaddr_in c_addr;
231 socklen_t len = sizeof(struct sockaddr_in);
232 struct client_data cdata;
233 int c_sock;
234 while (1) {
235 printf("waiting for connection\n");
236 c_sock = accept(mysock, (struct sockaddr*)&c_addr, &len);
237 if (c_sock) {
238 printf("accepted connection, fd = %d\n", c_sock);
239 cdata.fd = c_sock;
240 pthread_create(&acceptor, NULL, acceptor_thread, &cdata);
241 pthread_join(acceptor, NULL);
242 } else {
243 break;
244 }
245 }
246 close(c_sock);
247 close(mysock);
248 }
249
250 return 0;
251}
diff --git a/udev/39-usbmuxd.rules.in b/udev/39-usbmuxd.rules.in
new file mode 100644
index 0000000..ac15593
--- /dev/null
+++ b/udev/39-usbmuxd.rules.in
@@ -0,0 +1,16 @@
1# usbmuxd (Apple Mobile Device Muxer listening on /var/run/usbmuxd)
2
3# systemd should receive all events relating to device
4SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", ENV{PRODUCT}=="5ac/12[9a][0-9a-f]/*|5ac/190[1-5]/*|5ac/8600/*", TAG+="systemd"
5
6# Initialize iOS devices into "deactivated" USB configuration state and activate usbmuxd
7SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", ENV{PRODUCT}=="5ac/12[9a][0-9a-f]/*|5ac/190[1-5]/*", ACTION=="add", ENV{USBMUX_SUPPORTED}="1", ATTR{bConfigurationValue}="0", OWNER="usbmux", @udev_activation_rule@
8# but make sure iBridge (T1) doesn't end up in an unconfigured state
9SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", ENV{PRODUCT}=="5ac/8600/*", ACTION=="add", ENV{USBMUX_SUPPORTED}="1", ATTR{bConfigurationValue}="1", OWNER="usbmux", @udev_activation_rule@
10
11
12# Make sure properties don't get lost when bind action is called
13SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", ENV{PRODUCT}=="5ac/12[9a][0-9a-f]/*|5ac/190[1-5]/*|5ac/8600/*", ACTION=="bind", ENV{USBMUX_SUPPORTED}="1", OWNER="usbmux"
14
15# Exit usbmuxd when the last device is removed
16SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", ENV{PRODUCT}=="5ac/12[9a][0-9a-f]/*|5ac/190[1-5]/*|5ac/8600/*", ACTION=="remove", RUN+="@sbindir@/usbmuxd -x"
diff --git a/udev/85-usbmuxd.rules.in b/udev/85-usbmuxd.rules.in
deleted file mode 100644
index 3856c79..0000000
--- a/udev/85-usbmuxd.rules.in
+++ /dev/null
@@ -1,7 +0,0 @@
1# usbmuxd (iPhone "Apple Mobile Device" MUXer listening on /var/run/usbmuxd)
2
3# Forces iPhone 1.0, 3G, 3GS, iPodTouch 1/2 and iPad to USB configuration 3 and run usbmuxd
4ACTION=="add", SUBSYSTEM=="usb", ATTR{idVendor}=="05ac", ATTR{idProduct}=="129[0-9a-f]", ENV{USBMUX_SUPPORTED}="1", ATTR{bConfigurationValue}!="$attr{bNumConfigurations}", ATTR{bConfigurationValue}="$attr{bNumConfigurations}", RUN+="@CMAKE_INSTALL_PREFIX@/sbin/usbmuxd -u -U usbmux"
5
6# Exit usbmuxd when the last device is removed
7ACTION=="remove", SUBSYSTEM=="usb", ENV{PRODUCT}=="5ac/129[0-9a-f]/*", ENV{INTERFACE}=="255/*", RUN+="@CMAKE_INSTALL_PREFIX@/sbin/usbmuxd -x"
diff --git a/udev/CMakeLists.txt b/udev/CMakeLists.txt
deleted file mode 100644
index 0f7042d..0000000
--- a/udev/CMakeLists.txt
+++ /dev/null
@@ -1,2 +0,0 @@
1CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/85-usbmuxd.rules.in ${CMAKE_CURRENT_BINARY_DIR}/85-usbmuxd.rules @ONLY)
2install(FILES ${CMAKE_CURRENT_BINARY_DIR}/85-usbmuxd.rules DESTINATION /lib/udev/rules.d/)
diff --git a/udev/Makefile.am b/udev/Makefile.am
new file mode 100644
index 0000000..95f565d
--- /dev/null
+++ b/udev/Makefile.am
@@ -0,0 +1,20 @@
1edit = \
2 $(SED) -r \
3 -e 's|@udev_activation_rule[@]|$(udev_activation_rule)|g' \
4 -e 's|@sbindir[@]|$(sbindir)|g' \
5 < $< > $@ || rm $@
6
7udevrules_DATA = \
8 39-usbmuxd.rules
9
1039-usbmuxd.rules: 39-usbmuxd.rules.in
11 $(edit)
12
13EXTRA_DIST = \
14 39-usbmuxd.rules.in
15
16MAINTAINERCLEANFILES = \
17 39-usbmuxd.rules
18
19CLEANFILES = \
20 39-usbmuxd.rules