diff options
-rw-r--r-- | .github/workflows/build.yml | 73 | ||||
-rw-r--r-- | Makefile.am | 8 | ||||
-rw-r--r-- | NEWS | 26 | ||||
-rw-r--r-- | README.md | 183 | ||||
-rwxr-xr-x | autogen.sh | 42 | ||||
-rw-r--r-- | configure.ac | 39 | ||||
-rw-r--r-- | docs/usbmuxd.8 | 8 | ||||
-rwxr-xr-x | git-version-gen | 19 | ||||
-rw-r--r-- | m4/as-compiler-flag.m4 | 4 | ||||
-rw-r--r-- | src/Makefile.am | 37 | ||||
-rw-r--r-- | src/client.c | 172 | ||||
-rw-r--r-- | src/conf.c | 39 | ||||
-rw-r--r-- | src/device.c | 98 | ||||
-rw-r--r-- | src/main.c | 304 | ||||
-rw-r--r-- | src/preflight.c | 70 | ||||
-rw-r--r-- | src/usb.c | 489 | ||||
-rw-r--r-- | src/usb.h | 6 | ||||
-rw-r--r-- | src/usbmuxd-proto.h | 2 | ||||
-rw-r--r-- | src/utils.c | 248 | ||||
-rw-r--r-- | src/utils.h | 43 | ||||
-rw-r--r-- | systemd/Makefile.am | 2 | ||||
-rw-r--r-- | systemd/usbmuxd.service.in | 2 | ||||
-rw-r--r-- | udev/39-usbmuxd.rules.in | 11 |
23 files changed, 1191 insertions, 734 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 @@ +name: build + +on: [push] + +jobs: + build-linux-ubuntu: + runs-on: ubuntu-latest + steps: + - name: install dependencies + run: | + sudo apt-get update + sudo apt-get install libusb-1.0-0-dev + - name: prepare environment + run: | + echo "target_triplet=`gcc -dumpmachine`" >> $GITHUB_ENV + - name: fetch libplist + uses: dawidd6/action-download-artifact@v2 + with: + github_token: ${{secrets.GITHUB_TOKEN}} + workflow: build.yml + name: libplist-latest_${{env.target_triplet}} + repo: libimobiledevice/libplist + - name: fetch libusbmuxd + uses: dawidd6/action-download-artifact@v2 + with: + github_token: ${{secrets.GITHUB_TOKEN}} + workflow: build.yml + name: libusbmuxd-latest_${{env.target_triplet}} + repo: libimobiledevice/libusbmuxd + - name: fetch libimobiledevice-glue + uses: dawidd6/action-download-artifact@v2 + with: + github_token: ${{secrets.GITHUB_TOKEN}} + workflow: build.yml + name: libimobiledevice-glue-latest_${{env.target_triplet}} + repo: libimobiledevice/libimobiledevice-glue + - name: fetch libimobiledevice + uses: dawidd6/action-download-artifact@v2 + with: + github_token: ${{secrets.GITHUB_TOKEN}} + workflow: build.yml + name: libimobiledevice-latest_${{env.target_triplet}} + repo: libimobiledevice/libimobiledevice + - name: install external dependencies + run: | + mkdir extract + for I in *.tar; do + tar -C extract -xvf $I + done + sudo cp -r extract/* / + sudo ldconfig + - uses: actions/checkout@v3 + with: + fetch-depth: 0 + - name: autogen + run: ./autogen.sh PKG_CONFIG_PATH=/usr/local/lib/pkgconfig + - name: print config.log + if: ${{ failure() }} + run: cat config.log + - name: make + run: make + - name: make install + run: sudo make install + - name: prepare artifact + run: | + mkdir -p dest + DESTDIR=`pwd`/dest make install + tar -C dest -cf usbmuxd.tar usr lib + - name: publish artifact + uses: actions/upload-artifact@v3 + with: + name: usbmuxd-latest_${{env.target_triplet}} + path: usbmuxd.tar diff --git a/Makefile.am b/Makefile.am index a4d86c0..6da23b6 100644 --- a/Makefile.am +++ b/Makefile.am @@ -2,8 +2,12 @@ AUTOMAKE_OPTIONS = foreign ACLOCAL_AMFLAGS = -I m4 SUBDIRS = src $(UDEV_SUB) $(SYSTEMD_SUB) docs -EXTRA_DIST = docs COPYING.GPLv2 COPYING.GPLv3 +EXTRA_DIST = \ + docs \ + COPYING.GPLv2 \ + COPYING.GPLv3 \ + README.md -DISTCHECK_CONFIGURE_FLAGS = \ +DISTCHECK_CONFIGURE_FLAGS = \ --with-udevrulesdir=$$dc_install_base/$(udevrulesdir) \ --with-systemdsystemunitdir=$$dc_install_base/$(systemdsystemunitdir)
\ No newline at end of file @@ -6,8 +6,34 @@ Version 1.1.1 - Use clock_gettime() instead of gettimeofday() to avoid timing issues when calculating packet timeouts - Get correct USB device speed instead of hardcoded value + - Bump libusb dependency to 1.0.9 - Use non-blocking sockets for client communication to avoid hanging - Use correct manual section (8) for manpage + - Log pid of connecting clients if supported + - Implement device discovery using libusb hotplug events + - Fix wrong timeout value in debug messages + - Log error message if writing a config file fails + - Fix blocking by using libusb asynchronous I/O for getting initial device + information + - Tag all udev events with systemd tag + - Fix occasional USB reconfiguration due to udev rules being run again + - Fix wrong timestamps when running in foreground + - Set socket options for client connections to improve performance + - Implement "ListListeners" usbmux command handling + - Bump libimobiledevice dependency to 1.3.0 + - Bump libplist dependency to 2.2.0 + - Add support for iPhone XS/XR UDID format + - Add option to allow logging to dedicated logfile + - Convert README file to markdown format + - Fix USB reconnection issues on virtual machines with iOS 11+ devices + - Add support for connecting with T2 chip + - Various memory leak, deadlock and invalid free fixes + - Show actual libusb version in debug message on startup + - Enable libusb debugging output + - Log client process name alongside pid if possible on Linux + - Unify and improve log message output + - Improve README.md with project description, installation, contributing and + usage sections Version 1.1.0 ~~~~~~~~~~~~~ @@ -1,84 +1,159 @@ # usbmuxd -## About +*A socket daemon to multiplex connections from and to iOS devices.* -A socket daemon to multiplex connections from and to iOS devices. +![build](https://github.com/libimobiledevice/usbmuxd/actions/workflows/build.yml/badge.svg) -## Background +## Features usbmuxd stands for "USB multiplexing daemon". This daemon is in charge of -multiplexing connections over USB to an iOS device. To users, it means -you can sync your music, contacts, photos, etc. over USB. To developers, it -means you can connect to any listening localhost socket on the device. usbmuxd -is not used for tethering data transfer which uses a dedicated USB interface as -a virtual network device. Multiple connections to different TCP ports can happen -in parallel. The higher-level layers are handled by libimobiledevice. - -When usbmuxd is running (normally started or stopped as a result of _udev_ -auto-insertion messages, or by _systemd_) it provides a socket interface in -`/var/run/usbmuxd` that is designed to be compatible with the socket interface -that is provided on macOS. - -You should also create a `usbmux` user that has access to USB devices on your -system. Alternatively, you can pass a different username using the `-U` argument. - -The daemon also manages pairing records with iOS devices and the host in -`/var/lib/lockdown` (Linux) or `/var/db/lockdown` (macOS). -Ensure proper permissions are setup for the daemon to access the directory. - -## Requirements - -Development Packages of: -* libimobiledevice -* libplist -* libusb - -Software: -* make -* autoheader -* automake -* autoconf -* libtool -* pkg-config -* gcc or clang -* udev (Linux only) +multiplexing connections over USB to an iOS device. + +To users, it means you can use various applications to interact with your +device. + +To developers, it means you can connect to any listening localhost socket on +the device. + +Some key features are: + +- **Implementation**: Open-Source implementation of proprietary usbmuxd daemon +- **Cross-Platform:** Tested on Linux, macOS, Windows and Android platforms +- **Linux**: Supports udev and systemd for automatic activation +- **Compatibility**: Supports latest device firmware releases +- **Scalability**: Supports multiple connections to different ports in parallel + +usbmuxd is not used for tethering data transfers which uses a dedicated USB +interface to act as a virtual network device. + +The higher-level layers, especially if you want to write an application to +interact with the device, are handled by [libimobiledevice](https://github.com/libimobiledevice/libimobiledevice.git). + +The low-level layer is handled by [libusbmuxd](https://github.com/libimobiledevice/libusbmuxd.git). + +## Installation / Getting started + +### Debian / Ubuntu Linux + +First install all required dependencies and build tools: +```shell +sudo apt-get install \ + build-essential \ + pkg-config \ + checkinstall \ + git \ + autoconf \ + automake \ + libtool-bin \ + libplist-dev \ + libusbmuxd-dev \ + libimobiledevice-dev \ + libimobiledevice-glue-dev \ + libusb-1.0-0-dev \ + udev +``` -Optional: -* systemd (Linux only) +If systemd is not installed and should control spawning the daemon use: +```shell +sudo apt-get install \ + systemd +``` -## Installation +Then clone the actual project repository: +```shell +git clone https://github.com/libimobiledevice/usbmuxd.git +cd usbmuxd +``` -To compile run: +Now you can build and install it: +```shell +./autogen.sh +make +sudo make install +``` +If you require a custom prefix or other option being passed to `./configure` +you can pass them directly to `./autogen.sh` like this: ```bash -./autogen.sh +./autogen.sh --prefix=/opt/local --without-preflight --without-systemd make sudo make install ``` +To output a list of available configure options use: +```bash +./autogen.sh --help +``` + +## Usage + The daemon is automatically started by udev or systemd depending on what you -have configured it on hotplug of an iOS device and exits if the last device +have configured upon hotplug of an iOS device and exits if the last device was unplugged. +When usbmuxd is running it provides a socket interface at `/var/run/usbmuxd` +that is designed to be compatible with the socket interface that is provided +on macOS. + +You should also create an `usbmux` user that has access to USB devices on your +system. Alternatively, just pass a different username using the `-U` argument. + +The daemon also manages pairing records with iOS devices and the host in +`/var/lib/lockdown` (Linux) or `/var/db/lockdown` (macOS). + +Ensure proper permissions are setup for the daemon to access the directory. + For debugging purposes it is helpful to start usbmuxd using the foreground `-f` argument and enable verbose mode `-v` to get suitable logs. -## Who/What/Where? +Please consult the usage information or manual page for a full documentation of +available command line options: +```shell +usbmuxd --help +man usbmuxd +``` + +## Contributing -* Home: https://www.libimobiledevice.org/ -* Code: `git clone https://git.libimobiledevice.org/usbmuxd.git` -* Code (Mirror): `git clone https://github.com/libimobiledevice/usbmuxd.git` -* Tickets: https://github.com/libimobiledevice/usbmuxd/issues +We welcome contributions from anyone and are grateful for every pull request! + +If you'd like to contribute, please fork the `master` branch, change, commit and +send a pull request for review. Once approved it can be merged into the main +code base. + +If you plan to contribute larger changes or a major refactoring, please create a +ticket first to discuss the idea upfront to ensure less effort for everyone. + +Please make sure your contribution adheres to: +* Try to follow the code style of the project +* Commit messages should describe the change well without being too short +* Try to split larger changes into individual commits of a common domain +* Use your real name and a valid email address for your commits + +We are still working on the guidelines so bear with us! + +## Links + +* Homepage: https://libimobiledevice.org/ +* Repository: https://git.libimobiledevice.org/usbmuxd.git +* Repository (Mirror): https://github.com/libimobiledevice/usbmuxd.git +* Issue Tracker: https://github.com/libimobiledevice/usbmuxd/issues * Mailing List: https://lists.libimobiledevice.org/mailman/listinfo/libimobiledevice-devel -* IRC: irc://irc.freenode.net#libimobiledevice * Twitter: https://twitter.com/libimobiledev +## License + +This library and utilities are licensed under the [GNU General Public License v3.0](https://www.gnu.org/licenses/gpl-3.0.en.html), +also included in the repository in the `COPYING.GPLv3` file. + ## Credits -The first usbmuxd daemon implementation was authored by Hector Martin. +The initial usbmuxd daemon implementation was authored by Hector Martin. + +Apple, iPhone, iPad, iPod, iPod Touch, Apple TV, Apple Watch, Mac, iOS, +iPadOS, tvOS, watchOS, and macOS are trademarks of Apple Inc. -Apple, iPhone, iPod, and iPod Touch are trademarks of Apple Inc. -libimobiledevice is an independent software library and has not been +usbmuxd is an independent software application and has not been authorized, sponsored, or otherwise approved by Apple Inc. -README Updated on: 2019-05-16 +README Updated on: 2022-04-04 @@ -1,19 +1,31 @@ #!/bin/sh -gprefix=`which glibtoolize 2>&1 >/dev/null` -if [ $? -eq 0 ]; then - glibtoolize --force -else - libtoolize --force -fi -aclocal -I m4 -autoheader -automake --add-missing -autoconf -requires_pkgconfig=`which pkg-config 2>&1 >/dev/null` -if [ $? -ne 0 ]; then - echo "Missing required pkg-config. Please install it on your system and run again." -fi + +olddir=`pwd` +srcdir=`dirname $0` +test -z "$srcdir" && srcdir=. + +( + cd "$srcdir" + + gprefix=`which glibtoolize 2>&1 >/dev/null` + if [ $? -eq 0 ]; then + glibtoolize --force + else + libtoolize --force + fi + aclocal -I m4 + autoheader + automake --add-missing + autoconf + + requires_pkgconfig=`which pkg-config 2>&1 >/dev/null` + if [ $? -ne 0 ]; then + echo "Missing required pkg-config. Please install it on your system and run again." + fi + + cd "$olddir" +) if [ -z "$NOCONFIGURE" ]; then - ./configure "$@" + $srcdir/configure "$@" fi diff --git a/configure.ac b/configure.ac index 3b2dec0..c51d53a 100644 --- a/configure.ac +++ b/configure.ac @@ -1,30 +1,34 @@ # -*- Autoconf -*- # Process this file with autoconf to produce a configure script. -AC_PREREQ(2.64) -AC_INIT([usbmuxd], [1.1.1], [https://github.com/libimobiledevice/usbmuxd/issues],, [http://libimobiledevice.org]) +AC_PREREQ([2.68]) +AC_INIT([usbmuxd], [m4_esyscmd(./git-version-gen $RELEASE_VERSION)], [https://github.com/libimobiledevice/usbmuxd/issues], [], [https://libimobiledevice.org]) AM_INIT_AUTOMAKE([dist-bzip2 no-dist-gzip check-news]) m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES]) AC_CONFIG_SRCDIR([src/]) AC_CONFIG_HEADERS([config.h]) AC_CONFIG_MACRO_DIR([m4]) +# Check if we have a version defined +if test -z $PACKAGE_VERSION; then + 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.]) +fi + # Checks for programs. AC_PROG_CC -AC_PROG_CXX AM_PROG_CC_C_O -AC_PROG_LIBTOOL +LT_INIT # Checks for libraries. PKG_CHECK_MODULES(libusb, libusb-1.0 >= 1.0.9) -PKG_CHECK_MODULES(libplist, libplist >= 1.11) -PKG_CHECK_MODULES(libimobiledevice, libimobiledevice-1.0 >= 1.2.1, have_limd=yes, have_limd=no) -AC_CHECK_LIB(pthread, [pthread_create, pthread_mutex_lock], [AC_SUBST(libpthread_LIBS,[-lpthread])], [AC_MSG_ERROR([libpthread is required to build usbmuxd])]) +PKG_CHECK_MODULES(libplist, libplist-2.0 >= 2.3.0) +PKG_CHECK_MODULES(libimobiledevice, libimobiledevice-1.0 >= 1.3.0, have_limd=yes, have_limd=no) +PKG_CHECK_MODULES(limd_glue, libimobiledevice-glue-1.0 >= 1.0.0) AC_ARG_WITH([preflight], [AS_HELP_STRING([--without-preflight], [do not build with preflight worker support @<:@default=yes@:>@])], - [with_preflight=no], + [with_preflight=$withval], [with_preflight=yes]) if test "x$have_limd" = "xyes"; then @@ -38,11 +42,11 @@ if test "x$have_limd" = "xyes"; then CACHED_CFLAGS="$CFLAGS" CFLAGS+=" $libimobiledevice_CFLAGS" AC_CACHE_CHECK(for enum idevice_connection_type, ac_cv_enum_idevice_connection_type, - AC_TRY_COMPILE([ + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([ #include <libimobiledevice/libimobiledevice.h> ], [ enum idevice_connection_type conn_type = CONNECTION_USBMUXD; - ], ac_cv_enum_idevice_connection_type=yes, ac_cv_enum_idevice_connection_type=no)) + ])], ac_cv_enum_idevice_connection_type=yes, ac_cv_enum_idevice_connection_type=no)) CFLAGS="$CACHED_CFLAGS" if (test "$ac_cv_enum_idevice_connection_type" = "yes"); then AC_DEFINE(HAVE_ENUM_IDEVICE_CONNECTION_TYPE, 1, [Define if enum idevice_connection_type is available]) @@ -71,7 +75,7 @@ fi AC_ARG_WITH([systemd], [AS_HELP_STRING([--without-systemd], [do not build with systemd support @<:@default=yes@:>@])], - [], + [with_systemd=$withval], [with_systemd=yes]) AC_ARG_WITH([systemdsystemunitdir], @@ -93,7 +97,6 @@ fi AC_SUBST(udev_activation_rule) # Checks for header files. -AC_HEADER_STDC AC_CHECK_HEADERS([stdint.h stdlib.h string.h]) # Checks for typedefs, structures, and compiler characteristics. @@ -108,9 +111,7 @@ AC_TYPE_UINT8_T AC_SEARCH_LIBS([clock_gettime],[rt posix4]) # Checks for library functions. -AC_FUNC_MALLOC -AC_FUNC_REALLOC -AC_CHECK_FUNCS([strcasecmp strdup strerror strndup stpcpy]) +AC_CHECK_FUNCS([strcasecmp strdup strerror strndup malloc realloc]) AC_CHECK_FUNCS([ppoll clock_gettime localtime_r]) # Check for operating system @@ -157,13 +158,19 @@ AC_SUBST(GLOBAL_CFLAGS) m4_ifdef([AM_SILENT_RULES],[AM_SILENT_RULES([yes])]) -AC_OUTPUT([ +# workaround for older autoconf versions +if test "x$runstatedir" == "x"; then + runstatedir=$localstatedir/run +fi + +AC_CONFIG_FILES([ Makefile src/Makefile udev/Makefile systemd/Makefile docs/Makefile ]) +AC_OUTPUT echo " Configuration for $PACKAGE $VERSION: diff --git a/docs/usbmuxd.8 b/docs/usbmuxd.8 index 0a05f12..590afdc 100644 --- a/docs/usbmuxd.8 +++ b/docs/usbmuxd.8 @@ -72,9 +72,9 @@ The first usbmuxd daemon implementation was authored by Hector Martin. Now mainly developed by Nikias Bassen, Martin Szulecki and contributors (see AUTHORS file). .SH SEE ALSO +idevice_id(1), iproxy(1). -http://www.libimobiledevice.org - -http://github.com/libimobiledevice/usbmuxd/ +.SH ON THE WEB +https://libimobiledevice.org -idevice_id(1), iproxy(1). +https://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 @@ +#!/bin/sh +SRCDIR=`dirname $0` +if test -n "$1"; then + VER=$1 +else + if test -d "${SRCDIR}/.git" && test -x "`which git`" ; then + git update-index -q --refresh + if ! VER=`git describe --tags --dirty 2>/dev/null`; then + COMMIT=`git rev-parse --short HEAD` + DIRTY=`git diff --quiet HEAD || echo "-dirty"` + VER=`sed -n '1,/RE/s/Version \(.*\)/\1/p' ${SRCDIR}/NEWS`-git-${COMMIT}${DIRTY} + fi + else + if test -f "${SRCDIR}/.tarball-version"; then + VER=`cat "${SRCDIR}/.tarball-version"` + fi + fi +fi +printf %s "$VER" diff --git a/m4/as-compiler-flag.m4 b/m4/as-compiler-flag.m4 index 0f660cf..baab5d9 100644 --- a/m4/as-compiler-flag.m4 +++ b/m4/as-compiler-flag.m4 @@ -18,7 +18,7 @@ AC_DEFUN([AS_COMPILER_FLAG], save_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS $1" - AC_TRY_COMPILE([ ], [], [flag_ok=yes], [flag_ok=no]) + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([],[])], [flag_ok=yes], [flag_ok=no]) CFLAGS="$save_CFLAGS" if test "X$flag_ok" = Xyes ; then @@ -44,7 +44,7 @@ AC_DEFUN([AS_COMPILER_FLAGS], do save_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS $each" - AC_TRY_COMPILE([ ], [], [flag_ok=yes], [flag_ok=no]) + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([],[])], [flag_ok=yes], [flag_ok=no]) CFLAGS="$save_CFLAGS" if test "X$flag_ok" = Xyes ; then diff --git a/src/Makefile.am b/src/Makefile.am index 9faf204..8a96e46 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,17 +1,32 @@ -AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_srcdir) +AM_CPPFLAGS = \ + -I$(top_srcdir)/include \ + -I$(top_srcdir) -AM_CFLAGS = $(GLOBAL_CFLAGS) $(libplist_CFLAGS) $(libusb_CFLAGS) $(libimobiledevice_CFLAGS) -AM_LDFLAGS = $(libplist_LIBS) $(libusb_LIBS) $(libimobiledevice_LIBS) $(libpthread_LIBS) +AM_CFLAGS = \ + $(GLOBAL_CFLAGS) \ + $(libplist_CFLAGS) \ + $(libusb_CFLAGS) \ + $(limd_glue_CFLAGS) \ + $(libimobiledevice_CFLAGS) + +AM_LDFLAGS = \ + $(libplist_LIBS) \ + $(libusb_LIBS) \ + $(limd_glue_LIBS) \ + $(libimobiledevice_LIBS) \ + $(libpthread_LIBS) sbin_PROGRAMS = usbmuxd usbmuxd_CFLAGS = $(AM_CFLAGS) usbmuxd_LDFLAGS = $(AM_LDFLAGS) -no-undefined -usbmuxd_SOURCES = client.c client.h \ - device.c device.h \ - preflight.c preflight.h \ - log.c log.h \ - usbmuxd-proto.h usb.c usb.h \ - utils.c utils.h \ - conf.c conf.h \ - main.c +usbmuxd_SOURCES = \ + client.c client.h \ + device.c device.h \ + preflight.c preflight.h \ + log.c log.h \ + usbmuxd-proto.h \ + usb.c usb.h \ + utils.c utils.h \ + conf.c conf.h \ + main.c diff --git a/src/client.c b/src/client.c index 85d0c8b..dbbdd5f 100644 --- a/src/client.c +++ b/src/client.c @@ -31,13 +31,15 @@ #include <unistd.h> #include <sys/types.h> #include <sys/socket.h> +#include <netinet/in.h> #include <netinet/tcp.h> #include <sys/un.h> #include <arpa/inet.h> -#include <pthread.h> #include <fcntl.h> #include <plist/plist.h> +#include <libimobiledevice-glue/collection.h> +#include <libimobiledevice-glue/thread.h> #include "log.h" #include "usb.h" @@ -75,9 +77,30 @@ struct mux_client { }; static struct collection client_list; -pthread_mutex_t client_list_mutex; +mutex_t client_list_mutex; static uint32_t client_number = 0; +#ifdef SO_PEERCRED +static char* _get_process_name_by_pid(const int pid) +{ + char* name = (char*)calloc(1024, sizeof(char)); + if(name) { + sprintf(name, "/proc/%d/cmdline", pid); + FILE* f = fopen(name, "r"); + if(f) { + size_t size; + size = fread(name, sizeof(char), 1024, f); + if(size > 0) { + if('\n' == name[size-1]) + name[size-1]='\0'; + } + fclose(f); + } + } + return name; +} +#endif + /** * Receive raw data from the client socket. * @@ -203,32 +226,64 @@ int client_accept(int listenfd) client->events = POLLIN; client->info = NULL; - pthread_mutex_lock(&client_list_mutex); + mutex_lock(&client_list_mutex); client->number = client_number++; collection_add(&client_list, client); - pthread_mutex_unlock(&client_list_mutex); + mutex_unlock(&client_list_mutex); #ifdef SO_PEERCRED if (log_level >= LL_INFO) { struct ucred cr; len = sizeof(struct ucred); - getsockopt(cfd, SOL_SOCKET, SO_PEERCRED, &cr, &len); + getsockopt(client->fd, SOL_SOCKET, SO_PEERCRED, &cr, &len); if (getpid() == cr.pid) { - usbmuxd_log(LL_INFO, "New client on fd %d (self)", client->fd); + usbmuxd_log(LL_INFO, "Client %d accepted: %s[%d]", client->fd, PACKAGE_NAME, cr.pid); } else { - usbmuxd_log(LL_INFO, "New client on fd %d (pid %d)", client->fd, cr.pid); + char* process_name = _get_process_name_by_pid(cr.pid); + usbmuxd_log(LL_INFO, "Client %d accepted: %s[%d]", client->fd, process_name, cr.pid); + free(process_name); } } #else - usbmuxd_log(LL_INFO, "New client on fd %d", client->fd); + usbmuxd_log(LL_INFO, "Client %d accepted", client->fd); #endif return client->fd; } void client_close(struct mux_client *client) { - usbmuxd_log(LL_INFO, "Disconnecting client fd %d", client->fd); + int found = 0; + mutex_lock(&client_list_mutex); + FOREACH(struct mux_client *lc, &client_list) { + if (client == lc) { + found = 1; + break; + } + } ENDFOREACH + if (!found) { + // in case we get called again but client was already freed + usbmuxd_log(LL_DEBUG, "%s: ignoring for non-existing client %p", __func__, client); + mutex_unlock(&client_list_mutex); + return; + } +#ifdef SO_PEERCRED + if (log_level >= LL_INFO) { + struct ucred cr; + socklen_t len = sizeof(struct ucred); + getsockopt(client->fd, SOL_SOCKET, SO_PEERCRED, &cr, &len); + + if (getpid() == cr.pid) { + usbmuxd_log(LL_INFO, "Client %d is going to be disconnected: %s[%d]", client->fd, PACKAGE_NAME, cr.pid); + } else { + char* process_name = _get_process_name_by_pid(cr.pid); + usbmuxd_log(LL_INFO, "Client %d is going to be disconnected: %s[%d]", client->fd, process_name, cr.pid); + free(process_name); + } + } +#else + usbmuxd_log(LL_INFO, "Client %d is going to be disconnected", client->fd); +#endif if(client->state == CLIENT_CONNECTING1 || client->state == CLIENT_CONNECTING2) { usbmuxd_log(LL_INFO, "Client died mid-connect, aborting device %d connection", client->connect_device); client->state = CLIENT_DEAD; @@ -239,29 +294,28 @@ void client_close(struct mux_client *client) free(client->ib_buf); plist_free(client->info); - pthread_mutex_lock(&client_list_mutex); collection_remove(&client_list, client); - pthread_mutex_unlock(&client_list_mutex); + mutex_unlock(&client_list_mutex); free(client); } void client_get_fds(struct fdlist *list) { - pthread_mutex_lock(&client_list_mutex); + mutex_lock(&client_list_mutex); FOREACH(struct mux_client *client, &client_list) { fdlist_add(list, FD_CLIENT, client->fd, client->events); } ENDFOREACH - pthread_mutex_unlock(&client_list_mutex); + mutex_unlock(&client_list_mutex); } -static int send_pkt(struct mux_client *client, uint32_t tag, enum usbmuxd_msgtype msg, void *payload, int payload_length) +static int output_buffer_add_message(struct mux_client *client, uint32_t tag, enum usbmuxd_msgtype msg, void *payload, int payload_length) { struct usbmuxd_header hdr; hdr.version = client->proto_version; hdr.length = sizeof(hdr) + payload_length; hdr.message = msg; hdr.tag = tag; - usbmuxd_log(LL_DEBUG, "send_pkt fd %d tag %d msg %d payload_length %d", client->fd, tag, msg, payload_length); + usbmuxd_log(LL_DEBUG, "Client %d output buffer got tag %d msg %d payload_length %d", client->fd, tag, msg, payload_length); uint32_t available = client->ob_capacity - client->ob_size; /* the output buffer _should_ be large enough, but just in case */ @@ -285,14 +339,14 @@ static int send_pkt(struct mux_client *client, uint32_t tag, enum usbmuxd_msgtyp return hdr.length; } -static int send_plist_pkt(struct mux_client *client, uint32_t tag, plist_t plist) +static int send_plist(struct mux_client *client, uint32_t tag, plist_t plist) { int res = -1; char *xml = NULL; uint32_t xmlsize = 0; plist_to_xml(plist, &xml, &xmlsize); if (xml) { - res = send_pkt(client, tag, MESSAGE_PLIST, xml, xmlsize); + res = output_buffer_add_message(client, tag, MESSAGE_PLIST, xml, xmlsize); free(xml); } else { usbmuxd_log(LL_ERROR, "%s: Could not convert plist to xml", __func__); @@ -308,11 +362,11 @@ static int send_result(struct mux_client *client, uint32_t tag, uint32_t result) plist_t dict = plist_new_dict(); plist_dict_set_item(dict, "MessageType", plist_new_string("Result")); plist_dict_set_item(dict, "Number", plist_new_uint(result)); - res = send_plist_pkt(client, tag, dict); + res = send_plist(client, tag, dict); plist_free(dict); } else { /* binary packet */ - res = send_pkt(client, tag, MESSAGE_RESULT, &result, sizeof(uint32_t)); + res = output_buffer_add_message(client, tag, MESSAGE_RESULT, &result, sizeof(uint32_t)); } return res; } @@ -378,7 +432,7 @@ static int send_device_list(struct mux_client *client, uint32_t tag) free(devs); plist_dict_set_item(dict, "DeviceList", devices); - res = send_plist_pkt(client, tag, dict); + res = send_plist(client, tag, dict); plist_free(dict); return res; } @@ -390,7 +444,7 @@ static int send_listener_list(struct mux_client *client, uint32_t tag) plist_t dict = plist_new_dict(); plist_t listeners = plist_new_array(); - pthread_mutex_lock(&client_list_mutex); + mutex_lock(&client_list_mutex); FOREACH(struct mux_client *lc, &client_list) { if (lc->state == CLIENT_LISTEN) { plist_t n = NULL; @@ -437,10 +491,10 @@ static int send_listener_list(struct mux_client *client, uint32_t tag) plist_array_append_item(listeners, l); } } ENDFOREACH - pthread_mutex_unlock(&client_list_mutex); + mutex_unlock(&client_list_mutex); plist_dict_set_item(dict, "ListenerList", listeners); - res = send_plist_pkt(client, tag, dict); + res = send_plist(client, tag, dict); plist_free(dict); return res; @@ -456,7 +510,7 @@ static int send_system_buid(struct mux_client *client, uint32_t tag) plist_t dict = plist_new_dict(); plist_dict_set_item(dict, "BUID", plist_new_string(buid)); free(buid); - res = send_plist_pkt(client, tag, dict); + res = send_plist(client, tag, dict); plist_free(dict); return res; } @@ -472,12 +526,12 @@ static int send_pair_record(struct mux_client *client, uint32_t tag, const char* } config_get_device_record(record_id, &record_data, &record_size); - + if (record_data) { plist_t dict = plist_new_dict(); plist_dict_set_item(dict, "PairRecordData", plist_new_data(record_data, record_size)); free(record_data); - res = send_plist_pkt(client, tag, dict); + res = send_plist(client, tag, dict); plist_free(dict); } else { res = send_result(client, tag, ENOENT); @@ -485,13 +539,13 @@ static int send_pair_record(struct mux_client *client, uint32_t tag, const char* return res; } -static int notify_device_add(struct mux_client *client, struct device_info *dev) +static int send_device_add(struct mux_client *client, struct device_info *dev) { int res = -1; if (client->proto_version == 1) { /* XML plist packet */ plist_t dict = create_device_attached_plist(dev); - res = send_plist_pkt(client, 0, dict); + res = send_plist(client, 0, dict); plist_free(dict); } else { /* binary packet */ @@ -502,12 +556,12 @@ static int notify_device_add(struct mux_client *client, struct device_info *dev) dmsg.serial_number[255] = 0; dmsg.location = dev->location; dmsg.product_id = dev->pid; - res = send_pkt(client, 0, MESSAGE_DEVICE_ADD, &dmsg, sizeof(dmsg)); + res = output_buffer_add_message(client, 0, MESSAGE_DEVICE_ADD, &dmsg, sizeof(dmsg)); } return res; } -static int notify_device_remove(struct mux_client *client, uint32_t device_id) +static int send_device_remove(struct mux_client *client, uint32_t device_id) { int res = -1; if (client->proto_version == 1) { @@ -515,16 +569,16 @@ static int notify_device_remove(struct mux_client *client, uint32_t device_id) plist_t dict = plist_new_dict(); plist_dict_set_item(dict, "MessageType", plist_new_string("Detached")); plist_dict_set_item(dict, "DeviceID", plist_new_uint(device_id)); - res = send_plist_pkt(client, 0, dict); + res = send_plist(client, 0, dict); plist_free(dict); } else { /* binary packet */ - res = send_pkt(client, 0, MESSAGE_DEVICE_REMOVE, &device_id, sizeof(uint32_t)); + res = output_buffer_add_message(client, 0, MESSAGE_DEVICE_REMOVE, &device_id, sizeof(uint32_t)); } return res; } -static int notify_device_paired(struct mux_client *client, uint32_t device_id) +static int send_device_paired(struct mux_client *client, uint32_t device_id) { int res = -1; if (client->proto_version == 1) { @@ -532,12 +586,12 @@ static int notify_device_paired(struct mux_client *client, uint32_t device_id) plist_t dict = plist_new_dict(); plist_dict_set_item(dict, "MessageType", plist_new_string("Paired")); plist_dict_set_item(dict, "DeviceID", plist_new_uint(device_id)); - res = send_plist_pkt(client, 0, dict); + res = send_plist(client, 0, dict); plist_free(dict); } else { /* binary packet */ - res = send_pkt(client, 0, MESSAGE_DEVICE_PAIRED, &device_id, sizeof(uint32_t)); + res = output_buffer_add_message(client, 0, MESSAGE_DEVICE_PAIRED, &device_id, sizeof(uint32_t)); } return res; } @@ -553,7 +607,7 @@ static int start_listen(struct mux_client *client) count = device_get_list(0, &devs); dev = devs; for(i=0; devs && i < count; i++) { - if(notify_device_add(client, dev++) < 0) { + if(send_device_add(client, dev++) < 0) { free(devs); return -1; } @@ -604,13 +658,13 @@ static void update_client_info(struct mux_client *client, plist_t dict) client->info = info; } -static int client_command(struct mux_client *client, struct usbmuxd_header *hdr) +static int handle_command(struct mux_client *client, struct usbmuxd_header *hdr) { int res; - 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); + 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); if(client->state != CLIENT_COMMAND) { - usbmuxd_log(LL_ERROR, "Client %d command received in the wrong state", client->fd); + usbmuxd_log(LL_ERROR, "Client %d command received in the wrong state, got %d but want %d", client->fd, client->state, CLIENT_COMMAND); if(send_result(client, hdr->tag, RESULT_BADCOMMAND) < 0) return -1; client_close(client); @@ -691,7 +745,7 @@ static int client_command(struct mux_client *client, struct usbmuxd_header *hdr) portnum = (uint16_t)val; plist_free(dict); - usbmuxd_log(LL_DEBUG, "Client %d connection request to device %d port %d", client->fd, device_id, ntohs(portnum)); + usbmuxd_log(LL_DEBUG, "Client %d requesting connection to device %d port %d", client->fd, device_id, ntohs(portnum)); res = device_start_connect(device_id, ntohs(portnum), client); if(res < 0) { if (send_result(client, hdr->tag, -res) < 0) @@ -839,7 +893,7 @@ static int client_command(struct mux_client *client, struct usbmuxd_header *hdr) return -1; } -static void process_send(struct mux_client *client) +static void output_buffer_process(struct mux_client *client) { int res; if(!client->ob_size) { @@ -849,7 +903,7 @@ static void process_send(struct mux_client *client) } res = send(client->fd, client->ob_buf, client->ob_size, 0); if(res <= 0) { - usbmuxd_log(LL_ERROR, "Send to client fd %d failed: %d %s", client->fd, res, strerror(errno)); + usbmuxd_log(LL_ERROR, "Sending to client fd %d failed: %d %s", client->fd, res, strerror(errno)); client_close(client); return; } @@ -869,7 +923,7 @@ static void process_send(struct mux_client *client) memmove(client->ob_buf, client->ob_buf + res, client->ob_size); } } -static void process_recv(struct mux_client *client) +static void input_buffer_process(struct mux_client *client) { int res; int did_read = 0; @@ -916,21 +970,21 @@ static void process_recv(struct mux_client *client) if(client->ib_size < hdr->length) return; } - client_command(client, hdr); + handle_command(client, hdr); client->ib_size = 0; } void client_process(int fd, short events) { struct mux_client *client = NULL; - pthread_mutex_lock(&client_list_mutex); + mutex_lock(&client_list_mutex); FOREACH(struct mux_client *lc, &client_list) { if(lc->fd == fd) { client = lc; break; } } ENDFOREACH - pthread_mutex_unlock(&client_list_mutex); + mutex_unlock(&client_list_mutex); if(!client) { usbmuxd_log(LL_INFO, "client_process: fd %d not found in client list", fd); @@ -942,9 +996,9 @@ void client_process(int fd, short events) device_client_process(client->connect_device, client, events); } else { if(events & POLLIN) { - process_recv(client); + input_buffer_process(client); } else if(events & POLLOUT) { //not both in case client died as part of process_recv - process_send(client); + output_buffer_process(client); } } @@ -952,45 +1006,45 @@ void client_process(int fd, short events) void client_device_add(struct device_info *dev) { - pthread_mutex_lock(&client_list_mutex); + mutex_lock(&client_list_mutex); usbmuxd_log(LL_DEBUG, "client_device_add: id %d, location 0x%x, serial %s", dev->id, dev->location, dev->serial); device_set_visible(dev->id); FOREACH(struct mux_client *client, &client_list) { if(client->state == CLIENT_LISTEN) - notify_device_add(client, dev); + send_device_add(client, dev); } ENDFOREACH - pthread_mutex_unlock(&client_list_mutex); + mutex_unlock(&client_list_mutex); } void client_device_remove(int device_id) { - pthread_mutex_lock(&client_list_mutex); + mutex_lock(&client_list_mutex); uint32_t id = device_id; usbmuxd_log(LL_DEBUG, "client_device_remove: id %d", device_id); FOREACH(struct mux_client *client, &client_list) { if(client->state == CLIENT_LISTEN) - notify_device_remove(client, id); + send_device_remove(client, id); } ENDFOREACH - pthread_mutex_unlock(&client_list_mutex); + mutex_unlock(&client_list_mutex); } void client_device_paired(int device_id) { - pthread_mutex_lock(&client_list_mutex); + mutex_lock(&client_list_mutex); uint32_t id = device_id; usbmuxd_log(LL_DEBUG, "client_device_paired: id %d", device_id); FOREACH(struct mux_client *client, &client_list) { if (client->state == CLIENT_LISTEN) - notify_device_paired(client, id); + send_device_paired(client, id); } ENDFOREACH - pthread_mutex_unlock(&client_list_mutex); + mutex_unlock(&client_list_mutex); } void client_init(void) { usbmuxd_log(LL_DEBUG, "client_init"); collection_init(&client_list); - pthread_mutex_init(&client_list_mutex, NULL); + mutex_init(&client_list_mutex); } void client_shutdown(void) @@ -999,6 +1053,6 @@ void client_shutdown(void) FOREACH(struct mux_client *client, &client_list) { client_close(client); } ENDFOREACH - pthread_mutex_destroy(&client_list_mutex); + mutex_destroy(&client_list_mutex); collection_free(&client_list); } @@ -39,6 +39,9 @@ #include <shlobj.h> #endif +#include <libimobiledevice-glue/utils.h> +#include <plist/plist.h> + #include "conf.h" #include "utils.h" #include "log.h" @@ -132,7 +135,7 @@ const char *config_get_config_dir() __config_dir = string_concat(base_config_dir, DIR_SEP_S, CONFIG_DIR, NULL); if (__config_dir) { - int i = strlen(__config_dir)-1; + int i = strlen(__config_dir)-1; while ((i > 0) && (__config_dir[i] == DIR_SEP)) { __config_dir[i--] = '\0'; } @@ -140,7 +143,7 @@ const char *config_get_config_dir() free(base_config_dir); - usbmuxd_log(LL_DEBUG, "initialized config_dir to %s", __config_dir); + usbmuxd_log(LL_DEBUG, "Initialized config_dir to %s", __config_dir); return __config_dir; } @@ -160,7 +163,7 @@ static int mkdir_with_parents(const char *dir, int mode) if (__mkdir(dir, mode) == 0) { return 0; } else { - if (errno == EEXIST) return 0; + if (errno == EEXIST) return 0; } int res; char *parent = strdup(dir); @@ -230,7 +233,7 @@ static int internal_set_value(const char *config_file, const char *key, plist_t /* read file into plist */ plist_t config = NULL; - plist_read_from_filename(&config, config_file); + plist_read_from_file(config_file, &config, NULL); if (!config) { config = plist_new_dict(); plist_dict_set_item(config, key, value); @@ -247,14 +250,14 @@ static int internal_set_value(const char *config_file, const char *key, plist_t char *value_string = NULL; if (plist_get_node_type(value) == PLIST_STRING) { plist_get_string_val(value, &value_string); - usbmuxd_log(LL_DEBUG, "setting key %s to %s in config_file %s", key, value_string, config_file); + usbmuxd_log(LL_DEBUG, "Setting key %s to %s in config file %s", key, value_string, config_file); if (value_string) free(value_string); } else { - usbmuxd_log(LL_DEBUG, "setting key %s in config_file %s", key, config_file); + usbmuxd_log(LL_DEBUG, "Setting key %s in config file %s", key, config_file); } - int res = plist_write_to_filename(config, config_file, PLIST_FORMAT_XML); + int res = (plist_write_to_file(config, config_file, PLIST_FORMAT_XML, 0) == PLIST_ERR_SUCCESS); plist_free(config); @@ -274,7 +277,7 @@ static int config_set_value(const char *key, plist_t value) int result = internal_set_value(config_file, key, value); if (!result) { - usbmuxd_log(LL_ERROR, "ERROR: Failed to write to '%s': %s", config_file, strerror(errno)); + usbmuxd_log(LL_ERROR, "ERROR: Failed to write to '%s'", config_file); } free(config_file); @@ -288,8 +291,8 @@ static int internal_get_value(const char* config_file, const char *key, plist_t /* now parse file to get the SystemBUID */ plist_t config = NULL; - if (plist_read_from_filename(&config, config_file)) { - usbmuxd_log(LL_DEBUG, "reading key %s from config_file %s", key, config_file); + if (plist_read_from_file(config_file, &config, NULL) == PLIST_ERR_SUCCESS) { + usbmuxd_log(LL_DEBUG, "Reading key %s from config file %s", key, config_file); plist_t n = plist_dict_get_item(config, key); if (n) { *value = plist_copy(n); @@ -371,7 +374,7 @@ void config_get_system_buid(char **system_buid) if (value && (plist_get_node_type(value) == PLIST_STRING)) { plist_get_string_val(value, system_buid); - usbmuxd_log(LL_DEBUG, "got %s %s", CONFIG_SYSTEM_BUID_KEY, *system_buid); + usbmuxd_log(LL_DEBUG, "Got %s %s", CONFIG_SYSTEM_BUID_KEY, *system_buid); } if (value) @@ -379,14 +382,14 @@ void config_get_system_buid(char **system_buid) if (!*system_buid) { /* no config, generate system_buid */ - usbmuxd_log(LL_DEBUG, "no previous %s found", CONFIG_SYSTEM_BUID_KEY); + usbmuxd_log(LL_DEBUG, "No previous %s found", CONFIG_SYSTEM_BUID_KEY); *system_buid = config_generate_system_buid(); if (!config_set_system_buid(*system_buid)) { usbmuxd_log(LL_WARNING, "WARNING: Failed to store SystemBUID, this might be a problem"); } } - usbmuxd_log(LL_DEBUG, "using %s as %s", *system_buid, CONFIG_SYSTEM_BUID_KEY); + usbmuxd_log(LL_DEBUG, "Using %s as %s", *system_buid, CONFIG_SYSTEM_BUID_KEY); } /** @@ -428,8 +431,8 @@ int config_set_device_record(const char *udid, char* record_data, uint64_t recor remove(device_record_file); /* store file */ - if (!plist_write_to_filename(plist, device_record_file, PLIST_FORMAT_XML)) { - usbmuxd_log(LL_DEBUG, "could not open '%s' for writing: %s", device_record_file, strerror(errno)); + if (!plist_write_to_file(plist, device_record_file, PLIST_FORMAT_XML, 0)) { + usbmuxd_log(LL_DEBUG, "Could not open '%s' for writing: %s", device_record_file, strerror(errno)); res = -ENOENT; } free(device_record_file); @@ -464,7 +467,7 @@ int config_get_device_record(const char *udid, char **record_data, uint64_t *rec /* read file */ buffer_read_from_filename(device_record_file, record_data, record_size); if (!*record_data) { - usbmuxd_log(LL_ERROR, "%s: failed to read '%s': %s", __func__, device_record_file, strerror(errno)); + usbmuxd_log(LL_ERROR, "ERROR: Failed to read '%s': %s", device_record_file, strerror(errno)); res = -ENOENT; } free(device_record_file); @@ -490,7 +493,7 @@ int config_remove_device_record(const char *udid) /* remove file */ if (remove(device_record_file) != 0) { res = -errno; - usbmuxd_log(LL_DEBUG, "could not remove %s: %s", device_record_file, strerror(errno)); + usbmuxd_log(LL_DEBUG, "Could not remove %s: %s", device_record_file, strerror(errno)); } free(device_record_file); @@ -527,6 +530,6 @@ void config_device_record_get_host_id(const char *udid, char **host_id) plist_free(value); if (!*host_id) { - usbmuxd_log(LL_ERROR, "%s: ERROR couldn't get HostID from pairing record for udid %s", __func__, udid); + usbmuxd_log(LL_ERROR, "ERROR: Could not get HostID from pairing record for udid %s", udid); } } diff --git a/src/device.c b/src/device.c index 6858cf5..0928021 100644 --- a/src/device.c +++ b/src/device.c @@ -32,8 +32,11 @@ #include <string.h> #include <stdint.h> #include <inttypes.h> -#include <pthread.h> #include <unistd.h> + +#include <libimobiledevice-glue/collection.h> +#include <libimobiledevice-glue/thread.h> + #include "device.h" #include "client.h" #include "preflight.h" @@ -127,19 +130,19 @@ struct mux_device }; static struct collection device_list; -pthread_mutex_t device_list_mutex; +mutex_t device_list_mutex; static struct mux_device* get_mux_device_for_id(int device_id) { struct mux_device *dev = NULL; - pthread_mutex_lock(&device_list_mutex); + mutex_lock(&device_list_mutex); FOREACH(struct mux_device *cdev, &device_list) { if(cdev->id == device_id) { dev = cdev; break; } } ENDFOREACH - pthread_mutex_unlock(&device_list_mutex); + mutex_unlock(&device_list_mutex); return dev; } @@ -166,7 +169,7 @@ static int get_next_device_id(void) { while(1) { int ok = 1; - pthread_mutex_lock(&device_list_mutex); + mutex_lock(&device_list_mutex); FOREACH(struct mux_device *dev, &device_list) { if(dev->id == next_device_id) { next_device_id++; @@ -174,7 +177,7 @@ static int get_next_device_id(void) break; } } ENDFOREACH - pthread_mutex_unlock(&device_list_mutex); + mutex_unlock(&device_list_mutex); if(ok) return next_device_id++; } @@ -464,9 +467,9 @@ static int send_tcp_ack(struct mux_connection *conn) */ void device_client_process(int device_id, struct mux_client *client, short events) { - pthread_mutex_lock(&device_list_mutex); + mutex_lock(&device_list_mutex); struct mux_connection *conn = get_mux_connection(device_id, client); - pthread_mutex_unlock(&device_list_mutex); + mutex_unlock(&device_list_mutex); if(!conn) { usbmuxd_log(LL_WARNING, "Could not find connection for device %d client %p", device_id, client); return; @@ -549,6 +552,7 @@ void device_abort_connect(int device_id, struct mux_client *client) { struct mux_connection *conn = get_mux_connection(device_id, client); if (conn) { + conn->client = NULL; connection_teardown(conn); } else { usbmuxd_log(LL_WARNING, "Attempted to abort for nonexistent connection for device %d", device_id); @@ -565,9 +569,9 @@ static void device_version_input(struct mux_device *dev, struct version_header * vh->minor = ntohl(vh->minor); if(vh->major != 2 && vh->major != 1) { usbmuxd_log(LL_ERROR, "Device %d has unknown version %d.%d", dev->id, vh->major, vh->minor); - pthread_mutex_lock(&device_list_mutex); + mutex_lock(&device_list_mutex); collection_remove(&device_list, dev); - pthread_mutex_unlock(&device_list_mutex); + mutex_unlock(&device_list_mutex); free(dev); return; } @@ -595,29 +599,31 @@ static void device_control_input(struct mux_device *dev, unsigned char *payload, switch (payload[0]) { case 3: if (payload_length > 1) { - char* buf = malloc(payload_length); - strncpy(buf, (char*)payload+1, payload_length-1); - buf[payload_length-1] = '\0'; - usbmuxd_log(LL_ERROR, "%s: ERROR: %s", __func__, buf); - free(buf); + usbmuxd_log(LL_ERROR, "Device %d: ERROR: %.*s", dev->id, payload_length-1, payload+1); } else { - usbmuxd_log(LL_ERROR, "%s: Error occurred, but empty error message", __func__); + usbmuxd_log(LL_ERROR, "%s: Device %d: Got device error payload with empty message", __func__, dev->id); + } + break; + case 5: + if (payload_length > 1) { + usbmuxd_log(LL_WARNING, "Device %d: WARNING: %.*s", dev->id, payload_length-1, payload+1); + } else { + usbmuxd_log(LL_WARNING, "%s: Device %d: Got payload type %d with empty message", __func__, dev->id, payload[0]); } break; case 7: if (payload_length > 1) { - char* buf = malloc(payload_length); - strncpy(buf, (char*)payload+1, payload_length-1); - buf[payload_length-1] = '\0'; - usbmuxd_log(LL_INFO, "%s: %s", __func__, buf); - free(buf); + usbmuxd_log(LL_INFO, "Device %d: %.*s", dev->id, payload_length-1, payload+1); + } else { + usbmuxd_log(LL_WARNING, "%s: Device %d: Got payload type %d with empty message", __func__, dev->id, payload[0]); } break; default: + usbmuxd_log(LL_WARNING, "%s: Device %d: Got unhandled payload type %d: %.*s", __func__, dev->id, payload[0], payload_length-1, payload+1); break; } } else { - usbmuxd_log(LL_WARNING, "%s: got a type 1 packet without payload", __func__); + usbmuxd_log(LL_WARNING, "%s: Got a type 1 packet without payload for device %d", __func__, dev->id); } } @@ -690,7 +696,7 @@ static void device_tcp_input(struct mux_device *dev, struct tcphdr *th, unsigned return; } conn->state = CONN_CONNECTED; - usbmuxd_log(LL_DEBUG, "Client connected to device %d (%d->%d)", dev->id, sport, dport); + usbmuxd_log(LL_INFO, "Client connected to device %d (%d->%d)", dev->id, sport, dport); if(client_notify_connect(conn->client, RESULT_OK) < 0) { conn->client = NULL; connection_teardown(conn); @@ -723,14 +729,14 @@ static void device_tcp_input(struct mux_device *dev, struct tcphdr *th, unsigned void device_data_input(struct usb_device *usbdev, unsigned char *buffer, uint32_t length) { struct mux_device *dev = NULL; - pthread_mutex_lock(&device_list_mutex); + mutex_lock(&device_list_mutex); FOREACH(struct mux_device *tdev, &device_list) { if(tdev->usbdev == usbdev) { dev = tdev; break; } } ENDFOREACH - pthread_mutex_unlock(&device_list_mutex); + mutex_unlock(&device_list_mutex); if(!dev) { usbmuxd_log(LL_WARNING, "Cannot find device entry for RX input from USB device %p on location 0x%x", usbdev, usb_get_location(usbdev)); return; @@ -847,15 +853,15 @@ int device_add(struct usb_device *usbdev) free(dev); return res; } - pthread_mutex_lock(&device_list_mutex); + mutex_lock(&device_list_mutex); collection_add(&device_list, dev); - pthread_mutex_unlock(&device_list_mutex); + mutex_unlock(&device_list_mutex); return 0; } void device_remove(struct usb_device *usbdev) { - pthread_mutex_lock(&device_list_mutex); + mutex_lock(&device_list_mutex); FOREACH(struct mux_device *dev, &device_list) { if(dev->usbdev == usbdev) { usbmuxd_log(LL_NOTICE, "Removed device %d on location 0x%x", dev->id, usb_get_location(usbdev)); @@ -871,48 +877,48 @@ void device_remove(struct usb_device *usbdev) preflight_device_remove_cb(dev->preflight_cb_data); } collection_remove(&device_list, dev); - pthread_mutex_unlock(&device_list_mutex); + mutex_unlock(&device_list_mutex); free(dev->pktbuf); free(dev); return; } } ENDFOREACH - pthread_mutex_unlock(&device_list_mutex); + mutex_unlock(&device_list_mutex); usbmuxd_log(LL_WARNING, "Cannot find device entry while removing USB device %p on location 0x%x", usbdev, usb_get_location(usbdev)); } void device_set_visible(int device_id) { - pthread_mutex_lock(&device_list_mutex); + mutex_lock(&device_list_mutex); FOREACH(struct mux_device *dev, &device_list) { if(dev->id == device_id) { dev->visible = 1; break; } } ENDFOREACH - pthread_mutex_unlock(&device_list_mutex); + mutex_unlock(&device_list_mutex); } void device_set_preflight_cb_data(int device_id, void* data) { - pthread_mutex_lock(&device_list_mutex); + mutex_lock(&device_list_mutex); FOREACH(struct mux_device *dev, &device_list) { if(dev->id == device_id) { dev->preflight_cb_data = data; break; } } ENDFOREACH - pthread_mutex_unlock(&device_list_mutex); + mutex_unlock(&device_list_mutex); } int device_get_count(int include_hidden) { int count = 0; struct collection dev_list = {NULL, 0}; - pthread_mutex_lock(&device_list_mutex); + mutex_lock(&device_list_mutex); collection_copy(&dev_list, &device_list); - pthread_mutex_unlock(&device_list_mutex); + mutex_unlock(&device_list_mutex); FOREACH(struct mux_device *dev, &dev_list) { if((dev->state == MUXDEV_ACTIVE) && (include_hidden || dev->visible)) @@ -927,9 +933,9 @@ int device_get_list(int include_hidden, struct device_info **devices) { int count = 0; struct collection dev_list = {NULL, 0}; - pthread_mutex_lock(&device_list_mutex); + mutex_lock(&device_list_mutex); collection_copy(&dev_list, &device_list); - pthread_mutex_unlock(&device_list_mutex); + mutex_unlock(&device_list_mutex); *devices = malloc(sizeof(struct device_info) * dev_list.capacity); struct device_info *p = *devices; @@ -954,7 +960,7 @@ int device_get_list(int include_hidden, struct device_info **devices) int device_get_timeout(void) { uint64_t oldest = (uint64_t)-1LL; - pthread_mutex_lock(&device_list_mutex); + mutex_lock(&device_list_mutex); FOREACH(struct mux_device *dev, &device_list) { if(dev->state == MUXDEV_ACTIVE) { FOREACH(struct mux_connection *conn, &dev->connections) { @@ -963,7 +969,7 @@ int device_get_timeout(void) } ENDFOREACH } } ENDFOREACH - pthread_mutex_unlock(&device_list_mutex); + mutex_unlock(&device_list_mutex); uint64_t ct = mstime64(); if((int64_t)oldest == -1LL) return 100000; //meh @@ -975,7 +981,7 @@ int device_get_timeout(void) void device_check_timeouts(void) { uint64_t ct = mstime64(); - pthread_mutex_lock(&device_list_mutex); + mutex_lock(&device_list_mutex); FOREACH(struct mux_device *dev, &device_list) { if(dev->state == MUXDEV_ACTIVE) { FOREACH(struct mux_connection *conn, &dev->connections) { @@ -988,14 +994,14 @@ void device_check_timeouts(void) } ENDFOREACH } } ENDFOREACH - pthread_mutex_unlock(&device_list_mutex); + mutex_unlock(&device_list_mutex); } void device_init(void) { usbmuxd_log(LL_DEBUG, "device_init"); collection_init(&device_list); - pthread_mutex_init(&device_list_mutex, NULL); + mutex_init(&device_list_mutex); next_device_id = 1; } @@ -1016,7 +1022,7 @@ void device_kill_connections(void) void device_shutdown(void) { usbmuxd_log(LL_DEBUG, "device_shutdown"); - pthread_mutex_lock(&device_list_mutex); + mutex_lock(&device_list_mutex); FOREACH(struct mux_device *dev, &device_list) { FOREACH(struct mux_connection *conn, &dev->connections) { connection_teardown(conn); @@ -1025,7 +1031,7 @@ void device_shutdown(void) collection_remove(&device_list, dev); free(dev); } ENDFOREACH - pthread_mutex_unlock(&device_list_mutex); - pthread_mutex_destroy(&device_list_mutex); + mutex_unlock(&device_list_mutex); + mutex_destroy(&device_list_mutex); collection_free(&device_list); } @@ -1,7 +1,7 @@ /* * main.c * - * Copyright (C) 2009-2019 Nikias Bassen <nikias@gmx.li> + * Copyright (C) 2009-2021 Nikias Bassen <nikias@gmx.li> * Copyright (C) 2013-2014 Martin Szulecki <m.szulecki@libimobiledevice.org> * Copyright (C) 2009 Hector Martin <hector@marcansoft.com> * Copyright (C) 2009 Paul Sladen <libiphone@paul.sladen.org> @@ -36,6 +36,9 @@ #include <unistd.h> #include <sys/socket.h> #include <sys/un.h> +#include <netinet/in.h> +#include <netdb.h> +#include <arpa/inet.h> #include <sys/stat.h> #include <sys/types.h> #include <sys/resource.h> @@ -51,12 +54,16 @@ #include "conf.h" static const char *socket_path = "/var/run/usbmuxd"; -static const char *lockfile = "/var/run/usbmuxd.pid"; +#define DEFAULT_LOCKFILE "/var/run/usbmuxd.pid" +static const char *lockfile = DEFAULT_LOCKFILE; +// Global state used in other files int should_exit; int should_discover; int use_logfile = 0; +int no_preflight = 0; +// Global state for main.c static int verbose = 0; static int foreground = 0; static int drop_privileges = 0; @@ -66,22 +73,162 @@ static int opt_enable_exit = 0; static int opt_exit = 0; static int exit_signal = 0; static int daemon_pipe; +static const char *listen_addr = NULL; static int report_to_parent = 0; -static int create_socket(void) { - struct sockaddr_un bind_addr; +static int create_socket(void) +{ int listenfd; + const char* socket_addr = socket_path; + const char* tcp_port; + char listen_addr_str[256]; + + if (listen_addr) { + socket_addr = listen_addr; + } + tcp_port = strrchr(socket_addr, ':'); + if (tcp_port) { + tcp_port++; + size_t nlen = tcp_port - socket_addr; + char* hostname = malloc(nlen); + struct addrinfo hints; + struct addrinfo *result, *rp; + int yes = 1; + int res; + + strncpy(hostname, socket_addr, nlen-1); + hostname[nlen-1] = '\0'; + + memset(&hints, '\0', sizeof(struct addrinfo)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_PASSIVE | AI_NUMERICSERV; + hints.ai_protocol = IPPROTO_TCP; + + res = getaddrinfo(hostname, tcp_port, &hints, &result); + free(hostname); + if (res != 0) { + usbmuxd_log(LL_FATAL, "%s: getaddrinfo() failed: %s\n", __func__, gai_strerror(res)); + return -1; + } - if(unlink(socket_path) == -1 && errno != ENOENT) { - usbmuxd_log(LL_FATAL, "unlink(%s) failed: %s", socket_path, strerror(errno)); - return -1; - } + for (rp = result; rp != NULL; rp = rp->ai_next) { + listenfd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); + if (listenfd == -1) { + listenfd = -1; + continue; + } - listenfd = socket(AF_UNIX, SOCK_STREAM, 0); - if (listenfd == -1) { - usbmuxd_log(LL_FATAL, "socket() failed: %s", strerror(errno)); - return -1; + if (setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, (void*)&yes, sizeof(int)) == -1) { + usbmuxd_log(LL_ERROR, "%s: setsockopt(): %s", __func__, strerror(errno)); + close(listenfd); + listenfd = -1; + continue; + } + +#ifdef SO_NOSIGPIPE + if (setsockopt(listenfd, SOL_SOCKET, SO_NOSIGPIPE, (void*)&yes, sizeof(int)) == -1) { + usbmuxd_log(LL_ERROR, "%s: setsockopt(): %s", __func__, strerror(errno)); + close(listenfd); + listenfd = -1; + continue; + } +#endif + +#if defined(AF_INET6) && defined(IPV6_V6ONLY) + if (rp->ai_family == AF_INET6) { + if (setsockopt(listenfd, IPPROTO_IPV6, IPV6_V6ONLY, (void*)&yes, sizeof(int)) == -1) { + usbmuxd_log(LL_ERROR, "%s: setsockopt() IPV6_V6ONLY: %s", __func__, strerror(errno)); + } + } +#endif + + if (bind(listenfd, rp->ai_addr, rp->ai_addrlen) < 0) { + usbmuxd_log(LL_FATAL, "%s: bind() failed: %s", __func__, strerror(errno)); + close(listenfd); + listenfd = -1; + continue; + } + + const void *addrdata = NULL; + if (rp->ai_family == AF_INET) { + addrdata = &((struct sockaddr_in*)rp->ai_addr)->sin_addr; + } +#ifdef AF_INET6 + else if (rp->ai_family == AF_INET6) { + addrdata = &((struct sockaddr_in6*)rp->ai_addr)->sin6_addr; + } +#endif + if (addrdata) { + char* endp = NULL; + uint16_t listen_port = 0; + if (rp->ai_family == AF_INET) { + listen_port = ntohs(((struct sockaddr_in*)rp->ai_addr)->sin_port); + if (inet_ntop(AF_INET, addrdata, listen_addr_str, sizeof(listen_addr_str)-6)) { + endp = &listen_addr_str[0] + strlen(listen_addr_str); + } + } +#ifdef AF_INET6 + else if (rp->ai_family == AF_INET6) { + listen_port = ntohs(((struct sockaddr_in6*)rp->ai_addr)->sin6_port); + listen_addr_str[0] = '['; + if (inet_ntop(AF_INET6, addrdata, listen_addr_str+1, sizeof(listen_addr_str)-8)) { + endp = &listen_addr_str[0] + strlen(listen_addr_str); + } + if (endp) { + *endp = ']'; + endp++; + } + } +#endif + if (endp) { + sprintf(endp, ":%u", listen_port); + } + } + break; + } + freeaddrinfo(result); + if (listenfd == -1) { + usbmuxd_log(LL_FATAL, "%s: Failed to create listening socket", __func__); + return -1; + } + } else { + struct sockaddr_un bind_addr; + + if (strcmp(socket_addr, socket_path) != 0) { + struct stat fst; + if (stat(socket_addr, &fst) == 0) { + if (!S_ISSOCK(fst.st_mode)) { + usbmuxd_log(LL_FATAL, "FATAL: File '%s' already exists and is not a socket file. Refusing to continue.", socket_addr); + return -1; + } + } + } + + if (unlink(socket_addr) == -1 && errno != ENOENT) { + usbmuxd_log(LL_FATAL, "%s: unlink(%s) failed: %s", __func__, socket_addr, strerror(errno)); + return -1; + } + + listenfd = socket(AF_UNIX, SOCK_STREAM, 0); + if (listenfd == -1) { + usbmuxd_log(LL_FATAL, "socket() failed: %s", strerror(errno)); + return -1; + } + + bzero(&bind_addr, sizeof(bind_addr)); + bind_addr.sun_family = AF_UNIX; + strncpy(bind_addr.sun_path, socket_addr, sizeof(bind_addr.sun_path)); + bind_addr.sun_path[sizeof(bind_addr.sun_path) - 1] = '\0'; + + if (bind(listenfd, (struct sockaddr*)&bind_addr, sizeof(bind_addr)) != 0) { + usbmuxd_log(LL_FATAL, "bind() failed: %s", strerror(errno)); + return -1; + } + chmod(socket_addr, 0666); + + snprintf(listen_addr_str, sizeof(listen_addr_str), "%s", socket_addr); } int flags = fcntl(listenfd, F_GETFL, 0); @@ -93,21 +240,13 @@ static int create_socket(void) { } } - bzero(&bind_addr, sizeof(bind_addr)); - bind_addr.sun_family = AF_UNIX; - strcpy(bind_addr.sun_path, socket_path); - if (bind(listenfd, (struct sockaddr*)&bind_addr, sizeof(bind_addr)) != 0) { - usbmuxd_log(LL_FATAL, "bind() failed: %s", strerror(errno)); - return -1; - } - // Start listening - if (listen(listenfd, 5) != 0) { + if (listen(listenfd, 256) != 0) { usbmuxd_log(LL_FATAL, "listen() failed: %s", strerror(errno)); return -1; } - chmod(socket_path, 0666); + usbmuxd_log(LL_INFO, "Listening on %s", listen_addr_str); return listenfd; } @@ -151,7 +290,7 @@ static void set_signal_handlers(void) sigaddset(&set, SIGUSR1); sigaddset(&set, SIGUSR2); sigprocmask(SIG_SETMASK, &set, NULL); - + memset(&sa, 0, sizeof(struct sigaction)); sa.sa_handler = handle_signal; sigaction(SIGINT, &sa, NULL); @@ -359,7 +498,10 @@ static int notify_parent(int status) static void usage() { printf("Usage: %s [OPTIONS]\n", PACKAGE_NAME); - printf("Expose a socket to multiplex connections from and to iOS devices.\n\n"); + printf("\n"); + printf("Expose a socket to multiplex connections from and to iOS devices.\n"); + printf("\n"); + printf("OPTIONS:\n"); printf(" -h, --help\t\tPrint this message.\n"); printf(" -v, --verbose\t\tBe verbose (use twice or more to increase).\n"); printf(" -f, --foreground\tDo not daemonize (implies one -v).\n"); @@ -368,12 +510,18 @@ static void usage() printf(" \tStarting another instance will trigger discovery instead.\n"); printf(" -z, --enable-exit\tEnable \"--exit\" request from other instances and exit\n"); printf(" \tautomatically if no device is attached.\n"); + printf(" -p, --no-preflight\tDisable lockdownd preflight on new device.\n"); #ifdef HAVE_UDEV printf(" -u, --udev\t\tRun in udev operation mode (implies -n and -z).\n"); #endif #ifdef HAVE_SYSTEMD printf(" -s, --systemd\t\tRun in systemd operation mode (implies -z and -f).\n"); #endif + printf(" -S, --socket ADDR:PORT | PATH Specify source ADDR and PORT or a UNIX\n"); + printf(" \t\tsocket PATH to use for the listening socket.\n"); + printf(" \t\tDefault: %s\n", socket_path); + printf(" -P, --pidfile PATH\tSpecify a different location for the pid file, or pass\n"); + printf(" \t\tNONE to disable. Default: %s\n", DEFAULT_LOCKFILE); printf(" -x, --exit\t\tNotify a running instance to exit if there are no devices\n"); printf(" \t\tconnected (sends SIGUSR1 to running instance) and exit.\n"); printf(" -X, --force-exit\tNotify a running instance to exit even if there are still\n"); @@ -381,6 +529,8 @@ static void usage() printf(" -l, --logfile=LOGFILE\tLog (append) to LOGFILE instead of stderr or syslog.\n"); printf(" -V, --version\t\tPrint version information and exit.\n"); printf("\n"); + printf("Homepage: <" PACKAGE_URL ">\n"); + printf("Bug Reports: <" PACKAGE_BUGREPORT ">\n"); } static void parse_opts(int argc, char **argv) @@ -392,12 +542,15 @@ static void parse_opts(int argc, char **argv) {"user", required_argument, NULL, 'U'}, {"disable-hotplug", no_argument, NULL, 'n'}, {"enable-exit", no_argument, NULL, 'z'}, + {"no-preflight", no_argument, NULL, 'p'}, #ifdef HAVE_UDEV {"udev", no_argument, NULL, 'u'}, #endif #ifdef HAVE_SYSTEMD {"systemd", no_argument, NULL, 's'}, #endif + {"socket", required_argument, NULL, 'S'}, + {"pidfile", required_argument, NULL, 'P'}, {"exit", no_argument, NULL, 'x'}, {"force-exit", no_argument, NULL, 'X'}, {"logfile", required_argument, NULL, 'l'}, @@ -407,11 +560,11 @@ static void parse_opts(int argc, char **argv) int c; #ifdef HAVE_SYSTEMD - const char* opts_spec = "hfvVuU:xXsnzl:"; + const char* opts_spec = "hfvVuU:xXsnzl:pS:P:"; #elif HAVE_UDEV - const char* opts_spec = "hfvVuU:xXnzl:"; + const char* opts_spec = "hfvVuU:xXnzl:pS:P:"; #else - const char* opts_spec = "hfvVU:xXnzl:"; + const char* opts_spec = "hfvVU:xXnzl:pS:P:"; #endif while (1) { @@ -437,6 +590,9 @@ static void parse_opts(int argc, char **argv) drop_privileges = 1; drop_user = optarg; break; + case 'p': + no_preflight = 1; + break; #ifdef HAVE_UDEV case 'u': opt_disable_hotplug = 1; @@ -455,6 +611,26 @@ static void parse_opts(int argc, char **argv) case 'z': opt_enable_exit = 1; break; + case 'S': + if (!*optarg || *optarg == '-') { + usbmuxd_log(LL_FATAL, "ERROR: --socket requires an argument"); + usage(); + exit(2); + } + listen_addr = optarg; + break; + case 'P': + if (!*optarg || *optarg == '-') { + usbmuxd_log(LL_FATAL, "ERROR: --pidfile requires an argument"); + usage(); + exit(2); + } + if (!strcmp(optarg, "NONE")) { + lockfile = NULL; + } else { + lockfile = optarg; + } + break; case 'x': opt_exit = 1; exit_signal = SIGUSR1; @@ -516,19 +692,21 @@ int main(int argc, char *argv[]) set_signal_handlers(); signal(SIGPIPE, SIG_IGN); - res = lfd = open(lockfile, O_WRONLY|O_CREAT, 0644); - if(res == -1) { - usbmuxd_log(LL_FATAL, "Could not open lockfile"); - goto terminate; - } - lock.l_type = F_WRLCK; - lock.l_whence = SEEK_SET; - lock.l_start = 0; - lock.l_len = 0; - lock.l_pid = 0; - fcntl(lfd, F_GETLK, &lock); - close(lfd); - if (lock.l_type != F_UNLCK) { + if (lockfile) { + res = lfd = open(lockfile, O_WRONLY|O_CREAT, 0644); + if(res == -1) { + usbmuxd_log(LL_FATAL, "Could not open lockfile"); + goto terminate; + } + lock.l_type = F_WRLCK; + lock.l_whence = SEEK_SET; + lock.l_start = 0; + lock.l_len = 0; + lock.l_pid = 0; + fcntl(lfd, F_GETLK, &lock); + close(lfd); + } + if (lockfile && lock.l_type != F_UNLCK) { if (opt_exit) { if (lock.l_pid && !kill(lock.l_pid, 0)) { usbmuxd_log(LL_NOTICE, "Sending signal %d to instance with pid %d", exit_signal, lock.l_pid); @@ -564,7 +742,9 @@ int main(int argc, char *argv[]) goto terminate; } } - unlink(lockfile); + if (lockfile) { + unlink(lockfile); + } if (opt_exit) { usbmuxd_log(LL_NOTICE, "No running instance found, none killed. Exiting."); @@ -579,26 +759,28 @@ int main(int argc, char *argv[]) } } - // now open the lockfile and place the lock - res = lfd = open(lockfile, O_WRONLY|O_CREAT|O_TRUNC|O_EXCL, 0644); - if(res < 0) { - usbmuxd_log(LL_FATAL, "Could not open lockfile"); - goto terminate; - } - lock.l_type = F_WRLCK; - lock.l_whence = SEEK_SET; - lock.l_start = 0; - lock.l_len = 0; - if ((res = fcntl(lfd, F_SETLK, &lock)) < 0) { - usbmuxd_log(LL_FATAL, "Lockfile locking failed!"); - goto terminate; - } - sprintf(pids, "%d", getpid()); - if ((size_t)(res = write(lfd, pids, strlen(pids))) != strlen(pids)) { - usbmuxd_log(LL_FATAL, "Could not write pidfile!"); - if(res >= 0) - res = -2; - goto terminate; + if (lockfile) { + // now open the lockfile and place the lock + res = lfd = open(lockfile, O_WRONLY|O_CREAT|O_TRUNC|O_EXCL, 0644); + if(res < 0) { + usbmuxd_log(LL_FATAL, "Could not open pidfile '%s'", lockfile); + goto terminate; + } + lock.l_type = F_WRLCK; + lock.l_whence = SEEK_SET; + lock.l_start = 0; + lock.l_len = 0; + if ((res = fcntl(lfd, F_SETLK, &lock)) < 0) { + usbmuxd_log(LL_FATAL, "Locking pidfile '%s' failed!", lockfile); + goto terminate; + } + sprintf(pids, "%d", getpid()); + if ((size_t)(res = write(lfd, pids, strlen(pids))) != strlen(pids)) { + usbmuxd_log(LL_FATAL, "Could not write pidfile!"); + if(res >= 0) + res = -2; + goto terminate; + } } // set number of file descriptors to higher value diff --git a/src/preflight.c b/src/preflight.c index c7cfa50..9c57e98 100644 --- a/src/preflight.c +++ b/src/preflight.c @@ -26,8 +26,6 @@ #include <unistd.h> #include <errno.h> -#include <pthread.h> - #include <sys/time.h> #ifdef HAVE_LIBIMOBILEDEVICE @@ -36,11 +34,16 @@ #include <libimobiledevice/notification_proxy.h> #endif +#include <libimobiledevice-glue/thread.h> + #include "preflight.h" #include "device.h" #include "client.h" #include "conf.h" #include "log.h" +#include "usb.h" + +extern int no_preflight; #ifdef HAVE_LIBIMOBILEDEVICE #ifndef HAVE_ENUM_IDEVICE_CONNECTION_TYPE @@ -56,6 +59,7 @@ struct idevice_private { enum idevice_connection_type conn_type; void *conn_data; int version; + int device_class; }; struct cb_data { @@ -135,6 +139,7 @@ static void* preflight_worker_handle_device_add(void* userdata) _dev->conn_type = CONNECTION_USBMUXD; _dev->conn_data = NULL; _dev->version = 0; + _dev->device_class = 0; idevice_t dev = (idevice_t)_dev; @@ -143,6 +148,7 @@ static void* preflight_worker_handle_device_add(void* userdata) plist_t value = NULL; char* version_str = NULL; + char* deviceclass_str = NULL; usbmuxd_log(LL_INFO, "%s: Starting preflight on device %s...", __func__, _dev->udid); @@ -208,23 +214,43 @@ retry: lerr = lockdownd_get_value(lockdown, NULL, "ProductVersion", &value); if (lerr != LOCKDOWN_E_SUCCESS) { - usbmuxd_log(LL_ERROR, "%s: ERROR: Could not get ProductVersion from device %s, lockdown error %d", __func__, _dev->udid, lerr); - goto leave; + usbmuxd_log(LL_WARNING, "%s: Could not get ProductVersion from device %s, lockdown error %d", __func__, _dev->udid, lerr); + /* assume old iOS version */ + version_str = strdup("1.0"); + } else { + if (value && plist_get_node_type(value) == PLIST_STRING) { + plist_get_string_val(value, &version_str); + } + plist_free(value); + + if (!version_str) { + usbmuxd_log(LL_ERROR, "%s: Could not get ProductVersion string from device %s handle %d", __func__, _dev->udid, (int)(long)_dev->conn_data); + goto leave; + } } + lerr = lockdownd_get_value(lockdown, NULL, "DeviceClass", &value); + if (lerr != LOCKDOWN_E_SUCCESS) { + usbmuxd_log(LL_ERROR, "%s: ERROR: Could not get DeviceClass from device %s, lockdown error %d", __func__, _dev->udid, lerr); + goto leave; + } if (value && plist_get_node_type(value) == PLIST_STRING) { - plist_get_string_val(value, &version_str); + plist_get_string_val(value, &deviceclass_str); } + plist_free(value); - if (!version_str) { - usbmuxd_log(LL_ERROR, "%s: Could not get ProductVersion string from device %s handle %d", __func__, _dev->udid, (int)(long)_dev->conn_data); + if (!deviceclass_str) { + usbmuxd_log(LL_ERROR, "%s: Could not get DeviceClass string from device %s handle %d", __func__, _dev->udid, (int)(long)_dev->conn_data); goto leave; } int version_major = strtol(version_str, NULL, 10); - if (version_major >= 7) { - /* iOS 7.0 and later */ - usbmuxd_log(LL_INFO, "%s: Found ProductVersion %s device %s", __func__, version_str, _dev->udid); + if (((!strcmp(deviceclass_str, "iPhone") || !strcmp(deviceclass_str, "iPad")) && version_major >= 7) + || (!strcmp(deviceclass_str, "Watch") && version_major >= 2) + || (!strcmp(deviceclass_str, "AppleTV") && version_major >= 9) + ) { + /* iOS 7.0 / watchOS 2.0 / tvOS 9.0 and later */ + usbmuxd_log(LL_INFO, "%s: Found %s %s device %s", __func__, deviceclass_str, version_str, _dev->udid); lockdownd_set_untrusted_host_buid(lockdown); @@ -270,7 +296,7 @@ retry: "com.apple.mobile.lockdown.request_pair", "com.apple.mobile.lockdown.request_host_buid", NULL - }; + }; np_observe_notifications(np, spec); /* TODO send notification to user's desktop */ @@ -331,10 +357,8 @@ retry: } leave: - if (value) - plist_free(value); - if (version_str) - free(version_str); + free(deviceclass_str); + free(version_str); if (lockdown) lockdownd_client_free(lockdown); if (dev) @@ -353,6 +377,11 @@ void preflight_device_remove_cb(void *data) void preflight_worker_device_add(struct device_info* info) { + if (info->pid == PID_APPLE_T2_COPROCESSOR || no_preflight == 1) { + client_device_add(info); + return; + } + #ifdef HAVE_LIBIMOBILEDEVICE struct device_info *infocopy = (struct device_info*)malloc(sizeof(struct device_info)); @@ -361,18 +390,15 @@ void preflight_worker_device_add(struct device_info* info) infocopy->serial = strdup(info->serial); } - pthread_t th; - pthread_attr_t attr; - - pthread_attr_init(&attr); - pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); - - int perr = pthread_create(&th, &attr, preflight_worker_handle_device_add, infocopy); + THREAD_T th; + int perr = thread_new(&th, preflight_worker_handle_device_add, infocopy); if (perr != 0) { free((char*)infocopy->serial); free(infocopy); 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); client_device_add(info); + } else { + thread_detach(th); } #else client_device_add(info); @@ -3,7 +3,7 @@ * * Copyright (C) 2009 Hector Martin <hector@marcansoft.com> * Copyright (C) 2009 Nikias Bassen <nikias@gmx.li> - * Copyright (C) 2009 Martin Szulecki <opensuse@sukimashita.com> + * Copyright (C) 2009-2020 Martin Szulecki <martin.szulecki@libimobiledevice.org> * Copyright (C) 2014 Mikkel Kamstrup Erlandsen <mikkel.kamstrup@xamarin.com> * * This program is free software; you can redistribute it and/or modify @@ -31,6 +31,8 @@ #include <libusb.h> +#include <libimobiledevice-glue/collection.h> + #include "usb.h" #include "log.h" #include "device.h" @@ -63,6 +65,14 @@ struct usb_device { struct libusb_device_descriptor devdesc; }; +struct mode_context { + struct libusb_device* dev; + uint8_t bus, address; + uint8_t bRequest; + uint16_t wValue, wIndex, wLength; + unsigned int timeout; +}; + static struct collection device_list; static struct timeval next_dev_poll_time; @@ -97,7 +107,7 @@ static void usb_disconnect(struct usb_device *dev) tv.tv_sec = 0; tv.tv_usec = 1000; if((res = libusb_handle_events_timeout(NULL, &tv)) < 0) { - usbmuxd_log(LL_ERROR, "libusb_handle_events_timeout for usb_disconnect failed: %d", res); + usbmuxd_log(LL_ERROR, "libusb_handle_events_timeout for usb_disconnect failed: %s", libusb_error_name(res)); break; } } @@ -169,7 +179,7 @@ int usb_send(struct usb_device *dev, const unsigned char *buf, int length) struct libusb_transfer *xfer = libusb_alloc_transfer(0); libusb_fill_bulk_transfer(xfer, dev->dev, dev->ep_out, (void*)buf, length, tx_callback, dev, 0); if((res = libusb_submit_transfer(xfer)) < 0) { - usbmuxd_log(LL_ERROR, "Failed to submit TX transfer %p len %d to device %d-%d: %d", buf, length, dev->bus, dev->address, res); + 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)); libusb_free_transfer(xfer); return res; } @@ -181,7 +191,7 @@ int usb_send(struct usb_device *dev, const unsigned char *buf, int length) void *buffer = malloc(1); libusb_fill_bulk_transfer(xfer, dev->dev, dev->ep_out, buffer, 0, tx_callback, dev, 0); if((res = libusb_submit_transfer(xfer)) < 0) { - usbmuxd_log(LL_ERROR, "Failed to submit TX ZLP transfer to device %d-%d: %d", dev->bus, dev->address, res); + usbmuxd_log(LL_ERROR, "Failed to submit TX ZLP transfer to device %d-%d: %s", dev->bus, dev->address, libusb_error_name(res)); libusb_free_transfer(xfer); return res; } @@ -248,7 +258,7 @@ static int start_rx_loop(struct usb_device *dev) buf = malloc(USB_MRU); libusb_fill_bulk_transfer(xfer, dev->dev, dev->ep_in, buf, USB_MRU, rx_callback, dev, 0); if((res = libusb_submit_transfer(xfer)) != 0) { - usbmuxd_log(LL_ERROR, "Failed to submit RX transfer to device %d-%d: %d", dev->bus, dev->address, res); + usbmuxd_log(LL_ERROR, "Failed to submit RX transfer to device %d-%d: %s", dev->bus, dev->address, libusb_error_name(res)); libusb_free_transfer(xfer); return res; } @@ -350,160 +360,233 @@ static void get_langid_callback(struct libusb_transfer *transfer) libusb_fill_control_transfer(transfer, usbdev->dev, transfer->buffer, get_serial_callback, usbdev, 1000); if((res = libusb_submit_transfer(transfer)) < 0) { - usbmuxd_log(LL_ERROR, "Could not request transfer for device %d-%d (%d)", usbdev->bus, usbdev->address, res); + usbmuxd_log(LL_ERROR, "Could not request transfer for device %d-%d: %s", usbdev->bus, usbdev->address, libusb_error_name(res)); libusb_free_transfer(transfer); } } -static int usb_device_add(libusb_device* dev) +static int submit_vendor_specific(struct libusb_device_handle *handle, struct mode_context *context, libusb_transfer_cb_fn callback) +{ + struct libusb_transfer* ctrl_transfer = libusb_alloc_transfer(0); + int ret = 0; + unsigned char* buffer = calloc(LIBUSB_CONTROL_SETUP_SIZE + context->wLength, 1); + uint8_t bRequestType = LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_ENDPOINT_IN | LIBUSB_RECIPIENT_DEVICE; + libusb_fill_control_setup(buffer, bRequestType, context->bRequest, context->wValue, context->wIndex, context->wLength); + + ctrl_transfer->flags = LIBUSB_TRANSFER_FREE_TRANSFER; + libusb_fill_control_transfer(ctrl_transfer, handle, buffer, callback, context, context->timeout); + + ret = libusb_submit_transfer(ctrl_transfer); + return ret; +} + +static struct usb_device* find_device(int bus, int address) { - int j, res; - // the following are non-blocking operations on the device list - uint8_t bus = libusb_get_bus_number(dev); - uint8_t address = libusb_get_device_address(dev); - struct libusb_device_descriptor devdesc; - struct libusb_transfer *transfer; - int found = 0; FOREACH(struct usb_device *usbdev, &device_list) { if(usbdev->bus == bus && usbdev->address == address) { - usbdev->alive = 1; - found = 1; - break; + return usbdev; } } ENDFOREACH - if(found) - return 0; //device already found + return NULL; +} - if((res = libusb_get_device_descriptor(dev, &devdesc)) != 0) { - usbmuxd_log(LL_WARNING, "Could not get device descriptor for device %d-%d: %d", bus, address, res); - return -1; +/// @brief guess the current mode +/// @param dev +/// @param usbdev +/// @param handle +/// @return 0 - undetermined, 1 - initial, 2 - valeria, 3 - cdc_ncm +static int guess_mode(struct libusb_device* dev, struct usb_device *usbdev) +{ + int res, j; + int has_valeria = 0, has_cdc_ncm = 0, has_usbmux = 0; + struct libusb_device_descriptor devdesc = usbdev->devdesc; + struct libusb_config_descriptor *config; + int bus = usbdev->bus; + int address = usbdev->address; + + if(devdesc.bNumConfigurations <= 4) { + // Assume this is initial mode + return 1; } - if(devdesc.idVendor != VID_APPLE) - return -1; - if((devdesc.idProduct != PID_APPLE_T2_COPROCESSOR) && - ((devdesc.idProduct < PID_RANGE_LOW) || - (devdesc.idProduct > PID_RANGE_MAX))) - return -1; - libusb_device_handle *handle; - usbmuxd_log(LL_INFO, "Found new device with v/p %04x:%04x at %d-%d", devdesc.idVendor, devdesc.idProduct, bus, address); - // No blocking operation can follow: it may be run in the libusb hotplug callback and libusb will refuse any - // blocking call - if((res = libusb_open(dev, &handle)) != 0) { - usbmuxd_log(LL_WARNING, "Could not open device %d-%d: %d", bus, address, res); - return -1; + + if(devdesc.bNumConfigurations != 5) { + // No known modes with more then 5 configurations + return 0; } - int desired_config = devdesc.bNumConfigurations; - if (desired_config > 4) { - desired_config = 4; + if((res = libusb_get_config_descriptor_by_value(dev, 5, &config)) != 0) { + usbmuxd_log(LL_NOTICE, "Could not get configuration 5 descriptor for device %i-%i: %s", bus, address, libusb_error_name(res)); + return 0; + } + + // Require both usbmux and one of the other interfaces to determine this is a valid configuration + for(j = 0 ; j < config->bNumInterfaces ; j++) { + const struct libusb_interface_descriptor *intf = &config->interface[j].altsetting[0]; + if(intf->bInterfaceClass == INTERFACE_CLASS && + intf->bInterfaceSubClass == 42 && + intf->bInterfaceProtocol == 255) { + has_valeria = 1; + } + // https://github.com/torvalds/linux/blob/72a85e2b0a1e1e6fb4ee51ae902730212b2de25c/include/uapi/linux/usb/cdc.h#L22 + // 2 for Communication class, 0xd for CDC NCM subclass + if(intf->bInterfaceClass == 2 && + intf->bInterfaceSubClass == 0xd) { + has_cdc_ncm = 1; + } + if(intf->bInterfaceClass == INTERFACE_CLASS && + intf->bInterfaceSubClass == INTERFACE_SUBCLASS && + intf->bInterfaceProtocol == INTERFACE_PROTOCOL) { + has_usbmux = 1; + } } + + libusb_free_config_descriptor(config); + + if(has_valeria && has_usbmux) { + usbmuxd_log(LL_NOTICE, "Found Valeria and Apple USB Multiplexor in device %i-%i configuration 5", bus, address); + return 2; + } + + if(has_cdc_ncm && has_usbmux) { + usbmuxd_log(LL_NOTICE, "Found CDC-NCM and Apple USB Multiplexor in device %i-%i configuration 5", bus, address); + return 3; + } + + return 0; +} + +/// @brief Finds and sets the valid configuration, interface and endpoints on the usb_device +static int set_valid_configuration(struct libusb_device* dev, struct usb_device *usbdev, struct libusb_device_handle *handle) +{ + int j, k, res, found = 0; + struct libusb_config_descriptor *config; + const struct libusb_interface_descriptor *intf; + struct libusb_device_descriptor devdesc = usbdev->devdesc; + int bus = usbdev->bus; + int address = usbdev->address; int current_config = 0; + if((res = libusb_get_configuration(handle, ¤t_config)) != 0) { - usbmuxd_log(LL_WARNING, "Could not get configuration for device %d-%d: %d", bus, address, res); - libusb_close(handle); + usbmuxd_log(LL_WARNING, "Could not get current configuration for device %d-%d: %s", bus, address, libusb_error_name(res)); return -1; } - if (current_config != desired_config) { - struct libusb_config_descriptor *config; - if((res = libusb_get_active_config_descriptor(dev, &config)) != 0) { - usbmuxd_log(LL_NOTICE, "Could not get old configuration descriptor for device %d-%d: %d", bus, address, res); - } else { - for(j=0; j<config->bNumInterfaces; j++) { - const struct libusb_interface_descriptor *intf = &config->interface[j].altsetting[0]; - if((res = libusb_kernel_driver_active(handle, intf->bInterfaceNumber)) < 0) { - usbmuxd_log(LL_NOTICE, "Could not check kernel ownership of interface %d for device %d-%d: %d", intf->bInterfaceNumber, bus, address, res); + + for(j = devdesc.bNumConfigurations ; j > 0 ; j--) { + if((res = libusb_get_config_descriptor_by_value(dev, j, &config)) != 0) { + usbmuxd_log(LL_NOTICE, "Could not get configuration %i descriptor for device %i-%i: %s", j, bus, address, libusb_error_name(res)); + continue; + } + for(k = 0 ; k < config->bNumInterfaces ; k++) { + intf = &config->interface[k].altsetting[0]; + if(intf->bInterfaceClass == INTERFACE_CLASS || + intf->bInterfaceSubClass == INTERFACE_SUBCLASS || + intf->bInterfaceProtocol == INTERFACE_PROTOCOL) { + usbmuxd_log(LL_NOTICE, "Found usbmux interface for device %i-%i: %i", bus, address, intf->bInterfaceNumber); + if(intf->bNumEndpoints != 2) { + usbmuxd_log(LL_WARNING, "Endpoint count mismatch for interface %i of device %i-%i", intf->bInterfaceNumber, bus, address); + continue; + } + if((intf->endpoint[0].bEndpointAddress & 0x80) == LIBUSB_ENDPOINT_OUT && + (intf->endpoint[1].bEndpointAddress & 0x80) == LIBUSB_ENDPOINT_IN) { + usbdev->interface = intf->bInterfaceNumber; + usbdev->ep_out = intf->endpoint[0].bEndpointAddress; + usbdev->ep_in = intf->endpoint[1].bEndpointAddress; + 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); + found = 1; + break; + } else if((intf->endpoint[1].bEndpointAddress & 0x80) == LIBUSB_ENDPOINT_OUT && + (intf->endpoint[0].bEndpointAddress & 0x80) == LIBUSB_ENDPOINT_IN) { + usbdev->interface = intf->bInterfaceNumber; + usbdev->ep_out = intf->endpoint[1].bEndpointAddress; + usbdev->ep_in = intf->endpoint[0].bEndpointAddress; + 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); + found = 1; + break; + } else { + usbmuxd_log(LL_WARNING, "Endpoint type mismatch for interface %i of device %i-%i", intf->bInterfaceNumber, bus, address); + } + } + } + if(!found) { + libusb_free_config_descriptor(config); + continue; + } + // If set configuration is required, try to first detach all kernel drivers + if (current_config == 0) { + usbmuxd_log(LL_DEBUG, "Device %d-%d is unconfigured", bus, address); + } + if(current_config == 0 || config->bConfigurationValue != current_config) { + usbmuxd_log(LL_NOTICE, "Changing configuration of device %i-%i: %i -> %i", bus, address, current_config, config->bConfigurationValue); + for(k=0 ; k < config->bNumInterfaces ; k++) { + const struct libusb_interface_descriptor *intf1 = &config->interface[k].altsetting[0]; + if((res = libusb_kernel_driver_active(handle, intf1->bInterfaceNumber)) < 0) { + 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)); continue; } if(res == 1) { - usbmuxd_log(LL_INFO, "Detaching kernel driver for device %d-%d, interface %d", bus, address, intf->bInterfaceNumber); - if((res = libusb_detach_kernel_driver(handle, intf->bInterfaceNumber)) < 0) { - usbmuxd_log(LL_WARNING, "Could not detach kernel driver (%d), configuration change will probably fail!", res); + usbmuxd_log(LL_INFO, "Detaching kernel driver for device %d-%d, interface %d", bus, address, intf1->bInterfaceNumber); + if((res = libusb_detach_kernel_driver(handle, intf1->bInterfaceNumber)) < 0) { + usbmuxd_log(LL_WARNING, "Could not detach kernel driver, configuration change will probably fail! %s", libusb_error_name(res)); continue; } } } - libusb_free_config_descriptor(config); - } - - usbmuxd_log(LL_INFO, "Setting configuration for device %d-%d, from %d to %d", bus, address, current_config, desired_config); - if((res = libusb_set_configuration(handle, desired_config)) != 0) { - usbmuxd_log(LL_WARNING, "Could not set configuration %d for device %d-%d: %d", desired_config, bus, address, res); - libusb_close(handle); - return -1; + if((res = libusb_set_configuration(handle, j)) != 0) { + usbmuxd_log(LL_WARNING, "Could not set configuration %d for device %d-%d: %s", j, bus, address, libusb_error_name(res)); + libusb_free_config_descriptor(config); + continue; + } } + + libusb_free_config_descriptor(config); + break; } - struct libusb_config_descriptor *config; - if((res = libusb_get_active_config_descriptor(dev, &config)) != 0) { - usbmuxd_log(LL_WARNING, "Could not get configuration descriptor for device %d-%d: %d", bus, address, res); - libusb_close(handle); + if(!found) { + usbmuxd_log(LL_WARNING, "Could not find a suitable USB interface for device %i-%i", bus, address); return -1; } - struct usb_device *usbdev; - usbdev = malloc(sizeof(struct usb_device)); - memset(usbdev, 0, sizeof(*usbdev)); + return 0; +} - for(j=0; j<config->bNumInterfaces; j++) { - const struct libusb_interface_descriptor *intf = &config->interface[j].altsetting[0]; - if(intf->bInterfaceClass != INTERFACE_CLASS || - intf->bInterfaceSubClass != INTERFACE_SUBCLASS || - intf->bInterfaceProtocol != INTERFACE_PROTOCOL) - continue; - if(intf->bNumEndpoints != 2) { - usbmuxd_log(LL_WARNING, "Endpoint count mismatch for interface %d of device %d-%d", intf->bInterfaceNumber, bus, address); - continue; - } - if((intf->endpoint[0].bEndpointAddress & 0x80) == LIBUSB_ENDPOINT_OUT && - (intf->endpoint[1].bEndpointAddress & 0x80) == LIBUSB_ENDPOINT_IN) { - usbdev->interface = intf->bInterfaceNumber; - usbdev->ep_out = intf->endpoint[0].bEndpointAddress; - usbdev->ep_in = intf->endpoint[1].bEndpointAddress; - 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); - break; - } else if((intf->endpoint[1].bEndpointAddress & 0x80) == LIBUSB_ENDPOINT_OUT && - (intf->endpoint[0].bEndpointAddress & 0x80) == LIBUSB_ENDPOINT_IN) { - usbdev->interface = intf->bInterfaceNumber; - usbdev->ep_out = intf->endpoint[1].bEndpointAddress; - usbdev->ep_in = intf->endpoint[0].bEndpointAddress; - usbmuxd_log(LL_INFO, "Found interface %d with swapped endpoints %02x/%02x for device %d-%d", usbdev->interface, usbdev->ep_out, usbdev->ep_in, bus, address); - break; - } else { - usbmuxd_log(LL_WARNING, "Endpoint type mismatch for interface %d of device %d-%d", intf->bInterfaceNumber, bus, address); - } +static void device_complete_initialization(struct mode_context *context, struct libusb_device_handle *handle) +{ + struct usb_device *usbdev = find_device(context->bus, context->address); + if(!usbdev) { + usbmuxd_log(LL_ERROR, "Device %d-%d is missing from device list, aborting initialization", context->bus, context->address); + return; } + struct libusb_device *dev = context->dev; + struct libusb_device_descriptor devdesc = usbdev->devdesc; + int bus = context->bus; + int address = context->address; + int res; + struct libusb_transfer *transfer; - if(j == config->bNumInterfaces) { - usbmuxd_log(LL_WARNING, "Could not find a suitable USB interface for device %d-%d", bus, address); - libusb_free_config_descriptor(config); - libusb_close(handle); - free(usbdev); - return -1; + if((res = set_valid_configuration(dev, usbdev, handle)) != 0) { + usbdev->alive = 0; + return; } - libusb_free_config_descriptor(config); - if((res = libusb_claim_interface(handle, usbdev->interface)) != 0) { - usbmuxd_log(LL_WARNING, "Could not claim interface %d for device %d-%d: %d", usbdev->interface, bus, address, res); - libusb_close(handle); - free(usbdev); - return -1; + usbmuxd_log(LL_WARNING, "Could not claim interface %d for device %d-%d: %s", usbdev->interface, bus, address, libusb_error_name(res)); + usbdev->alive = 0; + return; } transfer = libusb_alloc_transfer(0); if(!transfer) { - usbmuxd_log(LL_WARNING, "Failed to allocate transfer for device %d-%d: %d", bus, address, res); - libusb_close(handle); - free(usbdev); - return -1; + usbmuxd_log(LL_WARNING, "Failed to allocate transfer for device %d-%d: %s", bus, address, libusb_error_name(res)); + usbdev->alive = 0; + return; } unsigned char *transfer_buffer = malloc(1024 + LIBUSB_CONTROL_SETUP_SIZE + 8); if (!transfer_buffer) { - usbmuxd_log(LL_WARNING, "Failed to allocate transfer buffer for device %d-%d: %d", bus, address, res); - libusb_close(handle); - free(usbdev); - return -1; + usbmuxd_log(LL_WARNING, "Failed to allocate transfer buffer for device %d-%d: %s", bus, address, libusb_error_name(res)); + usbdev->alive = 0; + return; } memset(transfer_buffer, '\0', 1024 + LIBUSB_CONTROL_SETUP_SIZE + 8); @@ -551,19 +634,166 @@ static int usb_device_add(libusb_device* dev) libusb_fill_control_transfer(transfer, handle, transfer_buffer, get_langid_callback, usbdev, 1000); if((res = libusb_submit_transfer(transfer)) < 0) { - usbmuxd_log(LL_ERROR, "Could not request transfer for device %d-%d (%d)", usbdev->bus, usbdev->address, res); + usbmuxd_log(LL_ERROR, "Could not request transfer for device %d-%d: %s", usbdev->bus, usbdev->address, libusb_error_name(res)); libusb_free_transfer(transfer); - libusb_close(handle); free(transfer_buffer); - free(usbdev); + usbdev->alive = 0; + return; + } +} + +static void switch_mode_cb(struct libusb_transfer* transfer) +{ + // For old devices not supporting mode swtich, if anything goes wrong - continue in current mode + struct mode_context* context = transfer->user_data; + struct usb_device *dev = find_device(context->bus, context->address); + if(!dev) { + usbmuxd_log(LL_WARNING, "Device %d-%d is missing from device list", context->bus, context->address); + } + if(transfer->status != LIBUSB_TRANSFER_COMPLETED) { + usbmuxd_log(LL_ERROR, "Failed to request mode switch for device %i-%i (%i). Completing initialization in current mode", + context->bus, context->address, transfer->status); + device_complete_initialization(context, transfer->dev_handle); + } + else { + unsigned char *data = libusb_control_transfer_get_data(transfer); + if(data[0] != 0) { + usbmuxd_log(LL_INFO, "Received unexpected response for device %i-%i mode switch (%i). Completing initialization in current mode", + context->bus, context->address, data[0]); + device_complete_initialization(context, transfer->dev_handle); + } + } + free(context); + if(transfer->buffer) + free(transfer->buffer); +} + +static void get_mode_cb(struct libusb_transfer* transfer) +{ + // For old devices not supporting mode swtich, if anything goes wrong - continue in current mode + int res; + struct mode_context* context = transfer->user_data; + struct usb_device *dev = find_device(context->bus, context->address); + if(!dev) { + usbmuxd_log(LL_ERROR, "Device %d-%d is missing from device list, aborting mode switch", context->bus, context->address); + free(context); + return; + } + + if(transfer->status != LIBUSB_TRANSFER_COMPLETED) { + usbmuxd_log(LL_ERROR, "Failed to request get mode for device %i-%i (%i). Completing initialization in current mode", + context->bus, context->address, transfer->status); + device_complete_initialization(context, transfer->dev_handle); + free(context); + return; + } + + unsigned char *data = libusb_control_transfer_get_data(transfer); + + char* desired_mode_char = getenv(ENV_DEVICE_MODE); + int desired_mode = desired_mode_char ? atoi(desired_mode_char) : 3; + int guessed_mode = guess_mode(context->dev, dev); + + // Response is 3:3:3:0 for initial mode, 5:3:3:0 otherwise. + 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); + if(desired_mode >= 1 && desired_mode <= 3 && + guessed_mode > 0 && // do not switch mode if guess failed + guessed_mode != desired_mode) { + usbmuxd_log(LL_WARNING, "Switching device %i-%i mode to %i", context->bus, context->address, desired_mode); + + context->bRequest = APPLE_VEND_SPECIFIC_SET_MODE; + context->wValue = 0; + context->wIndex = desired_mode; + context->wLength = 1; + + if((res = submit_vendor_specific(transfer->dev_handle, context, switch_mode_cb)) != 0) { + usbmuxd_log(LL_WARNING, "Could not request to switch mode %i for device %i-%i (%i)", context->wIndex, context->bus, context->address, res); + dev->alive = 0; + free(context); + } + } + else { + usbmuxd_log(LL_WARNING, "Skipping switch device %i-%i mode from %i to %i", context->bus, context->address, guessed_mode, desired_mode); + device_complete_initialization(context, transfer->dev_handle); + free(context); + } + if(transfer->buffer) + free(transfer->buffer); +} + +static int usb_device_add(libusb_device* dev) +{ + int res; + // the following are non-blocking operations on the device list + uint8_t bus = libusb_get_bus_number(dev); + uint8_t address = libusb_get_device_address(dev); + struct libusb_device_descriptor devdesc; + struct usb_device *usbdev = find_device(bus, address); + if(usbdev) { + usbdev->alive = 1; + return 0; //device already found + } + + if((res = libusb_get_device_descriptor(dev, &devdesc)) != 0) { + usbmuxd_log(LL_WARNING, "Could not get device descriptor for device %d-%d: %s", bus, address, libusb_error_name(res)); + return -1; + } + if(devdesc.idVendor != VID_APPLE) + return -1; + if((devdesc.idProduct != PID_APPLE_T2_COPROCESSOR) && + ((devdesc.idProduct < PID_APPLE_SILICON_RESTORE_LOW) || + (devdesc.idProduct > PID_APPLE_SILICON_RESTORE_MAX)) && + ((devdesc.idProduct < PID_RANGE_LOW) || + (devdesc.idProduct > PID_RANGE_MAX))) + return -1; + libusb_device_handle *handle; + usbmuxd_log(LL_INFO, "Found new device with v/p %04x:%04x at %d-%d", devdesc.idVendor, devdesc.idProduct, bus, address); + // No blocking operation can follow: it may be run in the libusb hotplug callback and libusb will refuse any + // blocking call + if((res = libusb_open(dev, &handle)) != 0) { + usbmuxd_log(LL_WARNING, "Could not open device %d-%d: %s", bus, address, libusb_error_name(res)); return -1; } + // Add the created handle to the device list, so we can close it in case of failure/disconnection + usbdev = malloc(sizeof(struct usb_device)); + memset(usbdev, 0, sizeof(*usbdev)); + + usbdev->serial[0] = 0; + usbdev->bus = bus; + usbdev->address = address; + usbdev->devdesc = devdesc; + usbdev->speed = 0; + usbdev->dev = handle; + usbdev->alive = 1; + collection_init(&usbdev->tx_xfers); collection_init(&usbdev->rx_xfers); collection_add(&device_list, usbdev); + // On top of configurations, Apple have multiple "modes" for devices, namely: + // 1: An "initial" mode with 4 configurations + // 2: "Valeria" mode, where configuration 5 is included with interface for H.265 video capture (activated when recording screen with QuickTime in macOS) + // 3: "CDC NCM" mode, where configuration 5 is included with interface for Ethernet/USB (activated using internet-sharing feature in macOS) + // Request current mode asynchroniously, so it can be changed in callback if needed + usbmuxd_log(LL_INFO, "Requesting current mode from device %i-%i", bus, address); + struct mode_context* context = malloc(sizeof(struct mode_context)); + context->dev = dev; + context->bus = bus; + context->address = address; + context->bRequest = APPLE_VEND_SPECIFIC_GET_MODE; + context->wValue = 0; + context->wIndex = 0; + context->wLength = 4; + context->timeout = 1000; + + if(submit_vendor_specific(handle, context, get_mode_cb) != 0) { + usbmuxd_log(LL_WARNING, "Could not request current mode from device %d-%d", bus, address); + // Schedule device for close and cleanup + usbdev->alive = 0; + return -1; + } return 0; } @@ -703,7 +933,7 @@ int usb_get_timeout(void) if(res == 0) return pollrem; if(res < 0) { - usbmuxd_log(LL_ERROR, "libusb_get_next_timeout failed: %d", res); + usbmuxd_log(LL_ERROR, "libusb_get_next_timeout failed: %s", libusb_error_name(res)); return pollrem; } msec = tv.tv_sec * 1000; @@ -720,7 +950,7 @@ int usb_process(void) tv.tv_sec = tv.tv_usec = 0; res = libusb_handle_events_timeout(NULL, &tv); if(res < 0) { - usbmuxd_log(LL_ERROR, "libusb_handle_events_timeout failed: %d", res); + usbmuxd_log(LL_ERROR, "libusb_handle_events_timeout failed: %s", libusb_error_name(res)); return res; } @@ -730,7 +960,7 @@ int usb_process(void) if(dev_poll_remain_ms() <= 0) { res = usb_discover(); if(res < 0) { - usbmuxd_log(LL_ERROR, "usb_discover failed: %d", res); + usbmuxd_log(LL_ERROR, "usb_discover failed: %s", libusb_error_name(res)); return res; } } @@ -755,7 +985,7 @@ int usb_process_timeout(int msec) } res = libusb_handle_events_timeout(NULL, &tleft); if(res < 0) { - usbmuxd_log(LL_ERROR, "libusb_handle_events_timeout failed: %d", res); + usbmuxd_log(LL_ERROR, "libusb_handle_events_timeout failed: %s", libusb_error_name(res)); return res; } // reap devices marked dead due to an RX error @@ -794,17 +1024,24 @@ static int usb_hotplug_cb(libusb_context *ctx, libusb_device *device, libusb_hot int usb_init(void) { int res; - usbmuxd_log(LL_DEBUG, "usb_init for linux / libusb 1.0"); + const struct libusb_version* libusb_version_info = libusb_get_version(); + usbmuxd_log(LL_NOTICE, "Using libusb %d.%d.%d", libusb_version_info->major, libusb_version_info->minor, libusb_version_info->micro); devlist_failures = 0; device_polling = 1; res = libusb_init(NULL); - //libusb_set_debug(NULL, 3); - if(res != 0) { - usbmuxd_log(LL_FATAL, "libusb_init failed: %d", res); + + if (res != 0) { + usbmuxd_log(LL_FATAL, "libusb_init failed: %s", libusb_error_name(res)); return -1; } +#if LIBUSB_API_VERSION >= 0x01000106 + 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))); +#else + libusb_set_debug(NULL, (log_level >= LL_DEBUG ? LIBUSB_LOG_LEVEL_DEBUG: (log_level >= LL_WARNING ? LIBUSB_LOG_LEVEL_WARNING: LIBUSB_LOG_LEVEL_NONE))); +#endif + collection_init(&device_list); #ifdef HAVE_LIBUSB_HOTPLUG_API @@ -814,7 +1051,7 @@ int usb_init(void) if (res == LIBUSB_SUCCESS) { device_polling = 0; } else { - usbmuxd_log(LL_ERROR, "ERROR: Could not register for libusb hotplug events (%d)", res); + usbmuxd_log(LL_ERROR, "ERROR: Could not register for libusb hotplug events. %s", libusb_error_name(res)); } } else { usbmuxd_log(LL_ERROR, "libusb does not support hotplug events"); @@ -47,6 +47,12 @@ #define PID_RANGE_LOW 0x1290 #define PID_RANGE_MAX 0x12af #define PID_APPLE_T2_COPROCESSOR 0x8600 +#define PID_APPLE_SILICON_RESTORE_LOW 0x1901 +#define PID_APPLE_SILICON_RESTORE_MAX 0x1905 + +#define ENV_DEVICE_MODE "USBMUXD_DEFAULT_DEVICE_MODE" +#define APPLE_VEND_SPECIFIC_GET_MODE 0x45 +#define APPLE_VEND_SPECIFIC_SET_MODE 0x52 struct usb_device; diff --git a/src/usbmuxd-proto.h b/src/usbmuxd-proto.h index 9416416..93df00e 100644 --- a/src/usbmuxd-proto.h +++ b/src/usbmuxd-proto.h @@ -19,7 +19,7 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -/* Protocol defintion for usbmuxd proxy protocol */ +/* Protocol definition for usbmuxd proxy protocol */ #ifndef USBMUXD_PROTO_H #define USBMUXD_PROTO_H diff --git a/src/utils.c b/src/utils.c index 206c684..2cc5675 100644 --- a/src/utils.c +++ b/src/utils.c @@ -76,254 +76,6 @@ void fdlist_reset(struct fdlist *list) list->count = 0; } -#define CAPACITY_STEP 8 - -void collection_init(struct collection *col) -{ - col->list = malloc(sizeof(void *) * CAPACITY_STEP); - memset(col->list, 0, sizeof(void *) * CAPACITY_STEP); - col->capacity = CAPACITY_STEP; -} - -void collection_free(struct collection *col) -{ - free(col->list); - col->list = NULL; - col->capacity = 0; -} - -void collection_add(struct collection *col, void *element) -{ - int i; - for(i=0; i<col->capacity; i++) { - if(!col->list[i]) { - col->list[i] = element; - return; - } - } - col->list = realloc(col->list, sizeof(void*) * (col->capacity + CAPACITY_STEP)); - memset(&col->list[col->capacity], 0, sizeof(void *) * CAPACITY_STEP); - col->list[col->capacity] = element; - col->capacity += CAPACITY_STEP; -} - -void collection_remove(struct collection *col, void *element) -{ - int i; - for(i=0; i<col->capacity; i++) { - if(col->list[i] == element) { - col->list[i] = NULL; - return; - } - } - util_error("collection_remove: element %p not present in collection %p (cap %d)", element, col, col->capacity); -} - -int collection_count(struct collection *col) -{ - int i, cnt = 0; - for(i=0; i<col->capacity; i++) { - if(col->list[i]) - cnt++; - } - return cnt; -} - -void collection_copy(struct collection *dest, struct collection *src) -{ - if (!dest || !src) return; - dest->capacity = src->capacity; - dest->list = malloc(sizeof(void*) * src->capacity); - memcpy(dest->list, src->list, sizeof(void*) * src->capacity); -} - -#ifndef HAVE_STPCPY -/** - * Copy characters from one string into another - * - * @note: The strings should not overlap, as the behavior is undefined. - * - * @s1: The source string. - * @s2: The destination string. - * - * @return a pointer to the terminating `\0' character of @s1, - * or NULL if @s1 or @s2 is NULL. - */ -char *stpcpy(char * s1, const char * s2) -{ - if (s1 == NULL || s2 == NULL) - return NULL; - - strcpy(s1, s2); - - return s1 + strlen(s2); -} -#endif - -/** - * Concatenate strings into a newly allocated string - * - * @note: Specify NULL for the last string in the varargs list - * - * @str: The first string in the list - * @...: Subsequent strings. Use NULL for the last item. - * - * @return a newly allocated string, or NULL if @str is NULL. This will also - * return NULL and set errno to ENOMEM if memory is exhausted. - */ -char *string_concat(const char *str, ...) -{ - size_t len; - va_list args; - char *s; - char *result; - char *dest; - - if (!str) - return NULL; - - /* Compute final length */ - - len = strlen(str) + 1; /* plus 1 for the null terminator */ - - va_start(args, str); - s = va_arg(args, char *); - while (s) { - len += strlen(s); - s = va_arg(args, char*); - } - va_end(args); - - /* Concat each string */ - - result = malloc(len); - if (!result) - return NULL; /* errno remains set */ - - dest = result; - - dest = stpcpy(dest, str); - - va_start(args, str); - s = va_arg(args, char *); - while (s) { - dest = stpcpy(dest, s); - s = va_arg(args, char *); - } - va_end(args); - - return result; -} - -int buffer_read_from_filename(const char *filename, char **buffer, uint64_t *length) -{ - FILE *f; - uint64_t size; - - *length = 0; - - f = fopen(filename, "rb"); - if (!f) { - return 0; - } - - fseek(f, 0, SEEK_END); - size = ftell(f); - rewind(f); - - if (size == 0) { - fclose(f); - return 0; - } - - *buffer = (char*)malloc(sizeof(char)*(size+1)); - - if (!buffer) { - return 0; - } - - int ret = 1; - if (fread(*buffer, sizeof(char), size, f) != size) { - usbmuxd_log(LL_ERROR, "%s: ERROR: couldn't read %d bytes from %s", __func__, (int)size, filename); - free(*buffer); - ret = 0; - errno = EIO; - } - fclose(f); - - *length = size; - return ret; -} - -int buffer_write_to_filename(const char *filename, const char *buffer, uint64_t length) -{ - FILE *f; - - f = fopen(filename, "wb"); - if (f) { - size_t written = fwrite(buffer, sizeof(char), length, f); - fclose(f); - - if (written == length) { - return 1; - } - else { - // Not all data could be written. - errno = EIO; - return 0; - } - } - else { - // Failed to open the file, let the caller know. - return 0; - } -} - -int plist_read_from_filename(plist_t *plist, const char *filename) -{ - char *buffer = NULL; - uint64_t length; - - if (!filename) - return 0; - - if (!buffer_read_from_filename(filename, &buffer, &length)) { - return 0; - } - - if ((length > 8) && (memcmp(buffer, "bplist00", 8) == 0)) { - plist_from_bin(buffer, length, plist); - } else { - plist_from_xml(buffer, length, plist); - } - - free(buffer); - - return 1; -} - -int plist_write_to_filename(plist_t plist, const char *filename, enum plist_format_t format) -{ - char *buffer = NULL; - uint32_t length; - - if (!plist || !filename) - return 0; - - if (format == PLIST_FORMAT_XML) - plist_to_xml(plist, &buffer, &length); - else if (format == PLIST_FORMAT_BINARY) - plist_to_bin(plist, &buffer, &length); - else - return 0; - - int res = buffer_write_to_filename(filename, buffer, length); - - free(buffer); - - return res; -} - #ifndef HAVE_CLOCK_GETTIME typedef int clockid_t; #define CLOCK_MONOTONIC 1 diff --git a/src/utils.h b/src/utils.h index b5cab3f..ce3b2e0 100644 --- a/src/utils.h +++ b/src/utils.h @@ -43,49 +43,6 @@ void fdlist_add(struct fdlist *list, enum fdowner owner, int fd, short events); void fdlist_free(struct fdlist *list); void fdlist_reset(struct fdlist *list); -struct collection { - void **list; - int capacity; -}; - -void collection_init(struct collection *col); -void collection_add(struct collection *col, void *element); -void collection_remove(struct collection *col, void *element); -int collection_count(struct collection *col); -void collection_free(struct collection *col); -void collection_copy(struct collection *dest, struct collection *src); - -#define MERGE_(a,b) a ## _ ## b -#define LABEL_(a,b) MERGE_(a, b) -#define UNIQUE_VAR(a) LABEL_(a, __LINE__) - -#define FOREACH(var, col) \ - do { \ - int UNIQUE_VAR(_iter); \ - for(UNIQUE_VAR(_iter)=0; UNIQUE_VAR(_iter)<(col)->capacity; UNIQUE_VAR(_iter)++) { \ - if(!(col)->list[UNIQUE_VAR(_iter)]) continue; \ - var = (col)->list[UNIQUE_VAR(_iter)]; - -#define ENDFOREACH \ - } \ - } while(0); - -#ifndef HAVE_STPCPY -char *stpcpy(char * s1, const char * s2); -#endif -char *string_concat(const char *str, ...); - -int buffer_read_from_filename(const char *filename, char **buffer, uint64_t *length); -int buffer_write_to_filename(const char *filename, const char *buffer, uint64_t length); - -enum plist_format_t { - PLIST_FORMAT_XML, - PLIST_FORMAT_BINARY -}; - -int plist_read_from_filename(plist_t *plist, const char *filename); -int plist_write_to_filename(plist_t plist, const char *filename, enum plist_format_t format); - uint64_t mstime64(void); void get_tick_count(struct timeval * tv); diff --git a/systemd/Makefile.am b/systemd/Makefile.am index a23f1d1..1d40c25 100644 --- a/systemd/Makefile.am +++ b/systemd/Makefile.am @@ -1,7 +1,7 @@ edit = \ $(SED) -r \ -e 's|@sbindir[@]|$(sbindir)|g' \ - -e 's|@localstatedir[@]|$(localstatedir)|g' \ + -e 's|@runstatedir[@]|$(runstatedir)|g' \ < $< > $@ || rm $@ if WANT_SYSTEMD diff --git a/systemd/usbmuxd.service.in b/systemd/usbmuxd.service.in index bee2476..3a27aee 100644 --- a/systemd/usbmuxd.service.in +++ b/systemd/usbmuxd.service.in @@ -4,4 +4,4 @@ Documentation=man:usbmuxd(8) [Service] ExecStart=@sbindir@/usbmuxd --user usbmux --systemd -PIDFile=@localstatedir@/run/usbmuxd.pid +PIDFile=@runstatedir@/usbmuxd.pid diff --git a/udev/39-usbmuxd.rules.in b/udev/39-usbmuxd.rules.in index 11d33c6..ac15593 100644 --- a/udev/39-usbmuxd.rules.in +++ b/udev/39-usbmuxd.rules.in @@ -1,13 +1,16 @@ # usbmuxd (Apple Mobile Device Muxer listening on /var/run/usbmuxd) # systemd should receive all events relating to device -SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", ENV{PRODUCT}=="5ac/12[9a][0-9a-f]/*|5ac/8600/*", TAG+="systemd" +SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", ENV{PRODUCT}=="5ac/12[9a][0-9a-f]/*|5ac/190[1-5]/*|5ac/8600/*", TAG+="systemd" # Initialize iOS devices into "deactivated" USB configuration state and activate usbmuxd -SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", ENV{PRODUCT}=="5ac/12[9a][0-9a-f]/*|5ac/8600/*", ACTION=="add", ENV{USBMUX_SUPPORTED}="1", ATTR{bConfigurationValue}="0", OWNER="usbmux", @udev_activation_rule@ +SUBSYSTEM=="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@ +# but make sure iBridge (T1) doesn't end up in an unconfigured state +SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", ENV{PRODUCT}=="5ac/8600/*", ACTION=="add", ENV{USBMUX_SUPPORTED}="1", ATTR{bConfigurationValue}="1", OWNER="usbmux", @udev_activation_rule@ + # Make sure properties don't get lost when bind action is called -SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", ENV{PRODUCT}=="5ac/12[9a][0-9a-f]/*|5ac/8600/*", ACTION=="bind", ENV{USBMUX_SUPPORTED}="1", OWNER="usbmux", @udev_activation_rule@ +SUBSYSTEM=="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" # Exit usbmuxd when the last device is removed -SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", ENV{PRODUCT}=="5ac/12[9a][0-9a-f]/*|5ac/8600/*", ACTION=="remove", RUN+="@sbindir@/usbmuxd -x" +SUBSYSTEM=="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" |