summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.github/workflows/build.yml73
-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.c1058
-rw-r--r--src/client.h47
-rw-r--r--src/conf.c535
-rw-r--r--src/conf.h40
-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.c920
-rw-r--r--src/preflight.c406
-rw-r--r--src/preflight.h28
-rw-r--r--src/usb.c1084
-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
69 files changed, 5748 insertions, 5679 deletions
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
new file mode 100644
index 0000000..f8c3f94
--- /dev/null
+++ b/.github/workflows/build.yml
@@ -0,0 +1,73 @@
1name: build
2
3on: [push]
4
5jobs:
6 build-linux-ubuntu:
7 runs-on: ubuntu-latest
8 steps:
9 - name: install dependencies
10 run: |
11 sudo apt-get update
12 sudo apt-get install libusb-1.0-0-dev
13 - name: prepare environment
14 run: |
15 echo "target_triplet=`gcc -dumpmachine`" >> $GITHUB_ENV
16 - name: fetch libplist
17 uses: dawidd6/action-download-artifact@v2
18 with:
19 github_token: ${{secrets.GITHUB_TOKEN}}
20 workflow: build.yml
21 name: libplist-latest_${{env.target_triplet}}
22 repo: libimobiledevice/libplist
23 - name: fetch libusbmuxd
24 uses: dawidd6/action-download-artifact@v2
25 with:
26 github_token: ${{secrets.GITHUB_TOKEN}}
27 workflow: build.yml
28 name: libusbmuxd-latest_${{env.target_triplet}}
29 repo: libimobiledevice/libusbmuxd
30 - name: fetch libimobiledevice-glue
31 uses: dawidd6/action-download-artifact@v2
32 with:
33 github_token: ${{secrets.GITHUB_TOKEN}}
34 workflow: build.yml
35 name: libimobiledevice-glue-latest_${{env.target_triplet}}
36 repo: libimobiledevice/libimobiledevice-glue
37 - name: fetch libimobiledevice
38 uses: dawidd6/action-download-artifact@v2
39 with:
40 github_token: ${{secrets.GITHUB_TOKEN}}
41 workflow: build.yml
42 name: libimobiledevice-latest_${{env.target_triplet}}
43 repo: libimobiledevice/libimobiledevice
44 - name: install external dependencies
45 run: |
46 mkdir extract
47 for I in *.tar; do
48 tar -C extract -xvf $I
49 done
50 sudo cp -r extract/* /
51 sudo ldconfig
52 - uses: actions/checkout@v3
53 with:
54 fetch-depth: 0
55 - name: autogen
56 run: ./autogen.sh PKG_CONFIG_PATH=/usr/local/lib/pkgconfig
57 - name: print config.log
58 if: ${{ failure() }}
59 run: cat config.log
60 - name: make
61 run: make
62 - name: make install
63 run: sudo make install
64 - name: prepare artifact
65 run: |
66 mkdir -p dest
67 DESTDIR=`pwd`/dest make install
68 tar -C dest -cf usbmuxd.tar usr lib
69 - name: publish artifact
70 uses: actions/upload-artifact@v3
71 with:
72 name: usbmuxd-latest_${{env.target_triplet}}
73 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..8249358
--- /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://git.libimobiledevice.org/usbmuxd.git
139* Repository (Mirror): https://github.com/libimobiledevice/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: 2022-04-04
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..c51d53a
--- /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.3.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+=" $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, "-g -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..dbbdd5f
--- /dev/null
+++ b/src/client.c
@@ -0,0 +1,1058 @@
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 n = plist_dict_get_item(lc->info, "BundleID");
456 }
457 if (n) {
458 plist_dict_set_item(l, "BundleID", plist_copy(n));
459 }
460 plist_dict_set_item(l, "ConnType", plist_new_uint(0));
461
462 n = NULL;
463 char *progname = NULL;
464 if (lc->info) {
465 n = plist_dict_get_item(lc->info, "ProgName");
466 }
467 if (n) {
468 plist_get_string_val(n, &progname);
469 }
470 if (!progname) {
471 progname = strdup("unknown");
472 }
473 char *idstring = malloc(strlen(progname) + 12);
474 sprintf(idstring, "%u-%s", client->number, progname);
475
476 plist_dict_set_item(l, "ID String", plist_new_string(idstring));
477 free(idstring);
478 plist_dict_set_item(l, "ProgName", plist_new_string(progname));
479 free(progname);
480
481 n = NULL;
482 uint64_t version = 0;
483 if (lc->info) {
484 n = plist_dict_get_item(lc->info, "kLibUSBMuxVersion");
485 }
486 if (n) {
487 plist_get_uint_val(n, &version);
488 }
489 plist_dict_set_item(l, "kLibUSBMuxVersion", plist_new_uint(version));
490
491 plist_array_append_item(listeners, l);
492 }
493 } ENDFOREACH
494 mutex_unlock(&client_list_mutex);
495
496 plist_dict_set_item(dict, "ListenerList", listeners);
497 res = send_plist(client, tag, dict);
498 plist_free(dict);
499
500 return res;
501}
502
503static int send_system_buid(struct mux_client *client, uint32_t tag)
504{
505 int res = -1;
506 char* buid = NULL;
507
508 config_get_system_buid(&buid);
509
510 plist_t dict = plist_new_dict();
511 plist_dict_set_item(dict, "BUID", plist_new_string(buid));
512 free(buid);
513 res = send_plist(client, tag, dict);
514 plist_free(dict);
515 return res;
516}
517
518static int send_pair_record(struct mux_client *client, uint32_t tag, const char* record_id)
519{
520 int res = -1;
521 char* record_data = NULL;
522 uint64_t record_size = 0;
523
524 if (!record_id) {
525 return send_result(client, tag, EINVAL);
526 }
527
528 config_get_device_record(record_id, &record_data, &record_size);
529
530 if (record_data) {
531 plist_t dict = plist_new_dict();
532 plist_dict_set_item(dict, "PairRecordData", plist_new_data(record_data, record_size));
533 free(record_data);
534 res = send_plist(client, tag, dict);
535 plist_free(dict);
536 } else {
537 res = send_result(client, tag, ENOENT);
538 }
539 return res;
540}
541
542static int send_device_add(struct mux_client *client, struct device_info *dev)
543{
544 int res = -1;
545 if (client->proto_version == 1) {
546 /* XML plist packet */
547 plist_t dict = create_device_attached_plist(dev);
548 res = send_plist(client, 0, dict);
549 plist_free(dict);
550 } else {
551 /* binary packet */
552 struct usbmuxd_device_record dmsg;
553 memset(&dmsg, 0, sizeof(dmsg));
554 dmsg.device_id = dev->id;
555 strncpy(dmsg.serial_number, dev->serial, 256);
556 dmsg.serial_number[255] = 0;
557 dmsg.location = dev->location;
558 dmsg.product_id = dev->pid;
559 res = output_buffer_add_message(client, 0, MESSAGE_DEVICE_ADD, &dmsg, sizeof(dmsg));
560 }
561 return res;
562}
563
564static int send_device_remove(struct mux_client *client, uint32_t device_id)
565{
566 int res = -1;
567 if (client->proto_version == 1) {
568 /* XML plist packet */
569 plist_t dict = plist_new_dict();
570 plist_dict_set_item(dict, "MessageType", plist_new_string("Detached"));
571 plist_dict_set_item(dict, "DeviceID", plist_new_uint(device_id));
572 res = send_plist(client, 0, dict);
573 plist_free(dict);
574 } else {
575 /* binary packet */
576 res = output_buffer_add_message(client, 0, MESSAGE_DEVICE_REMOVE, &device_id, sizeof(uint32_t));
577 }
578 return res;
579}
580
581static int send_device_paired(struct mux_client *client, uint32_t device_id)
582{
583 int res = -1;
584 if (client->proto_version == 1) {
585 /* XML plist packet */
586 plist_t dict = plist_new_dict();
587 plist_dict_set_item(dict, "MessageType", plist_new_string("Paired"));
588 plist_dict_set_item(dict, "DeviceID", plist_new_uint(device_id));
589 res = send_plist(client, 0, dict);
590 plist_free(dict);
591 }
592 else {
593 /* binary packet */
594 res = output_buffer_add_message(client, 0, MESSAGE_DEVICE_PAIRED, &device_id, sizeof(uint32_t));
595 }
596 return res;
597}
598
599static int start_listen(struct mux_client *client)
600{
601 struct device_info *devs = NULL;
602 struct device_info *dev;
603 int count, i;
604
605 client->state = CLIENT_LISTEN;
606
607 count = device_get_list(0, &devs);
608 dev = devs;
609 for(i=0; devs && i < count; i++) {
610 if(send_device_add(client, dev++) < 0) {
611 free(devs);
612 return -1;
613 }
614 }
615 if (devs)
616 free(devs);
617
618 return count;
619}
620
621static char* plist_dict_get_string_val(plist_t dict, const char* key)
622{
623 if (!dict || plist_get_node_type(dict) != PLIST_DICT)
624 return NULL;
625 plist_t item = plist_dict_get_item(dict, key);
626 if (!item || plist_get_node_type(item) != PLIST_STRING)
627 return NULL;
628 char *str = NULL;
629 plist_get_string_val(item, &str);
630 return str;
631}
632
633static void update_client_info(struct mux_client *client, plist_t dict)
634{
635 plist_t node = NULL;
636 plist_t info = plist_new_dict();
637
638 node = plist_dict_get_item(dict, "BundleID");
639 if (node && (plist_get_node_type(node) == PLIST_STRING)) {
640 plist_dict_set_item(info, "BundleID", plist_copy(node));
641 }
642
643 node = plist_dict_get_item(dict, "ClientVersionString");
644 if (node && (plist_get_node_type(node) == PLIST_STRING)) {
645 plist_dict_set_item(info, "ClientVersionString", plist_copy(node));
646 }
647
648 node = plist_dict_get_item(dict, "ProgName");
649 if (node && (plist_get_node_type(node) == PLIST_STRING)) {
650 plist_dict_set_item(info, "ProgName", plist_copy(node));
651 }
652
653 node = plist_dict_get_item(dict, "kLibUSBMuxVersion");
654 if (node && (plist_get_node_type(node) == PLIST_UINT)) {
655 plist_dict_set_item(info, "kLibUSBMuxVersion", plist_copy(node));
656 }
657 plist_free(client->info);
658 client->info = info;
659}
660
661static int handle_command(struct mux_client *client, struct usbmuxd_header *hdr)
662{
663 int res;
664 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);
665
666 if(client->state != CLIENT_COMMAND) {
667 usbmuxd_log(LL_ERROR, "Client %d command received in the wrong state, got %d but want %d", client->fd, client->state, CLIENT_COMMAND);
668 if(send_result(client, hdr->tag, RESULT_BADCOMMAND) < 0)
669 return -1;
670 client_close(client);
671 return -1;
672 }
673
674 if((hdr->version != 0) && (hdr->version != 1)) {
675 usbmuxd_log(LL_INFO, "Client %d version mismatch: expected 0 or 1, got %d", client->fd, hdr->version);
676 send_result(client, hdr->tag, RESULT_BADVERSION);
677 return 0;
678 }
679
680 struct usbmuxd_connect_request *ch;
681 char *payload;
682 uint32_t payload_size;
683
684 switch(hdr->message) {
685 case MESSAGE_PLIST:
686 client->proto_version = 1;
687 payload = (char*)(hdr) + sizeof(struct usbmuxd_header);
688 payload_size = hdr->length - sizeof(struct usbmuxd_header);
689 plist_t dict = NULL;
690 plist_from_xml(payload, payload_size, &dict);
691 if (!dict) {
692 usbmuxd_log(LL_ERROR, "Could not parse plist from payload!");
693 return -1;
694 } else {
695 char *message = NULL;
696 plist_t node = plist_dict_get_item(dict, "MessageType");
697 if (!node || plist_get_node_type(node) != PLIST_STRING) {
698 usbmuxd_log(LL_ERROR, "Could not read valid MessageType node from plist!");
699 plist_free(dict);
700 return -1;
701 }
702 plist_get_string_val(node, &message);
703 if (!message) {
704 usbmuxd_log(LL_ERROR, "Could not extract MessageType from plist!");
705 plist_free(dict);
706 return -1;
707 }
708 update_client_info(client, dict);
709 if (!strcmp(message, "Listen")) {
710 free(message);
711 plist_free(dict);
712 if (send_result(client, hdr->tag, 0) < 0)
713 return -1;
714 usbmuxd_log(LL_DEBUG, "Client %d now LISTENING", client->fd);
715 return start_listen(client);
716 } else if (!strcmp(message, "Connect")) {
717 uint64_t val;
718 uint16_t portnum = 0;
719 uint32_t device_id = 0;
720 free(message);
721 // get device id
722 node = plist_dict_get_item(dict, "DeviceID");
723 if (!node) {
724 usbmuxd_log(LL_ERROR, "Received connect request without device_id!");
725 plist_free(dict);
726 if (send_result(client, hdr->tag, RESULT_BADDEV) < 0)
727 return -1;
728 return 0;
729 }
730 val = 0;
731 plist_get_uint_val(node, &val);
732 device_id = (uint32_t)val;
733
734 // get port number
735 node = plist_dict_get_item(dict, "PortNumber");
736 if (!node) {
737 usbmuxd_log(LL_ERROR, "Received connect request without port number!");
738 plist_free(dict);
739 if (send_result(client, hdr->tag, RESULT_BADCOMMAND) < 0)
740 return -1;
741 return 0;
742 }
743 val = 0;
744 plist_get_uint_val(node, &val);
745 portnum = (uint16_t)val;
746 plist_free(dict);
747
748 usbmuxd_log(LL_DEBUG, "Client %d requesting connection to device %d port %d", client->fd, device_id, ntohs(portnum));
749 res = device_start_connect(device_id, ntohs(portnum), client);
750 if(res < 0) {
751 if (send_result(client, hdr->tag, -res) < 0)
752 return -1;
753 } else {
754 client->connect_tag = hdr->tag;
755 client->connect_device = device_id;
756 client->state = CLIENT_CONNECTING1;
757 }
758 return 0;
759 } else if (!strcmp(message, "ListDevices")) {
760 free(message);
761 plist_free(dict);
762 if (send_device_list(client, hdr->tag) < 0)
763 return -1;
764 return 0;
765 } else if (!strcmp(message, "ListListeners")) {
766 free(message);
767 plist_free(dict);
768 if (send_listener_list(client, hdr->tag) < 0)
769 return -1;
770 return 0;
771 } else if (!strcmp(message, "ReadBUID")) {
772 free(message);
773 plist_free(dict);
774 if (send_system_buid(client, hdr->tag) < 0)
775 return -1;
776 return 0;
777 } else if (!strcmp(message, "ReadPairRecord")) {
778 free(message);
779 char* record_id = plist_dict_get_string_val(dict, "PairRecordID");
780 plist_free(dict);
781
782 res = send_pair_record(client, hdr->tag, record_id);
783 if (record_id)
784 free(record_id);
785 if (res < 0)
786 return -1;
787 return 0;
788 } else if (!strcmp(message, "SavePairRecord")) {
789 uint32_t rval = RESULT_OK;
790 free(message);
791 char* record_id = plist_dict_get_string_val(dict, "PairRecordID");
792 char* record_data = NULL;
793 uint64_t record_size = 0;
794 plist_t rdata = plist_dict_get_item(dict, "PairRecordData");
795 if (rdata && plist_get_node_type(rdata) == PLIST_DATA) {
796 plist_get_data_val(rdata, &record_data, &record_size);
797 }
798
799 if (record_id && record_data) {
800 res = config_set_device_record(record_id, record_data, record_size);
801 if (res < 0) {
802 rval = -res;
803 } else {
804 plist_t p_dev_id = plist_dict_get_item(dict, "DeviceID");
805 uint32_t dev_id = 0;
806 if (p_dev_id && plist_get_node_type(p_dev_id) == PLIST_UINT) {
807 uint64_t u_dev_id = 0;
808 plist_get_uint_val(p_dev_id, &u_dev_id);
809 dev_id = (uint32_t)u_dev_id;
810 }
811 if (dev_id > 0) {
812 struct device_info *devs = NULL;
813 struct device_info *dev;
814 int i;
815 int count = device_get_list(1, &devs);
816 int found = 0;
817 dev = devs;
818 for (i = 0; devs && i < count; i++, dev++) {
819 if ((uint32_t)dev->id == dev_id && (strcmp(dev->serial, record_id) == 0)) {
820 found++;
821 break;
822 }
823 }
824 if (!found) {
825 usbmuxd_log(LL_ERROR, "ERROR: SavePairRecord: DeviceID %d (%s) is not connected\n", dev_id, record_id);
826 } else {
827 client_device_paired(dev_id);
828 }
829 free(devs);
830 }
831 }
832 free(record_id);
833 } else {
834 rval = EINVAL;
835 }
836 free(record_data);
837 plist_free(dict);
838 if (send_result(client, hdr->tag, rval) < 0)
839 return -1;
840 return 0;
841 } else if (!strcmp(message, "DeletePairRecord")) {
842 uint32_t rval = RESULT_OK;
843 free(message);
844 char* record_id = plist_dict_get_string_val(dict, "PairRecordID");
845 plist_free(dict);
846 if (record_id) {
847 res = config_remove_device_record(record_id);
848 if (res < 0) {
849 rval = -res;
850 }
851 free(record_id);
852 } else {
853 rval = EINVAL;
854 }
855 if (send_result(client, hdr->tag, rval) < 0)
856 return -1;
857 return 0;
858 } else {
859 usbmuxd_log(LL_ERROR, "Unexpected command '%s' received!", message);
860 free(message);
861 plist_free(dict);
862 if (send_result(client, hdr->tag, RESULT_BADCOMMAND) < 0)
863 return -1;
864 return 0;
865 }
866 }
867 // should not be reached?!
868 return -1;
869 case MESSAGE_LISTEN:
870 if(send_result(client, hdr->tag, 0) < 0)
871 return -1;
872 usbmuxd_log(LL_DEBUG, "Client %d now LISTENING", client->fd);
873 return start_listen(client);
874 case MESSAGE_CONNECT:
875 ch = (void*)hdr;
876 usbmuxd_log(LL_DEBUG, "Client %d connection request to device %d port %d", client->fd, ch->device_id, ntohs(ch->port));
877 res = device_start_connect(ch->device_id, ntohs(ch->port), client);
878 if(res < 0) {
879 if(send_result(client, hdr->tag, -res) < 0)
880 return -1;
881 } else {
882 client->connect_tag = hdr->tag;
883 client->connect_device = ch->device_id;
884 client->state = CLIENT_CONNECTING1;
885 }
886 return 0;
887 default:
888 usbmuxd_log(LL_ERROR, "Client %d invalid command %d", client->fd, hdr->message);
889 if(send_result(client, hdr->tag, RESULT_BADCOMMAND) < 0)
890 return -1;
891 return 0;
892 }
893 return -1;
894}
895
896static void output_buffer_process(struct mux_client *client)
897{
898 int res;
899 if(!client->ob_size) {
900 usbmuxd_log(LL_WARNING, "Client %d OUT process but nothing to send?", client->fd);
901 client->events &= ~POLLOUT;
902 return;
903 }
904 res = send(client->fd, client->ob_buf, client->ob_size, 0);
905 if(res <= 0) {
906 usbmuxd_log(LL_ERROR, "Sending to client fd %d failed: %d %s", client->fd, res, strerror(errno));
907 client_close(client);
908 return;
909 }
910 if((uint32_t)res == client->ob_size) {
911 client->ob_size = 0;
912 client->events &= ~POLLOUT;
913 if(client->state == CLIENT_CONNECTING2) {
914 usbmuxd_log(LL_DEBUG, "Client %d switching to CONNECTED state", client->fd);
915 client->state = CLIENT_CONNECTED;
916 client->events = client->devents;
917 // no longer need this
918 free(client->ob_buf);
919 client->ob_buf = NULL;
920 }
921 } else {
922 client->ob_size -= res;
923 memmove(client->ob_buf, client->ob_buf + res, client->ob_size);
924 }
925}
926static void input_buffer_process(struct mux_client *client)
927{
928 int res;
929 int did_read = 0;
930 if(client->ib_size < sizeof(struct usbmuxd_header)) {
931 res = recv(client->fd, client->ib_buf + client->ib_size, sizeof(struct usbmuxd_header) - client->ib_size, 0);
932 if(res <= 0) {
933 if(res < 0)
934 usbmuxd_log(LL_ERROR, "Receive from client fd %d failed: %s", client->fd, strerror(errno));
935 else
936 usbmuxd_log(LL_INFO, "Client %d connection closed", client->fd);
937 client_close(client);
938 return;
939 }
940 client->ib_size += res;
941 if(client->ib_size < sizeof(struct usbmuxd_header))
942 return;
943 did_read = 1;
944 }
945 struct usbmuxd_header *hdr = (void*)client->ib_buf;
946 if(hdr->length > client->ib_capacity) {
947 usbmuxd_log(LL_INFO, "Client %d message is too long (%d bytes)", client->fd, hdr->length);
948 client_close(client);
949 return;
950 }
951 if(hdr->length < sizeof(struct usbmuxd_header)) {
952 usbmuxd_log(LL_ERROR, "Client %d message is too short (%d bytes)", client->fd, hdr->length);
953 client_close(client);
954 return;
955 }
956 if(client->ib_size < hdr->length) {
957 if(did_read)
958 return; //maybe we would block, so defer to next loop
959 res = recv(client->fd, client->ib_buf + client->ib_size, hdr->length - client->ib_size, 0);
960 if(res < 0) {
961 usbmuxd_log(LL_ERROR, "Receive from client fd %d failed: %s", client->fd, strerror(errno));
962 client_close(client);
963 return;
964 } else if(res == 0) {
965 usbmuxd_log(LL_INFO, "Client %d connection closed", client->fd);
966 client_close(client);
967 return;
968 }
969 client->ib_size += res;
970 if(client->ib_size < hdr->length)
971 return;
972 }
973 handle_command(client, hdr);
974 client->ib_size = 0;
975}
976
977void client_process(int fd, short events)
978{
979 struct mux_client *client = NULL;
980 mutex_lock(&client_list_mutex);
981 FOREACH(struct mux_client *lc, &client_list) {
982 if(lc->fd == fd) {
983 client = lc;
984 break;
985 }
986 } ENDFOREACH
987 mutex_unlock(&client_list_mutex);
988
989 if(!client) {
990 usbmuxd_log(LL_INFO, "client_process: fd %d not found in client list", fd);
991 return;
992 }
993
994 if(client->state == CLIENT_CONNECTED) {
995 usbmuxd_log(LL_SPEW, "client_process in CONNECTED state");
996 device_client_process(client->connect_device, client, events);
997 } else {
998 if(events & POLLIN) {
999 input_buffer_process(client);
1000 } else if(events & POLLOUT) { //not both in case client died as part of process_recv
1001 output_buffer_process(client);
1002 }
1003 }
1004
1005}
1006
1007void client_device_add(struct device_info *dev)
1008{
1009 mutex_lock(&client_list_mutex);
1010 usbmuxd_log(LL_DEBUG, "client_device_add: id %d, location 0x%x, serial %s", dev->id, dev->location, dev->serial);
1011 device_set_visible(dev->id);
1012 FOREACH(struct mux_client *client, &client_list) {
1013 if(client->state == CLIENT_LISTEN)
1014 send_device_add(client, dev);
1015 } ENDFOREACH
1016 mutex_unlock(&client_list_mutex);
1017}
1018
1019void client_device_remove(int device_id)
1020{
1021 mutex_lock(&client_list_mutex);
1022 uint32_t id = device_id;
1023 usbmuxd_log(LL_DEBUG, "client_device_remove: id %d", device_id);
1024 FOREACH(struct mux_client *client, &client_list) {
1025 if(client->state == CLIENT_LISTEN)
1026 send_device_remove(client, id);
1027 } ENDFOREACH
1028 mutex_unlock(&client_list_mutex);
1029}
1030
1031void client_device_paired(int device_id)
1032{
1033 mutex_lock(&client_list_mutex);
1034 uint32_t id = device_id;
1035 usbmuxd_log(LL_DEBUG, "client_device_paired: id %d", device_id);
1036 FOREACH(struct mux_client *client, &client_list) {
1037 if (client->state == CLIENT_LISTEN)
1038 send_device_paired(client, id);
1039 } ENDFOREACH
1040 mutex_unlock(&client_list_mutex);
1041}
1042
1043void client_init(void)
1044{
1045 usbmuxd_log(LL_DEBUG, "client_init");
1046 collection_init(&client_list);
1047 mutex_init(&client_list_mutex);
1048}
1049
1050void client_shutdown(void)
1051{
1052 usbmuxd_log(LL_DEBUG, "client_shutdown");
1053 FOREACH(struct mux_client *client, &client_list) {
1054 client_close(client);
1055 } ENDFOREACH
1056 mutex_destroy(&client_list_mutex);
1057 collection_free(&client_list);
1058}
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..2e6c97f
--- /dev/null
+++ b/src/conf.c
@@ -0,0 +1,535 @@
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
38#ifdef WIN32
39#include <shlobj.h>
40#endif
41
42#include <libimobiledevice-glue/utils.h>
43#include <plist/plist.h>
44
45#include "conf.h"
46#include "utils.h"
47#include "log.h"
48
49#ifdef WIN32
50#define DIR_SEP '\\'
51#define DIR_SEP_S "\\"
52#else
53#define DIR_SEP '/'
54#define DIR_SEP_S "/"
55#endif
56
57#define CONFIG_SYSTEM_BUID_KEY "SystemBUID"
58#define CONFIG_HOST_ID_KEY "HostID"
59
60#define CONFIG_EXT ".plist"
61
62#ifdef WIN32
63#define CONFIG_DIR "Apple"DIR_SEP_S"Lockdown"
64#else
65#define CONFIG_DIR "lockdown"
66#endif
67
68#define CONFIG_FILE "SystemConfiguration"CONFIG_EXT
69
70static char *__config_dir = NULL;
71
72#ifdef WIN32
73static char *config_utf16_to_utf8(wchar_t *unistr, long len, long *items_read, long *items_written)
74{
75 if (!unistr || (len <= 0)) return NULL;
76 char *outbuf = (char*)malloc(3*(len+1));
77 int p = 0;
78 int i = 0;
79
80 wchar_t wc;
81
82 while (i < len) {
83 wc = unistr[i++];
84 if (wc >= 0x800) {
85 outbuf[p++] = (char)(0xE0 + ((wc >> 12) & 0xF));
86 outbuf[p++] = (char)(0x80 + ((wc >> 6) & 0x3F));
87 outbuf[p++] = (char)(0x80 + (wc & 0x3F));
88 } else if (wc >= 0x80) {
89 outbuf[p++] = (char)(0xC0 + ((wc >> 6) & 0x1F));
90 outbuf[p++] = (char)(0x80 + (wc & 0x3F));
91 } else {
92 outbuf[p++] = (char)(wc & 0x7F);
93 }
94 }
95 if (items_read) {
96 *items_read = i;
97 }
98 if (items_written) {
99 *items_written = p;
100 }
101 outbuf[p] = 0;
102
103 return outbuf;
104}
105#endif
106
107const char *config_get_config_dir()
108{
109 char *base_config_dir = NULL;
110
111 if (__config_dir)
112 return __config_dir;
113
114#ifdef WIN32
115 wchar_t path[MAX_PATH+1];
116 HRESULT hr;
117 LPITEMIDLIST pidl = NULL;
118 BOOL b = FALSE;
119
120 hr = SHGetSpecialFolderLocation (NULL, CSIDL_COMMON_APPDATA, &pidl);
121 if (hr == S_OK) {
122 b = SHGetPathFromIDListW (pidl, path);
123 if (b) {
124 base_config_dir = config_utf16_to_utf8 (path, wcslen(path), NULL, NULL);
125 CoTaskMemFree (pidl);
126 }
127 }
128#else
129#ifdef __APPLE__
130 base_config_dir = strdup("/var/db");
131#else
132 base_config_dir = strdup("/var/lib");
133#endif
134#endif
135 __config_dir = string_concat(base_config_dir, DIR_SEP_S, CONFIG_DIR, NULL);
136
137 if (__config_dir) {
138 int i = strlen(__config_dir)-1;
139 while ((i > 0) && (__config_dir[i] == DIR_SEP)) {
140 __config_dir[i--] = '\0';
141 }
142 }
143
144 free(base_config_dir);
145
146 usbmuxd_log(LL_DEBUG, "Initialized config_dir to %s", __config_dir);
147
148 return __config_dir;
149}
150
151static int __mkdir(const char *dir, int mode)
152{
153#ifdef WIN32
154 return mkdir(dir);
155#else
156 return mkdir(dir, mode);
157#endif
158}
159
160static int mkdir_with_parents(const char *dir, int mode)
161{
162 if (!dir) return -1;
163 if (__mkdir(dir, mode) == 0) {
164 return 0;
165 } else {
166 if (errno == EEXIST) return 0;
167 }
168 int res;
169 char *parent = strdup(dir);
170 char* parentdir = dirname(parent);
171 if (parentdir) {
172 res = mkdir_with_parents(parentdir, mode);
173 } else {
174 res = -1;
175 }
176 free(parent);
177 return res;
178}
179
180/**
181 * Creates a freedesktop compatible configuration directory.
182 */
183static void config_create_config_dir(void)
184{
185 const char *config_path = config_get_config_dir();
186 struct stat st;
187 if (stat(config_path, &st) != 0) {
188 mkdir_with_parents(config_path, 0755);
189 }
190}
191
192static int get_rand(int min, int max)
193{
194 int retval = (rand() % (max - min)) + min;
195 return retval;
196}
197
198static char *config_generate_uuid(int idx)
199{
200 char *uuid = (char *) malloc(sizeof(char) * 37);
201 const char *chars = "ABCDEF0123456789";
202 srand(time(NULL) - idx);
203 int i = 0;
204
205 for (i = 0; i < 36; i++) {
206 if (i == 8 || i == 13 || i == 18 || i == 23) {
207 uuid[i] = '-';
208 continue;
209 } else {
210 uuid[i] = chars[get_rand(0, 16)];
211 }
212 }
213 /* make it a real string */
214 uuid[36] = '\0';
215 return uuid;
216}
217
218/**
219 * Generates a valid BUID for this system (which is actually a UUID).
220 *
221 * @return A null terminated string containing a valid BUID.
222 */
223static char *config_generate_system_buid()
224{
225 return config_generate_uuid(1);
226}
227
228static int internal_set_value(const char *config_file, const char *key, plist_t value)
229{
230 if (!config_file)
231 return 0;
232
233 /* read file into plist */
234 plist_t config = NULL;
235
236 plist_read_from_file(config_file, &config, NULL);
237 if (!config) {
238 config = plist_new_dict();
239 plist_dict_set_item(config, key, value);
240 } else {
241 plist_t n = plist_dict_get_item(config, key);
242 if (n) {
243 plist_dict_remove_item(config, key);
244 }
245 plist_dict_set_item(config, key, value);
246 remove(config_file);
247 }
248
249 /* store in config file */
250 char *value_string = NULL;
251 if (plist_get_node_type(value) == PLIST_STRING) {
252 plist_get_string_val(value, &value_string);
253 usbmuxd_log(LL_DEBUG, "Setting key %s to %s in config file %s", key, value_string, config_file);
254 if (value_string)
255 free(value_string);
256 } else {
257 usbmuxd_log(LL_DEBUG, "Setting key %s in config file %s", key, config_file);
258 }
259
260 int res = (plist_write_to_file(config, config_file, PLIST_FORMAT_XML, 0) == PLIST_ERR_SUCCESS);
261
262 plist_free(config);
263
264 return res;
265}
266
267static int config_set_value(const char *key, plist_t value)
268{
269 const char *config_path = NULL;
270 char *config_file = NULL;
271
272 /* Make sure config directory exists */
273 config_create_config_dir();
274
275 config_path = config_get_config_dir();
276 config_file = string_concat(config_path, DIR_SEP_S, CONFIG_FILE, NULL);
277
278 int result = internal_set_value(config_file, key, value);
279 if (!result) {
280 usbmuxd_log(LL_ERROR, "ERROR: Failed to write to '%s'", config_file);
281 }
282
283 free(config_file);
284
285 return result;
286}
287
288static int internal_get_value(const char* config_file, const char *key, plist_t *value)
289{
290 *value = NULL;
291
292 /* now parse file to get the SystemBUID */
293 plist_t config = NULL;
294 if (plist_read_from_file(config_file, &config, NULL) == PLIST_ERR_SUCCESS) {
295 usbmuxd_log(LL_DEBUG, "Reading key %s from config file %s", key, config_file);
296 plist_t n = plist_dict_get_item(config, key);
297 if (n) {
298 *value = plist_copy(n);
299 n = NULL;
300 }
301 }
302 plist_free(config);
303
304 return 1;
305}
306
307static int config_get_value(const char *key, plist_t *value)
308{
309 const char *config_path = NULL;
310 char *config_file = NULL;
311
312 config_path = config_get_config_dir();
313 config_file = string_concat(config_path, DIR_SEP_S, CONFIG_FILE, NULL);
314
315 int result = internal_get_value(config_file, key, value);
316
317 free(config_file);
318
319 return result;
320}
321
322/**
323 * Store SystemBUID in config file.
324 *
325 * @param system_buid A null terminated string containing a valid SystemBUID.
326 */
327static int config_set_system_buid(const char *system_buid)
328{
329 return config_set_value(CONFIG_SYSTEM_BUID_KEY, plist_new_string(system_buid));
330}
331
332/**
333 * Determines whether a pairing record is present for the given device.
334 *
335 * @param udid The device UDID as given by the device.
336 *
337 * @return 1 if there's a pairing record for the given udid or 0 otherwise.
338 */
339int config_has_device_record(const char *udid)
340{
341 int res = 0;
342 if (!udid) return 0;
343
344 /* ensure config directory exists */
345 config_create_config_dir();
346
347 /* build file path */
348 const char *config_path = config_get_config_dir();
349 char *device_record_file = string_concat(config_path, DIR_SEP_S, udid, CONFIG_EXT, NULL);
350
351 struct stat st;
352
353 if ((stat(device_record_file, &st) == 0) && S_ISREG(st.st_mode))
354 res = 1;
355
356 free(device_record_file);
357
358 return res;
359}
360
361/**
362 * Reads the BUID from a previously generated configuration file.
363 *
364 * @param system_buid pointer to a variable that will be set to point to a
365 * newly allocated string containing the BUID.
366 *
367 * @note It is the responsibility of the calling function to free the returned system_buid
368 */
369void config_get_system_buid(char **system_buid)
370{
371 plist_t value = NULL;
372
373 config_get_value(CONFIG_SYSTEM_BUID_KEY, &value);
374
375 if (value && (plist_get_node_type(value) == PLIST_STRING)) {
376 plist_get_string_val(value, system_buid);
377 usbmuxd_log(LL_DEBUG, "Got %s %s", CONFIG_SYSTEM_BUID_KEY, *system_buid);
378 }
379
380 if (value)
381 plist_free(value);
382
383 if (!*system_buid) {
384 /* no config, generate system_buid */
385 usbmuxd_log(LL_DEBUG, "No previous %s found", CONFIG_SYSTEM_BUID_KEY);
386 *system_buid = config_generate_system_buid();
387 if (!config_set_system_buid(*system_buid)) {
388 usbmuxd_log(LL_WARNING, "WARNING: Failed to store SystemBUID, this might be a problem");
389 }
390 }
391
392 usbmuxd_log(LL_DEBUG, "Using %s as %s", *system_buid, CONFIG_SYSTEM_BUID_KEY);
393}
394
395/**
396 * Store a pairing record for the given device identifier.
397 *
398 * @param udid device identifier
399 * @param record_data buffer containing a pairing record
400 * @param record_size size of buffer passed in record_data
401 *
402 * @return 0 on success or a negative errno otherwise.
403 */
404int config_set_device_record(const char *udid, char* record_data, uint64_t record_size)
405{
406 int res = 0;
407
408 if (!udid || !record_data || record_size < 8)
409 return -EINVAL;
410
411 plist_t plist = NULL;
412 if (memcmp(record_data, "bplist00", 8) == 0) {
413 plist_from_bin(record_data, record_size, &plist);
414 } else {
415 plist_from_xml(record_data, record_size, &plist);
416 }
417
418 if (!plist || plist_get_node_type(plist) != PLIST_DICT) {
419 if (plist)
420 plist_free(plist);
421 return -EINVAL;
422 }
423
424 /* ensure config directory exists */
425 config_create_config_dir();
426
427 /* build file path */
428 const char *config_path = config_get_config_dir();
429 char *device_record_file = string_concat(config_path, DIR_SEP_S, udid, CONFIG_EXT, NULL);
430
431 remove(device_record_file);
432
433 /* store file */
434 if (!plist_write_to_file(plist, device_record_file, PLIST_FORMAT_XML, 0)) {
435 usbmuxd_log(LL_DEBUG, "Could not open '%s' for writing: %s", device_record_file, strerror(errno));
436 res = -ENOENT;
437 }
438 free(device_record_file);
439 if (plist)
440 plist_free(plist);
441
442 return res;
443}
444
445/**
446 * Retrieve a pairing record for the given device identifier
447 *
448 * @param udid device identifier
449 * @param record_data pointer to a variable that will be set to point to a
450 * newly allocated buffer holding the pairing record
451 * @param record_size pointer to a variable that will be set to the size
452 * of the buffer given in record_data.
453 *
454 * @return 0 on success or a negative errno otherwise.
455 */
456int config_get_device_record(const char *udid, char **record_data, uint64_t *record_size)
457{
458 int res = 0;
459
460 /* ensure config directory exists */
461 config_create_config_dir();
462
463 /* build file path */
464 const char *config_path = config_get_config_dir();
465 char *device_record_file = string_concat(config_path, DIR_SEP_S, udid, CONFIG_EXT, NULL);
466
467 /* read file */
468 buffer_read_from_filename(device_record_file, record_data, record_size);
469 if (!*record_data) {
470 usbmuxd_log(LL_ERROR, "ERROR: Failed to read '%s': %s", device_record_file, strerror(errno));
471 res = -ENOENT;
472 }
473 free(device_record_file);
474
475 return res;
476}
477
478/**
479 * Remove the pairing record stored for a device from this host.
480 *
481 * @param udid The udid of the device
482 *
483 * @return 0 on success or a negative errno otherwise.
484 */
485int config_remove_device_record(const char *udid)
486{
487 int res = 0;
488
489 /* build file path */
490 const char *config_path = config_get_config_dir();
491 char *device_record_file = string_concat(config_path, DIR_SEP_S, udid, CONFIG_EXT, NULL);
492
493 /* remove file */
494 if (remove(device_record_file) != 0) {
495 res = -errno;
496 usbmuxd_log(LL_DEBUG, "Could not remove %s: %s", device_record_file, strerror(errno));
497 }
498
499 free(device_record_file);
500
501 return res;
502}
503
504static int config_device_record_get_value(const char *udid, const char *key, plist_t *value)
505{
506 const char *config_path = NULL;
507 char *config_file = NULL;
508
509 config_path = config_get_config_dir();
510 config_file = string_concat(config_path, DIR_SEP_S, udid, CONFIG_EXT, NULL);
511
512 int result = internal_get_value(config_file, key, value);
513
514 free(config_file);
515
516 return result;
517}
518
519void config_device_record_get_host_id(const char *udid, char **host_id)
520{
521 plist_t value = NULL;
522
523 config_device_record_get_value(udid, CONFIG_HOST_ID_KEY, &value);
524
525 if (value && (plist_get_node_type(value) == PLIST_STRING)) {
526 plist_get_string_val(value, host_id);
527 }
528
529 if (value)
530 plist_free(value);
531
532 if (!*host_id) {
533 usbmuxd_log(LL_ERROR, "ERROR: Could not get HostID from pairing record for udid %s", udid);
534 }
535}
diff --git a/src/conf.h b/src/conf.h
new file mode 100644
index 0000000..bbfa965
--- /dev/null
+++ b/src/conf.h
@@ -0,0 +1,40 @@
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();
30
31void config_get_system_buid(char **system_buid);
32
33int config_has_device_record(const char *udid);
34int config_get_device_record(const char *udid, char **record_data, uint64_t *record_size);
35int config_set_device_record(const char *udid, char* record_data, uint64_t record_size);
36int config_remove_device_record(const char *udid);
37
38void config_device_record_get_host_id(const char *udid, char **host_id);
39
40#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..8702a4b
--- /dev/null
+++ b/src/main.c
@@ -0,0 +1,920 @@
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(" -S, --socket ADDR:PORT | PATH Specify source ADDR and PORT or a UNIX\n");
521 printf(" \t\tsocket PATH to use for the listening socket.\n");
522 printf(" \t\tDefault: %s\n", socket_path);
523 printf(" -P, --pidfile PATH\tSpecify a different location for the pid file, or pass\n");
524 printf(" \t\tNONE to disable. Default: %s\n", DEFAULT_LOCKFILE);
525 printf(" -x, --exit\t\tNotify a running instance to exit if there are no devices\n");
526 printf(" \t\tconnected (sends SIGUSR1 to running instance) and exit.\n");
527 printf(" -X, --force-exit\tNotify a running instance to exit even if there are still\n");
528 printf(" \tdevices connected (always works) and exit.\n");
529 printf(" -l, --logfile=LOGFILE\tLog (append) to LOGFILE instead of stderr or syslog.\n");
530 printf(" -V, --version\t\tPrint version information and exit.\n");
531 printf("\n");
532 printf("Homepage: <" PACKAGE_URL ">\n");
533 printf("Bug Reports: <" PACKAGE_BUGREPORT ">\n");
534}
535
536static void parse_opts(int argc, char **argv)
537{
538 static struct option longopts[] = {
539 {"help", no_argument, NULL, 'h'},
540 {"foreground", no_argument, NULL, 'f'},
541 {"verbose", no_argument, NULL, 'v'},
542 {"user", required_argument, NULL, 'U'},
543 {"disable-hotplug", no_argument, NULL, 'n'},
544 {"enable-exit", no_argument, NULL, 'z'},
545 {"no-preflight", no_argument, NULL, 'p'},
546#ifdef HAVE_UDEV
547 {"udev", no_argument, NULL, 'u'},
548#endif
549#ifdef HAVE_SYSTEMD
550 {"systemd", no_argument, NULL, 's'},
551#endif
552 {"socket", required_argument, NULL, 'S'},
553 {"pidfile", required_argument, NULL, 'P'},
554 {"exit", no_argument, NULL, 'x'},
555 {"force-exit", no_argument, NULL, 'X'},
556 {"logfile", required_argument, NULL, 'l'},
557 {"version", no_argument, NULL, 'V'},
558 {NULL, 0, NULL, 0}
559 };
560 int c;
561
562#ifdef HAVE_SYSTEMD
563 const char* opts_spec = "hfvVuU:xXsnzl:pS:P:";
564#elif HAVE_UDEV
565 const char* opts_spec = "hfvVuU:xXnzl:pS:P:";
566#else
567 const char* opts_spec = "hfvVU:xXnzl:pS:P:";
568#endif
569
570 while (1) {
571 c = getopt_long(argc, argv, opts_spec, longopts, (int *) 0);
572 if (c == -1) {
573 break;
574 }
575
576 switch (c) {
577 case 'h':
578 usage();
579 exit(0);
580 case 'f':
581 foreground = 1;
582 break;
583 case 'v':
584 ++verbose;
585 break;
586 case 'V':
587 printf("%s\n", PACKAGE_STRING);
588 exit(0);
589 case 'U':
590 drop_privileges = 1;
591 drop_user = optarg;
592 break;
593 case 'p':
594 no_preflight = 1;
595 break;
596#ifdef HAVE_UDEV
597 case 'u':
598 opt_disable_hotplug = 1;
599 opt_enable_exit = 1;
600 break;
601#endif
602#ifdef HAVE_SYSTEMD
603 case 's':
604 opt_enable_exit = 1;
605 foreground = 1;
606 break;
607#endif
608 case 'n':
609 opt_disable_hotplug = 1;
610 break;
611 case 'z':
612 opt_enable_exit = 1;
613 break;
614 case 'S':
615 if (!*optarg || *optarg == '-') {
616 usbmuxd_log(LL_FATAL, "ERROR: --socket requires an argument");
617 usage();
618 exit(2);
619 }
620 listen_addr = optarg;
621 break;
622 case 'P':
623 if (!*optarg || *optarg == '-') {
624 usbmuxd_log(LL_FATAL, "ERROR: --pidfile requires an argument");
625 usage();
626 exit(2);
627 }
628 if (!strcmp(optarg, "NONE")) {
629 lockfile = NULL;
630 } else {
631 lockfile = optarg;
632 }
633 break;
634 case 'x':
635 opt_exit = 1;
636 exit_signal = SIGUSR1;
637 break;
638 case 'X':
639 opt_exit = 1;
640 exit_signal = SIGTERM;
641 break;
642 case 'l':
643 if (!*optarg) {
644 usbmuxd_log(LL_FATAL, "ERROR: --logfile requires a non-empty filename");
645 usage();
646 exit(2);
647 }
648 if (use_logfile) {
649 usbmuxd_log(LL_FATAL, "ERROR: --logfile cannot be used multiple times");
650 exit(2);
651 }
652 if (!freopen(optarg, "a", stderr)) {
653 usbmuxd_log(LL_FATAL, "ERROR: fdreopen: %s", strerror(errno));
654 } else {
655 use_logfile = 1;
656 }
657 break;
658 default:
659 usage();
660 exit(2);
661 }
662 }
663}
664
665int main(int argc, char *argv[])
666{
667 int listenfd;
668 int res = 0;
669 int lfd;
670 struct flock lock;
671 char pids[10];
672
673 parse_opts(argc, argv);
674
675 argc -= optind;
676 argv += optind;
677
678 if (!foreground && !use_logfile) {
679 verbose += LL_WARNING;
680 log_enable_syslog();
681 } else {
682 verbose += LL_NOTICE;
683 }
684
685 /* set log level to specified verbosity */
686 log_level = verbose;
687
688 usbmuxd_log(LL_NOTICE, "usbmuxd v%s starting up", PACKAGE_VERSION);
689 should_exit = 0;
690 should_discover = 0;
691
692 set_signal_handlers();
693 signal(SIGPIPE, SIG_IGN);
694
695 if (lockfile) {
696 res = lfd = open(lockfile, O_WRONLY|O_CREAT, 0644);
697 if(res == -1) {
698 usbmuxd_log(LL_FATAL, "Could not open lockfile");
699 goto terminate;
700 }
701 lock.l_type = F_WRLCK;
702 lock.l_whence = SEEK_SET;
703 lock.l_start = 0;
704 lock.l_len = 0;
705 lock.l_pid = 0;
706 fcntl(lfd, F_GETLK, &lock);
707 close(lfd);
708 }
709 if (lockfile && lock.l_type != F_UNLCK) {
710 if (opt_exit) {
711 if (lock.l_pid && !kill(lock.l_pid, 0)) {
712 usbmuxd_log(LL_NOTICE, "Sending signal %d to instance with pid %d", exit_signal, lock.l_pid);
713 res = 0;
714 if (kill(lock.l_pid, exit_signal) < 0) {
715 usbmuxd_log(LL_FATAL, "Could not deliver signal %d to pid %d", exit_signal, lock.l_pid);
716 res = -1;
717 }
718 goto terminate;
719 } else {
720 usbmuxd_log(LL_ERROR, "Could not determine pid of the other running instance!");
721 res = -1;
722 goto terminate;
723 }
724 } else {
725 if (!opt_disable_hotplug) {
726 usbmuxd_log(LL_ERROR, "Another instance is already running (pid %d). exiting.", lock.l_pid);
727 res = -1;
728 } else {
729 usbmuxd_log(LL_NOTICE, "Another instance is already running (pid %d). Telling it to check for devices.", lock.l_pid);
730 if (lock.l_pid && !kill(lock.l_pid, 0)) {
731 usbmuxd_log(LL_NOTICE, "Sending signal SIGUSR2 to instance with pid %d", lock.l_pid);
732 res = 0;
733 if (kill(lock.l_pid, SIGUSR2) < 0) {
734 usbmuxd_log(LL_FATAL, "Could not deliver SIGUSR2 to pid %d", lock.l_pid);
735 res = -1;
736 }
737 } else {
738 usbmuxd_log(LL_ERROR, "Could not determine pid of the other running instance!");
739 res = -1;
740 }
741 }
742 goto terminate;
743 }
744 }
745 if (lockfile) {
746 unlink(lockfile);
747 }
748
749 if (opt_exit) {
750 usbmuxd_log(LL_NOTICE, "No running instance found, none killed. Exiting.");
751 goto terminate;
752 }
753
754 if (!foreground) {
755 if ((res = daemonize()) < 0) {
756 fprintf(stderr, "usbmuxd: FATAL: Could not daemonize!\n");
757 usbmuxd_log(LL_FATAL, "Could not daemonize!");
758 goto terminate;
759 }
760 }
761
762 if (lockfile) {
763 // now open the lockfile and place the lock
764 res = lfd = open(lockfile, O_WRONLY|O_CREAT|O_TRUNC|O_EXCL, 0644);
765 if(res < 0) {
766 usbmuxd_log(LL_FATAL, "Could not open pidfile '%s'", lockfile);
767 goto terminate;
768 }
769 lock.l_type = F_WRLCK;
770 lock.l_whence = SEEK_SET;
771 lock.l_start = 0;
772 lock.l_len = 0;
773 if ((res = fcntl(lfd, F_SETLK, &lock)) < 0) {
774 usbmuxd_log(LL_FATAL, "Locking pidfile '%s' failed!", lockfile);
775 goto terminate;
776 }
777 sprintf(pids, "%d", getpid());
778 if ((size_t)(res = write(lfd, pids, strlen(pids))) != strlen(pids)) {
779 usbmuxd_log(LL_FATAL, "Could not write pidfile!");
780 if(res >= 0)
781 res = -2;
782 goto terminate;
783 }
784 }
785
786 // set number of file descriptors to higher value
787 struct rlimit rlim;
788 getrlimit(RLIMIT_NOFILE, &rlim);
789 rlim.rlim_max = 65536;
790 setrlimit(RLIMIT_NOFILE, (const struct rlimit*)&rlim);
791
792 usbmuxd_log(LL_INFO, "Creating socket");
793 res = listenfd = create_socket();
794 if(listenfd < 0)
795 goto terminate;
796
797#ifdef HAVE_LIBIMOBILEDEVICE
798 const char* userprefdir = config_get_config_dir();
799 struct stat fst;
800 memset(&fst, '\0', sizeof(struct stat));
801 if (stat(userprefdir, &fst) < 0) {
802 if (mkdir(userprefdir, 0775) < 0) {
803 usbmuxd_log(LL_FATAL, "Failed to create required directory '%s': %s", userprefdir, strerror(errno));
804 res = -1;
805 goto terminate;
806 }
807 if (stat(userprefdir, &fst) < 0) {
808 usbmuxd_log(LL_FATAL, "stat() failed after creating directory '%s': %s", userprefdir, strerror(errno));
809 res = -1;
810 goto terminate;
811 }
812 }
813
814 // make sure permission bits are set correctly
815 if (fst.st_mode != 02775) {
816 if (chmod(userprefdir, 02775) < 0) {
817 usbmuxd_log(LL_WARNING, "chmod(%s, 02775) failed: %s", userprefdir, strerror(errno));
818 }
819 }
820#endif
821
822 // drop elevated privileges
823 if (drop_privileges && (getuid() == 0 || geteuid() == 0)) {
824 struct passwd *pw;
825 if (!drop_user) {
826 usbmuxd_log(LL_FATAL, "No user to drop privileges to?");
827 res = -1;
828 goto terminate;
829 }
830 pw = getpwnam(drop_user);
831 if (!pw) {
832 usbmuxd_log(LL_FATAL, "Dropping privileges failed, check if user '%s' exists!", drop_user);
833 res = -1;
834 goto terminate;
835 }
836 if (pw->pw_uid == 0) {
837 usbmuxd_log(LL_INFO, "Not dropping privileges to root");
838 } else {
839#ifdef HAVE_LIBIMOBILEDEVICE
840 /* make sure the non-privileged user has proper access to the config directory */
841 if ((fst.st_uid != pw->pw_uid) || (fst.st_gid != pw->pw_gid)) {
842 if (chown(userprefdir, pw->pw_uid, pw->pw_gid) < 0) {
843 usbmuxd_log(LL_WARNING, "chown(%s, %d, %d) failed: %s", userprefdir, pw->pw_uid, pw->pw_gid, strerror(errno));
844 }
845 }
846#endif
847
848 if ((res = initgroups(drop_user, pw->pw_gid)) < 0) {
849 usbmuxd_log(LL_FATAL, "Failed to drop privileges (cannot set supplementary groups)");
850 goto terminate;
851 }
852 if ((res = setgid(pw->pw_gid)) < 0) {
853 usbmuxd_log(LL_FATAL, "Failed to drop privileges (cannot set group ID to %d)", pw->pw_gid);
854 goto terminate;
855 }
856 if ((res = setuid(pw->pw_uid)) < 0) {
857 usbmuxd_log(LL_FATAL, "Failed to drop privileges (cannot set user ID to %d)", pw->pw_uid);
858 goto terminate;
859 }
860
861 // security check
862 if (setuid(0) != -1) {
863 usbmuxd_log(LL_FATAL, "Failed to drop privileges properly!");
864 res = -1;
865 goto terminate;
866 }
867 if (getuid() != pw->pw_uid || getgid() != pw->pw_gid) {
868 usbmuxd_log(LL_FATAL, "Failed to drop privileges properly!");
869 res = -1;
870 goto terminate;
871 }
872 usbmuxd_log(LL_NOTICE, "Successfully dropped privileges to '%s'", drop_user);
873 }
874 }
875
876 client_init();
877 device_init();
878 usbmuxd_log(LL_INFO, "Initializing USB");
879 if((res = usb_init()) < 0)
880 goto terminate;
881
882 usbmuxd_log(LL_INFO, "%d device%s detected", res, (res==1)?"":"s");
883
884 usbmuxd_log(LL_NOTICE, "Initialization complete");
885
886 if (report_to_parent)
887 if((res = notify_parent(0)) < 0)
888 goto terminate;
889
890 if(opt_disable_hotplug) {
891 usbmuxd_log(LL_NOTICE, "Automatic device discovery on hotplug disabled.");
892 usb_autodiscover(0); // discovery to be triggered by new instance
893 }
894 if (opt_enable_exit) {
895 usbmuxd_log(LL_NOTICE, "Enabled exit on SIGUSR1 if no devices are attached. Start a new instance with \"--exit\" to trigger.");
896 }
897
898 res = main_loop(listenfd);
899 if(res < 0)
900 usbmuxd_log(LL_FATAL, "main_loop failed");
901
902 usbmuxd_log(LL_NOTICE, "usbmuxd shutting down");
903 device_kill_connections();
904 usb_shutdown();
905 device_shutdown();
906 client_shutdown();
907 usbmuxd_log(LL_NOTICE, "Shutdown complete");
908
909terminate:
910 log_disable_syslog();
911
912 if (res < 0)
913 res = -res;
914 else
915 res = 0;
916 if (report_to_parent)
917 notify_parent(res);
918
919 return res;
920}
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..d3cb17c
--- /dev/null
+++ b/src/usb.c
@@ -0,0 +1,1084 @@
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
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 <= 4) {
408 // Assume this is initial mode
409 return 1;
410 }
411
412 if(devdesc.bNumConfigurations != 5) {
413 // No known modes with more then 5 configurations
414 return 0;
415 }
416
417 if((res = libusb_get_config_descriptor_by_value(dev, 5, &config)) != 0) {
418 usbmuxd_log(LL_NOTICE, "Could not get configuration 5 descriptor for device %i-%i: %s", bus, address, libusb_error_name(res));
419 return 0;
420 }
421
422 // Require both usbmux and one of the other interfaces to determine this is a valid configuration
423 for(j = 0 ; j < config->bNumInterfaces ; j++) {
424 const struct libusb_interface_descriptor *intf = &config->interface[j].altsetting[0];
425 if(intf->bInterfaceClass == INTERFACE_CLASS &&
426 intf->bInterfaceSubClass == 42 &&
427 intf->bInterfaceProtocol == 255) {
428 has_valeria = 1;
429 }
430 // https://github.com/torvalds/linux/blob/72a85e2b0a1e1e6fb4ee51ae902730212b2de25c/include/uapi/linux/usb/cdc.h#L22
431 // 2 for Communication class, 0xd for CDC NCM subclass
432 if(intf->bInterfaceClass == 2 &&
433 intf->bInterfaceSubClass == 0xd) {
434 has_cdc_ncm = 1;
435 }
436 if(intf->bInterfaceClass == INTERFACE_CLASS &&
437 intf->bInterfaceSubClass == INTERFACE_SUBCLASS &&
438 intf->bInterfaceProtocol == INTERFACE_PROTOCOL) {
439 has_usbmux = 1;
440 }
441 }
442
443 libusb_free_config_descriptor(config);
444
445 if(has_valeria && has_usbmux) {
446 usbmuxd_log(LL_NOTICE, "Found Valeria and Apple USB Multiplexor in device %i-%i configuration 5", bus, address);
447 return 2;
448 }
449
450 if(has_cdc_ncm && has_usbmux) {
451 usbmuxd_log(LL_NOTICE, "Found CDC-NCM and Apple USB Multiplexor in device %i-%i configuration 5", bus, address);
452 return 3;
453 }
454
455 return 0;
456}
457
458/// @brief Finds and sets the valid configuration, interface and endpoints on the usb_device
459static int set_valid_configuration(struct libusb_device* dev, struct usb_device *usbdev, struct libusb_device_handle *handle)
460{
461 int j, k, res, found = 0;
462 struct libusb_config_descriptor *config;
463 const struct libusb_interface_descriptor *intf;
464 struct libusb_device_descriptor devdesc = usbdev->devdesc;
465 int bus = usbdev->bus;
466 int address = usbdev->address;
467 int current_config = 0;
468
469 if((res = libusb_get_configuration(handle, &current_config)) != 0) {
470 usbmuxd_log(LL_WARNING, "Could not get current configuration for device %d-%d: %s", bus, address, libusb_error_name(res));
471 return -1;
472 }
473
474 for(j = devdesc.bNumConfigurations ; j > 0 ; j--) {
475 if((res = libusb_get_config_descriptor_by_value(dev, j, &config)) != 0) {
476 usbmuxd_log(LL_NOTICE, "Could not get configuration %i descriptor for device %i-%i: %s", j, bus, address, libusb_error_name(res));
477 continue;
478 }
479 for(k = 0 ; k < config->bNumInterfaces ; k++) {
480 intf = &config->interface[k].altsetting[0];
481 if(intf->bInterfaceClass == INTERFACE_CLASS ||
482 intf->bInterfaceSubClass == INTERFACE_SUBCLASS ||
483 intf->bInterfaceProtocol == INTERFACE_PROTOCOL) {
484 usbmuxd_log(LL_NOTICE, "Found usbmux interface for device %i-%i: %i", bus, address, intf->bInterfaceNumber);
485 if(intf->bNumEndpoints != 2) {
486 usbmuxd_log(LL_WARNING, "Endpoint count mismatch for interface %i of device %i-%i", intf->bInterfaceNumber, bus, address);
487 continue;
488 }
489 if((intf->endpoint[0].bEndpointAddress & 0x80) == LIBUSB_ENDPOINT_OUT &&
490 (intf->endpoint[1].bEndpointAddress & 0x80) == LIBUSB_ENDPOINT_IN) {
491 usbdev->interface = intf->bInterfaceNumber;
492 usbdev->ep_out = intf->endpoint[0].bEndpointAddress;
493 usbdev->ep_in = intf->endpoint[1].bEndpointAddress;
494 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);
495 found = 1;
496 break;
497 } else if((intf->endpoint[1].bEndpointAddress & 0x80) == LIBUSB_ENDPOINT_OUT &&
498 (intf->endpoint[0].bEndpointAddress & 0x80) == LIBUSB_ENDPOINT_IN) {
499 usbdev->interface = intf->bInterfaceNumber;
500 usbdev->ep_out = intf->endpoint[1].bEndpointAddress;
501 usbdev->ep_in = intf->endpoint[0].bEndpointAddress;
502 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);
503 found = 1;
504 break;
505 } else {
506 usbmuxd_log(LL_WARNING, "Endpoint type mismatch for interface %i of device %i-%i", intf->bInterfaceNumber, bus, address);
507 }
508 }
509 }
510 if(!found) {
511 libusb_free_config_descriptor(config);
512 continue;
513 }
514 // If set configuration is required, try to first detach all kernel drivers
515 if (current_config == 0) {
516 usbmuxd_log(LL_DEBUG, "Device %d-%d is unconfigured", bus, address);
517 }
518 if(current_config == 0 || config->bConfigurationValue != current_config) {
519 usbmuxd_log(LL_NOTICE, "Changing configuration of device %i-%i: %i -> %i", bus, address, current_config, config->bConfigurationValue);
520 for(k=0 ; k < config->bNumInterfaces ; k++) {
521 const struct libusb_interface_descriptor *intf1 = &config->interface[k].altsetting[0];
522 if((res = libusb_kernel_driver_active(handle, intf1->bInterfaceNumber)) < 0) {
523 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));
524 continue;
525 }
526 if(res == 1) {
527 usbmuxd_log(LL_INFO, "Detaching kernel driver for device %d-%d, interface %d", bus, address, intf1->bInterfaceNumber);
528 if((res = libusb_detach_kernel_driver(handle, intf1->bInterfaceNumber)) < 0) {
529 usbmuxd_log(LL_WARNING, "Could not detach kernel driver, configuration change will probably fail! %s", libusb_error_name(res));
530 continue;
531 }
532 }
533 }
534 if((res = libusb_set_configuration(handle, j)) != 0) {
535 usbmuxd_log(LL_WARNING, "Could not set configuration %d for device %d-%d: %s", j, bus, address, libusb_error_name(res));
536 libusb_free_config_descriptor(config);
537 continue;
538 }
539 }
540
541 libusb_free_config_descriptor(config);
542 break;
543 }
544
545 if(!found) {
546 usbmuxd_log(LL_WARNING, "Could not find a suitable USB interface for device %i-%i", bus, address);
547 return -1;
548 }
549
550 return 0;
551}
552
553static void device_complete_initialization(struct mode_context *context, struct libusb_device_handle *handle)
554{
555 struct usb_device *usbdev = find_device(context->bus, context->address);
556 if(!usbdev) {
557 usbmuxd_log(LL_ERROR, "Device %d-%d is missing from device list, aborting initialization", context->bus, context->address);
558 return;
559 }
560 struct libusb_device *dev = context->dev;
561 struct libusb_device_descriptor devdesc = usbdev->devdesc;
562 int bus = context->bus;
563 int address = context->address;
564 int res;
565 struct libusb_transfer *transfer;
566
567 if((res = set_valid_configuration(dev, usbdev, handle)) != 0) {
568 usbdev->alive = 0;
569 return;
570 }
571
572 if((res = libusb_claim_interface(handle, usbdev->interface)) != 0) {
573 usbmuxd_log(LL_WARNING, "Could not claim interface %d for device %d-%d: %s", usbdev->interface, bus, address, libusb_error_name(res));
574 usbdev->alive = 0;
575 return;
576 }
577
578 transfer = libusb_alloc_transfer(0);
579 if(!transfer) {
580 usbmuxd_log(LL_WARNING, "Failed to allocate transfer for device %d-%d: %s", bus, address, libusb_error_name(res));
581 usbdev->alive = 0;
582 return;
583 }
584
585 unsigned char *transfer_buffer = malloc(1024 + LIBUSB_CONTROL_SETUP_SIZE + 8);
586 if (!transfer_buffer) {
587 usbmuxd_log(LL_WARNING, "Failed to allocate transfer buffer for device %d-%d: %s", bus, address, libusb_error_name(res));
588 usbdev->alive = 0;
589 return;
590 }
591 memset(transfer_buffer, '\0', 1024 + LIBUSB_CONTROL_SETUP_SIZE + 8);
592
593 usbdev->serial[0] = 0;
594 usbdev->bus = bus;
595 usbdev->address = address;
596 usbdev->devdesc = devdesc;
597 usbdev->speed = 480000000;
598 usbdev->handle = handle;
599 usbdev->alive = 1;
600 usbdev->wMaxPacketSize = libusb_get_max_packet_size(dev, usbdev->ep_out);
601 if (usbdev->wMaxPacketSize <= 0) {
602 usbmuxd_log(LL_ERROR, "Could not determine wMaxPacketSize for device %d-%d, setting to 64", usbdev->bus, usbdev->address);
603 usbdev->wMaxPacketSize = 64;
604 } else {
605 usbmuxd_log(LL_INFO, "Using wMaxPacketSize=%d for device %d-%d", usbdev->wMaxPacketSize, usbdev->bus, usbdev->address);
606 }
607
608 switch (libusb_get_device_speed(dev)) {
609 case LIBUSB_SPEED_LOW:
610 usbdev->speed = 1500000;
611 break;
612 case LIBUSB_SPEED_FULL:
613 usbdev->speed = 12000000;
614 break;
615 case LIBUSB_SPEED_SUPER:
616 usbdev->speed = 5000000000;
617 break;
618 case LIBUSB_SPEED_HIGH:
619 case LIBUSB_SPEED_UNKNOWN:
620 default:
621 usbdev->speed = 480000000;
622 break;
623 }
624
625 usbmuxd_log(LL_INFO, "USB Speed is %g MBit/s for device %d-%d", (double)(usbdev->speed / 1000000.0), usbdev->bus, usbdev->address);
626
627 /**
628 * From libusb:
629 * Asking for the zero'th index is special - it returns a string
630 * descriptor that contains all the language IDs supported by the
631 * device.
632 **/
633 libusb_fill_control_setup(transfer_buffer, LIBUSB_ENDPOINT_IN, LIBUSB_REQUEST_GET_DESCRIPTOR, LIBUSB_DT_STRING << 8, 0, 1024 + LIBUSB_CONTROL_SETUP_SIZE);
634 libusb_fill_control_transfer(transfer, handle, transfer_buffer, get_langid_callback, usbdev, 1000);
635
636 if((res = libusb_submit_transfer(transfer)) < 0) {
637 usbmuxd_log(LL_ERROR, "Could not request transfer for device %d-%d: %s", usbdev->bus, usbdev->address, libusb_error_name(res));
638 libusb_free_transfer(transfer);
639 free(transfer_buffer);
640 usbdev->alive = 0;
641 return;
642 }
643}
644
645static void switch_mode_cb(struct libusb_transfer* transfer)
646{
647 // For old devices not supporting mode swtich, if anything goes wrong - continue in current mode
648 struct mode_context* context = transfer->user_data;
649 struct usb_device *dev = find_device(context->bus, context->address);
650 if(!dev) {
651 usbmuxd_log(LL_WARNING, "Device %d-%d is missing from device list", context->bus, context->address);
652 }
653 if(transfer->status != LIBUSB_TRANSFER_COMPLETED) {
654 usbmuxd_log(LL_ERROR, "Failed to request mode switch for device %i-%i (%i). Completing initialization in current mode",
655 context->bus, context->address, transfer->status);
656 device_complete_initialization(context, transfer->dev_handle);
657 }
658 else {
659 unsigned char *data = libusb_control_transfer_get_data(transfer);
660 if(data[0] != 0) {
661 usbmuxd_log(LL_INFO, "Received unexpected response for device %i-%i mode switch (%i). Completing initialization in current mode",
662 context->bus, context->address, data[0]);
663 device_complete_initialization(context, transfer->dev_handle);
664 }
665 }
666 free(context);
667 if(transfer->buffer)
668 free(transfer->buffer);
669}
670
671static void get_mode_cb(struct libusb_transfer* transfer)
672{
673 // For old devices not supporting mode swtich, if anything goes wrong - continue in current mode
674 int res;
675 struct mode_context* context = transfer->user_data;
676 struct usb_device *dev = find_device(context->bus, context->address);
677 if(!dev) {
678 usbmuxd_log(LL_ERROR, "Device %d-%d is missing from device list, aborting mode switch", context->bus, context->address);
679 free(context);
680 return;
681 }
682
683 if(transfer->status != LIBUSB_TRANSFER_COMPLETED) {
684 usbmuxd_log(LL_ERROR, "Failed to request get mode for device %i-%i (%i). Completing initialization in current mode",
685 context->bus, context->address, transfer->status);
686 device_complete_initialization(context, transfer->dev_handle);
687 free(context);
688 return;
689 }
690
691 unsigned char *data = libusb_control_transfer_get_data(transfer);
692
693 char* desired_mode_char = getenv(ENV_DEVICE_MODE);
694 int desired_mode = desired_mode_char ? atoi(desired_mode_char) : 3;
695 int guessed_mode = guess_mode(context->dev, dev);
696
697 // Response is 3:3:3:0 for initial mode, 5:3:3:0 otherwise.
698 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);
699 if(desired_mode >= 1 && desired_mode <= 3 &&
700 guessed_mode > 0 && // do not switch mode if guess failed
701 guessed_mode != desired_mode) {
702 usbmuxd_log(LL_WARNING, "Switching device %i-%i mode to %i", context->bus, context->address, desired_mode);
703
704 context->bRequest = APPLE_VEND_SPECIFIC_SET_MODE;
705 context->wValue = 0;
706 context->wIndex = desired_mode;
707 context->wLength = 1;
708
709 if((res = submit_vendor_specific(transfer->dev_handle, context, switch_mode_cb)) != 0) {
710 usbmuxd_log(LL_WARNING, "Could not request to switch mode %i for device %i-%i (%i)", context->wIndex, context->bus, context->address, res);
711 dev->alive = 0;
712 free(context);
713 }
714 }
715 else {
716 usbmuxd_log(LL_WARNING, "Skipping switch device %i-%i mode from %i to %i", context->bus, context->address, guessed_mode, desired_mode);
717 device_complete_initialization(context, transfer->dev_handle);
718 free(context);
719 }
720 if(transfer->buffer)
721 free(transfer->buffer);
722}
723
724static int usb_device_add(libusb_device* dev)
725{
726 int res;
727 // the following are non-blocking operations on the device list
728 uint8_t bus = libusb_get_bus_number(dev);
729 uint8_t address = libusb_get_device_address(dev);
730 struct libusb_device_descriptor devdesc;
731 struct usb_device *usbdev = find_device(bus, address);
732 if(usbdev) {
733 usbdev->alive = 1;
734 return 0; //device already found
735 }
736
737 if((res = libusb_get_device_descriptor(dev, &devdesc)) != 0) {
738 usbmuxd_log(LL_WARNING, "Could not get device descriptor for device %d-%d: %s", bus, address, libusb_error_name(res));
739 return -1;
740 }
741 if(devdesc.idVendor != VID_APPLE)
742 return -1;
743 if((devdesc.idProduct != PID_APPLE_T2_COPROCESSOR) &&
744 ((devdesc.idProduct < PID_APPLE_SILICON_RESTORE_LOW) ||
745 (devdesc.idProduct > PID_APPLE_SILICON_RESTORE_MAX)) &&
746 ((devdesc.idProduct < PID_RANGE_LOW) ||
747 (devdesc.idProduct > PID_RANGE_MAX)))
748 return -1;
749 libusb_device_handle *handle;
750 usbmuxd_log(LL_INFO, "Found new device with v/p %04x:%04x at %d-%d", devdesc.idVendor, devdesc.idProduct, bus, address);
751 // No blocking operation can follow: it may be run in the libusb hotplug callback and libusb will refuse any
752 // blocking call
753 if((res = libusb_open(dev, &handle)) != 0) {
754 usbmuxd_log(LL_WARNING, "Could not open device %d-%d: %s", bus, address, libusb_error_name(res));
755 return -1;
756 }
757
758 // Add the created handle to the device list, so we can close it in case of failure/disconnection
759 usbdev = malloc(sizeof(struct usb_device));
760 memset(usbdev, 0, sizeof(*usbdev));
761
762 usbdev->serial[0] = 0;
763 usbdev->bus = bus;
764 usbdev->address = address;
765 usbdev->devdesc = devdesc;
766 usbdev->speed = 0;
767 usbdev->handle = handle;
768 usbdev->alive = 1;
769
770 collection_init(&usbdev->tx_xfers);
771 collection_init(&usbdev->rx_xfers);
772
773 collection_add(&device_list, usbdev);
774
775 // On top of configurations, Apple have multiple "modes" for devices, namely:
776 // 1: An "initial" mode with 4 configurations
777 // 2: "Valeria" mode, where configuration 5 is included with interface for H.265 video capture (activated when recording screen with QuickTime in macOS)
778 // 3: "CDC NCM" mode, where configuration 5 is included with interface for Ethernet/USB (activated using internet-sharing feature in macOS)
779 // Request current mode asynchroniously, so it can be changed in callback if needed
780 usbmuxd_log(LL_INFO, "Requesting current mode from device %i-%i", bus, address);
781 struct mode_context* context = malloc(sizeof(struct mode_context));
782 context->dev = dev;
783 context->bus = bus;
784 context->address = address;
785 context->bRequest = APPLE_VEND_SPECIFIC_GET_MODE;
786 context->wValue = 0;
787 context->wIndex = 0;
788 context->wLength = 4;
789 context->timeout = 1000;
790
791 if(submit_vendor_specific(handle, context, get_mode_cb) != 0) {
792 usbmuxd_log(LL_WARNING, "Could not request current mode from device %d-%d", bus, address);
793 // Schedule device for close and cleanup
794 usbdev->alive = 0;
795 return -1;
796 }
797 return 0;
798}
799
800int usb_discover(void)
801{
802 int cnt, i;
803 int valid_count = 0;
804 libusb_device **devs;
805
806 cnt = libusb_get_device_list(NULL, &devs);
807 if(cnt < 0) {
808 usbmuxd_log(LL_WARNING, "Could not get device list: %d", cnt);
809 devlist_failures++;
810 // sometimes libusb fails getting the device list if you've just removed something
811 if(devlist_failures > 5) {
812 usbmuxd_log(LL_FATAL, "Too many errors getting device list");
813 return cnt;
814 } else {
815 get_tick_count(&next_dev_poll_time);
816 next_dev_poll_time.tv_usec += DEVICE_POLL_TIME * 1000;
817 next_dev_poll_time.tv_sec += next_dev_poll_time.tv_usec / 1000000;
818 next_dev_poll_time.tv_usec = next_dev_poll_time.tv_usec % 1000000;
819 return 0;
820 }
821 }
822 devlist_failures = 0;
823
824 usbmuxd_log(LL_SPEW, "usb_discover: scanning %d devices", cnt);
825
826 // Mark all devices as dead, and do a mark-sweep like
827 // collection of dead devices
828 FOREACH(struct usb_device *usbdev, &device_list) {
829 usbdev->alive = 0;
830 } ENDFOREACH
831
832 // Enumerate all USB devices and mark the ones we already know
833 // about as live, again
834 for(i=0; i<cnt; i++) {
835 libusb_device *dev = devs[i];
836 if (usb_device_add(dev) < 0) {
837 continue;
838 }
839 valid_count++;
840 }
841
842 // Clean out any device we didn't mark back as live
843 reap_dead_devices();
844
845 libusb_free_device_list(devs, 1);
846
847 get_tick_count(&next_dev_poll_time);
848 next_dev_poll_time.tv_usec += DEVICE_POLL_TIME * 1000;
849 next_dev_poll_time.tv_sec += next_dev_poll_time.tv_usec / 1000000;
850 next_dev_poll_time.tv_usec = next_dev_poll_time.tv_usec % 1000000;
851
852 return valid_count;
853}
854
855const char *usb_get_serial(struct usb_device *dev)
856{
857 if(!dev->handle) {
858 return NULL;
859 }
860 return dev->serial;
861}
862
863uint32_t usb_get_location(struct usb_device *dev)
864{
865 if(!dev->handle) {
866 return 0;
867 }
868 return (dev->bus << 16) | dev->address;
869}
870
871uint16_t usb_get_pid(struct usb_device *dev)
872{
873 if(!dev->handle) {
874 return 0;
875 }
876 return dev->devdesc.idProduct;
877}
878
879uint64_t usb_get_speed(struct usb_device *dev)
880{
881 if (!dev->handle) {
882 return 0;
883 }
884 return dev->speed;
885}
886
887void usb_get_fds(struct fdlist *list)
888{
889 const struct libusb_pollfd **usbfds;
890 const struct libusb_pollfd **p;
891 usbfds = libusb_get_pollfds(NULL);
892 if(!usbfds) {
893 usbmuxd_log(LL_ERROR, "libusb_get_pollfds failed");
894 return;
895 }
896 p = usbfds;
897 while(*p) {
898 fdlist_add(list, FD_USB, (*p)->fd, (*p)->events);
899 p++;
900 }
901 free(usbfds);
902}
903
904void usb_autodiscover(int enable)
905{
906 usbmuxd_log(LL_DEBUG, "usb polling enable: %d", enable);
907 device_polling = enable;
908 device_hotplug = enable;
909}
910
911static int dev_poll_remain_ms(void)
912{
913 int msecs;
914 struct timeval tv;
915 if(!device_polling)
916 return 100000; // devices will never be polled if this is > 0
917 get_tick_count(&tv);
918 msecs = (next_dev_poll_time.tv_sec - tv.tv_sec) * 1000;
919 msecs += (next_dev_poll_time.tv_usec - tv.tv_usec) / 1000;
920 if(msecs < 0)
921 return 0;
922 return msecs;
923}
924
925int usb_get_timeout(void)
926{
927 struct timeval tv;
928 int msec;
929 int res;
930 int pollrem;
931 pollrem = dev_poll_remain_ms();
932 res = libusb_get_next_timeout(NULL, &tv);
933 if(res == 0)
934 return pollrem;
935 if(res < 0) {
936 usbmuxd_log(LL_ERROR, "libusb_get_next_timeout failed: %s", libusb_error_name(res));
937 return pollrem;
938 }
939 msec = tv.tv_sec * 1000;
940 msec += tv.tv_usec / 1000;
941 if(msec > pollrem)
942 return pollrem;
943 return msec;
944}
945
946int usb_process(void)
947{
948 int res;
949 struct timeval tv;
950 tv.tv_sec = tv.tv_usec = 0;
951 res = libusb_handle_events_timeout(NULL, &tv);
952 if(res < 0) {
953 usbmuxd_log(LL_ERROR, "libusb_handle_events_timeout failed: %s", libusb_error_name(res));
954 return res;
955 }
956
957 // reap devices marked dead due to an RX error
958 reap_dead_devices();
959
960 if(dev_poll_remain_ms() <= 0) {
961 res = usb_discover();
962 if(res < 0) {
963 usbmuxd_log(LL_ERROR, "usb_discover failed: %s", libusb_error_name(res));
964 return res;
965 }
966 }
967 return 0;
968}
969
970int usb_process_timeout(int msec)
971{
972 int res;
973 struct timeval tleft, tcur, tfin;
974 get_tick_count(&tcur);
975 tfin.tv_sec = tcur.tv_sec + (msec / 1000);
976 tfin.tv_usec = tcur.tv_usec + (msec % 1000) * 1000;
977 tfin.tv_sec += tfin.tv_usec / 1000000;
978 tfin.tv_usec %= 1000000;
979 while((tfin.tv_sec > tcur.tv_sec) || ((tfin.tv_sec == tcur.tv_sec) && (tfin.tv_usec > tcur.tv_usec))) {
980 tleft.tv_sec = tfin.tv_sec - tcur.tv_sec;
981 tleft.tv_usec = tfin.tv_usec - tcur.tv_usec;
982 if(tleft.tv_usec < 0) {
983 tleft.tv_usec += 1000000;
984 tleft.tv_sec -= 1;
985 }
986 res = libusb_handle_events_timeout(NULL, &tleft);
987 if(res < 0) {
988 usbmuxd_log(LL_ERROR, "libusb_handle_events_timeout failed: %s", libusb_error_name(res));
989 return res;
990 }
991 // reap devices marked dead due to an RX error
992 reap_dead_devices();
993 get_tick_count(&tcur);
994 }
995 return 0;
996}
997
998#ifdef HAVE_LIBUSB_HOTPLUG_API
999static libusb_hotplug_callback_handle usb_hotplug_cb_handle;
1000
1001static int usb_hotplug_cb(libusb_context *ctx, libusb_device *device, libusb_hotplug_event event, void *user_data)
1002{
1003 if (LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED == event) {
1004 if (device_hotplug) {
1005 usb_device_add(device);
1006 }
1007 } else if (LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT == event) {
1008 uint8_t bus = libusb_get_bus_number(device);
1009 uint8_t address = libusb_get_device_address(device);
1010 FOREACH(struct usb_device *usbdev, &device_list) {
1011 if(usbdev->bus == bus && usbdev->address == address) {
1012 usbdev->alive = 0;
1013 device_remove(usbdev);
1014 break;
1015 }
1016 } ENDFOREACH
1017 } else {
1018 usbmuxd_log(LL_ERROR, "Unhandled event %d", event);
1019 }
1020 return 0;
1021}
1022#endif
1023
1024int usb_init(void)
1025{
1026 int res;
1027 const struct libusb_version* libusb_version_info = libusb_get_version();
1028 usbmuxd_log(LL_NOTICE, "Using libusb %d.%d.%d", libusb_version_info->major, libusb_version_info->minor, libusb_version_info->micro);
1029
1030 devlist_failures = 0;
1031 device_polling = 1;
1032 res = libusb_init(NULL);
1033
1034 if (res != 0) {
1035 usbmuxd_log(LL_FATAL, "libusb_init failed: %s", libusb_error_name(res));
1036 return -1;
1037 }
1038
1039#if LIBUSB_API_VERSION >= 0x01000106
1040 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)));
1041#else
1042 libusb_set_debug(NULL, (log_level >= LL_DEBUG ? LIBUSB_LOG_LEVEL_DEBUG: (log_level >= LL_WARNING ? LIBUSB_LOG_LEVEL_WARNING: LIBUSB_LOG_LEVEL_NONE)));
1043#endif
1044
1045 collection_init(&device_list);
1046
1047#ifdef HAVE_LIBUSB_HOTPLUG_API
1048 if (libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG)) {
1049 usbmuxd_log(LL_INFO, "Registering for libusb hotplug events");
1050 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);
1051 if (res == LIBUSB_SUCCESS) {
1052 device_polling = 0;
1053 } else {
1054 usbmuxd_log(LL_ERROR, "ERROR: Could not register for libusb hotplug events. %s", libusb_error_name(res));
1055 }
1056 } else {
1057 usbmuxd_log(LL_ERROR, "libusb does not support hotplug events");
1058 }
1059#endif
1060 if (device_polling) {
1061 res = usb_discover();
1062 if (res >= 0) {
1063 }
1064 } else {
1065 res = collection_count(&device_list);
1066 }
1067 return res;
1068}
1069
1070void usb_shutdown(void)
1071{
1072 usbmuxd_log(LL_DEBUG, "usb_shutdown");
1073
1074#ifdef HAVE_LIBUSB_HOTPLUG_API
1075 libusb_hotplug_deregister_callback(NULL, usb_hotplug_cb_handle);
1076#endif
1077
1078 FOREACH(struct usb_device *usbdev, &device_list) {
1079 device_remove(usbdev);
1080 usb_disconnect(usbdev);
1081 } ENDFOREACH
1082 collection_free(&device_list);
1083 libusb_exit(NULL);
1084}
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