summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.github/FUNDING.yml3
-rw-r--r--.github/workflows/build.yml45
-rw-r--r--3rd_party/ed25519/Makefile.am7
-rw-r--r--3rd_party/libsrp6a-sha512/Makefile.am3
-rw-r--r--3rd_party/libsrp6a-sha512/cstr.c62
-rw-r--r--3rd_party/libsrp6a-sha512/cstr.h14
-rw-r--r--3rd_party/libsrp6a-sha512/t_defines.h4
-rw-r--r--3rd_party/libsrp6a-sha512/t_misc.c12
-rw-r--r--3rd_party/libsrp6a-sha512/t_truerand.c8
-rw-r--r--Makefile.am1
-rw-r--r--NEWS31
-rw-r--r--README.md6
-rw-r--r--common/debug.c45
-rw-r--r--common/userpref.c53
-rw-r--r--common/userpref.h2
-rw-r--r--configure.ac39
-rw-r--r--cython/debugserver.pxi8
-rw-r--r--docs/Makefile.am3
-rw-r--r--docs/idevicecrashreport.119
-rw-r--r--docs/idevicenotificationproxy.115
-rw-r--r--docs/idevicesyslog.136
-rw-r--r--doxygen.cfg.in841
-rw-r--r--include/Makefile.am2
-rw-r--r--include/libimobiledevice/afc.h29
-rw-r--r--include/libimobiledevice/libimobiledevice.h33
-rw-r--r--include/libimobiledevice/ostrace.h198
-rw-r--r--m4/ax_python_devel.m4361
-rw-r--r--src/Makefile.am1
-rw-r--r--src/afc.c127
-rw-r--r--src/house_arrest.c4
-rw-r--r--src/idevice.c184
-rw-r--r--src/idevice.h2
-rw-r--r--src/installation_proxy.c6
-rw-r--r--src/lockdown-cu.c18
-rw-r--r--src/lockdown.c73
-rw-r--r--src/misagent.c6
-rw-r--r--src/mobile_image_mounter.c4
-rw-r--r--src/notification_proxy.c9
-rw-r--r--src/ostrace.c436
-rw-r--r--src/ostrace.h37
-rw-r--r--src/sbservices.c4
-rw-r--r--tools/Makefile.am12
-rw-r--r--tools/afcclient.c202
-rw-r--r--tools/idevicebackup.c19
-rw-r--r--tools/idevicebackup2.c94
-rw-r--r--tools/idevicebtlogger.c4
-rw-r--r--tools/idevicecrashreport.c144
-rw-r--r--tools/idevicedate.c6
-rw-r--r--tools/idevicedebug.c4
-rw-r--r--tools/idevicedebugserverproxy.c4
-rw-r--r--tools/idevicedevmodectl.c6
-rw-r--r--tools/idevicediagnostics.c4
-rw-r--r--tools/ideviceenterrecovery.c4
-rw-r--r--tools/ideviceimagemounter.c52
-rw-r--r--tools/ideviceinfo.c4
-rw-r--r--tools/idevicename.c4
-rw-r--r--tools/idevicenotificationproxy.c26
-rw-r--r--tools/idevicepair.c6
-rw-r--r--tools/ideviceprovision.c33
-rw-r--r--tools/idevicescreenshot.c4
-rw-r--r--tools/idevicesetlocation.c30
-rw-r--r--tools/idevicesyslog.c688
62 files changed, 3064 insertions, 1077 deletions
diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml
new file mode 100644
index 0000000..e995b30
--- /dev/null
+++ b/.github/FUNDING.yml
@@ -0,0 +1,3 @@
+github: nikias
+patreon: nikias
+custom: ["https://www.paypal.me/NikiasBassen"]
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 76cd02a..fbbf10a 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -2,6 +2,7 @@ name: build
on:
push:
+ pull_request:
schedule:
- cron: '0 0 1 * *'
@@ -17,26 +18,33 @@ jobs:
run: |
echo "target_triplet=`gcc -dumpmachine`" >> $GITHUB_ENV
- name: fetch libplist
- uses: dawidd6/action-download-artifact@v3
+ uses: dawidd6/action-download-artifact@v6
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@v3
+ uses: dawidd6/action-download-artifact@v6
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@v3
+ uses: dawidd6/action-download-artifact@v6
with:
github_token: ${{secrets.GITHUB_TOKEN}}
workflow: build.yml
name: libimobiledevice-glue-latest_${{env.target_triplet}}
repo: libimobiledevice/libimobiledevice-glue
+ - name: fetch libtatsu
+ uses: dawidd6/action-download-artifact@v6
+ with:
+ github_token: ${{secrets.GITHUB_TOKEN}}
+ workflow: build.yml
+ name: libtatsu-latest_${{env.target_triplet}}
+ repo: libimobiledevice/libtatsu
- name: install external dependencies
run: |
mkdir extract
@@ -77,26 +85,33 @@ jobs:
pip3 install --break-system-packages cython
shell: bash
- name: fetch libplist
- uses: dawidd6/action-download-artifact@v3
+ uses: dawidd6/action-download-artifact@v6
with:
github_token: ${{secrets.GITHUB_TOKEN}}
workflow: build.yml
name: libplist-latest_macOS
repo: libimobiledevice/libplist
- name: fetch libusbmuxd
- uses: dawidd6/action-download-artifact@v3
+ uses: dawidd6/action-download-artifact@v6
with:
github_token: ${{secrets.GITHUB_TOKEN}}
workflow: build.yml
name: libusbmuxd-latest_macOS
repo: libimobiledevice/libusbmuxd
- name: fetch libimobiledevice-glue
- uses: dawidd6/action-download-artifact@v3
+ uses: dawidd6/action-download-artifact@v6
with:
github_token: ${{secrets.GITHUB_TOKEN}}
workflow: build.yml
name: libimobiledevice-glue-latest_macOS
repo: libimobiledevice/libimobiledevice-glue
+ - name: fetch libtatsu
+ uses: dawidd6/action-download-artifact@v6
+ with:
+ github_token: ${{secrets.GITHUB_TOKEN}}
+ workflow: build.yml
+ name: libtatsu-latest_macOS
+ repo: libimobiledevice/libtatsu
- name: install external dependencies
run: |
mkdir extract
@@ -140,6 +155,7 @@ jobs:
PYTHON_EXEC_PREFIX=`$PYTHON3_BIN -c "import distutils.sysconfig; print(distutils.sysconfig.get_config_var('exec_prefix'))"`
PYTHON_LIBS_PATH=$PYTHON_EXEC_PREFIX/lib
PYTHON_FRAMEWORK_PATH=$PYTHON_EXEC_PREFIX/Python3
+ export PYTHON_CPPFLAGS="-I$PYTHON_EXEC_PREFIX/Headers"
export PYTHON_LIBS="-L$PYTHON_LIBS_PATH -lpython$PYTHON_VER"
export PYTHON_EXTRA_LDFLAGS="-Wl,-stack_size,1000000 -framework CoreFoundation $PYTHON_FRAMEWORK_PATH"
fi
@@ -161,7 +177,7 @@ jobs:
name: libimobiledevice-latest_macOS
path: libimobiledevice.tar
build-windows:
- runs-on: windows-2019
+ runs-on: windows-latest
defaults:
run:
shell: msys2 {0}
@@ -182,6 +198,8 @@ jobs:
base-devel
git
mingw-w64-${{ matrix.arch }}-gcc
+ mingw-w64-${{ matrix.arch }}-pkg-config
+ mingw-w64-${{ matrix.arch }}-openssl
make
libtool
autoconf
@@ -193,26 +211,33 @@ jobs:
echo "target_triplet=`gcc -dumpmachine`" >> $GITHUB_ENV
git config --global core.autocrlf false
- name: fetch libplist
- uses: dawidd6/action-download-artifact@v3
+ uses: dawidd6/action-download-artifact@v6
with:
github_token: ${{secrets.GITHUB_TOKEN}}
workflow: build.yml
name: libplist-latest_${{ matrix.arch }}-${{ env.dest }}
repo: libimobiledevice/libplist
- name: fetch libusbmuxd
- uses: dawidd6/action-download-artifact@v3
+ uses: dawidd6/action-download-artifact@v6
with:
github_token: ${{secrets.GITHUB_TOKEN}}
workflow: build.yml
name: libusbmuxd-latest_${{ matrix.arch }}-${{ env.dest }}
repo: libimobiledevice/libusbmuxd
- name: fetch libimobiledevice-glue
- uses: dawidd6/action-download-artifact@v3
+ uses: dawidd6/action-download-artifact@v6
with:
github_token: ${{secrets.GITHUB_TOKEN}}
workflow: build.yml
name: libimobiledevice-glue-latest_${{ matrix.arch }}-${{ env.dest }}
repo: libimobiledevice/libimobiledevice-glue
+ - name: fetch libtatsu
+ uses: dawidd6/action-download-artifact@v6
+ with:
+ github_token: ${{secrets.GITHUB_TOKEN}}
+ workflow: build.yml
+ name: libtatsu-latest_${{ matrix.arch }}-${{ env.dest }}
+ repo: libimobiledevice/libtatsu
- name: install external dependencies
run: |
mkdir extract
diff --git a/3rd_party/ed25519/Makefile.am b/3rd_party/ed25519/Makefile.am
index d8e4e04..438cb53 100644
--- a/3rd_party/ed25519/Makefile.am
+++ b/3rd_party/ed25519/Makefile.am
@@ -15,12 +15,19 @@ libed25519_la_LIBADD =
libed25519_la_LDFLAGS = $(AM_LDFLAGS) -no-undefined
libed25519_la_SOURCES = \
add_scalar.c \
+ ed25519.h \
fe.c \
+ fe.h \
+ fixedint.h \
ge.c \
+ ge.h \
keypair.c \
key_exchange.c \
+ precomp_data.h \
sc.c \
+ sc.h \
seed.c \
sign.c \
sha512.c \
+ sha512.h \
verify.c
diff --git a/3rd_party/libsrp6a-sha512/Makefile.am b/3rd_party/libsrp6a-sha512/Makefile.am
index 2acd582..bdacb5f 100644
--- a/3rd_party/libsrp6a-sha512/Makefile.am
+++ b/3rd_party/libsrp6a-sha512/Makefile.am
@@ -25,7 +25,8 @@ libsrp6a_sha512_la_SOURCES = \
t_truerand.c cstr.c \
srp.c srp6a_sha512_client.c \
srp.h srp_aux.h cstr.h \
- t_sha.c
+ t_defines.h t_pwd.h \
+ t_sha.c t_sha.h
#if !HAVE_OPENSSL
#libsrp6a_sha512_la_SOURCES += t_sha.c
#endif
diff --git a/3rd_party/libsrp6a-sha512/cstr.c b/3rd_party/libsrp6a-sha512/cstr.c
index 9856f46..58a5638 100644
--- a/3rd_party/libsrp6a-sha512/cstr.c
+++ b/3rd_party/libsrp6a-sha512/cstr.c
@@ -8,72 +8,31 @@
#define MINSIZE 4 /* Absolute minimum - one word */
static char cstr_empty_string[] = { '\0' };
-static cstr_allocator * default_alloc = NULL;
-
-/*
- * It is assumed, for efficiency, that it is okay to pass more arguments
- * to a function than are called for, as long as the required arguments
- * are in proper form. If extra arguments to malloc() and free() cause
- * problems, define PEDANTIC_ARGS below.
- */
-#ifdef PEDANTIC_ARGS
-static void * Cmalloc(int n, void * heap) { return malloc(n); }
-static void Cfree(void * p, void * heap) { free(p); }
-static cstr_allocator malloc_allocator = { Cmalloc, Cfree, NULL };
-#else
-static cstr_allocator malloc_allocator = { malloc, free, NULL };
-#endif
-
-_TYPE( void )
-cstr_set_allocator(cstr_allocator * alloc)
-{
- default_alloc = alloc;
-}
_TYPE( cstr * )
-cstr_new_alloc(cstr_allocator * alloc)
+cstr_new()
{
cstr * str;
- if(alloc == NULL) {
- if(default_alloc == NULL) {
- default_alloc = &malloc_allocator;
- }
- alloc = default_alloc;
- }
-
- str = (cstr *) (*alloc->alloc)(sizeof(cstr), alloc->heap);
+ str = (cstr *) malloc(sizeof(cstr));
if(str) {
str->data = cstr_empty_string;
str->length = str->cap = 0;
str->ref = 1;
- str->allocator = alloc;
}
return str;
}
_TYPE( cstr * )
-cstr_new()
-{
- return cstr_new_alloc(NULL);
-}
-
-_TYPE( cstr * )
-cstr_dup_alloc(const cstr * str, cstr_allocator * alloc)
+cstr_dup(const cstr * str)
{
- cstr * nstr = cstr_new_alloc(alloc);
+ cstr * nstr = cstr_new();
if(nstr)
cstr_setn(nstr, str->data, str->length);
return nstr;
}
_TYPE( cstr * )
-cstr_dup(const cstr * str)
-{
- return cstr_dup_alloc(str, NULL);
-}
-
-_TYPE( cstr * )
cstr_create(const char * s)
{
return cstr_createn(s, strlen(s));
@@ -101,9 +60,9 @@ cstr_clear_free(cstr * str)
if(--str->ref == 0) {
if(str->cap > 0) {
memset(str->data, 0, str->cap);
- (*str->allocator->free)(str->data, str->allocator->heap);
+ free(str->data);
}
- (*str->allocator->free)(str, str->allocator->heap);
+ free(str);
}
}
@@ -112,8 +71,8 @@ cstr_free(cstr * str)
{
if(--str->ref == 0) {
if(str->cap > 0)
- (*str->allocator->free)(str->data, str->allocator->heap);
- (*str->allocator->free)(str, str->allocator->heap);
+ free(str->data);
+ free(str);
}
}
@@ -121,7 +80,7 @@ _TYPE( void )
cstr_empty(cstr * str)
{
if(str->cap > 0)
- (*str->allocator->free)(str->data, str->allocator->heap);
+ free(str->data);
str->data = cstr_empty_string;
str->length = str->cap = 0;
}
@@ -137,8 +96,7 @@ cstr_alloc(cstr * str, int len)
if(len < MINSIZE)
len = MINSIZE;
- t = (char *) (*str->allocator->alloc)(len * sizeof(char),
- str->allocator->heap);
+ t = (char *) malloc(len * sizeof(char));
if(t) {
if(str->data) {
t[str->length] = 0;
diff --git a/3rd_party/libsrp6a-sha512/cstr.h b/3rd_party/libsrp6a-sha512/cstr.h
index 7cc019a..29afea7 100644
--- a/3rd_party/libsrp6a-sha512/cstr.h
+++ b/3rd_party/libsrp6a-sha512/cstr.h
@@ -38,7 +38,7 @@
#define _MSVC15DEXPORT
#define _MSVC20EXPORT
#define _DLLAPI
-#if defined(WINDOWS) || defined(WIN32)
+#if defined(WINDOWS) || defined(_WIN32)
#define _CDECL _cdecl
#else
#define _CDECL
@@ -51,27 +51,15 @@
extern "C" {
#endif /* __cplusplus */
-/* Arguments to allocator methods ordered this way for compatibility */
-typedef struct cstr_alloc_st {
- void * (_CDECL * alloc)(size_t n, void * heap);
- void (_CDECL * free)(void * p, void * heap);
- void * heap;
-} cstr_allocator;
-
typedef struct cstr_st {
char * data; /* Okay to access data and length fields directly */
int length;
int cap;
int ref; /* Simple reference counter */
- cstr_allocator * allocator;
} cstr;
-_TYPE( void ) cstr_set_allocator P((cstr_allocator * alloc));
-
_TYPE( cstr * ) cstr_new P((void));
-_TYPE( cstr * ) cstr_new_alloc P((cstr_allocator * alloc));
_TYPE( cstr * ) cstr_dup P((const cstr * str));
-_TYPE( cstr * ) cstr_dup_alloc P((const cstr * str, cstr_allocator * alloc));
_TYPE( cstr * ) cstr_create P((const char * s));
_TYPE( cstr * ) cstr_createn P((const char * s, int len));
diff --git a/3rd_party/libsrp6a-sha512/t_defines.h b/3rd_party/libsrp6a-sha512/t_defines.h
index 447263f..a2a5fe5 100644
--- a/3rd_party/libsrp6a-sha512/t_defines.h
+++ b/3rd_party/libsrp6a-sha512/t_defines.h
@@ -65,7 +65,7 @@
#define _MSVC15DEXPORT
#define _MSVC20EXPORT
#define _DLLAPI
-#if defined(WINDOWS) || defined(WIN32)
+#if defined(WINDOWS) || defined(_WIN32)
#define _CDECL _cdecl
#else
#define _CDECL
@@ -122,7 +122,7 @@ char *strchr(), *strrchr(), *strtok();
#define USE_SGTTY
#endif
-#ifdef WIN32
+#ifdef _WIN32
#define USE_FTIME 1
#define USE_RENAME 1
#define NO_FCHMOD 1
diff --git a/3rd_party/libsrp6a-sha512/t_misc.c b/3rd_party/libsrp6a-sha512/t_misc.c
index 3a2cda1..abd8e55 100644
--- a/3rd_party/libsrp6a-sha512/t_misc.c
+++ b/3rd_party/libsrp6a-sha512/t_misc.c
@@ -38,7 +38,7 @@
#include <sys/stat.h>
#include <fcntl.h>
-#ifdef WIN32
+#ifdef _WIN32
#include <process.h>
#include <io.h>
#endif
@@ -207,7 +207,7 @@ t_initrand()
#if defined(OPENSSL) /* OpenSSL has nifty win32 entropy-gathering code */
#if OPENSSL_VERSION_NUMBER >= 0x00905100
r = RAND_status();
-#if defined(WINDOWS) || defined(WIN32)
+#if defined(WINDOWS) || defined(_WIN32)
if(r) /* Don't do the Unix-y stuff on Windows if possible */
return;
#else
@@ -220,7 +220,7 @@ t_initrand()
if(r > 0) {
yarrow_add_entropy(entropy, r, &g_rng);
memset(entropy, 0, sizeof(entropy));
-# if defined(WINDOWS) || defined(WIN32)
+# if defined(WINDOWS) || defined(_WIN32)
/* Don't do the Unix-y stuff on Windows if possible */
yarrow_ready(&g_rng);
return;
@@ -228,13 +228,13 @@ t_initrand()
}
#endif
-#if !defined(WINDOWS) && !defined(WIN32)
+#if !defined(WINDOWS) && !defined(_WIN32)
i = open("/dev/urandom", O_RDONLY);
if(i > 0) {
r += read(i, preseed.devrand, sizeof(preseed.devrand));
close(i);
}
-#endif /* !WINDOWS && !WIN32 */
+#endif /* !WINDOWS && !_WIN32 */
/* Resort to truerand only if desperate for some Real entropy */
if(r == 0)
@@ -250,7 +250,7 @@ t_initrand()
preseed.subsec = t.tv_usec;
#endif
preseed.pid = getpid();
-#ifndef WIN32
+#ifndef _WIN32
preseed.ppid = getppid();
#endif
t_envhash(preseed.envh);
diff --git a/3rd_party/libsrp6a-sha512/t_truerand.c b/3rd_party/libsrp6a-sha512/t_truerand.c
index f995ed7..cd27d0d 100644
--- a/3rd_party/libsrp6a-sha512/t_truerand.c
+++ b/3rd_party/libsrp6a-sha512/t_truerand.c
@@ -54,7 +54,7 @@
#include "t_defines.h"
-#ifdef WIN32
+#ifdef _WIN32
# ifdef CRYPTOLIB
@@ -69,7 +69,7 @@ raw_truerand()
return truerand();
}
-# else /* !CRYPTOLIB && WIN32 */
+# else /* !CRYPTOLIB && _WIN32 */
#include <windows.h>
#include <wtypes.h>
@@ -128,7 +128,7 @@ raw_truerand() {
# endif /* CRYPTOLIB */
-#else /* !WIN32 */
+#else /* !_WIN32 */
#include <signal.h>
#include <setjmp.h>
@@ -238,4 +238,4 @@ raw_n_truerand(int n)
return v % n;
}
-#endif /* !CRYPTOLIB || !WIN32 */
+#endif /* !CRYPTOLIB || !_WIN32 */
diff --git a/Makefile.am b/Makefile.am
index 352b28f..f429f2f 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -3,7 +3,6 @@ ACLOCAL_AMFLAGS = -I m4
SUBDIRS = 3rd_party common src include $(CYTHON_SUB) tools docs
EXTRA_DIST = \
- docs \
README.md \
git-version-gen
diff --git a/NEWS b/NEWS
index 72823dc..4e075ce 100644
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,34 @@
+Version 1.4.0
+~~~~~~~~~~~~~
+
+* Development release
+ - From now on, releases will be made more frequently
+* Changes:
+ - Add support for MbedTLS
+ - Add Reverse Proxy implementation
+ - Add support for wireless pairing (AppleTV)
+ - Embed 3rd party libraries for ed25519 and SRP6a
+ - Fixes in idevicedebug
+ - idevicecrashreport: Allow filtering crash reports by filename
+ - Add idevicedevmodectl tool
+ - Fixes for idevicebackup2
+ - Add property_list_client_get_service_client() and service_get_connection() functions
+ - Add idevicebtlogger
+ - Add new idevice_events_subscribe/unsubscribe API
+ - Move LIBIMOBILEDEVICE_API to public headers
+ - Add afc_strerror function
+ - Add libimobiledevice_version() function
+ - Use libimobiledevice-glue's SHA1 implementation
+ - Add support for iOS 17+ Personalized Developer Disk image mounting
+ - Fix compilation on MSVC
+ - Add idevice_strerror() to interface
+ - Add new idevice_get_device_version() to interface
+ - Add os_trace_relay service implementation
+ - Fixes for idevicesyslog
+ - afc: Add afc_get_file_info_plist and afc_get_device_info_plist functions
+ ... and several other internal changes
+
+
Version 1.3.0
~~~~~~~~~~~~~
diff --git a/README.md b/README.md
index ec057a6..32e1570 100644
--- a/README.md
+++ b/README.md
@@ -179,8 +179,8 @@ We are still working on the guidelines so bear with us!
## Links
* Homepage: https://libimobiledevice.org/
-* Repository: https://git.libimobiledevice.org/libimobiledevice.git
-* Repository (Mirror): https://github.com/libimobiledevice/libimobiledevice.git
+* Repository: https://github.com/libimobiledevice/libimobiledevice.git
+* Repository (Mirror): https://git.libimobiledevice.org/libimobiledevice.git
* Issue Tracker: https://github.com/libimobiledevice/libimobiledevice/issues
* Mailing List: https://lists.libimobiledevice.org/mailman/listinfo/libimobiledevice-devel
* Twitter: https://twitter.com/libimobiledev
@@ -198,4 +198,4 @@ iPadOS, tvOS, watchOS, and macOS are trademarks of Apple Inc.
This project is an independent software and has not been authorized, sponsored,
or otherwise approved by Apple Inc.
-README Updated on: 2024-06-27
+README Updated on: 2024-10-22
diff --git a/common/debug.c b/common/debug.c
index cf1bc2f..7a593fc 100644
--- a/common/debug.c
+++ b/common/debug.c
@@ -30,6 +30,9 @@
#include <stdint.h>
#include <stdlib.h>
#include <time.h>
+#ifndef _WIN32
+#include <sys/time.h>
+#endif
#include "src/idevice.h"
#include "debug.h"
@@ -51,27 +54,31 @@ void internal_set_debug_level(int level)
#ifndef STRIP_DEBUG_CODE
static void debug_print_line(const char *func, const char *file, int line, const char *buffer)
{
- char *str_time = NULL;
- char *header = NULL;
+ char str_time[24];
+#ifdef _WIN32
+ SYSTEMTIME lt;
+ GetLocalTime(&lt);
+ snprintf(str_time, 24, "%02d:%02d:%02d.%03d", lt.wHour, lt.wMinute, lt.wSecond, lt.wMilliseconds);
+#else
+#ifdef HAVE_GETTIMEOFDAY
+ struct timeval tv;
+ struct tm *tp;
+ gettimeofday(&tv, NULL);
+#ifdef HAVE_LOCALTIME_R
+ struct tm tp_;
+ tp = localtime_r(&tv.tv_sec, &tp_);
+#else
+ tp = localtime(&tv.tv_sec);
+#endif
+ strftime(str_time, 9, "%H:%M:%S", tp);
+ snprintf(str_time+8, 10, ".%03d", (int)tv.tv_usec/1000);
+#else
time_t the_time;
-
time(&the_time);
- str_time = (char*)malloc(255);
- strftime(str_time, 254, "%H:%M:%S", localtime (&the_time));
-
- /* generate header text */
- if(asprintf(&header, "%s %s:%d %s()", str_time, file, line, func)<0){}
- free (str_time);
-
- /* trim ending newlines */
-
- /* print header */
- fprintf(stderr, "%s: ", header);
-
- /* print actual debug content */
- fprintf(stderr, "%s\n", buffer);
-
- free (header);
+ strftime(str_time, 15, "%H:%M:%S", localtime (&the_time));
+#endif
+#endif
+ fprintf(stderr, "%s %s:%d %s(): %s\n", str_time, file, line, func, buffer);
}
#endif
diff --git a/common/userpref.c b/common/userpref.c
index 48bcfcb..76945e1 100644
--- a/common/userpref.c
+++ b/common/userpref.c
@@ -29,13 +29,18 @@
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
+#include <errno.h>
+
#ifdef HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif
-#ifndef WIN32
+#include <dirent.h>
+#ifndef _WIN32
#include <pwd.h>
-#endif
#include <unistd.h>
+#include <libgen.h>
+#include <sys/stat.h>
+#endif
#include <usbmuxd.h>
#if defined(HAVE_OPENSSL)
#include <openssl/bn.h>
@@ -64,12 +69,7 @@
#error No supported TLS/SSL library enabled
#endif
-#include <dirent.h>
-#include <libgen.h>
-#include <sys/stat.h>
-#include <errno.h>
-
-#ifdef WIN32
+#ifdef _WIN32
#include <shlobj.h>
#endif
@@ -77,6 +77,7 @@
#define ETIMEDOUT 138
#endif
+#include <libimobiledevice/libimobiledevice.h>
#include <libimobiledevice-glue/utils.h>
#include "userpref.h"
@@ -93,7 +94,7 @@ const ASN1_ARRAY_TYPE pkcs1_asn1_tab[] = {
};
#endif
-#ifdef WIN32
+#ifdef _WIN32
#define DIR_SEP '\\'
#define DIR_SEP_S "\\"
#else
@@ -103,7 +104,7 @@ const ASN1_ARRAY_TYPE pkcs1_asn1_tab[] = {
#define USERPREF_CONFIG_EXTENSION ".plist"
-#ifdef WIN32
+#ifdef _WIN32
#define USERPREF_CONFIG_DIR "Apple"DIR_SEP_S"Lockdown"
#else
#define USERPREF_CONFIG_DIR "lockdown"
@@ -113,7 +114,7 @@ const ASN1_ARRAY_TYPE pkcs1_asn1_tab[] = {
static char *__config_dir = NULL;
-#ifdef WIN32
+#ifdef _WIN32
static char *userpref_utf16_to_utf8(wchar_t *unistr, long len, long *items_read, long *items_written)
{
if (!unistr || (len <= 0)) return NULL;
@@ -155,7 +156,7 @@ const char *userpref_get_config_dir()
if (__config_dir)
return __config_dir;
-#ifdef WIN32
+#ifdef _WIN32
wchar_t path[MAX_PATH+1];
HRESULT hr;
LPITEMIDLIST pidl = NULL;
@@ -419,7 +420,7 @@ static int _mbedtls_x509write_crt_set_basic_constraints_critical(mbedtls_x509wri
*
* @return 1 if keys were successfully generated, 0 otherwise
*/
-userpref_error_t pair_record_generate_keys_and_certs(plist_t pair_record, key_data_t public_key)
+userpref_error_t pair_record_generate_keys_and_certs(plist_t pair_record, key_data_t public_key, unsigned int device_version)
{
userpref_error_t ret = USERPREF_E_SSL_ERROR;
@@ -484,7 +485,7 @@ userpref_error_t pair_record_generate_keys_and_certs(plist_t pair_record, key_da
X509_set_pubkey(root_cert, root_pkey);
/* sign root cert with root private key */
- X509_sign(root_cert, root_pkey, EVP_sha1());
+ X509_sign(root_cert, root_pkey, (device_version < IDEVICE_DEVICE_VERSION(4,0,0)) ? EVP_sha1() : EVP_sha256());
}
/* create host certificate */
@@ -517,7 +518,7 @@ userpref_error_t pair_record_generate_keys_and_certs(plist_t pair_record, key_da
X509_set_pubkey(host_cert, host_pkey);
/* sign host cert with root private key */
- X509_sign(host_cert, root_pkey, EVP_sha1());
+ X509_sign(host_cert, root_pkey, (device_version < IDEVICE_DEVICE_VERSION(4,0,0)) ? EVP_sha1() : EVP_sha256());
}
if (root_cert && root_pkey && host_cert && host_pkey) {
@@ -609,7 +610,7 @@ userpref_error_t pair_record_generate_keys_and_certs(plist_t pair_record, key_da
X509_add_ext_helper(dev_cert, NID_key_usage, (char*)"critical,digitalSignature,keyEncipherment");
/* sign device certificate with root private key */
- if (X509_sign(dev_cert, root_pkey, EVP_sha1())) {
+ if (X509_sign(dev_cert, root_pkey, (device_version < IDEVICE_DEVICE_VERSION(4,0,0)) ? EVP_sha1() : EVP_sha256())) {
/* if signing succeeded, export in PEM format */
BIO* membp = BIO_new(BIO_s_mem());
if (PEM_write_bio_X509(membp, dev_cert) > 0) {
@@ -661,7 +662,7 @@ userpref_error_t pair_record_generate_keys_and_certs(plist_t pair_record, key_da
gnutls_x509_crt_set_ca_status(root_cert, 1);
gnutls_x509_crt_set_activation_time(root_cert, time(NULL));
gnutls_x509_crt_set_expiration_time(root_cert, time(NULL) + (60 * 60 * 24 * 365 * 10));
- gnutls_x509_crt_sign2(root_cert, root_cert, root_privkey, GNUTLS_DIG_SHA1, 0);
+ gnutls_x509_crt_sign2(root_cert, root_cert, root_privkey, (device_version < IDEVICE_DEVICE_VERSION(4,0,0)) ? GNUTLS_DIG_SHA1 : GNUTLS_DIG_SHA256, 0);
gnutls_x509_crt_set_key(host_cert, host_privkey);
gnutls_x509_crt_set_serial(host_cert, "\x01", 1);
@@ -670,7 +671,7 @@ userpref_error_t pair_record_generate_keys_and_certs(plist_t pair_record, key_da
gnutls_x509_crt_set_key_usage(host_cert, GNUTLS_KEY_KEY_ENCIPHERMENT | GNUTLS_KEY_DIGITAL_SIGNATURE);
gnutls_x509_crt_set_activation_time(host_cert, time(NULL));
gnutls_x509_crt_set_expiration_time(host_cert, time(NULL) + (60 * 60 * 24 * 365 * 10));
- gnutls_x509_crt_sign2(host_cert, root_cert, root_privkey, GNUTLS_DIG_SHA1, 0);
+ gnutls_x509_crt_sign2(host_cert, root_cert, root_privkey, (device_version < IDEVICE_DEVICE_VERSION(4,0,0)) ? GNUTLS_DIG_SHA1 : GNUTLS_DIG_SHA256, 0);
/* export to PEM format */
size_t root_key_export_size = 0;
@@ -768,17 +769,17 @@ userpref_error_t pair_record_generate_keys_and_certs(plist_t pair_record, key_da
gnutls_x509_crt_set_expiration_time(dev_cert, time(NULL) + (60 * 60 * 24 * 365 * 10));
/* use custom hash generation for compatibility with the "Apple ecosystem" */
- const gnutls_digest_algorithm_t dig_sha1 = GNUTLS_DIG_SHA1;
- size_t hash_size = gnutls_hash_get_len(dig_sha1);
+ const gnutls_digest_algorithm_t dig_sha = (device_version < IDEVICE_DEVICE_VERSION(4,0,0)) ? GNUTLS_DIG_SHA1 : GNUTLS_DIG_SHA256;
+ size_t hash_size = gnutls_hash_get_len(dig_sha);
unsigned char hash[hash_size];
- if (gnutls_hash_fast(dig_sha1, der_pub_key.data, der_pub_key.size, (unsigned char*)&hash) < 0) {
- debug_info("ERROR: Failed to generate SHA1 for public key");
+ if (gnutls_hash_fast(dig_sha, der_pub_key.data, der_pub_key.size, (unsigned char*)&hash) < 0) {
+ debug_info("ERROR: Failed to generate SHA for public key");
} else {
gnutls_x509_crt_set_subject_key_id(dev_cert, hash, hash_size);
}
gnutls_x509_crt_set_key_usage(dev_cert, GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT);
- gnutls_error = gnutls_x509_crt_sign2(dev_cert, root_cert, root_privkey, GNUTLS_DIG_SHA1, 0);
+ gnutls_error = gnutls_x509_crt_sign2(dev_cert, root_cert, root_privkey, (device_version < IDEVICE_DEVICE_VERSION(4,0,0)) ? GNUTLS_DIG_SHA1 : GNUTLS_DIG_SHA256, 0);
if (GNUTLS_E_SUCCESS == gnutls_error) {
/* if everything went well, export in PEM format */
size_t export_size = 0;
@@ -872,7 +873,7 @@ userpref_error_t pair_record_generate_keys_and_certs(plist_t pair_record, key_da
/* sign root cert with root private key */
mbedtls_x509write_crt_set_issuer_key(&cert, &root_pkey);
- mbedtls_x509write_crt_set_md_alg(&cert, MBEDTLS_MD_SHA1);
+ mbedtls_x509write_crt_set_md_alg(&cert, (device_version < IDEVICE_DEVICE_VERSION(4,0,0)) ? MBEDTLS_MD_SHA1 : MBEDTLS_MD_SHA256);
unsigned char outbuf[16384];
@@ -931,7 +932,7 @@ userpref_error_t pair_record_generate_keys_and_certs(plist_t pair_record, key_da
/* sign host cert with root private key */
mbedtls_x509write_crt_set_issuer_key(&cert, &root_pkey);
- mbedtls_x509write_crt_set_md_alg(&cert, MBEDTLS_MD_SHA1);
+ mbedtls_x509write_crt_set_md_alg(&cert, (device_version < IDEVICE_DEVICE_VERSION(4,0,0)) ? MBEDTLS_MD_SHA1 : MBEDTLS_MD_SHA256);
/* write host private key */
mbedtls_pk_write_key_pem(&host_pkey, outbuf, sizeof(outbuf));
@@ -991,7 +992,7 @@ userpref_error_t pair_record_generate_keys_and_certs(plist_t pair_record, key_da
/* sign device certificate with root private key */
mbedtls_x509write_crt_set_issuer_key(&cert, &root_pkey);
- mbedtls_x509write_crt_set_md_alg(&cert, MBEDTLS_MD_SHA1);
+ mbedtls_x509write_crt_set_md_alg(&cert, (device_version < IDEVICE_DEVICE_VERSION(4,0,0)) ? MBEDTLS_MD_SHA1 : MBEDTLS_MD_SHA256);
/* write device certificate */
mbedtls_x509write_crt_pem(&cert, outbuf, sizeof(outbuf), mbedtls_ctr_drbg_random, &ctr_drbg);
diff --git a/common/userpref.h b/common/userpref.h
index 75bb8b7..9a1832c 100644
--- a/common/userpref.h
+++ b/common/userpref.h
@@ -68,7 +68,7 @@ userpref_error_t userpref_read_pair_record(const char *udid, plist_t *pair_recor
userpref_error_t userpref_save_pair_record(const char *udid, uint32_t device_id, plist_t pair_record);
userpref_error_t userpref_delete_pair_record(const char *udid);
-userpref_error_t pair_record_generate_keys_and_certs(plist_t pair_record, key_data_t public_key);
+userpref_error_t pair_record_generate_keys_and_certs(plist_t pair_record, key_data_t public_key, unsigned int device_version);
#if defined(HAVE_OPENSSL) || defined(HAVE_MBEDTLS)
userpref_error_t pair_record_import_key_with_name(plist_t pair_record, const char* name, key_data_t* key);
userpref_error_t pair_record_import_crt_with_name(plist_t pair_record, const char* name, key_data_t* cert);
diff --git a/configure.ac b/configure.ac
index c67e896..055fe04 100644
--- a/configure.ac
+++ b/configure.ac
@@ -15,7 +15,7 @@ dnl libtool versioning
# changes to the signature and the semantic)
# ? :+1 : ? == just internal changes
# CURRENT : REVISION : AGE
-LIBIMOBILEDEVICE_SO_VERSION=6:0:0
+LIBIMOBILEDEVICE_SO_VERSION=7:0:1
AC_SUBST(LIBIMOBILEDEVICE_SO_VERSION)
@@ -70,7 +70,7 @@ AC_TYPE_UINT32_T
AC_TYPE_UINT8_T
# Checks for library functions.
-AC_CHECK_FUNCS([asprintf strcasecmp strdup strerror strndup stpcpy vasprintf getifaddrs])
+AC_CHECK_FUNCS([asprintf strcasecmp strdup strerror strndup stpcpy vasprintf getifaddrs gettimeofday localtime_r])
AC_CHECK_HEADER(endian.h, [ac_cv_have_endian_h="yes"], [ac_cv_have_endian_h="no"])
if test "x$ac_cv_have_endian_h" = "xno"; then
@@ -84,8 +84,24 @@ if test "x$ac_cv_have_endian_h" = "xno"; then
fi
fi
+CACHED_CFLAGS="$CFLAGS"
+CFLAGS+=" $libplist_CFLAGS -Werror"
+
AC_CHECK_DECL([plist_from_json], [AC_DEFINE([HAVE_PLIST_JSON], [1], [Define if libplist has JSON support])], [], [[#include <plist/plist.h>]])
+# check if libplist has plist_new_unix_date()
+AC_CACHE_CHECK(for plist_new_unix_date, ac_cv_plist_unix_date,
+ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
+ #include <plist/plist.h>
+ ]], [[
+ return plist_new_unix_date(0) ? 0 : 1
+ ]])],[ac_cv_plist_unix_date=yes],[ac_cv_plist_unix_date=no]))
+if test "$ac_cv_plist_unix_date" = "yes"; then
+ AC_DEFINE(HAVE_PLIST_UNIX_DATE, 1, [Define if libplist has new unix date API (>= 2.7.0)])
+fi
+
+CFLAGS="$CACHED_CFLAGS"
+
# Check for operating system
AC_MSG_CHECKING([for platform-specific build settings])
case ${host_os} in
@@ -93,6 +109,7 @@ case ${host_os} in
AC_MSG_RESULT([${host_os}])
win32=true
AC_DEFINE(WINVER, 0x0501, [minimum Windows version])
+ deplibs_check_method='pass_all'
;;
darwin*)
AC_MSG_RESULT([${host_os}])
@@ -107,24 +124,6 @@ esac
AM_CONDITIONAL(WIN32, test x$win32 = xtrue)
AM_CONDITIONAL(DARWIN, test x$darwin = xtrue)
-# Check if the C compiler supports __attribute__((constructor))
-AC_CACHE_CHECK([wether the C compiler supports constructor/destructor attributes],
- ac_cv_attribute_constructor, [
- ac_cv_attribute_constructor=no
- AC_COMPILE_IFELSE([AC_LANG_PROGRAM(
- [[
- static void __attribute__((constructor)) test_constructor(void) {
- }
- static void __attribute__((destructor)) test_destructor(void) {
- }
- ]], [])],
- [ac_cv_attribute_constructor=yes]
- )]
-)
-if test "$ac_cv_attribute_constructor" = "yes"; then
- AC_DEFINE(HAVE_ATTRIBUTE_CONSTRUCTOR, 1, [Define if the C compiler supports constructor/destructor attributes])
-fi
-
AC_CHECK_MEMBER(struct dirent.d_type, AC_DEFINE(HAVE_DIRENT_D_TYPE, 1, [define if struct dirent has member d_type]),, [#include <dirent.h>])
# Cython Python Bindings
diff --git a/cython/debugserver.pxi b/cython/debugserver.pxi
index a3b7d1e..fb96320 100644
--- a/cython/debugserver.pxi
+++ b/cython/debugserver.pxi
@@ -43,13 +43,7 @@ cdef class DebugServerError(BaseError):
BaseError.__init__(self, *args, **kwargs)
-# from http://stackoverflow.com/a/17511714
-# https://github.com/libimobiledevice/libimobiledevice/pull/198
-from cpython cimport PY_MAJOR_VERSION
-if PY_MAJOR_VERSION <= 2:
- from cpython.string cimport PyString_AsString
-else:
- from cpython.bytes cimport PyBytes_AsString as PyString_AsString
+from cpython.bytes cimport PyBytes_AsString as PyString_AsString
cdef char ** to_cstring_array(list_str):
if not list_str:
return NULL
diff --git a/docs/Makefile.am b/docs/Makefile.am
index 8156d4f..c9b742f 100644
--- a/docs/Makefile.am
+++ b/docs/Makefile.am
@@ -21,6 +21,7 @@ man_MANS = \
idevicesetlocation.1 \
afcclient.1
-EXTRA_DIST = $(man_MANS)
+EXTRA_DIST = $(man_MANS) \
+ doxygen
DISTCLEANFILES = html/* html
diff --git a/docs/idevicecrashreport.1 b/docs/idevicecrashreport.1
index 6acd6e9..f117c87 100644
--- a/docs/idevicecrashreport.1
+++ b/docs/idevicecrashreport.1
@@ -16,28 +16,31 @@ the device to the target DIRECTORY.
.SH OPTIONS
.TP
.B \-u, \-\-udid UDID
-target specific device by UDID.
+Target specific device by UDID.
.TP
.B \-n, \-\-network
-connect to network device.
+Connect to network device.
.TP
.B \-e, \-\-extract
-extract raw crash report into separate '.crash' files.
+Extract raw crash report into separate '.crash' files.
.TP
.B \-k, \-\-keep
-copy but do not remove crash reports from device.
+Copy but do not remove crash reports from device.
.TP
.B \-d, \-\-debug
-enable communication debugging.
+Enable communication debugging.
.TP
.B \-f, \-\-filter NAME
-filter crash reports by NAME (case sensitive)
+Filter crash reports by NAME (case sensitive)
+.TP
+.B \-\-remove\-all
+Remove all crash log files without copying. Can be used with \f[B]-f\f[] to only remove matching files.
.TP
.B \-h, \-\-help
-prints usage information.
+Prints usage information.
.TP
.B \-v, \-\-version
-prints version information.
+Prints version information.
.SH AUTHOR
Martin Szulecki
diff --git a/docs/idevicenotificationproxy.1 b/docs/idevicenotificationproxy.1
index 56dd0b0..627ee5d 100644
--- a/docs/idevicenotificationproxy.1
+++ b/docs/idevicenotificationproxy.1
@@ -12,19 +12,22 @@ Post or observe notifications on an iOS device from the command line.
.SH OPTIONS
.TP
.B \-u, \-\-udid UDID
-target specific device by UDID.
+Target specific device by UDID.
+.TP
+.B \-i, \-\-insecure
+Connect to insecure notification proxy (for non-paired devices).
.TP
.B \-n, \-\-network
-connect to network device.
+Connect to network device.
.TP
.B \-d, \-\-debug
-enable communication debugging.
+Enable communication debugging.
.TP
.B \-h, \-\-help
-prints usage information.
+Prints usage information.
.TP
.B \-v, \-\-version
-prints version information.
+Prints version information.
.SH COMMANDS
.TP
@@ -38,6 +41,8 @@ observe notification IDs in the foreground until CTRL+C or signal is received.
Martin Szulecki
+Nikias Bassen
+
.SH ON THE WEB
https://libimobiledevice.org
diff --git a/docs/idevicesyslog.1 b/docs/idevicesyslog.1
index 66ae2e4..5a677b6 100644
--- a/docs/idevicesyslog.1
+++ b/docs/idevicesyslog.1
@@ -38,13 +38,45 @@ If FILE already exists, it will be overwritten without warning.
.TP
.B \-\-colors
Force writing colored output, e.g. when using \f[B]\-\-output\f[].
+.TP
+.B \-\-syslog\-relay
+Use old syslog_relay service instead of os_trace_relay (iOS 9+).
+
+.SH COMMANDS
+.TP
+.B pidlist
+Print a list with PID and name of all processes currently running on the device.
+.TP
+.B archive PATH
+Request a logarchive from the device. It will be written in tar format to PATH. To pipe to another process use \- as PATH.
+Below are some options to restrict the log message data.
+
+In order to view the logarchive in a compatible log viewer, you can pipe the output data to \f[B]tar\f[] and have it extract into a new directory:
+
+\f[B]mkdir test.logarchive && tools/idevicesyslog archive - |tar -C test.logarchive -xv\f[]
+
+This will also print the filenames while they are extracted.
+.TP
+Further options for \f[B]archive\f[]:
+.TP
+.B \-\-start\-time VALUE
+Start time of the log data as UNIX timestamp. Earlier messages will be dropped.
+.TP
+.B \-\-age\-limit VALUE
+Maximum age of the log data, supposedly number of days.
+.TP
+.B \-\-size\-limit VALUE
+Limit the size of the archive. The unit is currently unknown, so feel free to experiment.
+.TP
+Keep in mind that the device usually only has a backlog of a few minutes so the options might not have the desired effect. This is not a bug.
.SH FILTER OPTIONS
.TP
.B \-m, \-\-match STRING
only print messages that contain STRING
-
-This option will set a filter to only printed log messages that contain the given string.
+.TP
+.B \-M, \-\-unmatch STRING
+print messages that do not contain STRING
.TP
.B \-t, \-\-trigger STRING
start logging when matching STRING
diff --git a/doxygen.cfg.in b/doxygen.cfg.in
index 4cbbb2d..7bc1160 100644
--- a/doxygen.cfg.in
+++ b/doxygen.cfg.in
@@ -1,4 +1,4 @@
-# Doxyfile 1.8.16
+# Doxyfile 1.11.0
# This file describes the settings to be used by the documentation system
# doxygen (www.doxygen.org) for a project.
@@ -12,6 +12,16 @@
# For lists, items can also be appended using:
# TAG += value [value, ...]
# Values that contain spaces should be placed between quotes (\" \").
+#
+# Note:
+#
+# Use doxygen to compare the used configuration file with the template
+# configuration file:
+# doxygen -x [configFile]
+# Use doxygen to compare the used configuration file with the template
+# configuration file without replacing the environment variables or CMake type
+# replacement variables:
+# doxygen -x_noenv [configFile]
#---------------------------------------------------------------------------
# Project related configuration options
@@ -53,6 +63,12 @@ PROJECT_BRIEF = "API Documentation"
PROJECT_LOGO = docs/doxygen/logo-vector-clean.svg
+# With the PROJECT_ICON tag one can specify an icon that is included in the tabs
+# when the HTML document is shown. Doxygen will copy the logo to the output
+# directory.
+
+PROJECT_ICON =
+
# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path
# into which the generated documentation will be written. If a relative path is
# entered, it will be relative to the location where doxygen was started. If
@@ -60,16 +76,28 @@ PROJECT_LOGO = docs/doxygen/logo-vector-clean.svg
OUTPUT_DIRECTORY = docs
-# If the CREATE_SUBDIRS tag is set to YES then doxygen will create 4096 sub-
-# directories (in 2 levels) under the output directory of each output format and
-# will distribute the generated files over these directories. Enabling this
+# If the CREATE_SUBDIRS tag is set to YES then doxygen will create up to 4096
+# sub-directories (in 2 levels) under the output directory of each output format
+# and will distribute the generated files over these directories. Enabling this
# option can be useful when feeding doxygen a huge amount of source files, where
# putting all generated files in the same directory would otherwise causes
-# performance problems for the file system.
+# performance problems for the file system. Adapt CREATE_SUBDIRS_LEVEL to
+# control the number of sub-directories.
# The default value is: NO.
CREATE_SUBDIRS = NO
+# Controls the number of sub-directories that will be created when
+# CREATE_SUBDIRS tag is set to YES. Level 0 represents 16 directories, and every
+# level increment doubles the number of directories, resulting in 4096
+# directories at level 8 which is the default and also the maximum value. The
+# sub-directories are organized in 2 levels, the first level always has a fixed
+# number of 16 directories.
+# Minimum value: 0, maximum value: 8, default value: 8.
+# This tag requires that the tag CREATE_SUBDIRS is set to YES.
+
+CREATE_SUBDIRS_LEVEL = 8
+
# If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII
# characters to appear in the names of generated files. If set to NO, non-ASCII
# characters will be escaped, for example _xE3_x81_x84 will be used for Unicode
@@ -81,14 +109,14 @@ ALLOW_UNICODE_NAMES = NO
# The OUTPUT_LANGUAGE tag is used to specify the language in which all
# documentation generated by doxygen is written. Doxygen will use this
# information to generate all constant output in the proper language.
-# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese,
-# Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States),
-# Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian,
-# Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages),
-# Korean, Korean-en (Korean with English messages), Latvian, Lithuanian,
-# Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian,
-# Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish,
-# Ukrainian and Vietnamese.
+# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Bulgarian,
+# Catalan, Chinese, Chinese-Traditional, Croatian, Czech, Danish, Dutch, English
+# (United States), Esperanto, Farsi (Persian), Finnish, French, German, Greek,
+# Hindi, Hungarian, Indonesian, Italian, Japanese, Japanese-en (Japanese with
+# English messages), Korean, Korean-en (Korean with English messages), Latvian,
+# Lithuanian, Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese,
+# Romanian, Russian, Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish,
+# Swedish, Turkish, Ukrainian and Vietnamese.
# The default value is: English.
OUTPUT_LANGUAGE = English
@@ -209,6 +237,14 @@ QT_AUTOBRIEF = NO
MULTILINE_CPP_IS_BRIEF = NO
+# By default Python docstrings are displayed as preformatted text and doxygen's
+# special commands cannot be used. By setting PYTHON_DOCSTRING to NO the
+# doxygen's special commands can be used and the contents of the docstring
+# documentation blocks is shown as doxygen documentation.
+# The default value is: YES.
+
+PYTHON_DOCSTRING = YES
+
# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the
# documentation from any documented member that it re-implements.
# The default value is: YES.
@@ -232,16 +268,16 @@ TAB_SIZE = 8
# the documentation. An alias has the form:
# name=value
# For example adding
-# "sideeffect=@par Side Effects:\n"
+# "sideeffect=@par Side Effects:^^"
# will allow you to put the command \sideeffect (or @sideeffect) in the
# documentation, which will result in a user-defined paragraph with heading
-# "Side Effects:". You can put \n's in the value part of an alias to insert
-# newlines (in the resulting output). You can put ^^ in the value part of an
-# alias to insert a newline as if a physical newline was in the original file.
-# When you need a literal { or } or , in the value part of an alias you have to
-# escape them by means of a backslash (\), this can lead to conflicts with the
-# commands \{ and \} for these it is advised to use the version @{ and @} or use
-# a double escape (\\{ and \\})
+# "Side Effects:". Note that you cannot put \n's in the value part of an alias
+# to insert newlines (in the resulting output). You can put ^^ in the value part
+# of an alias to insert a newline as if a physical newline was in the original
+# file. When you need a literal { or } or , in the value part of an alias you
+# have to escape them by means of a backslash (\), this can lead to conflicts
+# with the commands \{ and \} for these it is advised to use the version @{ and
+# @} or use a double escape (\\{ and \\})
ALIASES =
@@ -285,19 +321,22 @@ OPTIMIZE_OUTPUT_SLICE = YES
# parses. With this tag you can assign which parser to use for a given
# extension. Doxygen has a built-in mapping, but you can override or extend it
# using this tag. The format is ext=language, where ext is a file extension, and
-# language is one of the parsers supported by doxygen: IDL, Java, Javascript,
-# Csharp (C#), C, C++, D, PHP, md (Markdown), Objective-C, Python, Slice,
-# Fortran (fixed format Fortran: FortranFixed, free formatted Fortran:
+# language is one of the parsers supported by doxygen: IDL, Java, JavaScript,
+# Csharp (C#), C, C++, Lex, D, PHP, md (Markdown), Objective-C, Python, Slice,
+# VHDL, Fortran (fixed format Fortran: FortranFixed, free formatted Fortran:
# FortranFree, unknown formatted Fortran: Fortran. In the later case the parser
# tries to guess whether the code is fixed or free formatted code, this is the
-# default for Fortran type files), VHDL, tcl. For instance to make doxygen treat
-# .inc files as Fortran files (default is PHP), and .f files as C (default is
-# Fortran), use: inc=Fortran f=C.
+# default for Fortran type files). For instance to make doxygen treat .inc files
+# as Fortran files (default is PHP), and .f files as C (default is Fortran),
+# use: inc=Fortran f=C.
#
# Note: For files without extension you can use no_extension as a placeholder.
#
# Note that for custom extensions you also need to set FILE_PATTERNS otherwise
-# the files are not read by doxygen.
+# the files are not read by doxygen. When specifying no_extension you should add
+# * to the FILE_PATTERNS.
+#
+# Note see also the list of default file extension mappings.
EXTENSION_MAPPING =
@@ -315,11 +354,22 @@ MARKDOWN_SUPPORT = YES
# to that level are automatically included in the table of contents, even if
# they do not have an id attribute.
# Note: This feature currently applies only to Markdown headings.
-# Minimum value: 0, maximum value: 99, default value: 5.
+# Minimum value: 0, maximum value: 99, default value: 6.
# This tag requires that the tag MARKDOWN_SUPPORT is set to YES.
TOC_INCLUDE_HEADINGS = 5
+# The MARKDOWN_ID_STYLE tag can be used to specify the algorithm used to
+# generate identifiers for the Markdown headings. Note: Every identifier is
+# unique.
+# Possible values are: DOXYGEN use a fixed 'autotoc_md' string followed by a
+# sequence number starting at 0 and GITHUB use the lower case version of title
+# with any whitespace replaced by '-' and punctuation characters removed.
+# The default value is: DOXYGEN.
+# This tag requires that the tag MARKDOWN_SUPPORT is set to YES.
+
+MARKDOWN_ID_STYLE = DOXYGEN
+
# When enabled doxygen tries to link words that correspond to documented
# classes, or namespaces to their corresponding documentation. Such a link can
# be prevented in individual cases by putting a % sign in front of the word or
@@ -332,8 +382,8 @@ AUTOLINK_SUPPORT = YES
# to include (a tag file for) the STL sources as input, then you should set this
# tag to YES in order to let doxygen match functions declarations and
# definitions whose arguments contain STL classes (e.g. func(std::string);
-# versus func(std::string) {}). This also make the inheritance and collaboration
-# diagrams that involve STL classes more complete and accurate.
+# versus func(std::string) {}). This also makes the inheritance and
+# collaboration diagrams that involve STL classes more complete and accurate.
# The default value is: NO.
BUILTIN_STL_SUPPORT = NO
@@ -345,9 +395,9 @@ BUILTIN_STL_SUPPORT = NO
CPP_CLI_SUPPORT = NO
# Set the SIP_SUPPORT tag to YES if your project consists of sip (see:
-# https://www.riverbankcomputing.com/software/sip/intro) sources only. Doxygen
-# will parse them like normal C++ but will assume all classes use public instead
-# of private inheritance when no explicit protection keyword is present.
+# https://www.riverbankcomputing.com/software) sources only. Doxygen will parse
+# them like normal C++ but will assume all classes use public instead of private
+# inheritance when no explicit protection keyword is present.
# The default value is: NO.
SIP_SUPPORT = NO
@@ -431,6 +481,27 @@ TYPEDEF_HIDES_STRUCT = YES
LOOKUP_CACHE_SIZE = 0
+# The NUM_PROC_THREADS specifies the number of threads doxygen is allowed to use
+# during processing. When set to 0 doxygen will based this on the number of
+# cores available in the system. You can set it explicitly to a value larger
+# than 0 to get more control over the balance between CPU load and processing
+# speed. At this moment only the input processing can be done using multiple
+# threads. Since this is still an experimental feature the default is set to 1,
+# which effectively disables parallel processing. Please report any issues you
+# encounter. Generating dot graphs in parallel is controlled by the
+# DOT_NUM_THREADS setting.
+# Minimum value: 0, maximum value: 32, default value: 1.
+
+NUM_PROC_THREADS = 1
+
+# If the TIMESTAMP tag is set different from NO then each generated page will
+# contain the date or date and time when the page was generated. Setting this to
+# NO can help when comparing the output of multiple runs.
+# Possible values are: YES, NO, DATETIME and DATE.
+# The default value is: NO.
+
+TIMESTAMP = YES
+
#---------------------------------------------------------------------------
# Build related configuration options
#---------------------------------------------------------------------------
@@ -494,6 +565,13 @@ EXTRACT_LOCAL_METHODS = NO
EXTRACT_ANON_NSPACES = NO
+# If this flag is set to YES, the name of an unnamed parameter in a declaration
+# will be determined by the corresponding definition. By default unnamed
+# parameters remain unnamed in the output.
+# The default value is: YES.
+
+RESOLVE_UNNAMED_PARAMS = YES
+
# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all
# undocumented members inside documented classes or files. If set to NO these
# members will be included in the various overviews, but no documentation
@@ -505,14 +583,15 @@ HIDE_UNDOC_MEMBERS = NO
# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all
# undocumented classes that are normally visible in the class hierarchy. If set
# to NO, these classes will be included in the various overviews. This option
-# has no effect if EXTRACT_ALL is enabled.
+# will also hide undocumented C++ concepts if enabled. This option has no effect
+# if EXTRACT_ALL is enabled.
# The default value is: NO.
HIDE_UNDOC_CLASSES = NO
# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend
-# (class|struct|union) declarations. If set to NO, these declarations will be
-# included in the documentation.
+# declarations. If set to NO, these declarations will be included in the
+# documentation.
# The default value is: NO.
HIDE_FRIEND_COMPOUNDS = NO
@@ -531,12 +610,20 @@ HIDE_IN_BODY_DOCS = NO
INTERNAL_DOCS = NO
-# If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file
-# names in lower-case letters. If set to YES, upper-case letters are also
-# allowed. This is useful if you have classes or files whose names only differ
-# in case and if your file system supports case sensitive file names. Windows
-# (including Cygwin) ands Mac users are advised to set this option to NO.
-# The default value is: system dependent.
+# With the correct setting of option CASE_SENSE_NAMES doxygen will better be
+# able to match the capabilities of the underlying filesystem. In case the
+# filesystem is case sensitive (i.e. it supports files in the same directory
+# whose names only differ in casing), the option must be set to YES to properly
+# deal with such files in case they appear in the input. For filesystems that
+# are not case sensitive the option should be set to NO to properly deal with
+# output files written for symbols that only differ in casing, such as for two
+# classes, one named CLASS and the other named Class, and to also support
+# references to files without having to specify the exact matching casing. On
+# Windows (including Cygwin) and MacOS, users should typically set this option
+# to NO, whereas on Linux or other Unix flavors it should typically be set to
+# YES.
+# Possible values are: SYSTEM, NO and YES.
+# The default value is: SYSTEM.
CASE_SENSE_NAMES = NO
@@ -554,6 +641,12 @@ HIDE_SCOPE_NAMES = NO
HIDE_COMPOUND_REFERENCE= NO
+# If the SHOW_HEADERFILE tag is set to YES then the documentation for a class
+# will show which file needs to be included to use the class.
+# The default value is: YES.
+
+SHOW_HEADERFILE = YES
+
# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of
# the files that are included by a file in the documentation of that file.
# The default value is: YES.
@@ -711,7 +804,8 @@ FILE_VERSION_FILTER =
# output files in an output format independent way. To create the layout file
# that represents doxygen's defaults, run doxygen with the -l option. You can
# optionally specify a file name after the option, if omitted DoxygenLayout.xml
-# will be used as the name of the layout file.
+# will be used as the name of the layout file. See also section "Changing the
+# layout of pages" for information.
#
# Note that if you run doxygen from a directory containing a file called
# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE
@@ -757,24 +851,50 @@ WARNINGS = YES
WARN_IF_UNDOCUMENTED = YES
# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for
-# potential errors in the documentation, such as not documenting some parameters
-# in a documented function, or documenting parameters that don't exist or using
-# markup commands wrongly.
+# potential errors in the documentation, such as documenting some parameters in
+# a documented function twice, or documenting parameters that don't exist or
+# using markup commands wrongly.
# The default value is: YES.
WARN_IF_DOC_ERROR = YES
+# If WARN_IF_INCOMPLETE_DOC is set to YES, doxygen will warn about incomplete
+# function parameter documentation. If set to NO, doxygen will accept that some
+# parameters have no documentation without warning.
+# The default value is: YES.
+
+WARN_IF_INCOMPLETE_DOC = YES
+
# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that
# are documented, but have no documentation for their parameters or return
-# value. If set to NO, doxygen will only warn about wrong or incomplete
-# parameter documentation, but not about the absence of documentation. If
-# EXTRACT_ALL is set to YES then this flag will automatically be disabled.
+# value. If set to NO, doxygen will only warn about wrong parameter
+# documentation, but not about the absence of documentation. If EXTRACT_ALL is
+# set to YES then this flag will automatically be disabled. See also
+# WARN_IF_INCOMPLETE_DOC
# The default value is: NO.
WARN_NO_PARAMDOC = NO
+# If WARN_IF_UNDOC_ENUM_VAL option is set to YES, doxygen will warn about
+# undocumented enumeration values. If set to NO, doxygen will accept
+# undocumented enumeration values. If EXTRACT_ALL is set to YES then this flag
+# will automatically be disabled.
+# The default value is: NO.
+
+WARN_IF_UNDOC_ENUM_VAL = NO
+
# If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when
-# a warning is encountered.
+# a warning is encountered. If the WARN_AS_ERROR tag is set to FAIL_ON_WARNINGS
+# then doxygen will continue running as if WARN_AS_ERROR tag is set to NO, but
+# at the end of the doxygen process doxygen will return with a non-zero status.
+# If the WARN_AS_ERROR tag is set to FAIL_ON_WARNINGS_PRINT then doxygen behaves
+# like FAIL_ON_WARNINGS but in case no WARN_LOGFILE is defined doxygen will not
+# write the warning messages in between other messages but write them at the end
+# of a run, in case a WARN_LOGFILE is defined the warning messages will be
+# besides being in the defined file also be shown at the end of a run, unless
+# the WARN_LOGFILE is defined as - i.e. standard output (stdout) in that case
+# the behavior will remain as with the setting FAIL_ON_WARNINGS.
+# Possible values are: NO, YES, FAIL_ON_WARNINGS and FAIL_ON_WARNINGS_PRINT.
# The default value is: NO.
WARN_AS_ERROR = NO
@@ -785,13 +905,27 @@ WARN_AS_ERROR = NO
# and the warning text. Optionally the format may contain $version, which will
# be replaced by the version of the file (if it could be obtained via
# FILE_VERSION_FILTER)
+# See also: WARN_LINE_FORMAT
# The default value is: $file:$line: $text.
WARN_FORMAT = "$file:$line: $text"
+# In the $text part of the WARN_FORMAT command it is possible that a reference
+# to a more specific place is given. To make it easier to jump to this place
+# (outside of doxygen) the user can define a custom "cut" / "paste" string.
+# Example:
+# WARN_LINE_FORMAT = "'vi $file +$line'"
+# See also: WARN_FORMAT
+# The default value is: at line $line of file $file.
+
+WARN_LINE_FORMAT = "at line $line of file $file"
+
# The WARN_LOGFILE tag can be used to specify a file to which warning and error
# messages should be written. If left blank the output is written to standard
-# error (stderr).
+# error (stderr). In case the file specified cannot be opened for writing the
+# warning and error messages are written to standard error. When as file - is
+# specified the warning and error messages are written to standard output
+# (stdout).
WARN_LOGFILE =
@@ -805,19 +939,29 @@ WARN_LOGFILE =
# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING
# Note: If this tag is empty the current directory is searched.
-INPUT = \
- include/@PACKAGE@ \
- README.md
+INPUT = include/@PACKAGE@ \
+ README.md
# This tag can be used to specify the character encoding of the source files
# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
# libiconv (or the iconv built into libc) for the transcoding. See the libiconv
-# documentation (see: https://www.gnu.org/software/libiconv/) for the list of
-# possible encodings.
+# documentation (see:
+# https://www.gnu.org/software/libiconv/) for the list of possible encodings.
+# See also: INPUT_FILE_ENCODING
# The default value is: UTF-8.
INPUT_ENCODING = UTF-8
+# This tag can be used to specify the character encoding of the source files
+# that doxygen parses The INPUT_FILE_ENCODING tag can be used to specify
+# character encoding on a per file pattern basis. Doxygen will compare the file
+# name with each pattern and apply the encoding instead of the default
+# INPUT_ENCODING) if there is a match. The character encodings are a list of the
+# form: pattern=encoding (like *.php=ISO-8859-1).
+# See also: INPUT_ENCODING for further information on supported encodings.
+
+INPUT_FILE_ENCODING =
+
# If the value of the INPUT tag contains directories, you can use the
# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and
# *.h) to filter out the source-files in the directories.
@@ -826,11 +970,15 @@ INPUT_ENCODING = UTF-8
# need to set EXTENSION_MAPPING for the extension otherwise the files are not
# read by doxygen.
#
-# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp,
-# *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h,
-# *.hh, *.hxx, *.hpp, *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc,
-# *.m, *.markdown, *.md, *.mm, *.dox, *.py, *.pyw, *.f90, *.f95, *.f03, *.f08,
-# *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf, *.qsf and *.ice.
+# Note the list of default checked file patterns might differ from the list of
+# default file extension mappings.
+#
+# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cxxm,
+# *.cpp, *.cppm, *.ccm, *.c++, *.c++m, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl,
+# *.idl, *.ddl, *.odl, *.h, *.hh, *.hxx, *.hpp, *.h++, *.ixx, *.l, *.cs, *.d,
+# *.php, *.php4, *.php5, *.phtml, *.inc, *.m, *.markdown, *.md, *.mm, *.dox (to
+# be provided as doxygen C comment), *.py, *.pyw, *.f90, *.f95, *.f03, *.f08,
+# *.f18, *.f, *.for, *.vhd, *.vhdl, *.ucf, *.qsf and *.ice.
FILE_PATTERNS =
@@ -869,10 +1017,7 @@ EXCLUDE_PATTERNS =
# (namespaces, classes, functions, etc.) that should be excluded from the
# output. The symbol name can be a fully qualified name, a word, or if the
# wildcard * is used, a substring. Examples: ANamespace, AClass,
-# AClass::ANamespace, ANamespace::*Test
-#
-# Note that the wildcards are matched against the file with absolute path, so to
-# exclude all test directories use the pattern */test/*
+# ANamespace::AClass, ANamespace::*Test
EXCLUDE_SYMBOLS = LIBIMOBILEDEVICE_API
@@ -917,6 +1062,11 @@ IMAGE_PATH =
# code is scanned, but not when the output code is generated. If lines are added
# or removed, the anchors will not be placed correctly.
#
+# Note that doxygen will use the data processed and written to standard output
+# for further processing, therefore nothing else, like debug statements or used
+# commands (so in case of a Windows batch file always use @echo OFF), should be
+# written to standard output.
+#
# Note that for custom extensions or not directly supported extensions you also
# need to set EXTENSION_MAPPING for the extension otherwise the files are not
# properly processed by doxygen.
@@ -958,6 +1108,15 @@ FILTER_SOURCE_PATTERNS =
USE_MDFILE_AS_MAINPAGE = README.md
+# The Fortran standard specifies that for fixed formatted Fortran code all
+# characters from position 72 are to be considered as comment. A common
+# extension is to allow longer lines before the automatic comment starts. The
+# setting FORTRAN_COMMENT_AFTER will also make it possible that longer lines can
+# be processed before the automatic comment starts.
+# Minimum value: 7, maximum value: 10000, default value: 72.
+
+FORTRAN_COMMENT_AFTER = 72
+
#---------------------------------------------------------------------------
# Configuration options related to source browsing
#---------------------------------------------------------------------------
@@ -972,7 +1131,8 @@ USE_MDFILE_AS_MAINPAGE = README.md
SOURCE_BROWSER = NO
# Setting the INLINE_SOURCES tag to YES will include the body of functions,
-# classes and enums directly into the documentation.
+# multi-line macros, enums or list initialized variables directly into the
+# documentation.
# The default value is: NO.
INLINE_SOURCES = NO
@@ -1055,10 +1215,11 @@ VERBATIM_HEADERS = NO
ALPHABETICAL_INDEX = NO
-# In case all classes in a project start with a common prefix, all classes will
-# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag
-# can be used to specify a prefix (or a list of prefixes) that should be ignored
-# while generating the index headers.
+# The IGNORE_PREFIX tag can be used to specify a prefix (or a list of prefixes)
+# that should be ignored while generating the index headers. The IGNORE_PREFIX
+# tag works for classes, function and member names. The entity will be placed in
+# the alphabetical list under the first letter of the entity name that remains
+# after removing the prefix.
# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.
IGNORE_PREFIX =
@@ -1137,11 +1298,15 @@ HTML_STYLESHEET =
# Doxygen will copy the style sheet files to the output directory.
# Note: The order of the extra style sheet files is of importance (e.g. the last
# style sheet in the list overrules the setting of the previous ones in the
-# list). For an example see the documentation.
+# list).
+# Note: Since the styling of scrollbars can currently not be overruled in
+# Webkit/Chromium, the styling will be left out of the default doxygen.css if
+# one or more extra stylesheets have been specified. So if scrollbar
+# customization is desired it has to be added explicitly. For an example see the
+# documentation.
# This tag requires that the tag GENERATE_HTML is set to YES.
-HTML_EXTRA_STYLESHEET = \
- docs/doxygen/custom.css
+HTML_EXTRA_STYLESHEET = docs/doxygen/custom.css
# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or
# other source files which should be copied to the HTML output directory. Note
@@ -1151,15 +1316,27 @@ HTML_EXTRA_STYLESHEET = \
# files will be copied as-is; there are no commands or markers available.
# This tag requires that the tag GENERATE_HTML is set to YES.
-HTML_EXTRA_FILES = \
- docs/doxygen/favicon.ico \
- docs/doxygen/folder.png \
- docs/doxygen/folder-open.png \
- docs/doxygen/text-x-generic.png
+HTML_EXTRA_FILES = docs/doxygen/favicon.ico \
+ docs/doxygen/folder.png \
+ docs/doxygen/folder-open.png \
+ docs/doxygen/text-x-generic.png
+
+# The HTML_COLORSTYLE tag can be used to specify if the generated HTML output
+# should be rendered with a dark or light theme.
+# Possible values are: LIGHT always generates light mode output, DARK always
+# generates dark mode output, AUTO_LIGHT automatically sets the mode according
+# to the user preference, uses light mode if no preference is set (the default),
+# AUTO_DARK automatically sets the mode according to the user preference, uses
+# dark mode if no preference is set and TOGGLE allows a user to switch between
+# light and dark mode via a button.
+# The default value is: AUTO_LIGHT.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_COLORSTYLE = AUTO_LIGHT
# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen
# will adjust the colors in the style sheet and background images according to
-# this color. Hue is specified as an angle on a colorwheel, see
+# this color. Hue is specified as an angle on a color-wheel, see
# https://en.wikipedia.org/wiki/Hue for more information. For instance the value
# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300
# purple, and 360 is red again.
@@ -1169,7 +1346,7 @@ HTML_EXTRA_FILES = \
HTML_COLORSTYLE_HUE = 220
# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors
-# in the HTML output. For a value of 0 the output will use grayscales only. A
+# in the HTML output. For a value of 0 the output will use gray-scales only. A
# value of 255 will produce the most vivid colors.
# Minimum value: 0, maximum value: 255, default value: 100.
# This tag requires that the tag GENERATE_HTML is set to YES.
@@ -1187,20 +1364,11 @@ HTML_COLORSTYLE_SAT = 100
HTML_COLORSTYLE_GAMMA = 80
-# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML
-# page will contain the date and time when the page was generated. Setting this
-# to YES can help to show when doxygen was last run and thus if the
-# documentation is up to date.
-# The default value is: NO.
-# This tag requires that the tag GENERATE_HTML is set to YES.
-
-HTML_TIMESTAMP = YES
-
# If the HTML_DYNAMIC_MENUS tag is set to YES then the generated HTML
# documentation will contain a main index with vertical navigation menus that
-# are dynamically created via Javascript. If disabled, the navigation index will
+# are dynamically created via JavaScript. If disabled, the navigation index will
# consists of multiple levels of tabs that are statically embedded in every HTML
-# page. Disable this option to support browsers that do not have Javascript,
+# page. Disable this option to support browsers that do not have JavaScript,
# like the Qt help browser.
# The default value is: YES.
# This tag requires that the tag GENERATE_HTML is set to YES.
@@ -1215,6 +1383,33 @@ HTML_DYNAMIC_MENUS = NO
HTML_DYNAMIC_SECTIONS = NO
+# If the HTML_CODE_FOLDING tag is set to YES then classes and functions can be
+# dynamically folded and expanded in the generated HTML source code.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_CODE_FOLDING = YES
+
+# If the HTML_COPY_CLIPBOARD tag is set to YES then doxygen will show an icon in
+# the top right corner of code and text fragments that allows the user to copy
+# its content to the clipboard. Note this only works if supported by the browser
+# and the web page is served via a secure context (see:
+# https://www.w3.org/TR/secure-contexts/), i.e. using the https: or file:
+# protocol.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_COPY_CLIPBOARD = YES
+
+# Doxygen stores a couple of settings persistently in the browser (via e.g.
+# cookies). By default these settings apply to all HTML pages generated by
+# doxygen across all projects. The HTML_PROJECT_COOKIE tag can be used to store
+# the settings under a project specific key, such that the user preferences will
+# be stored separately.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_PROJECT_COOKIE =
+
# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries
# shown in the various tree structured indices initially; the user can expand
# and collapse entries dynamically later on. Doxygen will expand the tree to
@@ -1230,10 +1425,11 @@ HTML_INDEX_NUM_ENTRIES = 100
# If the GENERATE_DOCSET tag is set to YES, additional index files will be
# generated that can be used as input for Apple's Xcode 3 integrated development
-# environment (see: https://developer.apple.com/xcode/), introduced with OSX
-# 10.5 (Leopard). To create a documentation set, doxygen will generate a
-# Makefile in the HTML output directory. Running make will produce the docset in
-# that directory and running make install will install the docset in
+# environment (see:
+# https://developer.apple.com/xcode/), introduced with OSX 10.5 (Leopard). To
+# create a documentation set, doxygen will generate a Makefile in the HTML
+# output directory. Running make will produce the docset in that directory and
+# running make install will install the docset in
# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at
# startup. See https://developer.apple.com/library/archive/featuredarticles/Doxy
# genXcode/_index.html for more information.
@@ -1250,6 +1446,13 @@ GENERATE_DOCSET = NO
DOCSET_FEEDNAME = "Doxygen generated docs"
+# This tag determines the URL of the docset feed. A documentation feed provides
+# an umbrella under which multiple documentation sets from a single provider
+# (such as a company or product suite) can be grouped.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_FEEDURL =
+
# This tag specifies a string that should uniquely identify the documentation
# set bundle. This should be a reverse domain-name style string, e.g.
# com.mycompany.MyDocSet. Doxygen will append .docset to the name.
@@ -1275,8 +1478,12 @@ DOCSET_PUBLISHER_NAME = Publisher
# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three
# additional HTML index files: index.hhp, index.hhc, and index.hhk. The
# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop
-# (see: https://www.microsoft.com/en-us/download/details.aspx?id=21138) on
-# Windows.
+# on Windows. In the beginning of 2021 Microsoft took the original page, with
+# a.o. the download links, offline the HTML help workshop was already many years
+# in maintenance mode). You can download the HTML help workshop from the web
+# archives at Installation executable (see:
+# http://web.archive.org/web/20160201063255/http://download.microsoft.com/downlo
+# ad/0/A/9/0A939EF6-E31C-430F-A3DF-DFAE7960D564/htmlhelp.exe).
#
# The HTML Help Workshop contains a compiler that can convert all HTML output
# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML
@@ -1306,7 +1513,7 @@ CHM_FILE =
HHC_LOCATION =
# The GENERATE_CHI flag controls if a separate .chi index file is generated
-# (YES) or that it should be included in the master .chm file (NO).
+# (YES) or that it should be included in the main .chm file (NO).
# The default value is: NO.
# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
@@ -1333,6 +1540,16 @@ BINARY_TOC = NO
TOC_EXPAND = NO
+# The SITEMAP_URL tag is used to specify the full URL of the place where the
+# generated documentation will be placed on the server by the user during the
+# deployment of the documentation. The generated sitemap is called sitemap.xml
+# and placed on the directory specified by HTML_OUTPUT. In case no SITEMAP_URL
+# is specified no sitemap is generated. For information about the sitemap
+# protocol see https://www.sitemaps.org
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+SITEMAP_URL =
+
# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and
# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that
# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help
@@ -1351,7 +1568,8 @@ QCH_FILE =
# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help
# Project output. For more information please see Qt Help Project / Namespace
-# (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#namespace).
+# (see:
+# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#namespace).
# The default value is: org.doxygen.Project.
# This tag requires that the tag GENERATE_QHP is set to YES.
@@ -1359,8 +1577,8 @@ QHP_NAMESPACE = org.doxygen.Project
# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt
# Help Project output. For more information please see Qt Help Project / Virtual
-# Folders (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#virtual-
-# folders).
+# Folders (see:
+# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#virtual-folders).
# The default value is: doc.
# This tag requires that the tag GENERATE_QHP is set to YES.
@@ -1368,16 +1586,16 @@ QHP_VIRTUAL_FOLDER = doc
# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom
# filter to add. For more information please see Qt Help Project / Custom
-# Filters (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-
-# filters).
+# Filters (see:
+# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-filters).
# This tag requires that the tag GENERATE_QHP is set to YES.
QHP_CUST_FILTER_NAME =
# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the
# custom filter to add. For more information please see Qt Help Project / Custom
-# Filters (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-
-# filters).
+# Filters (see:
+# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-filters).
# This tag requires that the tag GENERATE_QHP is set to YES.
QHP_CUST_FILTER_ATTRS =
@@ -1389,9 +1607,9 @@ QHP_CUST_FILTER_ATTRS =
QHP_SECT_FILTER_ATTRS =
-# The QHG_LOCATION tag can be used to specify the location of Qt's
-# qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the
-# generated .qhp file.
+# The QHG_LOCATION tag can be used to specify the location (absolute path
+# including file name) of Qt's qhelpgenerator. If non-empty doxygen will try to
+# run qhelpgenerator on the generated .qhp file.
# This tag requires that the tag GENERATE_QHP is set to YES.
QHG_LOCATION =
@@ -1434,16 +1652,28 @@ DISABLE_INDEX = NO
# to work a browser that supports JavaScript, DHTML, CSS and frames is required
# (i.e. any modern browser). Windows users are probably better off using the
# HTML help feature. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can
-# further fine-tune the look of the index. As an example, the default style
-# sheet generated by doxygen has an example that shows how to put an image at
-# the root of the tree instead of the PROJECT_NAME. Since the tree basically has
-# the same information as the tab index, you could consider setting
-# DISABLE_INDEX to YES when enabling this option.
+# further fine tune the look of the index (see "Fine-tuning the output"). As an
+# example, the default style sheet generated by doxygen has an example that
+# shows how to put an image at the root of the tree instead of the PROJECT_NAME.
+# Since the tree basically has the same information as the tab index, you could
+# consider setting DISABLE_INDEX to YES when enabling this option.
# The default value is: NO.
# This tag requires that the tag GENERATE_HTML is set to YES.
GENERATE_TREEVIEW = NONE
+# When both GENERATE_TREEVIEW and DISABLE_INDEX are set to YES, then the
+# FULL_SIDEBAR option determines if the side bar is limited to only the treeview
+# area (value NO) or if it should extend to the full height of the window (value
+# YES). Setting this to YES gives a layout similar to
+# https://docs.readthedocs.io with more room for contents, but less room for the
+# project logo, title, and description. If either GENERATE_TREEVIEW or
+# DISABLE_INDEX is set to NO, this option has no effect.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+FULL_SIDEBAR = NO
+
# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that
# doxygen will group on one line in the generated HTML documentation.
#
@@ -1468,6 +1698,24 @@ TREEVIEW_WIDTH = 250
EXT_LINKS_IN_WINDOW = NO
+# If the OBFUSCATE_EMAILS tag is set to YES, doxygen will obfuscate email
+# addresses.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+OBFUSCATE_EMAILS = YES
+
+# If the HTML_FORMULA_FORMAT option is set to svg, doxygen will use the pdf2svg
+# tool (see https://github.com/dawbarton/pdf2svg) or inkscape (see
+# https://inkscape.org) to generate formulas as SVG images instead of PNGs for
+# the HTML output. These images will generally look nicer at scaled resolutions.
+# Possible values are: png (the default) and svg (looks nicer but requires the
+# pdf2svg or inkscape tool).
+# The default value is: png.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_FORMULA_FORMAT = png
+
# Use this tag to change the font size of LaTeX formulas included as images in
# the HTML documentation. When you change the font size after a successful
# doxygen run you need to manually remove any form_*.png images from the HTML
@@ -1477,19 +1725,14 @@ EXT_LINKS_IN_WINDOW = NO
FORMULA_FONTSIZE = 10
-# Use the FORMULA_TRANSPARENT tag to determine whether or not the images
-# generated for formulas are transparent PNGs. Transparent PNGs are not
-# supported properly for IE 6.0, but are supported on all modern browsers.
-#
-# Note that when changing this option you need to delete any form_*.png files in
-# the HTML output directory before the changes have effect.
-# The default value is: YES.
-# This tag requires that the tag GENERATE_HTML is set to YES.
+# The FORMULA_MACROFILE can contain LaTeX \newcommand and \renewcommand commands
+# to create new LaTeX commands to be used in formulas as building blocks. See
+# the section "Including formulas" for details.
-FORMULA_TRANSPARENT = YES
+FORMULA_MACROFILE =
# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see
-# https://www.mathjax.org) which uses client side Javascript for the rendering
+# https://www.mathjax.org) which uses client side JavaScript for the rendering
# instead of using pre-rendered bitmaps. Use this if you do not have LaTeX
# installed or if you want to formulas look prettier in the HTML output. When
# enabled you may also need to install MathJax separately and configure the path
@@ -1499,11 +1742,29 @@ FORMULA_TRANSPARENT = YES
USE_MATHJAX = NO
+# With MATHJAX_VERSION it is possible to specify the MathJax version to be used.
+# Note that the different versions of MathJax have different requirements with
+# regards to the different settings, so it is possible that also other MathJax
+# settings have to be changed when switching between the different MathJax
+# versions.
+# Possible values are: MathJax_2 and MathJax_3.
+# The default value is: MathJax_2.
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_VERSION = MathJax_2
+
# When MathJax is enabled you can set the default output format to be used for
-# the MathJax output. See the MathJax site (see:
-# http://docs.mathjax.org/en/latest/output.html) for more details.
+# the MathJax output. For more details about the output format see MathJax
+# version 2 (see:
+# http://docs.mathjax.org/en/v2.7-latest/output.html) and MathJax version 3
+# (see:
+# http://docs.mathjax.org/en/latest/web/components/output.html).
# Possible values are: HTML-CSS (which is slower, but has the best
-# compatibility), NativeMML (i.e. MathML) and SVG.
+# compatibility. This is the name for Mathjax version 2, for MathJax version 3
+# this will be translated into chtml), NativeMML (i.e. MathML. Only supported
+# for MathJax 2. For MathJax version 3 chtml will be used instead.), chtml (This
+# is the name for Mathjax version 3, for MathJax version 2 this will be
+# translated into HTML-CSS) and SVG.
# The default value is: HTML-CSS.
# This tag requires that the tag USE_MATHJAX is set to YES.
@@ -1516,22 +1777,29 @@ MATHJAX_FORMAT = HTML-CSS
# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax
# Content Delivery Network so you can quickly see the result without installing
# MathJax. However, it is strongly recommended to install a local copy of
-# MathJax from https://www.mathjax.org before deployment.
-# The default value is: https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/.
+# MathJax from https://www.mathjax.org before deployment. The default value is:
+# - in case of MathJax version 2: https://cdn.jsdelivr.net/npm/mathjax@2
+# - in case of MathJax version 3: https://cdn.jsdelivr.net/npm/mathjax@3
# This tag requires that the tag USE_MATHJAX is set to YES.
MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest
# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax
# extension names that should be enabled during MathJax rendering. For example
+# for MathJax version 2 (see
+# https://docs.mathjax.org/en/v2.7-latest/tex.html#tex-and-latex-extensions):
# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols
+# For example for MathJax version 3 (see
+# http://docs.mathjax.org/en/latest/input/tex/extensions/index.html):
+# MATHJAX_EXTENSIONS = ams
# This tag requires that the tag USE_MATHJAX is set to YES.
MATHJAX_EXTENSIONS =
# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces
# of code that will be used on startup of the MathJax code. See the MathJax site
-# (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an
+# (see:
+# http://docs.mathjax.org/en/v2.7-latest/output.html) for more details. For an
# example see the documentation.
# This tag requires that the tag USE_MATHJAX is set to YES.
@@ -1559,7 +1827,7 @@ MATHJAX_CODEFILE =
SEARCHENGINE = NO
# When the SERVER_BASED_SEARCH tag is enabled the search engine will be
-# implemented using a web server instead of a web client using Javascript. There
+# implemented using a web server instead of a web client using JavaScript. There
# are two flavors of web server based searching depending on the EXTERNAL_SEARCH
# setting. When disabled, doxygen will generate a PHP script for searching and
# an index file used by the script. When EXTERNAL_SEARCH is enabled the indexing
@@ -1578,7 +1846,8 @@ SERVER_BASED_SEARCH = NO
#
# Doxygen ships with an example indexer (doxyindexer) and search engine
# (doxysearch.cgi) which are based on the open source search engine library
-# Xapian (see: https://xapian.org/).
+# Xapian (see:
+# https://xapian.org/).
#
# See the section "External Indexing and Searching" for details.
# The default value is: NO.
@@ -1591,8 +1860,9 @@ EXTERNAL_SEARCH = NO
#
# Doxygen ships with an example indexer (doxyindexer) and search engine
# (doxysearch.cgi) which are based on the open source search engine library
-# Xapian (see: https://xapian.org/). See the section "External Indexing and
-# Searching" for details.
+# Xapian (see:
+# https://xapian.org/). See the section "External Indexing and Searching" for
+# details.
# This tag requires that the tag SEARCHENGINE is set to YES.
SEARCHENGINE_URL =
@@ -1701,29 +1971,31 @@ PAPER_TYPE = a4
EXTRA_PACKAGES =
-# The LATEX_HEADER tag can be used to specify a personal LaTeX header for the
-# generated LaTeX document. The header should contain everything until the first
-# chapter. If it is left blank doxygen will generate a standard header. See
-# section "Doxygen usage" for information on how to let doxygen write the
-# default header to a separate file.
+# The LATEX_HEADER tag can be used to specify a user-defined LaTeX header for
+# the generated LaTeX document. The header should contain everything until the
+# first chapter. If it is left blank doxygen will generate a standard header. It
+# is highly recommended to start with a default header using
+# doxygen -w latex new_header.tex new_footer.tex new_stylesheet.sty
+# and then modify the file new_header.tex. See also section "Doxygen usage" for
+# information on how to generate the default header that doxygen normally uses.
#
-# Note: Only use a user-defined header if you know what you are doing! The
-# following commands have a special meaning inside the header: $title,
-# $datetime, $date, $doxygenversion, $projectname, $projectnumber,
-# $projectbrief, $projectlogo. Doxygen will replace $title with the empty
-# string, for the replacement values of the other commands the user is referred
-# to HTML_HEADER.
+# Note: Only use a user-defined header if you know what you are doing!
+# Note: The header is subject to change so you typically have to regenerate the
+# default header when upgrading to a newer version of doxygen. The following
+# commands have a special meaning inside the header (and footer): For a
+# description of the possible markers and block names see the documentation.
# This tag requires that the tag GENERATE_LATEX is set to YES.
LATEX_HEADER =
-# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for the
-# generated LaTeX document. The footer should contain everything after the last
-# chapter. If it is left blank doxygen will generate a standard footer. See
+# The LATEX_FOOTER tag can be used to specify a user-defined LaTeX footer for
+# the generated LaTeX document. The footer should contain everything after the
+# last chapter. If it is left blank doxygen will generate a standard footer. See
# LATEX_HEADER for more information on how to generate a default footer and what
-# special commands can be used inside the footer.
-#
-# Note: Only use a user-defined footer if you know what you are doing!
+# special commands can be used inside the footer. See also section "Doxygen
+# usage" for information on how to generate the default footer that doxygen
+# normally uses. Note: Only use a user-defined footer if you know what you are
+# doing!
# This tag requires that the tag GENERATE_LATEX is set to YES.
LATEX_FOOTER =
@@ -1756,18 +2028,26 @@ LATEX_EXTRA_FILES =
PDF_HYPERLINKS = NO
-# If the USE_PDFLATEX tag is set to YES, doxygen will use pdflatex to generate
-# the PDF file directly from the LaTeX files. Set this option to YES, to get a
-# higher quality PDF documentation.
+# If the USE_PDFLATEX tag is set to YES, doxygen will use the engine as
+# specified with LATEX_CMD_NAME to generate the PDF file directly from the LaTeX
+# files. Set this option to YES, to get a higher quality PDF documentation.
+#
+# See also section LATEX_CMD_NAME for selecting the engine.
# The default value is: YES.
# This tag requires that the tag GENERATE_LATEX is set to YES.
USE_PDFLATEX = NO
-# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \batchmode
-# command to the generated LaTeX files. This will instruct LaTeX to keep running
-# if errors occur, instead of asking the user for help. This option is also used
-# when generating formulas in HTML.
+# The LATEX_BATCHMODE tag signals the behavior of LaTeX in case of an error.
+# Possible values are: NO same as ERROR_STOP, YES same as BATCH, BATCH In batch
+# mode nothing is printed on the terminal, errors are scrolled as if <return> is
+# hit at every error; missing files that TeX tries to input or request from
+# keyboard input (\read on a not open input stream) cause the job to abort,
+# NON_STOP In nonstop mode the diagnostic message will appear on the terminal,
+# but there is no possibility of user interaction just like in batch mode,
+# SCROLL In scroll mode, TeX will stop only for missing files to input or if
+# keyboard input is necessary and ERROR_STOP In errorstop mode, TeX will stop at
+# each error, asking for user intervention.
# The default value is: NO.
# This tag requires that the tag GENERATE_LATEX is set to YES.
@@ -1788,14 +2068,6 @@ LATEX_HIDE_INDICES = NO
LATEX_BIB_STYLE = plain
-# If the LATEX_TIMESTAMP tag is set to YES then the footer of each generated
-# page will contain the date and time when the page was generated. Setting this
-# to NO can help when comparing the output of multiple runs.
-# The default value is: NO.
-# This tag requires that the tag GENERATE_LATEX is set to YES.
-
-LATEX_TIMESTAMP = NO
-
# The LATEX_EMOJI_DIRECTORY tag is used to specify the (relative or absolute)
# path from which the emoji images will be read. If a relative path is entered,
# it will be relative to the LATEX_OUTPUT directory. If left blank the
@@ -1860,6 +2132,14 @@ RTF_STYLESHEET_FILE =
RTF_EXTENSIONS_FILE =
+# The RTF_EXTRA_FILES tag can be used to specify one or more extra images or
+# other source files which should be copied to the RTF_OUTPUT output directory.
+# Note that the files will be copied as-is; there are no commands or markers
+# available.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_EXTRA_FILES =
+
#---------------------------------------------------------------------------
# Configuration options related to the man page output
#---------------------------------------------------------------------------
@@ -1961,7 +2241,7 @@ DOCBOOK_OUTPUT = docbook
#---------------------------------------------------------------------------
# If the GENERATE_AUTOGEN_DEF tag is set to YES, doxygen will generate an
-# AutoGen Definitions (see http://autogen.sourceforge.net/) file that captures
+# AutoGen Definitions (see https://autogen.sourceforge.net/) file that captures
# the structure of the code including all documentation. Note that this feature
# is still experimental and incomplete at the moment.
# The default value is: NO.
@@ -1969,6 +2249,32 @@ DOCBOOK_OUTPUT = docbook
GENERATE_AUTOGEN_DEF = NO
#---------------------------------------------------------------------------
+# Configuration options related to Sqlite3 output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_SQLITE3 tag is set to YES doxygen will generate a Sqlite3
+# database with symbols found by doxygen stored in tables.
+# The default value is: NO.
+
+GENERATE_SQLITE3 = NO
+
+# The SQLITE3_OUTPUT tag is used to specify where the Sqlite3 database will be
+# put. If a relative path is entered the value of OUTPUT_DIRECTORY will be put
+# in front of it.
+# The default directory is: sqlite3.
+# This tag requires that the tag GENERATE_SQLITE3 is set to YES.
+
+SQLITE3_OUTPUT = sqlite3
+
+# The SQLITE3_RECREATE_DB tag is set to YES, the existing doxygen_sqlite3.db
+# database file will be recreated with each doxygen run. If set to NO, doxygen
+# will warn if a database file is already found and not modify it.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_SQLITE3 is set to YES.
+
+SQLITE3_RECREATE_DB = YES
+
+#---------------------------------------------------------------------------
# Configuration options related to the Perl module output
#---------------------------------------------------------------------------
@@ -2042,7 +2348,8 @@ SEARCH_INCLUDES = NO
# The INCLUDE_PATH tag can be used to specify one or more directories that
# contain include files that are not input files but should be processed by the
-# preprocessor.
+# preprocessor. Note that the INCLUDE_PATH is not recursive, so the setting of
+# RECURSIVE has no effect here.
# This tag requires that the tag SEARCH_INCLUDES is set to YES.
INCLUDE_PATH =
@@ -2109,15 +2416,15 @@ TAGFILES =
GENERATE_TAGFILE =
-# If the ALLEXTERNALS tag is set to YES, all external class will be listed in
-# the class index. If set to NO, only the inherited external classes will be
-# listed.
+# If the ALLEXTERNALS tag is set to YES, all external classes and namespaces
+# will be listed in the class and namespace index. If set to NO, only the
+# inherited external classes will be listed.
# The default value is: NO.
ALLEXTERNALS = NO
# If the EXTERNAL_GROUPS tag is set to YES, all external groups will be listed
-# in the modules index. If set to NO, only the current project's groups will be
+# in the topic index. If set to NO, only the current project's groups will be
# listed.
# The default value is: YES.
@@ -2131,25 +2438,9 @@ EXTERNAL_GROUPS = YES
EXTERNAL_PAGES = YES
#---------------------------------------------------------------------------
-# Configuration options related to the dot tool
+# Configuration options related to diagram generator tools
#---------------------------------------------------------------------------
-# If the CLASS_DIAGRAMS tag is set to YES, doxygen will generate a class diagram
-# (in HTML and LaTeX) for classes with base or super classes. Setting the tag to
-# NO turns the diagrams off. Note that this option also works with HAVE_DOT
-# disabled, but it is recommended to install and use dot, since it yields more
-# powerful graphs.
-# The default value is: YES.
-
-CLASS_DIAGRAMS = YES
-
-# You can include diagrams made with dia in doxygen documentation. Doxygen will
-# then run dia to produce the diagram and insert it in the documentation. The
-# DIA_PATH tag allows you to specify the directory where the dia binary resides.
-# If left empty dia is assumed to be found in the default search path.
-
-DIA_PATH =
-
# If set to YES the inheritance and collaboration graphs will hide inheritance
# and usage relations if the target is undocumented or is not a class.
# The default value is: YES.
@@ -2158,7 +2449,7 @@ HIDE_UNDOC_RELATIONS = YES
# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
# available from the path. This tool is part of Graphviz (see:
-# http://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent
+# https://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent
# Bell Labs. The other options in this section have no effect if this option is
# set to NO
# The default value is: NO.
@@ -2175,49 +2466,77 @@ HAVE_DOT = NO
DOT_NUM_THREADS = 0
-# When you want a differently looking font in the dot files that doxygen
-# generates you can specify the font name using DOT_FONTNAME. You need to make
-# sure dot is able to find the font, which can be done by putting it in a
-# standard location or by setting the DOTFONTPATH environment variable or by
-# setting DOT_FONTPATH to the directory containing the font.
-# The default value is: Helvetica.
+# DOT_COMMON_ATTR is common attributes for nodes, edges and labels of
+# subgraphs. When you want a differently looking font in the dot files that
+# doxygen generates you can specify fontname, fontcolor and fontsize attributes.
+# For details please see <a href=https://graphviz.org/doc/info/attrs.html>Node,
+# Edge and Graph Attributes specification</a> You need to make sure dot is able
+# to find the font, which can be done by putting it in a standard location or by
+# setting the DOTFONTPATH environment variable or by setting DOT_FONTPATH to the
+# directory containing the font. Default graphviz fontsize is 14.
+# The default value is: fontname=Helvetica,fontsize=10.
# This tag requires that the tag HAVE_DOT is set to YES.
-DOT_FONTNAME =
+DOT_COMMON_ATTR = "fontname=Helvetica,fontsize=10"
-# The DOT_FONTSIZE tag can be used to set the size (in points) of the font of
-# dot graphs.
-# Minimum value: 4, maximum value: 24, default value: 10.
+# DOT_EDGE_ATTR is concatenated with DOT_COMMON_ATTR. For elegant style you can
+# add 'arrowhead=open, arrowtail=open, arrowsize=0.5'. <a
+# href=https://graphviz.org/doc/info/arrows.html>Complete documentation about
+# arrows shapes.</a>
+# The default value is: labelfontname=Helvetica,labelfontsize=10.
# This tag requires that the tag HAVE_DOT is set to YES.
-DOT_FONTSIZE = 10
+DOT_EDGE_ATTR = "labelfontname=Helvetica,labelfontsize=10"
-# By default doxygen will tell dot to use the default font as specified with
-# DOT_FONTNAME. If you specify a different font using DOT_FONTNAME you can set
-# the path where dot can find it using this tag.
+# DOT_NODE_ATTR is concatenated with DOT_COMMON_ATTR. For view without boxes
+# around nodes set 'shape=plain' or 'shape=plaintext' <a
+# href=https://www.graphviz.org/doc/info/shapes.html>Shapes specification</a>
+# The default value is: shape=box,height=0.2,width=0.4.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_NODE_ATTR = "shape=box,height=0.2,width=0.4"
+
+# You can set the path where dot can find font specified with fontname in
+# DOT_COMMON_ATTR and others dot attributes.
# This tag requires that the tag HAVE_DOT is set to YES.
DOT_FONTPATH =
-# If the CLASS_GRAPH tag is set to YES then doxygen will generate a graph for
-# each documented class showing the direct and indirect inheritance relations.
-# Setting this tag to YES will force the CLASS_DIAGRAMS tag to NO.
+# If the CLASS_GRAPH tag is set to YES or GRAPH or BUILTIN then doxygen will
+# generate a graph for each documented class showing the direct and indirect
+# inheritance relations. In case the CLASS_GRAPH tag is set to YES or GRAPH and
+# HAVE_DOT is enabled as well, then dot will be used to draw the graph. In case
+# the CLASS_GRAPH tag is set to YES and HAVE_DOT is disabled or if the
+# CLASS_GRAPH tag is set to BUILTIN, then the built-in generator will be used.
+# If the CLASS_GRAPH tag is set to TEXT the direct and indirect inheritance
+# relations will be shown as texts / links. Explicit enabling an inheritance
+# graph or choosing a different representation for an inheritance graph of a
+# specific class, can be accomplished by means of the command \inheritancegraph.
+# Disabling an inheritance graph can be accomplished by means of the command
+# \hideinheritancegraph.
+# Possible values are: NO, YES, TEXT, GRAPH and BUILTIN.
# The default value is: YES.
-# This tag requires that the tag HAVE_DOT is set to YES.
CLASS_GRAPH = YES
# If the COLLABORATION_GRAPH tag is set to YES then doxygen will generate a
# graph for each documented class showing the direct and indirect implementation
# dependencies (inheritance, containment, and class references variables) of the
-# class with other documented classes.
+# class with other documented classes. Explicit enabling a collaboration graph,
+# when COLLABORATION_GRAPH is set to NO, can be accomplished by means of the
+# command \collaborationgraph. Disabling a collaboration graph can be
+# accomplished by means of the command \hidecollaborationgraph.
# The default value is: YES.
# This tag requires that the tag HAVE_DOT is set to YES.
COLLABORATION_GRAPH = YES
# If the GROUP_GRAPHS tag is set to YES then doxygen will generate a graph for
-# groups, showing the direct groups dependencies.
+# groups, showing the direct groups dependencies. Explicit enabling a group
+# dependency graph, when GROUP_GRAPHS is set to NO, can be accomplished by means
+# of the command \groupgraph. Disabling a directory graph can be accomplished by
+# means of the command \hidegroupgraph. See also the chapter Grouping in the
+# manual.
# The default value is: YES.
# This tag requires that the tag HAVE_DOT is set to YES.
@@ -2240,10 +2559,32 @@ UML_LOOK = NO
# but if the number exceeds 15, the total amount of fields shown is limited to
# 10.
# Minimum value: 0, maximum value: 100, default value: 10.
-# This tag requires that the tag HAVE_DOT is set to YES.
+# This tag requires that the tag UML_LOOK is set to YES.
UML_LIMIT_NUM_FIELDS = 10
+# If the DOT_UML_DETAILS tag is set to NO, doxygen will show attributes and
+# methods without types and arguments in the UML graphs. If the DOT_UML_DETAILS
+# tag is set to YES, doxygen will add type and arguments for attributes and
+# methods in the UML graphs. If the DOT_UML_DETAILS tag is set to NONE, doxygen
+# will not generate fields with class member information in the UML graphs. The
+# class diagrams will look similar to the default class diagrams but using UML
+# notation for the relationships.
+# Possible values are: NO, YES and NONE.
+# The default value is: NO.
+# This tag requires that the tag UML_LOOK is set to YES.
+
+DOT_UML_DETAILS = NO
+
+# The DOT_WRAP_THRESHOLD tag can be used to set the maximum number of characters
+# to display on a single line. If the actual line length exceeds this threshold
+# significantly it will be wrapped across multiple lines. Some heuristics are
+# applied to avoid ugly line breaks.
+# Minimum value: 0, maximum value: 1000, default value: 17.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_WRAP_THRESHOLD = 17
+
# If the TEMPLATE_RELATIONS tag is set to YES then the inheritance and
# collaboration graphs will show the relations between templates and their
# instances.
@@ -2255,7 +2596,9 @@ TEMPLATE_RELATIONS = NO
# If the INCLUDE_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are set to
# YES then doxygen will generate a graph for each documented file showing the
# direct and indirect include dependencies of the file with other documented
-# files.
+# files. Explicit enabling an include graph, when INCLUDE_GRAPH is is set to NO,
+# can be accomplished by means of the command \includegraph. Disabling an
+# include graph can be accomplished by means of the command \hideincludegraph.
# The default value is: YES.
# This tag requires that the tag HAVE_DOT is set to YES.
@@ -2264,7 +2607,10 @@ INCLUDE_GRAPH = YES
# If the INCLUDED_BY_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are
# set to YES then doxygen will generate a graph for each documented file showing
# the direct and indirect include dependencies of the file with other documented
-# files.
+# files. Explicit enabling an included by graph, when INCLUDED_BY_GRAPH is set
+# to NO, can be accomplished by means of the command \includedbygraph. Disabling
+# an included by graph can be accomplished by means of the command
+# \hideincludedbygraph.
# The default value is: YES.
# This tag requires that the tag HAVE_DOT is set to YES.
@@ -2304,16 +2650,26 @@ GRAPHICAL_HIERARCHY = YES
# If the DIRECTORY_GRAPH tag is set to YES then doxygen will show the
# dependencies a directory has on other directories in a graphical way. The
# dependency relations are determined by the #include relations between the
-# files in the directories.
+# files in the directories. Explicit enabling a directory graph, when
+# DIRECTORY_GRAPH is set to NO, can be accomplished by means of the command
+# \directorygraph. Disabling a directory graph can be accomplished by means of
+# the command \hidedirectorygraph.
# The default value is: YES.
# This tag requires that the tag HAVE_DOT is set to YES.
DIRECTORY_GRAPH = YES
+# The DIR_GRAPH_MAX_DEPTH tag can be used to limit the maximum number of levels
+# of child directories generated in directory dependency graphs by dot.
+# Minimum value: 1, maximum value: 25, default value: 1.
+# This tag requires that the tag DIRECTORY_GRAPH is set to YES.
+
+DIR_GRAPH_MAX_DEPTH = 1
+
# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
# generated by dot. For an explanation of the image formats see the section
# output formats in the documentation of the dot tool (Graphviz (see:
-# http://www.graphviz.org/)).
+# https://www.graphviz.org/)).
# Note: If you choose svg you need to set HTML_FILE_EXTENSION to xhtml in order
# to make the SVG files visible in IE 9+ (other browsers do not have this
# requirement).
@@ -2350,11 +2706,12 @@ DOT_PATH =
DOTFILE_DIRS =
-# The MSCFILE_DIRS tag can be used to specify one or more directories that
-# contain msc files that are included in the documentation (see the \mscfile
-# command).
+# You can include diagrams made with dia in doxygen documentation. Doxygen will
+# then run dia to produce the diagram and insert it in the documentation. The
+# DIA_PATH tag allows you to specify the directory where the dia binary resides.
+# If left empty dia is assumed to be found in the default search path.
-MSCFILE_DIRS =
+DIA_PATH =
# The DIAFILE_DIRS tag can be used to specify one or more directories that
# contain dia files that are included in the documentation (see the \diafile
@@ -2363,10 +2720,10 @@ MSCFILE_DIRS =
DIAFILE_DIRS =
# When using plantuml, the PLANTUML_JAR_PATH tag should be used to specify the
-# path where java can find the plantuml.jar file. If left blank, it is assumed
-# PlantUML is not used or called during a preprocessing step. Doxygen will
-# generate a warning when it encounters a \startuml command in this case and
-# will not generate output for the diagram.
+# path where java can find the plantuml.jar file or to the filename of jar file
+# to be used. If left blank, it is assumed PlantUML is not used or called during
+# a preprocessing step. Doxygen will generate a warning when it encounters a
+# \startuml command in this case and will not generate output for the diagram.
PLANTUML_JAR_PATH =
@@ -2383,7 +2740,7 @@ PLANTUML_INCLUDE_PATH =
# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of nodes
# that will be shown in the graph. If the number of nodes in a graph becomes
# larger than this value, doxygen will truncate the graph, which is visualized
-# by representing a node as a red box. Note that doxygen if the number of direct
+# by representing a node as a red box. Note that if the number of direct
# children of the root node in a graph is already larger than
# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note that
# the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.
@@ -2404,18 +2761,6 @@ DOT_GRAPH_MAX_NODES = 50
MAX_DOT_GRAPH_DEPTH = 0
-# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
-# background. This is disabled by default, because dot on Windows does not seem
-# to support this out of the box.
-#
-# Warning: Depending on the platform used, enabling this option may lead to
-# badly anti-aliased labels on the edges of a graph (i.e. they become hard to
-# read).
-# The default value is: NO.
-# This tag requires that the tag HAVE_DOT is set to YES.
-
-DOT_TRANSPARENT = NO
-
# Set the DOT_MULTI_TARGETS tag to YES to allow dot to generate multiple output
# files in one run (i.e. multiple -o and -T options on the command line). This
# makes dot run faster, but since only newer versions of dot (>1.8.10) support
@@ -2428,14 +2773,34 @@ DOT_MULTI_TARGETS = NO
# If the GENERATE_LEGEND tag is set to YES doxygen will generate a legend page
# explaining the meaning of the various boxes and arrows in the dot generated
# graphs.
+# Note: This tag requires that UML_LOOK isn't set, i.e. the doxygen internal
+# graphical representation for inheritance and collaboration diagrams is used.
# The default value is: YES.
# This tag requires that the tag HAVE_DOT is set to YES.
GENERATE_LEGEND = YES
-# If the DOT_CLEANUP tag is set to YES, doxygen will remove the intermediate dot
+# If the DOT_CLEANUP tag is set to YES, doxygen will remove the intermediate
# files that are used to generate the various graphs.
+#
+# Note: This setting is not only used for dot files but also for msc temporary
+# files.
# The default value is: YES.
-# This tag requires that the tag HAVE_DOT is set to YES.
DOT_CLEANUP = YES
+
+# You can define message sequence charts within doxygen comments using the \msc
+# command. If the MSCGEN_TOOL tag is left empty (the default), then doxygen will
+# use a built-in version of mscgen tool to produce the charts. Alternatively,
+# the MSCGEN_TOOL tag can also specify the name an external tool. For instance,
+# specifying prog as the value, doxygen will call the tool as prog -T
+# <outfile_format> -o <outputfile> <inputfile>. The external tool should support
+# output file formats "png", "eps", "svg", and "ismap".
+
+MSCGEN_TOOL =
+
+# The MSCFILE_DIRS tag can be used to specify one or more directories that
+# contain msc files that are included in the documentation (see the \mscfile
+# command).
+
+MSCFILE_DIRS =
diff --git a/include/Makefile.am b/include/Makefile.am
index 2abaf49..a41f53d 100644
--- a/include/Makefile.am
+++ b/include/Makefile.am
@@ -23,9 +23,11 @@ nobase_include_HEADERS = \
libimobiledevice/diagnostics_relay.h \
libimobiledevice/debugserver.h \
libimobiledevice/syslog_relay.h \
+ libimobiledevice/ostrace.h \
libimobiledevice/mobileactivation.h \
libimobiledevice/preboard.h \
libimobiledevice/companion_proxy.h \
libimobiledevice/reverse_proxy.h \
+ libimobiledevice/bt_packet_logger.h \
libimobiledevice/property_list_service.h \
libimobiledevice/service.h
diff --git a/include/libimobiledevice/afc.h b/include/libimobiledevice/afc.h
index 4ad3dbd..3dcb5da 100644
--- a/include/libimobiledevice/afc.h
+++ b/include/libimobiledevice/afc.h
@@ -145,6 +145,20 @@ LIBIMOBILEDEVICE_API afc_error_t afc_client_free(afc_client_t client);
LIBIMOBILEDEVICE_API afc_error_t afc_get_device_info(afc_client_t client, char ***device_information);
/**
+ * Get device information for a connected client. The device information
+ * returned is the device model as well as the free space, the total capacity
+ * and blocksize on the accessed disk partition.
+ *
+ * @param client The client to get device info for.
+ * @param device_information A pointer to a plist_t that will be populated
+ * with key-value pairs (dictionary) representing the device’s
+ * storage and model information. Free with plist_free().
+ *
+ * @return AFC_E_SUCCESS on success or an AFC_E_* error value.
+ */
+LIBIMOBILEDEVICE_API afc_error_t afc_get_device_info_plist(afc_client_t client, plist_t *device_information);
+
+/**
* Gets a directory listing of the directory requested.
*
* @param client The client to get a directory listing from.
@@ -163,7 +177,7 @@ LIBIMOBILEDEVICE_API afc_error_t afc_read_directory(afc_client_t client, const c
* @param client The client to use to get the information of the file.
* @param path The fully-qualified path to the file.
* @param file_information Pointer to a buffer that will be filled with a
- * NULL-terminated list of strings with the file information. Set to NULL
+ * NULL-terminated list of strings with the file attributes. Set to NULL
* before calling this function. Free with afc_dictionary_free().
*
* @return AFC_E_SUCCESS on success or an AFC_E_* error value.
@@ -171,6 +185,19 @@ LIBIMOBILEDEVICE_API afc_error_t afc_read_directory(afc_client_t client, const c
LIBIMOBILEDEVICE_API afc_error_t afc_get_file_info(afc_client_t client, const char *path, char ***file_information);
/**
+ * Gets information about a specific file.
+ *
+ * @param client The client to use to get the information of the file.
+ * @param path The fully-qualified path to the file.
+ * @param file_information A pointer to a plist_t that will be populated
+ * with key-value pairs (dictionary) representing the file attributes.
+ * Free with plist_free().
+ *
+ * @return AFC_E_SUCCESS on success or an AFC_E_* error value.
+ */
+LIBIMOBILEDEVICE_API afc_error_t afc_get_file_info_plist(afc_client_t client, const char *path, plist_t *file_information);
+
+/**
* Opens a file on the device.
*
* @param client The client to use to open the file.
diff --git a/include/libimobiledevice/libimobiledevice.h b/include/libimobiledevice/libimobiledevice.h
index a9d270b..bc57778 100644
--- a/include/libimobiledevice/libimobiledevice.h
+++ b/include/libimobiledevice/libimobiledevice.h
@@ -31,10 +31,13 @@ extern "C" {
#endif
#include <stdint.h>
-#include <sys/types.h>
-#include <sys/stat.h>
#include <plist/plist.h>
+#if defined(_MSC_VER)
+#include <basetsd.h>
+typedef SSIZE_T ssize_t;
+#endif
+
#ifndef LIBIMOBILEDEVICE_API
#ifdef LIBIMOBILEDEVICE_STATIC
#define LIBIMOBILEDEVICE_API
@@ -401,12 +404,38 @@ LIBIMOBILEDEVICE_API idevice_error_t idevice_get_handle(idevice_t device, uint32
LIBIMOBILEDEVICE_API idevice_error_t idevice_get_udid(idevice_t device, char **udid);
/**
+ * Returns the device ProductVersion in numerical form, where "X.Y.Z"
+ * will be returned as (X << 16) | (Y << 8) | Z .
+ * Use IDEVICE_DEVICE_VERSION macro for easy version comparison.
+ * @see IDEVICE_DEVICE_VERSION
+ *
+ * @param client Initialized device client
+ *
+ * @return A numerical representation of the X.Y.Z ProductVersion string
+ * or 0 if the version cannot be retrieved.
+ */
+LIBIMOBILEDEVICE_API unsigned int idevice_get_device_version(idevice_t device);
+
+/**
+ * Gets a readable error string for a given idevice error code.
+ *
+ * @param err An idevice error code
+ *
+ * @return A readable error string
+ */
+LIBIMOBILEDEVICE_API const char* idevice_strerror(idevice_error_t err);
+
+/**
* Returns a static string of the libimobiledevice version.
*
* @return The libimobiledevice version as static ascii string
*/
LIBIMOBILEDEVICE_API const char* libimobiledevice_version();
+/* macros */
+/** Helper macro to get a numerical representation of a product version tuple */
+#define IDEVICE_DEVICE_VERSION(maj, min, patch) ((((maj) & 0xFF) << 16) | (((min) & 0xFF) << 8) | ((patch) & 0xFF))
+
#ifdef __cplusplus
}
#endif
diff --git a/include/libimobiledevice/ostrace.h b/include/libimobiledevice/ostrace.h
new file mode 100644
index 0000000..f083ba7
--- /dev/null
+++ b/include/libimobiledevice/ostrace.h
@@ -0,0 +1,198 @@
+/**
+ * @file libimobiledevice/ostrace.h
+ * @brief System log and tracing capabilities.
+ * \internal
+ *
+ * Copyright (c) 2020-2025 Nikias Bassen, All Rights Reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef OSTRACE_H
+#define OSTRACE_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <libimobiledevice/libimobiledevice.h>
+#include <libimobiledevice/lockdown.h>
+
+/** Service identifier passed to lockdownd_start_service() to start the os trace relay service */
+#define OSTRACE_SERVICE_NAME "com.apple.os_trace_relay"
+
+/** Error Codes */
+typedef enum {
+ OSTRACE_E_SUCCESS = 0,
+ OSTRACE_E_INVALID_ARG = -1,
+ OSTRACE_E_MUX_ERROR = -2,
+ OSTRACE_E_SSL_ERROR = -3,
+ OSTRACE_E_NOT_ENOUGH_DATA = -4,
+ OSTRACE_E_TIMEOUT = -5,
+ OSTRACE_E_PLIST_ERROR = -6,
+ OSTRACE_E_REQUEST_FAILED = -7,
+ OSTRACE_E_UNKNOWN_ERROR = -256
+} ostrace_error_t;
+
+typedef struct ostrace_client_private ostrace_client_private; /**< \private */
+typedef ostrace_client_private *ostrace_client_t; /**< The client handle. */
+
+#pragma pack(push,1)
+struct ostrace_packet_header_t {
+ uint8_t marker;
+ uint32_t type;
+ uint32_t header_size; // 0x81
+ uint32_t pid;
+ uint64_t procid; // == pid
+ unsigned char procuuid[16]; // procuuid
+ uint16_t procpath_len; // path to process
+ uint64_t aid; // activity id, usually 0
+ uint64_t paid; // (parent?) activity id, usually 0
+ uint64_t time_sec; // tv.tv_sec 64 bit
+ uint32_t time_usec; // tv.usec 32 bit
+ uint8_t unk06;
+ uint8_t level; // Notice=0, Info=0x01, Debug=0x02, Error=0x10, Fault=0x11
+ uint8_t unk07;
+ uint8_t unk08;
+ uint8_t unk09;
+ uint8_t unk10;
+ uint8_t unk11;
+ uint8_t unk12;
+ uint64_t timestamp; // ?
+ uint32_t thread_id;
+ uint32_t unk13; // 0
+ unsigned char imageuuid[16]; // framework/dylib uuid
+ uint16_t imagepath_len; // framework/dylib
+ uint32_t message_len; // actual log message
+ uint32_t offset; // offset for like timestamp or sth
+ uint16_t subsystem_len; // "subsystem"
+ uint16_t unk14;
+ uint16_t category_len; // "category"
+ uint16_t unk15;
+ uint32_t unk16; // 0
+};
+#pragma pack(pop)
+
+/** Receives unparsed ostrace data from the ostrace service */
+typedef void (*ostrace_activity_cb_t)(const void* buf, size_t len, void *user_data);
+
+/** Receives archive data from the ostrace service */
+typedef int (*ostrace_archive_write_cb_t)(const void* buf, size_t len, void *user_data);
+
+/* Interface */
+
+/**
+ * Connects to the os_trace_relay service on the specified device.
+ *
+ * @param device The device to connect to.
+ * @param service The service descriptor returned by lockdownd_start_service.
+ * @param client Pointer that will point to a newly allocated
+ * ostrace_client_t upon successful return. Must be freed using
+ * ostrace_client_free() after use.
+ *
+ * @return OSTRACE_E_SUCCESS on success, OSTRACE_E_INVALID_ARG when
+ * client is NULL, or an OSTRACE_E_* error code otherwise.
+ */
+LIBIMOBILEDEVICE_API ostrace_error_t ostrace_client_new(idevice_t device, lockdownd_service_descriptor_t service, ostrace_client_t * client);
+
+/**
+ * Starts a new os_trace_relay service on the specified device and connects to it.
+ *
+ * @param device The device to connect to.
+ * @param client Pointer that will point to a newly allocated
+ * ostrace_client_t upon successful return. Must be freed using
+ * ostrace_client_free() after use.
+ * @param label The label to use for communication. Usually the program name.
+ * Pass NULL to disable sending the label in requests to lockdownd.
+ *
+ * @return OSTRACE_E_SUCCESS on success, or an OSTRACE_E_* error code otherwise.
+ */
+LIBIMOBILEDEVICE_API ostrace_error_t ostrace_client_start_service(idevice_t device, ostrace_client_t * client, const char* label);
+
+/**
+ * Disconnects a ostrace client from the device and frees up the
+ * ostrace client data.
+ *
+ * @param client The ostrace client to disconnect and free.
+ *
+ * @return OSTRACE_E_SUCCESS on success, OSTRACE_E_INVALID_ARG when
+ * client is NULL, or an OSTRACE_E_* error code otherwise.
+ */
+LIBIMOBILEDEVICE_API ostrace_error_t ostrace_client_free(ostrace_client_t client);
+
+/**
+ * Starts capturing OS trace activity data of the device using a callback.
+ *
+ * Use ostrace_stop_activity() to stop receiving the ostrace.
+ *
+ * @param client The ostrace client to use
+ * @param options Options dictionary to pass to StartActivity request.
+ * Valid options are MessageFilter (PLIST_INT, default 65535),
+ * Pid (PLIST_INT, default -1), and StreamFlags (PLIST_INT, default 60)
+ * @param callback Callback to receive data from ostrace.
+ * @param user_data Custom pointer passed to the callback function.
+ * @param user_data_free_func Function pointer that will be called when the
+ * activity is stopped to release user_data. Can be NULL for none.
+ *
+ * @return OSTRACE_E_SUCCESS on success,
+ * OSTRACE_E_INVALID_ARG when one or more parameters are
+ * invalid or OSTRACE_E_UNKNOWN_ERROR when an unspecified
+ * error occurs or an ostrace activity has already been started.
+ */
+LIBIMOBILEDEVICE_API ostrace_error_t ostrace_start_activity(ostrace_client_t client, plist_t options, ostrace_activity_cb_t callback, void* user_data);
+
+/**
+ * Stops the ostrace activity.
+ *
+ * Use ostrace_start_activity() to start receiving OS trace data.
+ *
+ * @param client The ostrace client to use
+ *
+ * @return OSTRACE_E_SUCCESS on success,
+ * OSTRACE_E_INVALID_ARG when one or more parameters are
+ * invalid or OSTRACE_E_UNKNOWN_ERROR when an unspecified
+ * error occurs or an ostrace activity has already been started.
+ */
+LIBIMOBILEDEVICE_API ostrace_error_t ostrace_stop_activity(ostrace_client_t client);
+
+/**
+ * Returns a dictionary with all currently running processes on the device.
+ *
+ * @param client The ostrace client to use
+ * @param list Pointer that will receive an allocated PLIST_DICT structure with the process data
+ *
+ * @return OSTRACE_E_SUCCESS on success, or an OSTRACE_E_* error code otherwise
+ */
+LIBIMOBILEDEVICE_API ostrace_error_t ostrace_get_pid_list(ostrace_client_t client, plist_t* list);
+
+/**
+ * Creates a syslog archive.
+ *
+ * @note The device will close the connection once the transfer is complete. The client
+ * is not usable after that anymore and must be disposed with ostrace_client_free.
+ *
+ * @param client The ostrace client to use
+ * @param options A dictionary with options for the request.
+ * Valid parameters are StartTime (PLIST_UINT), SizeLimit (PLIST_UINT), and AgeLimit (PLIST_UINT).
+ *
+ * @return OSTRACE_E_SUCCESS on success, or an OSTRACE_E_* error code otherwise
+ */
+LIBIMOBILEDEVICE_API ostrace_error_t ostrace_create_archive(ostrace_client_t client, plist_t options, ostrace_archive_write_cb_t callback, void* user_data);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/m4/ax_python_devel.m4 b/m4/ax_python_devel.m4
index 44dbd83..935056c 100644
--- a/m4/ax_python_devel.m4
+++ b/m4/ax_python_devel.m4
@@ -4,7 +4,7 @@
#
# SYNOPSIS
#
-# AX_PYTHON_DEVEL([version])
+# AX_PYTHON_DEVEL([version[,optional]])
#
# DESCRIPTION
#
@@ -23,6 +23,11 @@
# version number. Don't use "PYTHON_VERSION" for this: that environment
# variable is declared as precious and thus reserved for the end-user.
#
+# By default this will fail if it does not detect a development version of
+# python. If you want it to continue, set optional to true, like
+# AX_PYTHON_DEVEL([], [true]). The ax_python_devel_found variable will be
+# "no" if it fails.
+#
# This macro should work for all versions of Python >= 2.1.0. As an end
# user, you can disable the check for the python version by setting the
# PYTHON_NOVERSIONCHECK environment variable to something else than the
@@ -67,10 +72,18 @@
# modified version of the Autoconf Macro, you may extend this special
# exception to the GPL to apply to your modified version as well.
-#serial 21
+#serial 37
AU_ALIAS([AC_PYTHON_DEVEL], [AX_PYTHON_DEVEL])
AC_DEFUN([AX_PYTHON_DEVEL],[
+ # Get whether it's optional
+ if test -z "$2"; then
+ ax_python_devel_optional=false
+ else
+ ax_python_devel_optional=$2
+ fi
+ ax_python_devel_found=yes
+
#
# Allow the use of a (user set) custom python version
#
@@ -81,21 +94,26 @@ AC_DEFUN([AX_PYTHON_DEVEL],[
AC_PATH_PROG([PYTHON],[python[$PYTHON_VERSION]])
if test -z "$PYTHON"; then
- AC_MSG_ERROR([Cannot find python$PYTHON_VERSION in your system path])
+ AC_MSG_WARN([Cannot find python$PYTHON_VERSION in your system path])
+ if ! $ax_python_devel_optional; then
+ AC_MSG_ERROR([Giving up, python development not available])
+ fi
+ ax_python_devel_found=no
PYTHON_VERSION=""
fi
- #
- # Check for a version of Python >= 2.1.0
- #
- AC_MSG_CHECKING([for a version of Python >= '2.1.0'])
- ac_supports_python_ver=`$PYTHON -c "import sys; \
+ if test $ax_python_devel_found = yes; then
+ #
+ # Check for a version of Python >= 2.1.0
+ #
+ AC_MSG_CHECKING([for a version of Python >= '2.1.0'])
+ ac_supports_python_ver=`$PYTHON -c "import sys; \
ver = sys.version.split ()[[0]]; \
print (ver >= '2.1.0')"`
- if test "$ac_supports_python_ver" != "True"; then
+ if test "$ac_supports_python_ver" != "True"; then
if test -z "$PYTHON_NOVERSIONCHECK"; then
AC_MSG_RESULT([no])
- AC_MSG_FAILURE([
+ AC_MSG_WARN([
This version of the AC@&t@_PYTHON_DEVEL macro
doesn't work properly with versions of Python before
2.1.0. You may need to re-run configure, setting the
@@ -104,58 +122,119 @@ PYTHON_EXTRA_LIBS and PYTHON_EXTRA_LDFLAGS by hand.
Moreover, to disable this check, set PYTHON_NOVERSIONCHECK
to something else than an empty string.
])
+ if ! $ax_python_devel_optional; then
+ AC_MSG_FAILURE([Giving up])
+ fi
+ ax_python_devel_found=no
+ PYTHON_VERSION=""
else
AC_MSG_RESULT([skip at user request])
fi
- else
+ else
AC_MSG_RESULT([yes])
+ fi
fi
- #
- # if the macro parameter ``version'' is set, honour it
- #
- if test -n "$1"; then
+ if test $ax_python_devel_found = yes; then
+ #
+ # If the macro parameter ``version'' is set, honour it.
+ # A Python shim class, VPy, is used to implement correct version comparisons via
+ # string expressions, since e.g. a naive textual ">= 2.7.3" won't work for
+ # Python 2.7.10 (the ".1" being evaluated as less than ".3").
+ #
+ if test -n "$1"; then
AC_MSG_CHECKING([for a version of Python $1])
- ac_supports_python_ver=`$PYTHON -c "import sys; \
- ver = sys.version.split ()[[0]]; \
+ cat << EOF > ax_python_devel_vpy.py
+class VPy:
+ def vtup(self, s):
+ return tuple(map(int, s.strip().replace("rc", ".").split(".")))
+ def __init__(self):
+ import sys
+ self.vpy = tuple(sys.version_info)[[:3]]
+ def __eq__(self, s):
+ return self.vpy == self.vtup(s)
+ def __ne__(self, s):
+ return self.vpy != self.vtup(s)
+ def __lt__(self, s):
+ return self.vpy < self.vtup(s)
+ def __gt__(self, s):
+ return self.vpy > self.vtup(s)
+ def __le__(self, s):
+ return self.vpy <= self.vtup(s)
+ def __ge__(self, s):
+ return self.vpy >= self.vtup(s)
+EOF
+ ac_supports_python_ver=`$PYTHON -c "import ax_python_devel_vpy; \
+ ver = ax_python_devel_vpy.VPy(); \
print (ver $1)"`
+ rm -rf ax_python_devel_vpy*.py* __pycache__/ax_python_devel_vpy*.py*
if test "$ac_supports_python_ver" = "True"; then
- AC_MSG_RESULT([yes])
+ AC_MSG_RESULT([yes])
else
AC_MSG_RESULT([no])
- AC_MSG_ERROR([this package requires Python $1.
+ AC_MSG_WARN([this package requires Python $1.
If you have it installed, but it isn't the default Python
interpreter in your system path, please pass the PYTHON_VERSION
variable to configure. See ``configure --help'' for reference.
])
+ if ! $ax_python_devel_optional; then
+ AC_MSG_ERROR([Giving up])
+ fi
+ ax_python_devel_found=no
PYTHON_VERSION=""
fi
+ fi
fi
- #
- # Check if you have distutils, else fail
- #
- AC_MSG_CHECKING([for the distutils Python package])
- ac_distutils_result=`$PYTHON -c "import distutils" 2>&1`
- if test $? -eq 0; then
+ if test $ax_python_devel_found = yes; then
+ #
+ # Check if you have distutils, else fail
+ #
+ AC_MSG_CHECKING([for the sysconfig Python package])
+ ac_sysconfig_result=`$PYTHON -c "import sysconfig" 2>&1`
+ if test $? -eq 0; then
AC_MSG_RESULT([yes])
- else
+ IMPORT_SYSCONFIG="import sysconfig"
+ else
AC_MSG_RESULT([no])
- AC_MSG_ERROR([cannot import Python module "distutils".
+
+ AC_MSG_CHECKING([for the distutils Python package])
+ ac_sysconfig_result=`$PYTHON -c "from distutils import sysconfig" 2>&1`
+ if test $? -eq 0; then
+ AC_MSG_RESULT([yes])
+ IMPORT_SYSCONFIG="from distutils import sysconfig"
+ else
+ AC_MSG_WARN([cannot import Python module "distutils".
Please check your Python installation. The error was:
-$ac_distutils_result])
- PYTHON_VERSION=""
+$ac_sysconfig_result])
+ if ! $ax_python_devel_optional; then
+ AC_MSG_ERROR([Giving up])
+ fi
+ ax_python_devel_found=no
+ PYTHON_VERSION=""
+ fi
+ fi
fi
- #
- # Check for Python include path
- #
- AC_MSG_CHECKING([for Python include path])
- if test -z "$PYTHON_CPPFLAGS"; then
- python_path=`$PYTHON -c "import distutils.sysconfig; \
- print (distutils.sysconfig.get_python_inc ());"`
- plat_python_path=`$PYTHON -c "import distutils.sysconfig; \
- print (distutils.sysconfig.get_python_inc (plat_specific=1));"`
+ if test $ax_python_devel_found = yes; then
+ #
+ # Check for Python include path
+ #
+ AC_MSG_CHECKING([for Python include path])
+ if test -z "$PYTHON_CPPFLAGS"; then
+ if test "$IMPORT_SYSCONFIG" = "import sysconfig"; then
+ # sysconfig module has different functions
+ python_path=`$PYTHON -c "$IMPORT_SYSCONFIG; \
+ print (sysconfig.get_path ('include'));"`
+ plat_python_path=`$PYTHON -c "$IMPORT_SYSCONFIG; \
+ print (sysconfig.get_path ('platinclude'));"`
+ else
+ # old distutils way
+ python_path=`$PYTHON -c "$IMPORT_SYSCONFIG; \
+ print (sysconfig.get_python_inc ());"`
+ plat_python_path=`$PYTHON -c "$IMPORT_SYSCONFIG; \
+ print (sysconfig.get_python_inc (plat_specific=1));"`
+ fi
if test -n "${python_path}"; then
if test "${plat_python_path}" != "${python_path}"; then
python_path="-I$python_path -I$plat_python_path"
@@ -164,22 +243,22 @@ $ac_distutils_result])
fi
fi
PYTHON_CPPFLAGS=$python_path
- fi
- AC_MSG_RESULT([$PYTHON_CPPFLAGS])
- AC_SUBST([PYTHON_CPPFLAGS])
+ fi
+ AC_MSG_RESULT([$PYTHON_CPPFLAGS])
+ AC_SUBST([PYTHON_CPPFLAGS])
- #
- # Check for Python library path
- #
- AC_MSG_CHECKING([for Python library path])
- if test -z "$PYTHON_LIBS"; then
+ #
+ # Check for Python library path
+ #
+ AC_MSG_CHECKING([for Python library path])
+ if test -z "$PYTHON_LIBS"; then
# (makes two attempts to ensure we've got a version number
# from the interpreter)
ac_python_version=`cat<<EOD | $PYTHON -
# join all versioning strings, on some systems
# major/minor numbers could be in different list elements
-from distutils.sysconfig import *
+from sysconfig import *
e = get_config_var('VERSION')
if e is not None:
print(e)
@@ -190,7 +269,7 @@ EOD`
ac_python_version=$PYTHON_VERSION
else
ac_python_version=`$PYTHON -c "import sys; \
- print (sys.version[[:3]])"`
+ print ("%d.%d" % sys.version_info[[:2]])"`
fi
fi
@@ -202,8 +281,8 @@ EOD`
ac_python_libdir=`cat<<EOD | $PYTHON -
# There should be only one
-import distutils.sysconfig
-e = distutils.sysconfig.get_config_var('LIBDIR')
+$IMPORT_SYSCONFIG
+e = sysconfig.get_config_var('LIBDIR')
if e is not None:
print (e)
EOD`
@@ -211,8 +290,8 @@ EOD`
# Now, for the library:
ac_python_library=`cat<<EOD | $PYTHON -
-import distutils.sysconfig
-c = distutils.sysconfig.get_config_vars()
+$IMPORT_SYSCONFIG
+c = sysconfig.get_config_vars()
if 'LDVERSION' in c:
print ('python'+c[['LDVERSION']])
else:
@@ -231,83 +310,140 @@ EOD`
else
# old way: use libpython from python_configdir
ac_python_libdir=`$PYTHON -c \
- "from distutils.sysconfig import get_python_lib as f; \
+ "from sysconfig import get_python_lib as f; \
import os; \
print (os.path.join(f(plat_specific=1, standard_lib=1), 'config'));"`
PYTHON_LIBS="-L$ac_python_libdir -lpython$ac_python_version"
fi
- if test -z "PYTHON_LIBS"; then
- AC_MSG_ERROR([
+ if test -z "$PYTHON_LIBS"; then
+ AC_MSG_WARN([
Cannot determine location of your Python DSO. Please check it was installed with
dynamic libraries enabled, or try setting PYTHON_LIBS by hand.
])
+ if ! $ax_python_devel_optional; then
+ AC_MSG_ERROR([Giving up])
+ fi
+ ax_python_devel_found=no
+ PYTHON_VERSION=""
fi
+ fi
fi
- AC_MSG_RESULT([$PYTHON_LIBS])
- AC_SUBST([PYTHON_LIBS])
- #
- # Check for site packages
- #
- AC_MSG_CHECKING([for Python site-packages path])
- if test -z "$PYTHON_SITE_PKG"; then
- PYTHON_SITE_PKG=`$PYTHON -c "import distutils.sysconfig; \
- print (distutils.sysconfig.get_python_lib(0,0));"`
- fi
- AC_MSG_RESULT([$PYTHON_SITE_PKG])
- AC_SUBST([PYTHON_SITE_PKG])
+ if test $ax_python_devel_found = yes; then
+ AC_MSG_RESULT([$PYTHON_LIBS])
+ AC_SUBST([PYTHON_LIBS])
- #
- # libraries which must be linked in when embedding
- #
- AC_MSG_CHECKING(python extra libraries)
- if test -z "$PYTHON_EXTRA_LIBS"; then
- PYTHON_EXTRA_LIBS=`$PYTHON -c "import distutils.sysconfig; \
- conf = distutils.sysconfig.get_config_var; \
+ #
+ # Check for site packages
+ #
+ AC_MSG_CHECKING([for Python site-packages path])
+ if test -z "$PYTHON_SITE_PKG"; then
+ if test "$IMPORT_SYSCONFIG" = "import sysconfig"; then
+ PYTHON_SITE_PKG=`$PYTHON -c "
+$IMPORT_SYSCONFIG;
+if hasattr(sysconfig, 'get_default_scheme'):
+ scheme = sysconfig.get_default_scheme()
+else:
+ scheme = sysconfig._get_default_scheme()
+if scheme == 'posix_local':
+ # Debian's default scheme installs to /usr/local/ but we want to find headers in /usr/
+ scheme = 'posix_prefix'
+prefix = '$prefix'
+if prefix == 'NONE':
+ prefix = '$ac_default_prefix'
+sitedir = sysconfig.get_path('purelib', scheme, vars={'base': prefix})
+print(sitedir)"`
+ else
+ # distutils.sysconfig way
+ PYTHON_SITE_PKG=`$PYTHON -c "$IMPORT_SYSCONFIG; \
+ print (sysconfig.get_python_lib(0,0));"`
+ fi
+ fi
+ AC_MSG_RESULT([$PYTHON_SITE_PKG])
+ AC_SUBST([PYTHON_SITE_PKG])
+
+ #
+ # Check for platform-specific site packages
+ #
+ AC_MSG_CHECKING([for Python platform specific site-packages path])
+ if test -z "$PYTHON_PLATFORM_SITE_PKG"; then
+ if test "$IMPORT_SYSCONFIG" = "import sysconfig"; then
+ PYTHON_PLATFORM_SITE_PKG=`$PYTHON -c "
+$IMPORT_SYSCONFIG;
+if hasattr(sysconfig, 'get_default_scheme'):
+ scheme = sysconfig.get_default_scheme()
+else:
+ scheme = sysconfig._get_default_scheme()
+if scheme == 'posix_local':
+ # Debian's default scheme installs to /usr/local/ but we want to find headers in /usr/
+ scheme = 'posix_prefix'
+prefix = '$prefix'
+if prefix == 'NONE':
+ prefix = '$ac_default_prefix'
+sitedir = sysconfig.get_path('platlib', scheme, vars={'platbase': prefix})
+print(sitedir)"`
+ else
+ # distutils.sysconfig way
+ PYTHON_PLATFORM_SITE_PKG=`$PYTHON -c "$IMPORT_SYSCONFIG; \
+ print (sysconfig.get_python_lib(1,0));"`
+ fi
+ fi
+ AC_MSG_RESULT([$PYTHON_PLATFORM_SITE_PKG])
+ AC_SUBST([PYTHON_PLATFORM_SITE_PKG])
+
+ #
+ # libraries which must be linked in when embedding
+ #
+ AC_MSG_CHECKING(python extra libraries)
+ if test -z "$PYTHON_EXTRA_LIBS"; then
+ PYTHON_EXTRA_LIBS=`$PYTHON -c "$IMPORT_SYSCONFIG; \
+ conf = sysconfig.get_config_var; \
print (conf('LIBS') + ' ' + conf('SYSLIBS'))"`
- fi
- AC_MSG_RESULT([$PYTHON_EXTRA_LIBS])
- AC_SUBST(PYTHON_EXTRA_LIBS)
+ fi
+ AC_MSG_RESULT([$PYTHON_EXTRA_LIBS])
+ AC_SUBST(PYTHON_EXTRA_LIBS)
- #
- # linking flags needed when embedding
- #
- AC_MSG_CHECKING(python extra linking flags)
- if test -z "$PYTHON_EXTRA_LDFLAGS"; then
- PYTHON_EXTRA_LDFLAGS=`$PYTHON -c "import distutils.sysconfig; \
- conf = distutils.sysconfig.get_config_var; \
+ #
+ # linking flags needed when embedding
+ #
+ AC_MSG_CHECKING(python extra linking flags)
+ if test -z "$PYTHON_EXTRA_LDFLAGS"; then
+ PYTHON_EXTRA_LDFLAGS=`$PYTHON -c "$IMPORT_SYSCONFIG; \
+ conf = sysconfig.get_config_var; \
print (conf('LINKFORSHARED'))"`
- fi
- AC_MSG_RESULT([$PYTHON_EXTRA_LDFLAGS])
- AC_SUBST(PYTHON_EXTRA_LDFLAGS)
+ # Hack for macos, it sticks this in here.
+ PYTHON_EXTRA_LDFLAGS=`echo $PYTHON_EXTRA_LDFLAGS | sed 's/CoreFoundation.*$/CoreFoundation/'`
+ fi
+ AC_MSG_RESULT([$PYTHON_EXTRA_LDFLAGS])
+ AC_SUBST(PYTHON_EXTRA_LDFLAGS)
- #
- # final check to see if everything compiles alright
- #
- AC_MSG_CHECKING([consistency of all components of python development environment])
- # save current global flags
- ac_save_LIBS="$LIBS"
- ac_save_LDFLAGS="$LDFLAGS"
- ac_save_CPPFLAGS="$CPPFLAGS"
- LIBS="$ac_save_LIBS $PYTHON_LIBS $PYTHON_EXTRA_LIBS $PYTHON_EXTRA_LIBS"
- LDFLAGS="$ac_save_LDFLAGS $PYTHON_EXTRA_LDFLAGS"
- CPPFLAGS="$ac_save_CPPFLAGS $PYTHON_CPPFLAGS"
- AC_LANG_PUSH([C])
- AC_LINK_IFELSE([
+ #
+ # final check to see if everything compiles alright
+ #
+ AC_MSG_CHECKING([consistency of all components of python development environment])
+ # save current global flags
+ ac_save_LIBS="$LIBS"
+ ac_save_LDFLAGS="$LDFLAGS"
+ ac_save_CPPFLAGS="$CPPFLAGS"
+ LIBS="$ac_save_LIBS $PYTHON_LIBS $PYTHON_EXTRA_LIBS"
+ LDFLAGS="$ac_save_LDFLAGS $PYTHON_EXTRA_LDFLAGS"
+ CPPFLAGS="$ac_save_CPPFLAGS $PYTHON_CPPFLAGS"
+ AC_LANG_PUSH([C])
+ AC_LINK_IFELSE([
AC_LANG_PROGRAM([[#include <Python.h>]],
[[Py_Initialize();]])
],[pythonexists=yes],[pythonexists=no])
- AC_LANG_POP([C])
- # turn back to default flags
- CPPFLAGS="$ac_save_CPPFLAGS"
- LIBS="$ac_save_LIBS"
- LDFLAGS="$ac_save_LDFLAGS"
+ AC_LANG_POP([C])
+ # turn back to default flags
+ CPPFLAGS="$ac_save_CPPFLAGS"
+ LIBS="$ac_save_LIBS"
+ LDFLAGS="$ac_save_LDFLAGS"
- AC_MSG_RESULT([$pythonexists])
+ AC_MSG_RESULT([$pythonexists])
- if test ! "x$pythonexists" = "xyes"; then
- AC_MSG_FAILURE([
+ if test ! "x$pythonexists" = "xyes"; then
+ AC_MSG_WARN([
Could not link test program to Python. Maybe the main Python library has been
installed in some non-standard library path. If so, pass it to configure,
via the LIBS environment variable.
@@ -317,8 +453,13 @@ EOD`
You probably have to install the development version of the Python package
for your distribution. The exact name of this package varies among them.
============================================================================
- ])
- PYTHON_VERSION=""
+ ])
+ if ! $ax_python_devel_optional; then
+ AC_MSG_ERROR([Giving up])
+ fi
+ ax_python_devel_found=no
+ PYTHON_VERSION=""
+ fi
fi
#
diff --git a/src/Makefile.am b/src/Makefile.am
index 58cf07c..1ee9be8 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -58,6 +58,7 @@ libimobiledevice_1_0_la_SOURCES = \
companion_proxy.c companion_proxy.h \
reverse_proxy.c reverse_proxy.h \
syslog_relay.c syslog_relay.h \
+ ostrace.c ostrace.h \
bt_packet_logger.c bt_packet_logger.h
if WIN32
diff --git a/src/afc.c b/src/afc.c
index 1b4070b..c7eed5c 100644
--- a/src/afc.c
+++ b/src/afc.c
@@ -26,9 +26,12 @@
#endif
#include <stdio.h>
#include <stdlib.h>
-#include <unistd.h>
#include <string.h>
+#ifndef _MSC_VER
+#include <unistd.h>
+#endif
+
#include "idevice.h"
#include "afc.h"
#include "common/debug.h"
@@ -115,7 +118,7 @@ afc_error_t afc_client_new(idevice_t device, lockdownd_service_descriptor_t serv
afc_error_t afc_client_start_service(idevice_t device, afc_client_t * client, const char* label)
{
- afc_error_t err = AFC_E_UNKNOWN_ERROR;
+ int32_t err = AFC_E_UNKNOWN_ERROR;
service_client_factory_start_service(device, AFC_SERVICE_NAME, (void**)client, label, SERVICE_CONSTRUCTOR(afc_client_new), &err);
return err;
}
@@ -338,7 +341,7 @@ static afc_error_t afc_receive_data(afc_client_t client, char **bytes, uint32_t
free(buf);
debug_info("WARNING: Unknown operation code received 0x%llx param1=%lld", header.operation, param1);
-#ifndef WIN32
+#ifndef _WIN32
fprintf(stderr, "%s: WARNING: Unknown operation code received 0x%llx param1=%lld", __func__, (long long)header.operation, (long long)param1);
#endif
@@ -399,6 +402,50 @@ static char **make_strings_list(char *tokens, uint32_t length)
return list;
}
+static plist_t *make_dictionary(char *tokens, size_t length)
+{
+ size_t j = 0;
+ plist_t dict = NULL;
+
+ if (!tokens || !length)
+ return NULL;
+
+ dict = plist_new_dict();
+
+ while (j < length) {
+ size_t key_len = strnlen(tokens + j, length - j);
+ if (j + key_len >= length) {
+ plist_free(dict);
+ return NULL;
+ }
+ char* key = tokens + j;
+ j += key_len + 1;
+
+ if (j >= length) {
+ plist_free(dict);
+ return NULL;
+ }
+
+ size_t val_len = strnlen(tokens + j, length - j);
+ if (j + val_len >= length) {
+ plist_free(dict);
+ return NULL;
+ }
+ char* val = tokens + j;
+ j += val_len + 1;
+
+ char* endp = NULL;
+ unsigned long long u64val = strtoull(val, &endp, 10);
+ if (endp && *endp == '\0') {
+ plist_dict_set_item(dict, key, plist_new_uint(u64val));
+ } else {
+ plist_dict_set_item(dict, key, plist_new_string(val));
+ }
+ }
+
+ return dict;
+}
+
static int _afc_check_packet_buffer(afc_client_t client, uint32_t data_len)
{
if (data_len > client->packet_extra) {
@@ -495,6 +542,40 @@ afc_error_t afc_get_device_info(afc_client_t client, char ***device_information)
return ret;
}
+afc_error_t afc_get_device_info_plist(afc_client_t client, plist_t *device_information)
+{
+ uint32_t bytes = 0;
+ char *data = NULL;
+ afc_error_t ret = AFC_E_UNKNOWN_ERROR;
+
+ if (!client || !device_information)
+ return AFC_E_INVALID_ARG;
+
+ afc_lock(client);
+
+ /* Send the command */
+ ret = afc_dispatch_packet(client, AFC_OP_GET_DEVINFO, 0, NULL, 0, &bytes);
+ if (ret != AFC_E_SUCCESS) {
+ afc_unlock(client);
+ return AFC_E_NOT_ENOUGH_DATA;
+ }
+ /* Receive the data */
+ ret = afc_receive_data(client, &data, &bytes);
+ if (ret != AFC_E_SUCCESS) {
+ if (data)
+ free(data);
+ afc_unlock(client);
+ return ret;
+ }
+ /* Parse the data */
+ *device_information = make_dictionary(data, bytes);
+ free(data);
+
+ afc_unlock(client);
+
+ return ret;
+}
+
afc_error_t afc_get_device_info_key(afc_client_t client, const char *key, char **value)
{
afc_error_t ret = AFC_E_INTERNAL_ERROR;
@@ -644,8 +725,6 @@ afc_error_t afc_get_file_info(afc_client_t client, const char *path, char ***fil
return AFC_E_NO_MEM;
}
- debug_info("We got %p and %p", client->afc_packet, AFC_PACKET_DATA_PTR);
-
/* Send command */
memcpy(AFC_PACKET_DATA_PTR, path, data_len);
ret = afc_dispatch_packet(client, AFC_OP_GET_FILE_INFO, data_len, NULL, 0, &bytes);
@@ -666,6 +745,44 @@ afc_error_t afc_get_file_info(afc_client_t client, const char *path, char ***fil
return ret;
}
+afc_error_t afc_get_file_info_plist(afc_client_t client, const char *path, plist_t *file_information)
+{
+ char *received = NULL;
+ uint32_t bytes = 0;
+ afc_error_t ret = AFC_E_UNKNOWN_ERROR;
+
+ if (!client || !path || !file_information)
+ return AFC_E_INVALID_ARG;
+
+ afc_lock(client);
+
+ uint32_t data_len = (uint32_t)strlen(path)+1;
+ if (_afc_check_packet_buffer(client, data_len) < 0) {
+ afc_unlock(client);
+ debug_info("Failed to realloc packet buffer");
+ return AFC_E_NO_MEM;
+ }
+
+ /* Send command */
+ memcpy(AFC_PACKET_DATA_PTR, path, data_len);
+ ret = afc_dispatch_packet(client, AFC_OP_GET_FILE_INFO, data_len, NULL, 0, &bytes);
+ if (ret != AFC_E_SUCCESS) {
+ afc_unlock(client);
+ return AFC_E_NOT_ENOUGH_DATA;
+ }
+
+ /* Receive data */
+ ret = afc_receive_data(client, &received, &bytes);
+ if (received) {
+ *file_information = make_dictionary(received, bytes);
+ free(received);
+ }
+
+ afc_unlock(client);
+
+ return ret;
+}
+
afc_error_t afc_file_open(afc_client_t client, const char *filename, afc_file_mode_t file_mode, uint64_t *handle)
{
if (!client || !client->parent || !client->afc_packet)
diff --git a/src/house_arrest.c b/src/house_arrest.c
index caad731..06068c6 100644
--- a/src/house_arrest.c
+++ b/src/house_arrest.c
@@ -24,7 +24,11 @@
#endif
#include <string.h>
#include <stdlib.h>
+
+#ifndef _MSC_VER
#include <unistd.h>
+#endif
+
#include <plist/plist.h>
#include "house_arrest.h"
diff --git a/src/idevice.c b/src/idevice.c
index b9bbb1f..0af27fd 100644
--- a/src/idevice.c
+++ b/src/idevice.c
@@ -30,7 +30,7 @@
#include <errno.h>
#include <time.h>
-#ifdef WIN32
+#ifdef _WIN32
#include <winsock2.h>
#include <ws2tcpip.h>
#include <windows.h>
@@ -124,32 +124,32 @@ static void id_function(CRYPTO_THREADID *thread)
#endif
#endif /* HAVE_OPENSSL */
-static void internal_idevice_init(void)
-{
-#if defined(HAVE_OPENSSL)
-#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
- int i;
- SSL_library_init();
-
- mutex_buf = malloc(CRYPTO_num_locks() * sizeof(mutex_t));
- if (!mutex_buf)
- return;
- for (i = 0; i < CRYPTO_num_locks(); i++)
- mutex_init(&mutex_buf[i]);
-
-#if OPENSSL_VERSION_NUMBER < 0x10000000L
- CRYPTO_set_id_callback(id_function);
+// Reference: https://stackoverflow.com/a/2390626/1806760
+// Initializer/finalizer sample for MSVC and GCC/Clang.
+// 2010-2016 Joe Lowe. Released into the public domain.
+
+#ifdef __cplusplus
+ #define INITIALIZER(f) \
+ static void f(void); \
+ struct f##_t_ { f##_t_(void) { f(); } }; static f##_t_ f##_; \
+ static void f(void)
+#elif defined(_MSC_VER)
+ #pragma section(".CRT$XCU",read)
+ #define INITIALIZER2_(f,p) \
+ static void f(void); \
+ __declspec(allocate(".CRT$XCU")) void (*f##_)(void) = f; \
+ __pragma(comment(linker,"/include:" p #f "_")) \
+ static void f(void)
+ #ifdef _WIN64
+ #define INITIALIZER(f) INITIALIZER2_(f,"")
+ #else
+ #define INITIALIZER(f) INITIALIZER2_(f,"_")
+ #endif
#else
- CRYPTO_THREADID_set_callback(id_function);
-#endif
- CRYPTO_set_locking_callback(locking_function);
-#endif
-#elif defined(HAVE_GNUTLS)
- gnutls_global_init();
-#elif defined(HAVE_MBEDTLS)
- // NO-OP
+ #define INITIALIZER(f) \
+ static void f(void) __attribute__((__constructor__)); \
+ static void f(void)
#endif
-}
static void internal_idevice_deinit(void)
{
@@ -181,43 +181,33 @@ static void internal_idevice_deinit(void)
#endif
}
-static thread_once_t init_once = THREAD_ONCE_INIT;
-static thread_once_t deinit_once = THREAD_ONCE_INIT;
-
-#ifndef HAVE_ATTRIBUTE_CONSTRUCTOR
- #if defined(__llvm__) || defined(__GNUC__)
- #define HAVE_ATTRIBUTE_CONSTRUCTOR
- #endif
-#endif
-
-#ifdef HAVE_ATTRIBUTE_CONSTRUCTOR
-static void __attribute__((constructor)) libimobiledevice_initialize(void)
+INITIALIZER(internal_idevice_init)
{
- thread_once(&init_once, internal_idevice_init);
-}
+#if defined(HAVE_OPENSSL)
+#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
+ int i;
+ SSL_library_init();
-static void __attribute__((destructor)) libimobiledevice_deinitialize(void)
-{
- thread_once(&deinit_once, internal_idevice_deinit);
-}
-#elif defined(WIN32)
-BOOL WINAPI DllMain(HINSTANCE hModule, DWORD dwReason, LPVOID lpReserved)
-{
- switch (dwReason) {
- case DLL_PROCESS_ATTACH:
- thread_once(&init_once, internal_idevice_init);
- break;
- case DLL_PROCESS_DETACH:
- thread_once(&deinit_once, internal_idevice_deinit);
- break;
- default:
- break;
- }
- return 1;
-}
+ mutex_buf = malloc(CRYPTO_num_locks() * sizeof(mutex_t));
+ if (!mutex_buf)
+ return;
+ for (i = 0; i < CRYPTO_num_locks(); i++)
+ mutex_init(&mutex_buf[i]);
+
+#if OPENSSL_VERSION_NUMBER < 0x10000000L
+ CRYPTO_set_id_callback(id_function);
#else
-#warning No compiler support for constructor/destructor attributes, some features might not be available.
+ CRYPTO_THREADID_set_callback(id_function);
+#endif
+ CRYPTO_set_locking_callback(locking_function);
+#endif
+#elif defined(HAVE_GNUTLS)
+ gnutls_global_init();
+#elif defined(HAVE_MBEDTLS)
+ // NO-OP
#endif
+ atexit(internal_idevice_deinit);
+}
const char* libimobiledevice_version()
{
@@ -548,7 +538,7 @@ idevice_error_t idevice_connect(idevice_t device, uint16_t port, idevice_connect
}
idevice_connection_t new_connection = (idevice_connection_t)malloc(sizeof(struct idevice_connection_private));
new_connection->type = CONNECTION_USBMUXD;
- new_connection->data = (void*)(long)sfd;
+ new_connection->data = (void*)(uintptr_t)sfd;
new_connection->ssl_data = NULL;
new_connection->device = device;
new_connection->ssl_recv_timeout = (unsigned int)-1;
@@ -593,7 +583,7 @@ idevice_error_t idevice_connect(idevice_t device, uint16_t port, idevice_connect
idevice_connection_t new_connection = (idevice_connection_t)malloc(sizeof(struct idevice_connection_private));
new_connection->type = CONNECTION_NETWORK;
- new_connection->data = (void*)(long)sfd;
+ new_connection->data = (void*)(uintptr_t)sfd;
new_connection->ssl_data = NULL;
new_connection->device = device;
new_connection->ssl_recv_timeout = (unsigned int)-1;
@@ -618,11 +608,11 @@ idevice_error_t idevice_disconnect(idevice_connection_t connection)
}
idevice_error_t result = IDEVICE_E_UNKNOWN_ERROR;
if (connection->type == CONNECTION_USBMUXD) {
- usbmuxd_disconnect((int)(long)connection->data);
+ usbmuxd_disconnect((int)(uintptr_t)connection->data);
connection->data = NULL;
result = IDEVICE_E_SUCCESS;
} else if (connection->type == CONNECTION_NETWORK) {
- socket_close((int)(long)connection->data);
+ socket_close((int)(uintptr_t)connection->data);
connection->data = NULL;
result = IDEVICE_E_SUCCESS;
} else {
@@ -647,7 +637,7 @@ static idevice_error_t internal_connection_send(idevice_connection_t connection,
if (connection->type == CONNECTION_USBMUXD) {
int res;
do {
- res = usbmuxd_send((int)(long)connection->data, data, len, sent_bytes);
+ res = usbmuxd_send((int)(uintptr_t)connection->data, data, len, sent_bytes);
} while (res == -EAGAIN);
if (res < 0) {
debug_info("ERROR: usbmuxd_send returned %d (%s)", res, strerror(-res));
@@ -656,7 +646,7 @@ static idevice_error_t internal_connection_send(idevice_connection_t connection,
return IDEVICE_E_SUCCESS;
}
if (connection->type == CONNECTION_NETWORK) {
- int s = socket_send((int)(long)connection->data, (void*)data, len);
+ int s = socket_send((int)(uintptr_t)connection->data, (void*)data, len);
if (s < 0) {
*sent_bytes = 0;
return IDEVICE_E_UNKNOWN_ERROR;
@@ -763,7 +753,7 @@ static idevice_error_t internal_connection_receive_timeout(idevice_connection_t
}
if (connection->type == CONNECTION_USBMUXD) {
- int conn_error = usbmuxd_recv_timeout((int)(long)connection->data, data, len, recv_bytes, timeout);
+ int conn_error = usbmuxd_recv_timeout((int)(uintptr_t)connection->data, data, len, recv_bytes, timeout);
idevice_error_t error = socket_recv_to_idevice_error(conn_error, len, *recv_bytes);
if (error == IDEVICE_E_UNKNOWN_ERROR) {
debug_info("ERROR: usbmuxd_recv_timeout returned %d (%s)", conn_error, strerror(-conn_error));
@@ -771,7 +761,7 @@ static idevice_error_t internal_connection_receive_timeout(idevice_connection_t
return error;
}
if (connection->type == CONNECTION_NETWORK) {
- int res = socket_receive_timeout((int)(long)connection->data, data, len, 0, timeout);
+ int res = socket_receive_timeout((int)(uintptr_t)connection->data, data, len, 0, timeout);
idevice_error_t error = socket_recv_to_idevice_error(res, 0, 0);
if (error == IDEVICE_E_SUCCESS) {
*recv_bytes = (uint32_t)res;
@@ -863,7 +853,7 @@ static idevice_error_t internal_connection_receive(idevice_connection_t connecti
}
if (connection->type == CONNECTION_USBMUXD) {
- int res = usbmuxd_recv((int)(long)connection->data, data, len, recv_bytes);
+ int res = usbmuxd_recv((int)(uintptr_t)connection->data, data, len, recv_bytes);
if (res < 0) {
debug_info("ERROR: usbmuxd_recv returned %d (%s)", res, strerror(-res));
return IDEVICE_E_UNKNOWN_ERROR;
@@ -871,7 +861,7 @@ static idevice_error_t internal_connection_receive(idevice_connection_t connecti
return IDEVICE_E_SUCCESS;
}
if (connection->type == CONNECTION_NETWORK) {
- int res = socket_receive((int)(long)connection->data, data, len);
+ int res = socket_receive((int)(uintptr_t)connection->data, data, len);
if (res < 0) {
debug_info("ERROR: socket_receive returned %d (%s)", res, strerror(-res));
return IDEVICE_E_UNKNOWN_ERROR;
@@ -924,11 +914,11 @@ idevice_error_t idevice_connection_get_fd(idevice_connection_t connection, int *
}
if (connection->type == CONNECTION_USBMUXD) {
- *fd = (int)(long)connection->data;
+ *fd = (int)(uintptr_t)connection->data;
return IDEVICE_E_SUCCESS;
}
if (connection->type == CONNECTION_NETWORK) {
- *fd = (int)(long)connection->data;
+ *fd = (int)(uintptr_t)connection->data;
return IDEVICE_E_SUCCESS;
}
@@ -956,6 +946,20 @@ idevice_error_t idevice_get_udid(idevice_t device, char **udid)
return IDEVICE_E_SUCCESS;
}
+unsigned int idevice_get_device_version(idevice_t device)
+{
+ if (!device) {
+ return 0;
+ }
+ if (!device->version) {
+ lockdownd_client_t lockdown = NULL;
+ lockdownd_client_new(device, &lockdown, NULL);
+ // we don't handle any errors here. We should have the product version cached now.
+ lockdownd_client_free(lockdown);
+ }
+ return device->version;
+}
+
#if defined(HAVE_OPENSSL) || defined(HAVE_GNUTLS)
typedef ssize_t ssl_cb_ret_type_t;
#elif defined(HAVE_MBEDTLS)
@@ -1075,13 +1079,14 @@ static long ssl_idevice_bio_callback(BIO *b, int oper, const char *argp, int arg
idevice_connection_t conn = (idevice_connection_t)BIO_get_callback_arg(b);
#if OPENSSL_VERSION_NUMBER < 0x30000000L
size_t len = (size_t)argi;
- size_t *processed = (size_t*)&bytes;
#endif
switch (oper) {
case (BIO_CB_READ|BIO_CB_RETURN):
if (argp) {
bytes = internal_ssl_read(conn, (char *)argp, len);
- *processed = bytes;
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+ *processed = (size_t)(bytes < 0) ? 0 : bytes;
+#endif
return (long)bytes;
}
return 0;
@@ -1090,7 +1095,9 @@ static long ssl_idevice_bio_callback(BIO *b, int oper, const char *argp, int arg
// fallthrough
case (BIO_CB_WRITE|BIO_CB_RETURN):
bytes = internal_ssl_write(conn, argp, len);
- *processed = bytes;
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+ *processed = (size_t)(bytes < 0) ? 0 : bytes;
+#endif
return (long)bytes;
default:
return retvalue;
@@ -1239,7 +1246,7 @@ idevice_error_t idevice_connection_enable_ssl(idevice_connection_t connection)
#if OPENSSL_VERSION_NUMBER < 0x10100002L || \
(defined(LIBRESSL_VERSION_NUMBER) && (LIBRESSL_VERSION_NUMBER < 0x2060000fL))
/* force use of TLSv1 for older devices */
- if (connection->device->version < DEVICE_VERSION(10,0,0)) {
+ if (connection->device->version < IDEVICE_DEVICE_VERSION(10,0,0)) {
#ifdef SSL_OP_NO_TLSv1_1
SSL_CTX_set_options(ssl_ctx, SSL_OP_NO_TLSv1_1);
#endif
@@ -1252,7 +1259,7 @@ idevice_error_t idevice_connection_enable_ssl(idevice_connection_t connection)
}
#else
SSL_CTX_set_min_proto_version(ssl_ctx, TLS1_VERSION);
- if (connection->device->version < DEVICE_VERSION(10,0,0)) {
+ if (connection->device->version < IDEVICE_DEVICE_VERSION(10,0,0)) {
SSL_CTX_set_max_proto_version(ssl_ctx, TLS1_VERSION);
if (connection->device->version == 0) {
/*
@@ -1338,7 +1345,7 @@ idevice_error_t idevice_connection_enable_ssl(idevice_connection_t connection)
if (ssl_error == 0 || ssl_error != SSL_ERROR_WANT_READ) {
break;
}
-#ifdef WIN32
+#ifdef _WIN32
Sleep(100);
#else
struct timespec ts = { 0, 100000000 };
@@ -1544,3 +1551,28 @@ idevice_error_t idevice_connection_disable_bypass_ssl(idevice_connection_t conne
return IDEVICE_E_SUCCESS;
}
+
+const char* idevice_strerror(idevice_error_t err)
+{
+ switch (err) {
+ case IDEVICE_E_SUCCESS:
+ return "Success";
+ case IDEVICE_E_INVALID_ARG:
+ return "Invalid argument";
+ case IDEVICE_E_UNKNOWN_ERROR:
+ return "Unknown Error";
+ case IDEVICE_E_NO_DEVICE:
+ return "No device";
+ case IDEVICE_E_NOT_ENOUGH_DATA:
+ return "Not enough data";
+ case IDEVICE_E_CONNREFUSED:
+ return "Connection refused";
+ case IDEVICE_E_SSL_ERROR:
+ return "SSL error";
+ case IDEVICE_E_TIMEOUT:
+ return "Timeout";
+ default:
+ break;
+ }
+ return "Unknown Error";
+}
diff --git a/src/idevice.h b/src/idevice.h
index dd72f9d..e05338e 100644
--- a/src/idevice.h
+++ b/src/idevice.h
@@ -52,8 +52,6 @@
#include "common/userpref.h"
#include "libimobiledevice/libimobiledevice.h"
-#define DEVICE_VERSION(maj, min, patch) (((maj & 0xFF) << 16) | ((min & 0xFF) << 8) | (patch & 0xFF))
-
#define DEVICE_CLASS_IPHONE 1
#define DEVICE_CLASS_IPAD 2
#define DEVICE_CLASS_IPOD 3
diff --git a/src/installation_proxy.c b/src/installation_proxy.c
index ec19da0..bb6ef01 100644
--- a/src/installation_proxy.c
+++ b/src/installation_proxy.c
@@ -26,7 +26,11 @@
#include <string.h>
#include <stdlib.h>
#include <inttypes.h>
+
+#ifndef _MSC_VER
#include <unistd.h>
+#endif
+
#include <plist/plist.h>
#include "installation_proxy.h"
@@ -251,7 +255,7 @@ instproxy_error_t instproxy_client_new(idevice_t device, lockdownd_service_descr
instproxy_error_t instproxy_client_start_service(idevice_t device, instproxy_client_t * client, const char* label)
{
- instproxy_error_t err = INSTPROXY_E_UNKNOWN_ERROR;
+ int32_t err = INSTPROXY_E_UNKNOWN_ERROR;
service_client_factory_start_service(device, INSTPROXY_SERVICE_NAME, (void**)client, label, SERVICE_CONSTRUCTOR(instproxy_client_new), &err);
return err;
}
diff --git a/src/lockdown-cu.c b/src/lockdown-cu.c
index 1afc2c5..c457cb2 100644
--- a/src/lockdown-cu.c
+++ b/src/lockdown-cu.c
@@ -29,7 +29,11 @@
#define __USE_GNU 1
#include <stdio.h>
#include <ctype.h>
+
+#ifndef _MSC_VER
#include <unistd.h>
+#endif
+
#include <plist/plist.h>
#include "idevice.h"
@@ -505,7 +509,7 @@ lockdownd_error_t lockdownd_cu_pairing_create(lockdownd_client_t client, lockdow
char *s_version = NULL;
plist_get_string_val(p_version, &s_version);
if (s_version && sscanf(s_version, "%d.%d.%d", &vers[0], &vers[1], &vers[2]) >= 2) {
- client->device->version = DEVICE_VERSION(vers[0], vers[1], vers[2]);
+ client->device->version = IDEVICE_DEVICE_VERSION(vers[0], vers[1], vers[2]);
}
free(s_version);
}
@@ -653,7 +657,7 @@ lockdownd_error_t lockdownd_cu_pairing_create(lockdownd_client_t client, lockdow
CFStringGetCString(cname, hostname, sizeof(hostname), kCFStringEncodingUTF8);
CFRelease(cname);
#else
-#ifdef WIN32
+#ifdef _WIN32
DWORD hostname_len = sizeof(hostname);
GetComputerName(hostname, &hostname_len);
#else
@@ -957,12 +961,12 @@ lockdownd_error_t lockdownd_cu_send_request_and_get_reply(lockdownd_client_t cli
hkdf_md(MD_ALGO_SHA512, (unsigned char*)READ_KEY_SALT_MDLD, sizeof(READ_KEY_SALT_MDLD)-1, (unsigned char*)READ_KEY_INFO_MDLD, sizeof(READ_KEY_INFO_MDLD)-1, client->cu_key, client->cu_key_len, cu_read_key, &cu_read_key_len);
// Starting with iOS/tvOS 11.2 and WatchOS 4.2, this nonce is random and sent along with the request. Before, the request doesn't have a nonce and it uses hardcoded nonce "sendone01234".
- unsigned char cu_nonce[12] = "sendone01234"; // guaranteed to be random by fair dice troll
- if (client->device->version >= DEVICE_VERSION(11,2,0)) {
+ unsigned char cu_nonce[] = "sendone01234"; // guaranteed to be random by fair dice troll
+ if (client->device->version >= IDEVICE_DEVICE_VERSION(11,2,0)) {
#if defined(HAVE_OPENSSL)
- RAND_bytes(cu_nonce, sizeof(cu_nonce));
+ RAND_bytes(cu_nonce, sizeof(cu_nonce)-1);
#elif defined(HAVE_GCRYPT)
- gcry_create_nonce(cu_nonce, sizeof(cu_nonce));
+ gcry_create_nonce(cu_nonce, sizeof(cu_nonce)-1);
#endif
}
@@ -1128,7 +1132,7 @@ lockdownd_error_t lockdownd_pair_cu(lockdownd_client_t client)
plist_free(pubkey);
plist_t pair_record_plist = plist_new_dict();
- pair_record_generate_keys_and_certs(pair_record_plist, public_key);
+ pair_record_generate_keys_and_certs(pair_record_plist, public_key, client->device->version);
char* host_id = NULL;
char* system_buid = NULL;
diff --git a/src/lockdown.c b/src/lockdown.c
index 256bff0..32389c9 100644
--- a/src/lockdown.c
+++ b/src/lockdown.c
@@ -32,7 +32,11 @@
#define __USE_GNU 1
#include <stdio.h>
#include <ctype.h>
+
+#ifndef _MSC_VER
#include <unistd.h>
+#endif
+
#include <plist/plist.h>
#include <libimobiledevice-glue/utils.h>
@@ -43,7 +47,7 @@
#include "common/userpref.h"
#include "asprintf.h"
-#ifdef WIN32
+#ifdef _WIN32
#include <windows.h>
#define sleep(x) Sleep(x*1000)
#endif
@@ -617,6 +621,7 @@ lockdownd_error_t lockdownd_client_new(idevice_t device, lockdownd_client_t *cli
.port = 0xf27e,
.ssl_enabled = 0
};
+ char *type = NULL;
property_list_service_client_t plistclient = NULL;
if (property_list_service_client_new(device, (lockdownd_service_descriptor_t)&service, &plistclient) != PROPERTY_LIST_SERVICE_E_SUCCESS) {
@@ -638,51 +643,32 @@ lockdownd_error_t lockdownd_client_new(idevice_t device, lockdownd_client_t *cli
client_loc->label = label ? strdup(label) : NULL;
- *client = client_loc;
-
- return LOCKDOWN_E_SUCCESS;
-}
-
-lockdownd_error_t lockdownd_client_new_with_handshake(idevice_t device, lockdownd_client_t *client, const char *label)
-{
- if (!client)
- return LOCKDOWN_E_INVALID_ARG;
-
- lockdownd_error_t ret = LOCKDOWN_E_SUCCESS;
- lockdownd_client_t client_loc = NULL;
- plist_t pair_record = NULL;
- char *host_id = NULL;
- char *type = NULL;
-
- ret = lockdownd_client_new(device, &client_loc, label);
- if (LOCKDOWN_E_SUCCESS != ret) {
- debug_info("failed to create lockdownd client.");
- return ret;
- }
-
- /* perform handshake */
- ret = lockdownd_query_type(client_loc, &type);
- if (LOCKDOWN_E_SUCCESS != ret) {
+ int is_lockdownd = 0;
+ if (lockdownd_query_type(client_loc, &type) != LOCKDOWN_E_SUCCESS) {
debug_info("QueryType failed in the lockdownd client.");
- } else if (strcmp("com.apple.mobile.lockdown", type) != 0) {
- debug_info("Warning QueryType request returned \"%s\".", type);
+ } else if (!strcmp("com.apple.mobile.lockdown", type)) {
+ is_lockdownd = 1;
+ } else {
+ debug_info("QueryType request returned \"%s\"", type);
}
free(type);
- if (device->version == 0) {
+ *client = client_loc;
+
+ if (is_lockdownd && device->version == 0) {
plist_t p_version = NULL;
if (lockdownd_get_value(client_loc, NULL, "ProductVersion", &p_version) == LOCKDOWN_E_SUCCESS) {
int vers[3] = {0, 0, 0};
char *s_version = NULL;
plist_get_string_val(p_version, &s_version);
if (s_version && sscanf(s_version, "%d.%d.%d", &vers[0], &vers[1], &vers[2]) >= 2) {
- device->version = DEVICE_VERSION(vers[0], vers[1], vers[2]);
+ device->version = IDEVICE_DEVICE_VERSION(vers[0], vers[1], vers[2]);
}
free(s_version);
}
plist_free(p_version);
}
- if (device->device_class == 0) {
+ if (is_lockdownd && device->device_class == 0) {
plist_t p_device_class = NULL;
if (lockdownd_get_value(client_loc, NULL, "DeviceClass", &p_device_class) == LOCKDOWN_E_SUCCESS) {
char* s_device_class = NULL;
@@ -707,6 +693,26 @@ lockdownd_error_t lockdownd_client_new_with_handshake(idevice_t device, lockdown
plist_free(p_device_class);
}
+ return LOCKDOWN_E_SUCCESS;
+}
+
+lockdownd_error_t lockdownd_client_new_with_handshake(idevice_t device, lockdownd_client_t *client, const char *label)
+{
+ if (!client)
+ return LOCKDOWN_E_INVALID_ARG;
+
+ lockdownd_error_t ret = LOCKDOWN_E_SUCCESS;
+ lockdownd_client_t client_loc = NULL;
+ plist_t pair_record = NULL;
+ char *host_id = NULL;
+
+ ret = lockdownd_client_new(device, &client_loc, label);
+ if (LOCKDOWN_E_SUCCESS != ret) {
+ debug_info("failed to create lockdownd client.");
+ return ret;
+ }
+
+ /* perform handshake */
userpref_error_t uerr = userpref_read_pair_record(client_loc->device->udid, &pair_record);
if (uerr == USERPREF_E_READ_ERROR) {
debug_info("ERROR: Failed to retrieve pair record for %s", client_loc->device->udid);
@@ -730,7 +736,7 @@ lockdownd_error_t lockdownd_client_new_with_handshake(idevice_t device, lockdown
plist_free(pair_record);
pair_record = NULL;
- if (device->version < DEVICE_VERSION(7,0,0) && device->device_class != DEVICE_CLASS_WATCH) {
+ if (device->version < IDEVICE_DEVICE_VERSION(7,0,0) && device->device_class != DEVICE_CLASS_WATCH) {
/* for older devices, we need to validate pairing to receive trusted host status */
ret = lockdownd_validate_pair(client_loc, NULL);
@@ -836,7 +842,7 @@ static lockdownd_error_t pair_record_generate(lockdownd_client_t client, plist_t
/* generate keys and certificates into pair record */
userpref_error_t uret = USERPREF_E_SUCCESS;
- uret = pair_record_generate_keys_and_certs(*pair_record, public_key);
+ uret = pair_record_generate_keys_and_certs(*pair_record, public_key, client->device->version);
switch(uret) {
case USERPREF_E_INVALID_ARG:
ret = LOCKDOWN_E_INVALID_ARG;
@@ -846,6 +852,7 @@ static lockdownd_error_t pair_record_generate(lockdownd_client_t client, plist_t
break;
case USERPREF_E_SSL_ERROR:
ret = LOCKDOWN_E_SSL_ERROR;
+ break;
default:
break;
}
diff --git a/src/misagent.c b/src/misagent.c
index e3da997..3fdca4d 100644
--- a/src/misagent.c
+++ b/src/misagent.c
@@ -24,9 +24,13 @@
#endif
#include <string.h>
#include <stdlib.h>
+#include <stdio.h>
+
+#ifndef _MSC_VER
#include <unistd.h>
+#endif
+
#include <plist/plist.h>
-#include <stdio.h>
#include "misagent.h"
#include "property_list_service.h"
diff --git a/src/mobile_image_mounter.c b/src/mobile_image_mounter.c
index 6df50c4..6677882 100644
--- a/src/mobile_image_mounter.c
+++ b/src/mobile_image_mounter.c
@@ -24,7 +24,11 @@
#endif
#include <string.h>
#include <stdlib.h>
+
+#ifndef _MSC_VER
#include <unistd.h>
+#endif
+
#include <plist/plist.h>
#include "mobile_image_mounter.h"
diff --git a/src/notification_proxy.c b/src/notification_proxy.c
index 60b2e03..c7e4660 100644
--- a/src/notification_proxy.c
+++ b/src/notification_proxy.c
@@ -24,14 +24,19 @@
#endif
#include <string.h>
#include <stdlib.h>
+
+#ifndef _MSC_VER
#include <unistd.h>
+#endif
+
#include <plist/plist.h>
#include "notification_proxy.h"
#include "property_list_service.h"
#include "common/debug.h"
-#ifdef WIN32
+#ifdef _WIN32
+#include <windows.h>
#define sleep(x) Sleep(x*1000)
#endif
@@ -109,7 +114,7 @@ np_error_t np_client_new(idevice_t device, lockdownd_service_descriptor_t servic
np_error_t np_client_start_service(idevice_t device, np_client_t* client, const char* label)
{
- np_error_t err = NP_E_UNKNOWN_ERROR;
+ int32_t err = NP_E_UNKNOWN_ERROR;
service_client_factory_start_service(device, NP_SERVICE_NAME, (void**)client, label, SERVICE_CONSTRUCTOR(np_client_new), &err);
return err;
}
diff --git a/src/ostrace.c b/src/ostrace.c
new file mode 100644
index 0000000..68eb6bf
--- /dev/null
+++ b/src/ostrace.c
@@ -0,0 +1,436 @@
+/*
+ * ostrace.c
+ * com.apple.os_trace_relay service implementation.
+ *
+ * Copyright (c) 2020-2025 Nikias Bassen, All Rights Reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include <string.h>
+#include <stdlib.h>
+
+#include <plist/plist.h>
+
+#include "ostrace.h"
+#include "lockdown.h"
+#include "common/debug.h"
+#include "endianness.h"
+
+struct ostrace_worker_thread {
+ ostrace_client_t client;
+ ostrace_activity_cb_t cbfunc;
+ void *user_data;
+};
+
+/**
+ * Convert a service_error_t value to a ostrace_error_t value.
+ * Used internally to get correct error codes.
+ *
+ * @param err An service_error_t error code
+ *
+ * @return A matching ostrace_error_t error code,
+ * OSTRACE_E_UNKNOWN_ERROR otherwise.
+ */
+static ostrace_error_t ostrace_error(service_error_t err)
+{
+ switch (err) {
+ case SERVICE_E_SUCCESS:
+ return OSTRACE_E_SUCCESS;
+ case SERVICE_E_INVALID_ARG:
+ return OSTRACE_E_INVALID_ARG;
+ case SERVICE_E_MUX_ERROR:
+ return OSTRACE_E_MUX_ERROR;
+ case SERVICE_E_SSL_ERROR:
+ return OSTRACE_E_SSL_ERROR;
+ case SERVICE_E_NOT_ENOUGH_DATA:
+ return OSTRACE_E_NOT_ENOUGH_DATA;
+ case SERVICE_E_TIMEOUT:
+ return OSTRACE_E_TIMEOUT;
+ default:
+ break;
+ }
+ return OSTRACE_E_UNKNOWN_ERROR;
+}
+
+ostrace_error_t ostrace_client_new(idevice_t device, lockdownd_service_descriptor_t service, ostrace_client_t * client)
+{
+ *client = NULL;
+
+ if (!device || !service || service->port == 0 || !client || *client) {
+ debug_info("Incorrect parameter passed to ostrace_client_new.");
+ return OSTRACE_E_INVALID_ARG;
+ }
+
+ debug_info("Creating ostrace_client, port = %d.", service->port);
+
+ service_client_t parent = NULL;
+ ostrace_error_t ret = ostrace_error(service_client_new(device, service, &parent));
+ if (ret != OSTRACE_E_SUCCESS) {
+ debug_info("Creating base service client failed. Error: %i", ret);
+ return ret;
+ }
+
+ ostrace_client_t client_loc = (ostrace_client_t) malloc(sizeof(struct ostrace_client_private));
+ client_loc->parent = parent;
+ client_loc->worker = THREAD_T_NULL;
+
+ *client = client_loc;
+
+ debug_info("ostrace_client successfully created.");
+ return 0;
+}
+
+ostrace_error_t ostrace_client_start_service(idevice_t device, ostrace_client_t * client, const char* label)
+{
+ ostrace_error_t err = OSTRACE_E_UNKNOWN_ERROR;
+ service_client_factory_start_service(device, OSTRACE_SERVICE_NAME, (void**)client, label, SERVICE_CONSTRUCTOR(ostrace_client_new), &err);
+ return err;
+}
+
+ostrace_error_t ostrace_client_free(ostrace_client_t client)
+{
+ if (!client)
+ return OSTRACE_E_INVALID_ARG;
+ ostrace_stop_activity(client);
+ ostrace_error_t err = ostrace_error(service_client_free(client->parent));
+ free(client);
+
+ return err;
+}
+
+static ostrace_error_t ostrace_send_plist(ostrace_client_t client, plist_t plist)
+{
+ ostrace_error_t res = OSTRACE_E_UNKNOWN_ERROR;
+ uint32_t blen = 0;
+ char* bin = NULL;
+ uint32_t sent = 0;
+ uint32_t swapped_len = 0;
+
+ if (!client || !plist) {
+ return OSTRACE_E_INVALID_ARG;
+ }
+
+ plist_to_bin(plist, &bin, &blen);
+ swapped_len = htobe32(blen);
+
+ res = ostrace_error(service_send(client->parent, (char*)&swapped_len, 4, &sent));
+ if (res == OSTRACE_E_SUCCESS) {
+ res = ostrace_error(service_send(client->parent, bin, blen, &sent));
+ }
+ free(bin);
+ return res;
+}
+
+static ostrace_error_t ostrace_receive_plist(ostrace_client_t client, plist_t *plist)
+{
+ ostrace_error_t res = OSTRACE_E_UNKNOWN_ERROR;
+ uint8_t msgtype = 0;
+ uint32_t received = 0;
+ res = ostrace_error(service_receive(client->parent, (char*)&msgtype, 1, &received));
+ if (res != OSTRACE_E_SUCCESS) {
+ debug_info("Failed to read message type from service");
+ return res;
+ }
+ uint32_t rlen = 0;
+ res = ostrace_error(service_receive(client->parent, (char*)&rlen, 4, &received));
+ if (res != OSTRACE_E_SUCCESS) {
+ debug_info("Failed to read message size from service");
+ return res;
+ }
+
+ if (msgtype == 1) {
+ rlen = be32toh(rlen);
+ } else if (msgtype == 2) {
+ rlen = le32toh(rlen);
+ } else {
+ debug_info("Unexpected message type %d", msgtype);
+ return OSTRACE_E_UNKNOWN_ERROR;
+ }
+ debug_info("got length %d", rlen);
+
+ char* buf = (char*)malloc(rlen);
+ res = ostrace_error(service_receive(client->parent, buf, rlen, &received));
+ if (res != OSTRACE_E_SUCCESS) {
+ return res;
+ }
+
+ plist_t reply = NULL;
+ plist_err_t perr = plist_from_memory(buf, received, &reply, NULL);
+ free(buf);
+ if (perr != PLIST_ERR_SUCCESS) {
+ return OSTRACE_E_UNKNOWN_ERROR;
+ }
+ *plist = reply;
+ return OSTRACE_E_SUCCESS;
+}
+
+static ostrace_error_t _ostrace_check_result(plist_t reply)
+{
+ ostrace_error_t res = OSTRACE_E_REQUEST_FAILED;
+ if (!reply) {
+ return res;
+ }
+ plist_t p_status = plist_dict_get_item(reply, "Status");
+ if (!p_status) {
+ return res;
+ }
+ const char* status = plist_get_string_ptr(p_status, NULL);
+ if (!status) {
+ return res;
+ }
+ if (!strcmp(status, "RequestSuccessful")) {
+ res = OSTRACE_E_SUCCESS;
+ }
+ return res;
+}
+
+void *ostrace_worker(void *arg)
+{
+ ostrace_error_t res = OSTRACE_E_UNKNOWN_ERROR;
+ struct ostrace_worker_thread *oswt = (struct ostrace_worker_thread*)arg;
+
+ if (!oswt)
+ return NULL;
+
+ uint8_t msgtype = 0;
+ uint32_t received = 0;
+
+ debug_info("Running");
+
+ while (oswt->client->parent) {
+ res = ostrace_error(service_receive_with_timeout(oswt->client->parent, (char*)&msgtype, 1, &received, 100));
+ if (res == OSTRACE_E_TIMEOUT) {
+ continue;
+ }
+ if (res != OSTRACE_E_SUCCESS) {
+ debug_info("Failed to read message type from service");
+ break;
+ }
+ uint32_t rlen = 0;
+ res = ostrace_error(service_receive(oswt->client->parent, (char*)&rlen, 4, &received));
+ if (res != OSTRACE_E_SUCCESS) {
+ debug_info("Failed to read message size from service");
+ break;
+ }
+
+ if (msgtype == 1) {
+ rlen = be32toh(rlen);
+ } else if (msgtype == 2) {
+ rlen = le32toh(rlen);
+ } else {
+ debug_info("Unexpected message type %d", msgtype);
+ break;
+ }
+
+ debug_info("got length %d", rlen);
+
+ void* buf = malloc(rlen);
+ res = ostrace_error(service_receive(oswt->client->parent, (char*)buf, rlen, &received));
+ if (res != OSTRACE_E_SUCCESS) {
+ debug_info("Failed to receive %d bytes, error %d", rlen, res);
+ break;
+ }
+ if (received < rlen) {
+ debug_info("Failed to receive all data, got %d/%d", received, rlen);
+ break;
+ }
+ oswt->cbfunc(buf, received, oswt->user_data);
+ }
+
+ if (oswt) {
+ free(oswt);
+ }
+
+ debug_info("Exiting");
+
+ return NULL;
+}
+
+ostrace_error_t ostrace_start_activity(ostrace_client_t client, plist_t options, ostrace_activity_cb_t callback, void* user_data)
+{
+ if (!client || !callback)
+ return OSTRACE_E_INVALID_ARG;
+
+ ostrace_error_t res = OSTRACE_E_UNKNOWN_ERROR;
+
+ if (client->worker) {
+ debug_info("Another ostrace activity thread appears to be running already.");
+ return res;
+ }
+
+ plist_t dict = plist_new_dict();
+ plist_dict_set_item(dict, "Pid", plist_new_uint(0x0FFFFFFFF));
+ plist_dict_set_item(dict, "MessageFilter", plist_new_uint(0xFFFF));
+ plist_dict_set_item(dict, "StreamFlags", plist_new_uint(0x3C));
+ if (options) {
+ plist_dict_merge(&dict, options);
+ }
+ plist_dict_set_item(dict, "Request", plist_new_string("StartActivity"));
+
+ res = ostrace_send_plist(client, dict);
+ plist_free(dict);
+ if (res != OSTRACE_E_SUCCESS) {
+ return res;
+ }
+
+ dict = NULL;
+ res = ostrace_receive_plist(client, &dict);
+ if (res != OSTRACE_E_SUCCESS) {
+ return res;
+ }
+ res = _ostrace_check_result(dict);
+ if (res != OSTRACE_E_SUCCESS) {
+ return res;
+ }
+
+ /* start worker thread */
+ struct ostrace_worker_thread *oswt = (struct ostrace_worker_thread*)malloc(sizeof(struct ostrace_worker_thread));
+ if (oswt) {
+ oswt->client = client;
+ oswt->cbfunc = callback;
+ oswt->user_data = user_data;
+
+ if (thread_new(&client->worker, ostrace_worker, oswt) == 0) {
+ res = OSTRACE_E_SUCCESS;
+ }
+ }
+
+ return res;
+}
+
+ostrace_error_t ostrace_stop_activity(ostrace_client_t client)
+{
+ if (client->worker) {
+ /* notify thread to finish */
+ service_client_t parent = client->parent;
+ client->parent = NULL;
+ /* join thread to make it exit */
+ thread_join(client->worker);
+ thread_free(client->worker);
+ client->worker = THREAD_T_NULL;
+ client->parent = parent;
+ }
+
+ return OSTRACE_E_SUCCESS;
+}
+
+ostrace_error_t ostrace_get_pid_list(ostrace_client_t client, plist_t* list)
+{
+ ostrace_error_t res = OSTRACE_E_UNKNOWN_ERROR;
+ plist_t dict = plist_new_dict();
+ plist_dict_set_item(dict, "Request", plist_new_string("PidList"));
+
+ if (!client || !list) {
+ return OSTRACE_E_INVALID_ARG;
+ }
+
+ res = ostrace_send_plist(client, dict);
+ plist_free(dict);
+ if (res != OSTRACE_E_SUCCESS) {
+ return res;
+ }
+
+ plist_t reply = NULL;
+ res = ostrace_receive_plist(client, &reply);
+ if (res != OSTRACE_E_SUCCESS) {
+ return res;
+ }
+ res = _ostrace_check_result(reply);
+ if (res != OSTRACE_E_SUCCESS) {
+ return res;
+ }
+
+ plist_t payload = plist_dict_get_item(reply, "Payload");
+ if (!payload) {
+ return OSTRACE_E_REQUEST_FAILED;
+ }
+ *list = plist_copy(payload);
+ plist_free(reply);
+
+ return OSTRACE_E_SUCCESS;
+}
+
+ostrace_error_t ostrace_create_archive(ostrace_client_t client, plist_t options, ostrace_archive_write_cb_t callback, void* user_data)
+{
+ ostrace_error_t res = OSTRACE_E_UNKNOWN_ERROR;
+ if (!client || !callback) {
+ return OSTRACE_E_INVALID_ARG;
+ }
+ plist_t dict = plist_new_dict();
+ if (options) {
+ plist_dict_merge(&dict, options);
+ }
+ plist_dict_set_item(dict, "Request", plist_new_string("CreateArchive"));
+
+ res = ostrace_send_plist(client, dict);
+ plist_free(dict);
+ if (res != OSTRACE_E_SUCCESS) {
+ return res;
+ }
+
+ plist_t reply = NULL;
+ res = ostrace_receive_plist(client, &reply);
+ if (res != OSTRACE_E_SUCCESS) {
+ return res;
+ }
+
+ res = _ostrace_check_result(reply);
+ if (res != OSTRACE_E_SUCCESS) {
+ return res;
+ }
+
+ debug_info("Receiving archive...\n");
+ while (1) {
+ uint8_t msgtype = 0;
+ uint32_t received = 0;
+ res = ostrace_error(service_receive(client->parent, (char*)&msgtype, 1, &received));
+ if (res != OSTRACE_E_SUCCESS) {
+ debug_info("Could not read message type from service: %d", res);
+ break;
+ }
+ if (msgtype != 3) {
+ debug_info("Unexpected packet type %d", msgtype);
+ return OSTRACE_E_REQUEST_FAILED;
+ }
+ uint32_t rlen = 0;
+ res = ostrace_error(service_receive(client->parent, (char*)&rlen, 4, &received));
+ if (res != OSTRACE_E_SUCCESS) {
+ debug_info("Failed to read message size from service");
+ break;
+ }
+
+ rlen = le32toh(rlen);
+ debug_info("got length %d", rlen);
+
+ unsigned char* buf = (unsigned char*)malloc(rlen);
+ res = ostrace_error(service_receive(client->parent, (char*)buf, rlen, &received));
+ if (res != OSTRACE_E_SUCCESS) {
+ debug_info("Could not read data from service: %d", res);
+ break;
+ }
+ if (callback(buf, received, user_data) < 0) {
+ debug_info("Aborted through callback");
+ return OSTRACE_E_REQUEST_FAILED;
+ }
+ }
+ debug_info("Done.\n");
+
+ return OSTRACE_E_SUCCESS;
+}
+
diff --git a/src/ostrace.h b/src/ostrace.h
new file mode 100644
index 0000000..dcc3e8d
--- /dev/null
+++ b/src/ostrace.h
@@ -0,0 +1,37 @@
+/*
+ * ostrace.h
+ * com.apple.os_trace_relay service header file.
+ *
+ * Copyright (c) 2020-2025 Nikias Bassen, All Rights Reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef _OSTRACE_H
+#define _OSTRACE_H
+
+#include "idevice.h"
+#include "libimobiledevice/ostrace.h"
+#include "service.h"
+#include <libimobiledevice-glue/thread.h>
+
+struct ostrace_client_private {
+ service_client_t parent;
+ THREAD_T worker;
+};
+
+void *ostrace_worker(void *arg);
+
+#endif
diff --git a/src/sbservices.c b/src/sbservices.c
index 365e130..5df5122 100644
--- a/src/sbservices.c
+++ b/src/sbservices.c
@@ -24,7 +24,11 @@
#endif
#include <string.h>
#include <stdlib.h>
+
+#ifndef _MSC_VER
#include <unistd.h>
+#endif
+
#include <plist/plist.h>
#include "sbservices.h"
diff --git a/tools/Makefile.am b/tools/Makefile.am
index 7c9060b..24cfc66 100644
--- a/tools/Makefile.am
+++ b/tools/Makefile.am
@@ -35,7 +35,7 @@ bin_PROGRAMS = \
afcclient
idevicebtlogger_SOURCES = idevicebtlogger.c
-iidevicebtlogger_CFLAGS = $(AM_CFLAGS)
+idevicebtlogger_CFLAGS = $(AM_CFLAGS)
idevicebtlogger_LDFLAGS = $(top_builddir)/common/libinternalcommon.la $(AM_LDFLAGS)
idevicebtlogger_LDADD = $(top_builddir)/src/libimobiledevice-1.0.la
@@ -51,8 +51,8 @@ idevicename_LDADD = $(top_builddir)/src/libimobiledevice-1.0.la
idevicepair_SOURCES = idevicepair.c
idevicepair_CFLAGS = $(AM_CFLAGS)
-idevicepair_LDFLAGS = $(AM_LDFLAGS) $(libusbmuxd_LIBS) $(ssl_lib_LIBS)
-idevicepair_LDADD = $(top_builddir)/src/libimobiledevice-1.0.la $(top_builddir)/common/libinternalcommon.la $(limd_glue_LIBS)
+idevicepair_LDFLAGS = $(AM_LDFLAGS) $(libusbmuxd_LIBS)
+idevicepair_LDADD = $(top_builddir)/src/libimobiledevice-1.0.la $(top_builddir)/common/libinternalcommon.la $(limd_glue_LIBS) $(ssl_lib_LIBS)
idevicesyslog_SOURCES = idevicesyslog.c
idevicesyslog_CFLAGS = $(AM_CFLAGS) $(limd_glue_CFLAGS)
@@ -76,7 +76,7 @@ idevicebackup2_LDADD = $(top_builddir)/src/libimobiledevice-1.0.la
ideviceimagemounter_SOURCES = ideviceimagemounter.c
ideviceimagemounter_CFLAGS = $(AM_CFLAGS) $(limd_glue_CFLAGS) $(libtatsu_CFLAGS)
-ideviceimagemounter_LDFLAGS = $(AM_LDFLAGS) $(limd_glue_LIBS) $(ssl_lib_LIBS) $(libtatsu_LIBS)
+ideviceimagemounter_LDFLAGS = $(AM_LDFLAGS) $(limd_glue_LIBS) $(libtatsu_LIBS)
ideviceimagemounter_LDADD = $(top_builddir)/src/libimobiledevice-1.0.la
idevicescreenshot_SOURCES = idevicescreenshot.c
@@ -135,8 +135,8 @@ idevicesetlocation_LDFLAGS = $(AM_LDFLAGS)
idevicesetlocation_LDADD = $(top_builddir)/src/libimobiledevice-1.0.la
afcclient_SOURCES = afcclient.c
-afcclient_CFLAGS = $(AM_CFLAGS)
-afcclient_LDFLAGS = $(AM_LDFLAGS)
+afcclient_CFLAGS = $(AM_CFLAGS) $(limd_glue_CFLAGS)
+afcclient_LDFLAGS = $(AM_LDFLAGS) $(limd_glue_LIBS)
if HAVE_READLINE
afcclient_CFLAGS += $(readline_CFLAGS)
afcclient_LDFLAGS += $(readline_LIBS)
diff --git a/tools/afcclient.c b/tools/afcclient.c
index 8f49831..a958c23 100644
--- a/tools/afcclient.c
+++ b/tools/afcclient.c
@@ -38,8 +38,9 @@
#include <unistd.h>
#include <dirent.h>
#include <time.h>
+#include <sys/stat.h>
-#ifdef WIN32
+#ifdef _WIN32
#include <windows.h>
#include <sys/time.h>
#include <conio.h>
@@ -95,7 +96,7 @@ static size_t curdir_len = 0;
static int file_exists(const char* path)
{
struct stat tst;
-#ifdef WIN32
+#ifdef _WIN32
return (stat(path, &tst) == 0);
#else
return (lstat(path, &tst) == 0);
@@ -105,7 +106,7 @@ static int file_exists(const char* path)
static int is_directory(const char* path)
{
struct stat tst;
-#ifdef WIN32
+#ifdef _WIN32
return (stat(path, &tst) == 0) && S_ISDIR(tst.st_mode);
#else
return (lstat(path, &tst) == 0) && S_ISDIR(tst.st_mode);
@@ -138,7 +139,7 @@ static void print_usage(int argc, char **argv, int is_error)
}
#ifndef HAVE_READLINE
-#ifdef WIN32
+#ifdef _WIN32
#define BS_CC '\b'
#else
#define BS_CC 0x7f
@@ -175,7 +176,7 @@ int stop_requested = 0;
static void handle_signal(int sig)
{
stop_requested++;
-#ifdef WIN32
+#ifdef _WIN32
GenerateConsoleCtrlEvent(CTRL_C_EVENT, 0);
#else
kill(getpid(), SIGINT);
@@ -364,62 +365,55 @@ static char* get_realpath(const char* path)
static void handle_devinfo(afc_client_t afc, int argc, char** argv)
{
- char **info = NULL;
- afc_error_t err = afc_get_device_info(afc, &info);
+ plist_t info = NULL;
+ afc_error_t err = afc_get_device_info_plist(afc, &info);
if (err == AFC_E_SUCCESS && info) {
- int i;
- for (i = 0; info[i]; i += 2) {
- printf("%s: %s\n", info[i], info[i+1]);
+ if (argc > 0 && !strcmp(argv[0], "--plain")) {
+ plist_write_to_stream(info, stdout, PLIST_FORMAT_LIMD, PLIST_OPT_NONE);
+ } else {
+ plist_write_to_stream(info, stdout, PLIST_FORMAT_JSON, PLIST_OPT_NONE);
}
} else {
printf("Error: Failed to get device info: %s (%d)\n", afc_strerror(err), err);
}
- afc_dictionary_free(info);
+ plist_free(info);
}
static int get_file_info_stat(afc_client_t afc, const char* path, struct afc_file_stat *stbuf)
{
- char **info = NULL;
- afc_error_t ret = afc_get_file_info(afc, path, &info);
+ plist_t info = NULL;
+ afc_error_t ret = afc_get_file_info_plist(afc, path, &info);
memset(stbuf, 0, sizeof(struct afc_file_stat));
if (ret != AFC_E_SUCCESS) {
return -1;
} else if (!info) {
return -1;
- } else {
- // get file attributes from info list
- int i;
- for (i = 0; info[i]; i += 2) {
- if (!strcmp(info[i], "st_size")) {
- stbuf->st_size = atoll(info[i+1]);
- } else if (!strcmp(info[i], "st_blocks")) {
- stbuf->st_blocks = atoi(info[i+1]);
- } else if (!strcmp(info[i], "st_ifmt")) {
- if (!strcmp(info[i+1], "S_IFREG")) {
- stbuf->st_mode = S_IFREG;
- } else if (!strcmp(info[i+1], "S_IFDIR")) {
- stbuf->st_mode = S_IFDIR;
- } else if (!strcmp(info[i+1], "S_IFLNK")) {
- stbuf->st_mode = S_IFLNK;
- } else if (!strcmp(info[i+1], "S_IFBLK")) {
- stbuf->st_mode = S_IFBLK;
- } else if (!strcmp(info[i+1], "S_IFCHR")) {
- stbuf->st_mode = S_IFCHR;
- } else if (!strcmp(info[i+1], "S_IFIFO")) {
- stbuf->st_mode = S_IFIFO;
- } else if (!strcmp(info[i+1], "S_IFSOCK")) {
- stbuf->st_mode = S_IFSOCK;
- }
- } else if (!strcmp(info[i], "st_nlink")) {
- stbuf->st_nlink = atoi(info[i+1]);
- } else if (!strcmp(info[i], "st_mtime")) {
- stbuf->st_mtime = (time_t)(atoll(info[i+1]) / 1000000000);
- } else if (!strcmp(info[i], "st_birthtime")) { /* available on iOS 7+ */
- stbuf->st_birthtime = (time_t)(atoll(info[i+1]) / 1000000000);
- }
- }
- afc_dictionary_free(info);
}
+ stbuf->st_size = plist_dict_get_uint(info, "st_size");
+ stbuf->st_blocks = plist_dict_get_uint(info, "st_blocks");
+ const char* s_ifmt = plist_get_string_ptr(plist_dict_get_item(info, "st_ifmt"), NULL);
+ if (s_ifmt) {
+ if (!strcmp(s_ifmt, "S_IFREG")) {
+ stbuf->st_mode = S_IFREG;
+ } else if (!strcmp(s_ifmt, "S_IFDIR")) {
+ stbuf->st_mode = S_IFDIR;
+ } else if (!strcmp(s_ifmt, "S_IFLNK")) {
+ stbuf->st_mode = S_IFLNK;
+ } else if (!strcmp(s_ifmt, "S_IFBLK")) {
+ stbuf->st_mode = S_IFBLK;
+ } else if (!strcmp(s_ifmt, "S_IFCHR")) {
+ stbuf->st_mode = S_IFCHR;
+ } else if (!strcmp(s_ifmt, "S_IFIFO")) {
+ stbuf->st_mode = S_IFIFO;
+ } else if (!strcmp(s_ifmt, "S_IFSOCK")) {
+ stbuf->st_mode = S_IFSOCK;
+ }
+ }
+ stbuf->st_nlink = plist_dict_get_uint(info, "st_nlink");
+ stbuf->st_mtime = (time_t)(plist_dict_get_uint(info, "st_mtime") / 1000000000);
+ /* available on iOS 7+ */
+ stbuf->st_birthtime = (time_t)(plist_dict_get_uint(info, "st_birthtime") / 1000000000);
+ plist_free(info);
return 0;
}
@@ -430,22 +424,23 @@ static void handle_file_info(afc_client_t afc, int argc, char** argv)
return;
}
- char **info = NULL;
+ plist_t info = NULL;
char* abspath = get_absolute_path(argv[0]);
if (!abspath) {
printf("Error: Invalid argument\n");
return;
}
- afc_error_t err = afc_get_file_info(afc, abspath, &info);
+ afc_error_t err = afc_get_file_info_plist(afc, abspath, &info);
if (err == AFC_E_SUCCESS && info) {
- int i;
- for (i = 0; info[i]; i += 2) {
- printf("%s: %s\n", info[i], info[i+1]);
+ if (argc > 1 && !strcmp(argv[1], "--plain")) {
+ plist_write_to_stream(info, stdout, PLIST_FORMAT_LIMD, PLIST_OPT_NONE);
+ } else {
+ plist_write_to_stream(info, stdout, PLIST_FORMAT_JSON, PLIST_OPT_NONE);
}
} else {
printf("Error: Failed to get file info for %s: %s (%d)\n", argv[0], afc_strerror(err), err);
}
- afc_dictionary_free(info);
+ plist_free(info);
free(abspath);
}
@@ -483,7 +478,7 @@ static void print_file_info(afc_client_t afc, const char* path, int list_verbose
printf(" ");
printf("%10lld", (long long)st.st_size);
printf(" ");
-#ifdef WIN32
+#ifdef _WIN32
strftime(timebuf, 64, "%d %b %Y %H:%M:%S", localtime(&t));
#else
strftime(timebuf, 64, "%d %h %Y %H:%M:%S", localtime(&t));
@@ -774,7 +769,7 @@ static uint8_t get_single_file(afc_client_t afc, const char *srcpath, const char
static int __mkdir(const char* path)
{
-#ifdef WIN32
+#ifdef _WIN32
return mkdir(path);
#else
return mkdir(path, 0755);
@@ -783,28 +778,19 @@ static int __mkdir(const char* path)
static uint8_t get_file(afc_client_t afc, const char *srcpath, const char *dstpath, uint8_t force_overwrite, uint8_t recursive_get)
{
- char **info = NULL;
+ plist_t info = NULL;
uint64_t file_size = 0;
- afc_error_t err = afc_get_file_info(afc, srcpath, &info);
+ afc_error_t err = afc_get_file_info_plist(afc, srcpath, &info);
if (err == AFC_E_OBJECT_NOT_FOUND) {
printf("Error: Failed to read from file '%s': %s (%d)\n", srcpath, afc_strerror(err), err);
return 0;
}
uint8_t is_dir = 0;
if (info) {
- char **p = info;
- while (p && *p) {
- if (!strcmp(*p, "st_size")) {
- p++;
- file_size = (uint64_t) strtoull(*p, NULL, 10);
- }
- if (!strcmp(*p, "st_ifmt")) {
- p++;
- is_dir = !strcmp(*p, "S_IFDIR");
- }
- p++;
- }
- afc_dictionary_free(info);
+ file_size = plist_dict_get_uint(info, "st_size");
+ const char* ifmt = plist_get_string_ptr(plist_dict_get_item(info, "st_ifmt"), NULL);
+ is_dir = (ifmt && !strcmp(ifmt, "S_IFDIR"));
+ plist_free(info);
}
uint8_t succeed = 1;
if (is_dir) {
@@ -837,7 +823,7 @@ static uint8_t get_file(afc_client_t afc, const char *srcpath, const char *dstpa
p++;
continue;
}
- size_t len = srcpath_is_root ? strlen(*p) + 1 : srcpath_len + 1 + strlen(*p) + 1;
+ size_t len = srcpath_is_root ? (strlen(*p) + 2) : (srcpath_len + 1 + strlen(*p) + 1);
char *testpath = (char *) malloc(len);
if (srcpath_is_root) {
snprintf(testpath, len, "/%s", *p);
@@ -845,7 +831,7 @@ static uint8_t get_file(afc_client_t afc, const char *srcpath, const char *dstpa
snprintf(testpath, len, "%s/%s", srcpath, *p);
}
uint8_t dst_is_root = strcmp(srcpath, "/") == 0;
- size_t dst_len = dst_is_root ? strlen(*p) + 1 : strlen(dstpath) + 1 + strlen(*p) + 1;
+ size_t dst_len = dst_is_root ? (strlen(*p) + 2) : (strlen(dstpath) + 1 + strlen(*p) + 1);
char *newdst = (char *) malloc(dst_len);
if (dst_is_root) {
snprintf(newdst, dst_len, "/%s", *p);
@@ -921,9 +907,9 @@ static void handle_get(afc_client_t afc, int argc, char **argv)
// target is a directory, put file under this target
if (is_directory(dstpath)) {
- const char *basen = path_get_basename(argv[0]);
+ const char *basen = path_get_basename(srcpath);
uint8_t dst_is_root = strcmp(dstpath, "/") == 0;
- size_t len = dst_is_root ? (strlen(basen) + 1) : (strlen(dstpath) + 1 + strlen(basen) + 1);
+ size_t len = dst_is_root ? (strlen(basen) + 2) : (strlen(dstpath) + 1 + strlen(basen) + 1);
char *newdst = (char *) malloc(len);
if (dst_is_root) {
snprintf(newdst, len, "/%s", basen);
@@ -944,11 +930,11 @@ static void handle_get(afc_client_t afc, int argc, char **argv)
static uint8_t put_single_file(afc_client_t afc, const char *srcpath, const char *dstpath, uint8_t force_overwrite)
{
- char **info = NULL;
- afc_error_t ret = afc_get_file_info(afc, dstpath, &info);
+ plist_t info = NULL;
+ afc_error_t ret = afc_get_file_info_plist(afc, dstpath, &info);
// file exists, only overwrite with '-f' option was set
if (ret == AFC_E_SUCCESS && info) {
- afc_dictionary_free(info);
+ plist_free(info);
if (!force_overwrite) {
printf("Error: Failed to write into existing file without '-f' option: %s\n", dstpath);
return 0;
@@ -1029,10 +1015,11 @@ static uint8_t put_file(afc_client_t afc, const char *srcpath, const char *dstpa
printf("Error: Failed to put directory without '-r' option: %s\n", srcpath);
return 0;
}
- char **info = NULL;
- afc_error_t err = afc_get_file_info(afc, dstpath, &info);
+ plist_t info = NULL;
+ afc_error_t err = afc_get_file_info_plist(afc, dstpath, &info);
//create if target directory does not exist
- afc_dictionary_free(info);
+ plist_free(info);
+ info = NULL;
if (err == AFC_E_OBJECT_NOT_FOUND) {
err = afc_make_directory(afc, dstpath);
if (err != AFC_E_SUCCESS) {
@@ -1043,19 +1030,12 @@ static uint8_t put_file(afc_client_t afc, const char *srcpath, const char *dstpa
printf("Error: Failed to put existing directory without '-f' option: %s\n", dstpath);
return 0;
}
- afc_get_file_info(afc, dstpath, &info);
+ afc_get_file_info_plist(afc, dstpath, &info);
uint8_t is_dir = 0;
if (info) {
- char **p = info;
- while (p && *p) {
- if (!strcmp(*p, "st_ifmt")) {
- p++;
- is_dir = !strcmp(*p, "S_IFDIR");
- break;
- }
- p++;
- }
- afc_dictionary_free(info);
+ const char* ifmt = plist_get_string_ptr(plist_dict_get_item(info, "st_ifmt"), NULL);
+ is_dir = (ifmt && !strcmp(ifmt, "S_IFDIR"));
+ plist_free(info);
}
if (!is_dir) {
printf("Error: Failed to create or access directory: '%s'\n", dstpath);
@@ -1073,7 +1053,7 @@ static uint8_t put_file(afc_client_t afc, const char *srcpath, const char *dstpa
char *fpath = string_build_path(srcpath, ep->d_name, NULL);
if (fpath) {
uint8_t dst_is_root = strcmp(dstpath, "/") == 0;
- size_t len = dst_is_root ? strlen(ep->d_name) + 1 : strlen(dstpath) + 1 + strlen(ep->d_name) + 1;
+ size_t len = dst_is_root ? (strlen(ep->d_name) + 2) : (strlen(dstpath) + 1 + strlen(ep->d_name) + 1);
char *newdst = (char *) malloc(len);
if (dst_is_root) {
snprintf(newdst, len, "/%s", ep->d_name);
@@ -1147,8 +1127,8 @@ static void handle_put(afc_client_t afc, int argc, char **argv)
printf("Error: Invalid number of arguments\n");
return;
}
- char **info = NULL;
- afc_error_t err = afc_get_file_info(afc, dstpath, &info);
+ plist_t info = NULL;
+ afc_error_t err = afc_get_file_info_plist(afc, dstpath, &info);
// target does not exist, put directly
if (err == AFC_E_OBJECT_NOT_FOUND) {
put_file(afc, srcpath, dstpath, force_overwrite, recursive_put);
@@ -1157,22 +1137,15 @@ static void handle_put(afc_client_t afc, int argc, char **argv)
} else {
uint8_t is_dir = 0;
if (info) {
- char **p = info;
- while (p && *p) {
- if (!strcmp(*p, "st_ifmt")) {
- p++;
- is_dir = !strcmp(*p, "S_IFDIR");
- break;
- }
- p++;
- }
- afc_dictionary_free(info);
+ const char* ifmt = plist_get_string_ptr(plist_dict_get_item(info, "st_ifmt"), NULL);
+ is_dir = (ifmt && !strcmp(ifmt, "S_IFDIR"));
+ plist_free(info);
}
// target is a directory, try to put under this directory
if (is_dir) {
const char *basen = path_get_basename(srcpath);
uint8_t dst_is_root = strcmp(dstpath, "/") == 0;
- size_t len = dst_is_root ? strlen(basen) + 1 : strlen(dstpath) + 1 + strlen(basen) + 1;
+ size_t len = dst_is_root ? (strlen(basen) + 2) : (strlen(dstpath) + 1 + strlen(basen) + 1);
char *newdst = (char *) malloc(len);
if (dst_is_root) {
snprintf(newdst, len, "/%s", basen);
@@ -1227,19 +1200,12 @@ static void handle_cd(afc_client_t afc, int argc, char** argv)
char* path = get_realpath(argv[0]);
int is_dir = 0;
- char **info = NULL;
- afc_error_t err = afc_get_file_info(afc, path, &info);
+ plist_t info = NULL;
+ afc_error_t err = afc_get_file_info_plist(afc, path, &info);
if (err == AFC_E_SUCCESS && info) {
- int i;
- for (i = 0; info[i]; i += 2) {
- if (!strcmp(info[i], "st_ifmt")) {
- if (!strcmp(info[i+1], "S_IFDIR")) {
- is_dir = 1;
- }
- break;
- }
- }
- afc_dictionary_free(info);
+ const char* ifmt = plist_get_string_ptr(plist_dict_get_item(info, "st_ifmt"), NULL);
+ is_dir = (ifmt && !strcmp(ifmt, "S_IFDIR"));
+ plist_free(info);
} else {
printf("Error: Failed to get file info for %s: %s (%d)\n", path, afc_strerror(err), err);
free(path);
@@ -1483,7 +1449,7 @@ int main(int argc, char** argv)
};
signal(SIGTERM, handle_signal);
-#ifndef WIN32
+#ifndef _WIN32
signal(SIGQUIT, handle_signal);
signal(SIGPIPE, SIG_IGN);
#endif
@@ -1561,7 +1527,7 @@ int main(int argc, char** argv)
idevice_events_subscribe(&context, device_event_cb, NULL);
while (!connected && !stop_requested) {
-#ifdef WIN32
+#ifdef _WIN32
Sleep(100);
#else
usleep(100000);
diff --git a/tools/idevicebackup.c b/tools/idevicebackup.c
index c0537b8..363abad 100644
--- a/tools/idevicebackup.c
+++ b/tools/idevicebackup.c
@@ -35,6 +35,7 @@
#include <unistd.h>
#include <ctype.h>
#include <time.h>
+#include <sys/stat.h>
#include <libimobiledevice/libimobiledevice.h>
#include <libimobiledevice/lockdown.h>
@@ -51,7 +52,7 @@
#define LOCK_ATTEMPTS 50
#define LOCK_WAIT 200000
-#ifdef WIN32
+#ifdef _WIN32
#include <windows.h>
#define sleep(x) Sleep(x*1000)
#endif
@@ -175,7 +176,13 @@ static plist_t mobilebackup_factory_info_plist_new(const char* udid)
if (value_node)
plist_dict_set_item(ret, "IMEI", plist_copy(value_node));
- plist_dict_set_item(ret, "Last Backup Date", plist_new_date(time(NULL) - MAC_EPOCH, 0));
+ plist_dict_set_item(ret, "Last Backup Date",
+#ifdef HAVE_PLIST_UNIX_DATE
+ plist_new_unix_date(time(NULL))
+#else
+ plist_new_date(time(NULL) - MAC_EPOCH, 0)
+#endif
+ );
value_node = plist_dict_get_item(root_node, "ProductType");
plist_dict_set_item(ret, "Product Type", plist_copy(value_node));
@@ -212,7 +219,11 @@ static void mobilebackup_info_update_last_backup_date(plist_t info_plist)
return;
node = plist_dict_get_item(info_plist, "Last Backup Date");
+#ifdef HAVE_PLIST_UNIX_DATE
+ plist_set_unix_date_val(node, time(NULL));
+#else
plist_set_date_val(node, time(NULL) - MAC_EPOCH, 0);
+#endif
node = NULL;
}
@@ -642,7 +653,7 @@ int main(int argc, char *argv[])
/* we need to exit cleanly on running backups and restores or we cause havok */
signal(SIGINT, clean_exit);
signal(SIGTERM, clean_exit);
-#ifndef WIN32
+#ifndef _WIN32
signal(SIGQUIT, clean_exit);
signal(SIGPIPE, SIG_IGN);
#endif
@@ -1352,7 +1363,7 @@ files_out:
file_info_path = mobilebackup_build_path(backup_directory, hash, ".mddata");
/* determine file size */
-#ifdef WIN32
+#ifdef _WIN32
struct _stati64 fst;
if (_stati64(file_info_path, &fst) != 0)
#else
diff --git a/tools/idevicebackup2.c b/tools/idevicebackup2.c
index c73b269..12d6083 100644
--- a/tools/idevicebackup2.c
+++ b/tools/idevicebackup2.c
@@ -54,7 +54,7 @@
#define LOCK_ATTEMPTS 50
#define LOCK_WAIT 200000
-#ifdef WIN32
+#ifdef _WIN32
#include <windows.h>
#include <conio.h>
#define sleep(x) Sleep(x*1000)
@@ -74,6 +74,7 @@
static int verbose = 1;
static int quit_flag = 0;
+static int passcode_requested = 0;
#define PRINT_VERBOSE(min_level, ...) if (verbose >= min_level) { printf(__VA_ARGS__); };
@@ -115,6 +116,10 @@ static void notify_cb(const char *notification, void *userdata)
quit_flag++;
} else if (!strcmp(notification, NP_BACKUP_DOMAIN_CHANGED)) {
backup_domain_changed = 1;
+ } else if (!strcmp(notification, "com.apple.LocalAuthentication.ui.presented")) {
+ passcode_requested = 1;
+ } else if (!strcmp(notification, "com.apple.LocalAuthentication.ui.dismissed")) {
+ passcode_requested = 0;
} else {
PRINT_VERBOSE(1, "Unhandled notification '%s' (TODO: implement)\n", notification);
}
@@ -126,21 +131,15 @@ static void mobilebackup_afc_get_file_contents(afc_client_t afc, const char *fil
return;
}
- char **fileinfo = NULL;
+ plist_t fileinfo = NULL;
uint32_t fsize = 0;
- afc_get_file_info(afc, filename, &fileinfo);
+ afc_get_file_info_plist(afc, filename, &fileinfo);
if (!fileinfo) {
return;
}
- int i;
- for (i = 0; fileinfo[i]; i+=2) {
- if (!strcmp(fileinfo[i], "st_size")) {
- fsize = atol(fileinfo[i+1]);
- break;
- }
- }
- afc_dictionary_free(fileinfo);
+ fsize = plist_dict_get_uint(fileinfo, "st_size");
+ plist_free(fileinfo);
if (fsize == 0) {
return;
@@ -173,7 +172,7 @@ static void mobilebackup_afc_get_file_contents(afc_client_t afc, const char *fil
static int __mkdir(const char* path, int mode)
{
-#ifdef WIN32
+#ifdef _WIN32
return mkdir(path);
#else
return mkdir(path, mode);
@@ -202,7 +201,7 @@ static int mkdir_with_parents(const char *dir, int mode)
return res;
}
-#ifdef WIN32
+#ifdef _WIN32
static int win32err_to_errno(int err_value)
{
switch (err_value) {
@@ -219,7 +218,7 @@ static int win32err_to_errno(int err_value)
static int remove_file(const char* path)
{
int e = 0;
-#ifdef WIN32
+#ifdef _WIN32
if (!DeleteFile(path)) {
e = win32err_to_errno(GetLastError());
}
@@ -234,7 +233,7 @@ static int remove_file(const char* path)
static int remove_directory(const char* path)
{
int e = 0;
-#ifdef WIN32
+#ifdef _WIN32
if (!RemoveDirectory(path)) {
e = win32err_to_errno(GetLastError());
}
@@ -455,7 +454,13 @@ static plist_t mobilebackup_factory_info_plist_new(const char* udid, idevice_t d
/* Installed Applications */
plist_dict_set_item(ret, "Installed Applications", installed_apps);
- plist_dict_set_item(ret, "Last Backup Date", plist_new_date(time(NULL) - MAC_EPOCH, 0));
+ plist_dict_set_item(ret, "Last Backup Date",
+#ifdef HAVE_PLIST_UNIX_DATE
+ plist_new_unix_date(time(NULL))
+#else
+ plist_new_date(time(NULL) - MAC_EPOCH, 0)
+#endif
+ );
value_node = plist_dict_get_item(root_node, "MobileEquipmentIdentifier");
if (value_node)
@@ -768,7 +773,7 @@ static int mb2_handle_send_file(mobilebackup2_client_t mobilebackup2, const char
uint32_t bytes = 0;
char *localfile = string_build_path(backup_dir, path, NULL);
char buf[32768];
-#ifdef WIN32
+#ifdef _WIN32
struct _stati64 fst;
#else
struct stat fst;
@@ -779,7 +784,7 @@ static int mb2_handle_send_file(mobilebackup2_client_t mobilebackup2, const char
int errcode = -1;
int result = -1;
uint32_t length;
-#ifdef WIN32
+#ifdef _WIN32
uint64_t total;
uint64_t sent;
#else
@@ -810,7 +815,7 @@ static int mb2_handle_send_file(mobilebackup2_client_t mobilebackup2, const char
goto leave_proto_err;
}
-#ifdef WIN32
+#ifdef _WIN32
if (_stati64(localfile, &fst) < 0)
#else
if (stat(localfile, &fst) < 0)
@@ -1218,7 +1223,12 @@ static void mb2_handle_list_directory(mobilebackup2_client_t mobilebackup2, plis
plist_dict_set_item(fdict, "DLFileType", plist_new_string(ftype));
plist_dict_set_item(fdict, "DLFileSize", plist_new_uint(st.st_size));
plist_dict_set_item(fdict, "DLFileModificationDate",
- plist_new_date(st.st_mtime - MAC_EPOCH, 0));
+#ifdef HAVE_PLIST_UNIX_DATE
+ plist_new_unix_date(st.st_mtime)
+#else
+ plist_new_date(st.st_mtime - MAC_EPOCH, 0)
+#endif
+ );
plist_dict_set_item(dirlist, ep->d_name, fdict);
free(fpath);
@@ -1343,7 +1353,7 @@ static void mb2_copy_directory_by_path(const char *src, const char *dst)
}
}
-#ifdef WIN32
+#ifdef _WIN32
#define BS_CC '\b'
#define my_getch getch
#else
@@ -1463,8 +1473,6 @@ static void print_usage(int argc, char **argv, int is_error)
);
}
-#define DEVICE_VERSION(maj, min, patch) ((((maj) & 0xFF) << 16) | (((min) & 0xFF) << 8) | ((patch) & 0xFF))
-
int main(int argc, char *argv[])
{
idevice_error_t ret = IDEVICE_E_UNKNOWN_ERROR;
@@ -1530,7 +1538,7 @@ int main(int argc, char *argv[])
/* we need to exit cleanly on running backups and restores or we cause havok */
signal(SIGINT, clean_exit);
signal(SIGTERM, clean_exit);
-#ifndef WIN32
+#ifndef _WIN32
signal(SIGQUIT, clean_exit);
signal(SIGPIPE, SIG_IGN);
#endif
@@ -1844,34 +1852,20 @@ int main(int argc, char *argv[])
}
/* get ProductVersion */
- char *product_version = NULL;
- int device_version = 0;
- node_tmp = NULL;
- lockdownd_get_value(lockdown, NULL, "ProductVersion", &node_tmp);
- if (node_tmp) {
- if (plist_get_node_type(node_tmp) == PLIST_STRING) {
- plist_get_string_val(node_tmp, &product_version);
- }
- plist_free(node_tmp);
- node_tmp = NULL;
- }
- if (product_version) {
- int vers[3] = { 0, 0, 0 };
- if (sscanf(product_version, "%d.%d.%d", &vers[0], &vers[1], &vers[2]) >= 2) {
- device_version = DEVICE_VERSION(vers[0], vers[1], vers[2]);
- }
- }
+ int device_version = idevice_get_device_version(device);
/* start notification_proxy */
ldret = lockdownd_start_service(lockdown, NP_SERVICE_NAME, &service);
if ((ldret == LOCKDOWN_E_SUCCESS) && service && service->port) {
np_client_new(device, service, &np);
np_set_notify_callback(np, notify_cb, NULL);
- const char *noties[5] = {
+ const char *noties[7] = {
NP_SYNC_CANCEL_REQUEST,
NP_SYNC_SUSPEND_REQUEST,
NP_SYNC_RESUME_REQUEST,
NP_BACKUP_DOMAIN_CHANGED,
+ "com.apple.LocalAuthentication.ui.presented",
+ "com.apple.LocalAuthentication.ui.dismissed",
NULL
};
np_observe_notifications(np, noties);
@@ -2058,6 +2052,16 @@ checkpoint:
} else {
PRINT_VERBOSE(1, "Incremental backup mode.\n");
}
+ if (device_version >= IDEVICE_DEVICE_VERSION(16,1,0)) {
+ /* let's wait 2 second to see if the device passcode is requested */
+ int retries = 20;
+ while (retries-- > 0 && !passcode_requested) {
+ usleep(100000);
+ }
+ if (passcode_requested) {
+ printf("*** Waiting for passcode to be entered on the device ***\n");
+ }
+ }
} else {
if (err == MOBILEBACKUP2_E_BAD_VERSION) {
printf("ERROR: Could not start backup process: backup protocol version mismatch!\n");
@@ -2229,7 +2233,7 @@ checkpoint:
if (newpw || backup_password) {
mobilebackup2_send_message(mobilebackup2, "ChangePassword", opts);
uint8_t passcode_hint = 0;
- if (device_version >= DEVICE_VERSION(13,0,0)) {
+ if (device_version >= IDEVICE_DEVICE_VERSION(13,0,0)) {
diagnostics_relay_client_t diag = NULL;
if (diagnostics_relay_client_start_service(device, &diag, TOOL_NAME) == DIAGNOSTICS_RELAY_E_SUCCESS) {
plist_t dict = NULL;
@@ -2307,7 +2311,7 @@ checkpoint:
/* device wants to know how much disk space is available on the computer */
uint64_t freespace = 0;
int res = -1;
-#ifdef WIN32
+#ifdef _WIN32
if (GetDiskFreeSpaceEx(backup_directory, (PULARGE_INTEGER)&freespace, NULL, NULL)) {
res = 0;
}
@@ -2316,7 +2320,7 @@ checkpoint:
memset(&fs, '\0', sizeof(fs));
res = statvfs(backup_directory, &fs);
if (res == 0) {
- freespace = (uint64_t)fs.f_bavail * (uint64_t)fs.f_bsize;
+ freespace = (uint64_t)fs.f_bavail * (uint64_t)fs.f_frsize;
}
#endif
plist_t freespace_item = plist_new_uint(freespace);
diff --git a/tools/idevicebtlogger.c b/tools/idevicebtlogger.c
index 8de6b22..ca68b59 100644
--- a/tools/idevicebtlogger.c
+++ b/tools/idevicebtlogger.c
@@ -36,7 +36,7 @@
#include <assert.h>
#include <fcntl.h>
-#ifdef WIN32
+#ifdef _WIN32
#include <windows.h>
#define sleep(x) Sleep(x*1000)
#else
@@ -334,7 +334,7 @@ int main(int argc, char *argv[])
signal(SIGINT, clean_exit);
signal(SIGTERM, clean_exit);
-#ifndef WIN32
+#ifndef _WIN32
signal(SIGQUIT, clean_exit);
signal(SIGPIPE, SIG_IGN);
#endif
diff --git a/tools/idevicecrashreport.c b/tools/idevicecrashreport.c
index 09bd537..b9869ae 100644
--- a/tools/idevicecrashreport.c
+++ b/tools/idevicecrashreport.c
@@ -31,7 +31,8 @@
#include <string.h>
#include <unistd.h>
#include <getopt.h>
-#ifndef WIN32
+#include <sys/stat.h>
+#ifndef _WIN32
#include <signal.h>
#endif
#include <libimobiledevice-glue/utils.h>
@@ -42,7 +43,7 @@
#include <libimobiledevice/afc.h>
#include <plist/plist.h>
-#ifdef WIN32
+#ifdef _WIN32
#include <windows.h>
#define S_IFLNK S_IFREG
#define S_IFSOCK S_IFREG
@@ -54,11 +55,12 @@
const char* target_directory = NULL;
static int extract_raw_crash_reports = 0;
static int keep_crash_reports = 0;
+static int remove_all = 0;
static int file_exists(const char* path)
{
struct stat tst;
-#ifdef WIN32
+#ifdef _WIN32
return (stat(path, &tst) == 0);
#else
return (lstat(path, &tst) == 0);
@@ -144,7 +146,7 @@ static int afc_client_copy_and_remove_crash_reports(afc_client_t afc, const char
continue;
}
- char **fileinfo = NULL;
+ plist_t fileinfo = NULL;
struct stat stbuf;
memset(&stbuf, '\0', sizeof(struct stat));
@@ -152,7 +154,7 @@ static int afc_client_copy_and_remove_crash_reports(afc_client_t afc, const char
strcpy(((char*)source_filename) + device_directory_length, list[k]);
/* assemble absolute target filename */
-#ifdef WIN32
+#ifdef _WIN32
/* replace every ':' with '-' since ':' is an illegal character for file names in windows */
char* current_pos = strchr(list[k], ':');
while (current_pos) {
@@ -171,88 +173,93 @@ static int afc_client_copy_and_remove_crash_reports(afc_client_t afc, const char
}
/* get file information */
- afc_get_file_info(afc, source_filename, &fileinfo);
+ afc_get_file_info_plist(afc, source_filename, &fileinfo);
if (!fileinfo) {
printf("Failed to read information for '%s'. Skipping...\n", source_filename);
continue;
}
/* parse file information */
- int i;
- for (i = 0; fileinfo[i]; i+=2) {
- if (!strcmp(fileinfo[i], "st_size")) {
- stbuf.st_size = atoll(fileinfo[i+1]);
- } else if (!strcmp(fileinfo[i], "st_ifmt")) {
- if (!strcmp(fileinfo[i+1], "S_IFREG")) {
- stbuf.st_mode = S_IFREG;
- } else if (!strcmp(fileinfo[i+1], "S_IFDIR")) {
- stbuf.st_mode = S_IFDIR;
- } else if (!strcmp(fileinfo[i+1], "S_IFLNK")) {
- stbuf.st_mode = S_IFLNK;
- } else if (!strcmp(fileinfo[i+1], "S_IFBLK")) {
- stbuf.st_mode = S_IFBLK;
- } else if (!strcmp(fileinfo[i+1], "S_IFCHR")) {
- stbuf.st_mode = S_IFCHR;
- } else if (!strcmp(fileinfo[i+1], "S_IFIFO")) {
- stbuf.st_mode = S_IFIFO;
- } else if (!strcmp(fileinfo[i+1], "S_IFSOCK")) {
- stbuf.st_mode = S_IFSOCK;
- }
- } else if (!strcmp(fileinfo[i], "st_nlink")) {
- stbuf.st_nlink = atoi(fileinfo[i+1]);
- } else if (!strcmp(fileinfo[i], "st_mtime")) {
- stbuf.st_mtime = (time_t)(atoll(fileinfo[i+1]) / 1000000000);
- } else if (!strcmp(fileinfo[i], "LinkTarget")) {
- /* report latest crash report filename */
- printf("Link: %s\n", (char*)target_filename + strlen(target_directory));
-
- /* remove any previous symlink */
- if (file_exists(target_filename)) {
- remove(target_filename);
- }
+ stbuf.st_size = plist_dict_get_uint(fileinfo, "st_size");
+ const char* s_ifmt = plist_get_string_ptr(plist_dict_get_item(fileinfo, "st_ifmt"), NULL);
+ if (s_ifmt) {
+ if (!strcmp(s_ifmt, "S_IFREG")) {
+ stbuf.st_mode = S_IFREG;
+ } else if (!strcmp(s_ifmt, "S_IFDIR")) {
+ stbuf.st_mode = S_IFDIR;
+ } else if (!strcmp(s_ifmt, "S_IFLNK")) {
+ stbuf.st_mode = S_IFLNK;
+ } else if (!strcmp(s_ifmt, "S_IFBLK")) {
+ stbuf.st_mode = S_IFBLK;
+ } else if (!strcmp(s_ifmt, "S_IFCHR")) {
+ stbuf.st_mode = S_IFCHR;
+ } else if (!strcmp(s_ifmt, "S_IFIFO")) {
+ stbuf.st_mode = S_IFIFO;
+ } else if (!strcmp(s_ifmt, "S_IFSOCK")) {
+ stbuf.st_mode = S_IFSOCK;
+ }
+ }
+ stbuf.st_nlink = plist_dict_get_uint(fileinfo, "st_nlink");
+ stbuf.st_mtime = (time_t)(plist_dict_get_uint(fileinfo, "st_mtime") / 1000000000);
+ const char* linktarget = plist_get_string_ptr(plist_dict_get_item(fileinfo, "LinkTarget"), NULL);
+ if (linktarget && !remove_all) {
+ /* report latest crash report filename */
+ printf("Link: %s\n", (char*)target_filename + strlen(target_directory));
+
+ /* remove any previous symlink */
+ if (file_exists(target_filename)) {
+ remove(target_filename);
+ }
-#ifndef WIN32
- /* use relative filename */
- char* b = strrchr(fileinfo[i+1], '/');
- if (b == NULL) {
- b = fileinfo[i+1];
+#ifndef _WIN32
+ /* use relative filename */
+ const char* b = strrchr(linktarget, '/');
+ if (b == NULL) {
+ b = linktarget;
} else {
- b++;
- }
+ b++;
+ }
- /* create a symlink pointing to latest log */
- if (symlink(b, target_filename) < 0) {
- fprintf(stderr, "Can't create symlink to %s\n", b);
- }
+ /* create a symlink pointing to latest log */
+ if (symlink(b, target_filename) < 0) {
+ fprintf(stderr, "Can't create symlink to %s\n", b);
+ }
#endif
- if (!keep_crash_reports)
- afc_remove_path(afc, source_filename);
+ if (!keep_crash_reports)
+ afc_remove_path(afc, source_filename);
- res = 0;
- }
+ res = 0;
}
/* free file information */
- afc_dictionary_free(fileinfo);
+ plist_free(fileinfo);
/* recurse into child directories */
if (S_ISDIR(stbuf.st_mode)) {
-#ifdef WIN32
- mkdir(target_filename);
+ if (!remove_all) {
+#ifdef _WIN32
+ mkdir(target_filename);
#else
- mkdir(target_filename, 0755);
+ mkdir(target_filename, 0755);
#endif
+ }
res = afc_client_copy_and_remove_crash_reports(afc, source_filename, target_filename, filename_filter);
/* remove directory from device */
- if (!keep_crash_reports)
+ if (!remove_all && !keep_crash_reports)
afc_remove_path(afc, source_filename);
} else if (S_ISREG(stbuf.st_mode)) {
if (filename_filter != NULL && strstr(source_filename, filename_filter) == NULL) {
continue;
}
+ if (remove_all) {
+ printf("Remove: %s\n", source_filename);
+ afc_remove_path(afc, source_filename);
+ continue;
+ }
+
/* copy file to host */
afc_error = afc_file_open(afc, source_filename, AFC_FOPEN_RDONLY, &handle);
if(afc_error != AFC_E_SUCCESS) {
@@ -331,6 +338,7 @@ static void print_usage(int argc, char **argv, int is_error)
" -f, --filter NAME filter crash reports by NAME (case sensitive)\n"
" -h, --help prints usage information\n"
" -v, --version prints version information\n"
+ " --remove-all remove all crash logs found\n"
"\n"
"Homepage: <" PACKAGE_URL ">\n"
"Bug Reports: <" PACKAGE_BUGREPORT ">\n"
@@ -361,10 +369,11 @@ int main(int argc, char* argv[])
{ "filter", required_argument, NULL, 'f' },
{ "extract", no_argument, NULL, 'e' },
{ "keep", no_argument, NULL, 'k' },
+ { "remove-all", no_argument, NULL, 1 },
{ NULL, 0, NULL, 0}
};
-#ifndef WIN32
+#ifndef _WIN32
signal(SIGPIPE, SIG_IGN);
#endif
@@ -405,6 +414,9 @@ int main(int argc, char* argv[])
case 'k':
keep_crash_reports = 1;
break;
+ case 1:
+ remove_all = 1;
+ break;
default:
print_usage(argc, argv, 1);
return 2;
@@ -414,12 +426,16 @@ int main(int argc, char* argv[])
argv += optind;
/* ensure a target directory was supplied */
- if (!argv[0]) {
- fprintf(stderr, "ERROR: missing target directory.\n");
- print_usage(argc+optind, argv-optind, 1);
- return 2;
+ if (!remove_all) {
+ if (!argv[0]) {
+ fprintf(stderr, "ERROR: missing target directory.\n");
+ print_usage(argc+optind, argv-optind, 1);
+ return 2;
+ }
+ target_directory = argv[0];
+ } else {
+ target_directory = ".";
}
- target_directory = argv[0];
/* check if target directory exists */
if (!file_exists(target_directory)) {
diff --git a/tools/idevicedate.c b/tools/idevicedate.c
index d05f63e..31b0cf7 100644
--- a/tools/idevicedate.c
+++ b/tools/idevicedate.c
@@ -33,7 +33,7 @@
#if HAVE_LANGINFO_CODESET
#include <langinfo.h>
#endif
-#ifndef WIN32
+#ifndef _WIN32
#include <signal.h>
#endif
@@ -43,7 +43,7 @@
#ifdef _DATE_FMT
#define DATE_FMT_LANGINFO nl_langinfo (_DATE_FMT)
#else
-#ifdef WIN32
+#ifdef _WIN32
#define DATE_FMT_LANGINFO "%a %b %#d %H:%M:%S %Z %Y"
#else
#define DATE_FMT_LANGINFO "%a %b %e %H:%M:%S %Z %Y"
@@ -104,7 +104,7 @@ int main(int argc, char *argv[])
{ NULL, 0, NULL, 0}
};
-#ifndef WIN32
+#ifndef _WIN32
signal(SIGPIPE, SIG_IGN);
#endif
/* parse cmdline args */
diff --git a/tools/idevicedebug.c b/tools/idevicedebug.c
index 36c594e..3f2e289 100644
--- a/tools/idevicedebug.c
+++ b/tools/idevicedebug.c
@@ -34,7 +34,7 @@
#include <libgen.h>
#include <getopt.h>
-#ifdef WIN32
+#ifdef _WIN32
#include <windows.h>
#define sleep(x) Sleep(x*1000)
#endif
@@ -239,7 +239,7 @@ int main(int argc, char *argv[])
/* map signals */
signal(SIGINT, on_signal);
signal(SIGTERM, on_signal);
-#ifndef WIN32
+#ifndef _WIN32
signal(SIGQUIT, on_signal);
signal(SIGPIPE, SIG_IGN);
#endif
diff --git a/tools/idevicedebugserverproxy.c b/tools/idevicedebugserverproxy.c
index 9fe7051..fb082b3 100644
--- a/tools/idevicedebugserverproxy.c
+++ b/tools/idevicedebugserverproxy.c
@@ -32,7 +32,7 @@
#include <getopt.h>
#include <errno.h>
#include <signal.h>
-#ifdef WIN32
+#ifdef _WIN32
#include <winsock2.h>
#include <windows.h>
#else
@@ -219,7 +219,7 @@ int main(int argc, char *argv[])
{ NULL, 0, NULL, 0}
};
-#ifndef WIN32
+#ifndef _WIN32
struct sigaction sa;
struct sigaction si;
memset(&sa, '\0', sizeof(struct sigaction));
diff --git a/tools/idevicedevmodectl.c b/tools/idevicedevmodectl.c
index bd1de6a..6bf1a1c 100644
--- a/tools/idevicedevmodectl.c
+++ b/tools/idevicedevmodectl.c
@@ -32,11 +32,11 @@
#include <sys/stat.h>
#include <unistd.h>
#include <errno.h>
-#ifndef WIN32
+#ifndef _WIN32
#include <signal.h>
#endif
-#ifdef WIN32
+#ifdef _WIN32
#include <windows.h>
#define __usleep(x) Sleep(x/1000)
#else
@@ -259,7 +259,7 @@ int main(int argc, char *argv[])
{ NULL, 0, NULL, 0}
};
-#ifndef WIN32
+#ifndef _WIN32
signal(SIGPIPE, SIG_IGN);
#endif
/* parse cmdline args */
diff --git a/tools/idevicediagnostics.c b/tools/idevicediagnostics.c
index e699bc4..365c0a4 100644
--- a/tools/idevicediagnostics.c
+++ b/tools/idevicediagnostics.c
@@ -31,7 +31,7 @@
#include <getopt.h>
#include <errno.h>
#include <time.h>
-#ifndef WIN32
+#ifndef _WIN32
#include <signal.h>
#endif
@@ -113,7 +113,7 @@ int main(int argc, char **argv)
{ NULL, 0, NULL, 0}
};
-#ifndef WIN32
+#ifndef _WIN32
signal(SIGPIPE, SIG_IGN);
#endif
/* parse cmdline args */
diff --git a/tools/ideviceenterrecovery.c b/tools/ideviceenterrecovery.c
index 29cc5c9..65eb882 100644
--- a/tools/ideviceenterrecovery.c
+++ b/tools/ideviceenterrecovery.c
@@ -30,7 +30,7 @@
#include <stdlib.h>
#include <getopt.h>
#include <errno.h>
-#ifndef WIN32
+#ifndef _WIN32
#include <signal.h>
#endif
@@ -70,7 +70,7 @@ int main(int argc, char *argv[])
{ NULL, 0, NULL, 0}
};
-#ifndef WIN32
+#ifndef _WIN32
signal(SIGPIPE, SIG_IGN);
#endif
/* parse cmdline args */
diff --git a/tools/ideviceimagemounter.c b/tools/ideviceimagemounter.c
index 52b0666..b319d05 100644
--- a/tools/ideviceimagemounter.c
+++ b/tools/ideviceimagemounter.c
@@ -36,7 +36,8 @@
#include <time.h>
#include <sys/time.h>
#include <inttypes.h>
-#ifndef WIN32
+#include <sys/stat.h>
+#ifndef _WIN32
#include <signal.h>
#endif
@@ -87,7 +88,7 @@ static void print_usage(int argc, char **argv, int is_error)
" mount PATH Mount the developer disk image at PATH.\n"
" For iOS 17+, PATH is a directory containing a .dmg image,\n"
" a BuildManifest.plist, and a Firmware sub-directory;\n"
- " for older versions PATH is a .dmg filename with a"
+ " for older versions PATH is a .dmg filename with a\n"
" .dmg.signature in the same directory, or with another\n"
" parameter pointing to a file elsewhere.\n"
" list List mounted disk images.\n"
@@ -183,7 +184,7 @@ int main(int argc, char **argv)
size_t image_size = 0;
char *image_sig_path = NULL;
-#ifndef WIN32
+#ifndef _WIN32
signal(SIGPIPE, SIG_IGN);
#endif
parse_opts(argc, argv);
@@ -267,23 +268,14 @@ int main(int argc, char **argv)
goto leave;
}
- plist_t pver = NULL;
- char *product_version = NULL;
- lockdownd_get_value(lckd, NULL, "ProductVersion", &pver);
- if (pver && plist_get_node_type(pver) == PLIST_STRING) {
- plist_get_string_val(pver, &product_version);
- }
+ unsigned int device_version = idevice_get_device_version(device);
+
disk_image_upload_type_t disk_image_upload_type = DISK_IMAGE_UPLOAD_TYPE_AFC;
- int product_version_major = 0;
- int product_version_minor = 0;
- if (product_version) {
- if (sscanf(product_version, "%d.%d.%*d", &product_version_major, &product_version_minor) == 2) {
- if (product_version_major >= 7)
- disk_image_upload_type = DISK_IMAGE_UPLOAD_TYPE_UPLOAD_IMAGE;
- }
+ if (device_version >= IDEVICE_DEVICE_VERSION(7,0,0)) {
+ disk_image_upload_type = DISK_IMAGE_UPLOAD_TYPE_UPLOAD_IMAGE;
}
- if (product_version_major >= 16) {
+ if (device_version >= IDEVICE_DEVICE_VERSION(16,0,0)) {
uint8_t dev_mode_status = 0;
plist_t val = NULL;
ldret = lockdownd_get_value(lckd, "com.apple.security.mac.amfi", "DeveloperModeStatus", &val);
@@ -336,7 +328,7 @@ int main(int argc, char **argv)
goto leave;
}
image_size = fst.st_size;
- if (product_version_major < 17 && stat(image_sig_path, &fst) != 0) {
+ if (device_version < IDEVICE_DEVICE_VERSION(17,0,0) && stat(image_sig_path, &fst) != 0) {
fprintf(stderr, "ERROR: stat: %s: %s\n", image_sig_path, strerror(errno));
goto leave;
}
@@ -351,7 +343,7 @@ int main(int argc, char **argv)
if (cmd == CMD_LIST) {
/* list mounts mode */
if (!imagetype) {
- if (product_version_major < 17) {
+ if (device_version < IDEVICE_DEVICE_VERSION(17,0,0)) {
imagetype = "Developer";
} else {
imagetype = "Personalized";
@@ -371,14 +363,17 @@ int main(int argc, char **argv)
struct stat fst;
plist_t mount_options = NULL;
- if (product_version_major < 17) {
+ if (device_version < IDEVICE_DEVICE_VERSION(17,0,0)) {
f = fopen(image_sig_path, "rb");
if (!f) {
fprintf(stderr, "Error opening signature file '%s': %s\n", image_sig_path, strerror(errno));
goto leave;
}
- fstat(fileno(f), &fst);
- sig = malloc(sig_length);
+ if (fstat(fileno(f), &fst) != 0) {
+ fprintf(stderr, "Error: fstat: %s\n", strerror(errno));
+ goto leave;
+ }
+ sig = malloc(fst.st_size);
sig_length = fread(sig, 1, fst.st_size, f);
fclose(f);
if (sig_length == 0) {
@@ -603,20 +598,13 @@ int main(int argc, char **argv)
case DISK_IMAGE_UPLOAD_TYPE_AFC:
default:
printf("Uploading %s --> afc:///%s\n", image_path, targetname);
- char **strs = NULL;
- if (afc_get_file_info(afc, PKG_PATH, &strs) != AFC_E_SUCCESS) {
+ plist_t fileinfo = NULL;
+ if (afc_get_file_info_plist(afc, PKG_PATH, &fileinfo) != AFC_E_SUCCESS) {
if (afc_make_directory(afc, PKG_PATH) != AFC_E_SUCCESS) {
fprintf(stderr, "WARNING: Could not create directory '%s' on device!\n", PKG_PATH);
}
}
- if (strs) {
- int i = 0;
- while (strs[i]) {
- free(strs[i]);
- i++;
- }
- free(strs);
- }
+ plist_free(fileinfo);
uint64_t af = 0;
if ((afc_file_open(afc, targetname, AFC_FOPEN_WRONLY, &af) !=
diff --git a/tools/ideviceinfo.c b/tools/ideviceinfo.c
index fd45763..20cc916 100644
--- a/tools/ideviceinfo.c
+++ b/tools/ideviceinfo.c
@@ -31,7 +31,7 @@
#include <errno.h>
#include <stdlib.h>
#include <getopt.h>
-#ifndef WIN32
+#ifndef _WIN32
#include <signal.h>
#endif
@@ -152,7 +152,7 @@ int main(int argc, char *argv[])
{ NULL, 0, NULL, 0}
};
-#ifndef WIN32
+#ifndef _WIN32
signal(SIGPIPE, SIG_IGN);
#endif
diff --git a/tools/idevicename.c b/tools/idevicename.c
index 69b76f6..248bda3 100644
--- a/tools/idevicename.c
+++ b/tools/idevicename.c
@@ -30,7 +30,7 @@
#include <unistd.h>
#include <stdlib.h>
#include <getopt.h>
-#ifndef WIN32
+#ifndef _WIN32
#include <signal.h>
#endif
@@ -72,7 +72,7 @@ int main(int argc, char** argv)
const char* udid = NULL;
int use_network = 0;
-#ifndef WIN32
+#ifndef _WIN32
signal(SIGPIPE, SIG_IGN);
#endif
diff --git a/tools/idevicenotificationproxy.c b/tools/idevicenotificationproxy.c
index d1e25c1..192192a 100644
--- a/tools/idevicenotificationproxy.c
+++ b/tools/idevicenotificationproxy.c
@@ -2,7 +2,8 @@
* idevicenotificationproxy.c
* Simple client for the notification_proxy service
*
- * Copyright (c) 2009-2015 Martin Szulecki All Rights Reserved.
+ * Copyright (c) 2018-2024 Nikias Bassen, All Rights Reserved.
+ * Copyright (c) 2009-2015 Martin Szulecki, All Rights Reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -32,7 +33,7 @@
#include <errno.h>
#include <signal.h>
-#ifdef WIN32
+#ifdef _WIN32
#include <windows.h>
#define sleep(x) Sleep(x*1000)
#else
@@ -75,6 +76,7 @@ static void print_usage(int argc, char **argv, int is_error)
"\n"
"The following OPTIONS are accepted:\n"
" -u, --udid UDID target specific device by UDID\n"
+ " -i, --insecure use insecure notification proxy (non-paired device)\n"
" -n, --network connect to network device\n"
" -d, --debug enable communication debugging\n"
" -h, --help prints usage information\n"
@@ -102,6 +104,7 @@ int main(int argc, char *argv[])
int i = 0;
const char* udid = NULL;
int use_network = 0;
+ int insecure = 0;
int cmd = CMD_NONE;
char* cmd_arg = NULL;
@@ -114,6 +117,7 @@ int main(int argc, char *argv[])
{ "debug", no_argument, NULL, 'd' },
{ "help", no_argument, NULL, 'h' },
{ "udid", required_argument, NULL, 'u' },
+ { "insecure", no_argument, NULL, 'i' },
{ "network", no_argument, NULL, 'n' },
{ "version", no_argument, NULL, 'v' },
{ NULL, 0, NULL, 0}
@@ -121,13 +125,13 @@ int main(int argc, char *argv[])
signal(SIGINT, clean_exit);
signal(SIGTERM, clean_exit);
-#ifndef WIN32
+#ifndef _WIN32
signal(SIGQUIT, clean_exit);
signal(SIGPIPE, SIG_IGN);
#endif
/* parse cmdline args */
- while ((c = getopt_long(argc, argv, "dhu:nv", longopts, NULL)) != -1) {
+ while ((c = getopt_long(argc, argv, "dhu:inv", longopts, NULL)) != -1) {
switch (c) {
case 'd':
idevice_set_debug_level(1);
@@ -143,6 +147,9 @@ int main(int argc, char *argv[])
case 'n':
use_network = 1;
break;
+ case 'i':
+ insecure = 1;
+ break;
case 'h':
print_usage(argc, argv, 0);
return 0;
@@ -214,12 +221,17 @@ int main(int argc, char *argv[])
goto cleanup;
}
- if (LOCKDOWN_E_SUCCESS != (ret = lockdownd_client_new_with_handshake(device, &client, TOOL_NAME))) {
- fprintf(stderr, "ERROR: Could not connect to lockdownd, error code %d\n", ret);
+ if (insecure) {
+ ret = lockdownd_client_new(device, &client, TOOL_NAME);
+ } else {
+ ret = lockdownd_client_new_with_handshake(device, &client, TOOL_NAME);
+ }
+ if (LOCKDOWN_E_SUCCESS != ret) {
+ fprintf(stderr, "ERROR: Could not connect to lockdownd: %s (%d)\n", lockdownd_strerror(ret), ret);
goto cleanup;
}
- ret = lockdownd_start_service(client, NP_SERVICE_NAME, &service);
+ ret = lockdownd_start_service(client, (insecure) ? "com.apple.mobile.insecure_notification_proxy" : NP_SERVICE_NAME, &service);
lockdownd_client_free(client);
diff --git a/tools/idevicepair.c b/tools/idevicepair.c
index 94d3f04..884c690 100644
--- a/tools/idevicepair.c
+++ b/tools/idevicepair.c
@@ -32,7 +32,7 @@
#include <getopt.h>
#include <ctype.h>
#include <unistd.h>
-#ifdef WIN32
+#ifdef _WIN32
#include <windows.h>
#include <conio.h>
#else
@@ -50,7 +50,7 @@ static char *udid = NULL;
#ifdef HAVE_WIRELESS_PAIRING
-#ifdef WIN32
+#ifdef _WIN32
#define BS_CC '\b'
#define my_getch getch
#else
@@ -293,7 +293,7 @@ int main(int argc, char **argv)
}
}
-#ifndef WIN32
+#ifndef _WIN32
signal(SIGPIPE, SIG_IGN);
#endif
diff --git a/tools/ideviceprovision.c b/tools/ideviceprovision.c
index 4080a28..94f4ec5 100644
--- a/tools/ideviceprovision.c
+++ b/tools/ideviceprovision.c
@@ -32,11 +32,12 @@
#include <getopt.h>
#include <sys/stat.h>
#include <errno.h>
-#ifndef WIN32
+#ifndef _WIN32
#include <signal.h>
#endif
-#ifdef WIN32
+#ifdef _WIN32
+#include <winsock2.h>
#include <windows.h>
#else
#include <arpa/inet.h>
@@ -314,7 +315,7 @@ int main(int argc, char *argv[])
{ NULL, 0, NULL, 0}
};
-#ifndef WIN32
+#ifndef _WIN32
signal(SIGPIPE, SIG_IGN);
#endif
/* parse cmdline args */
@@ -475,27 +476,7 @@ int main(int argc, char *argv[])
return -1;
}
- plist_t pver = NULL;
- char *pver_s = NULL;
- lockdownd_get_value(client, NULL, "ProductVersion", &pver);
- if (pver && plist_get_node_type(pver) == PLIST_STRING) {
- plist_get_string_val(pver, &pver_s);
- }
- plist_free(pver);
- int product_version_major = 0;
- int product_version_minor = 0;
- int product_version_patch = 0;
- if (pver_s) {
- sscanf(pver_s, "%d.%d.%d", &product_version_major, &product_version_minor, &product_version_patch);
- free(pver_s);
- }
- if (product_version_major == 0) {
- fprintf(stderr, "ERROR: Could not determine the device's ProductVersion\n");
- lockdownd_client_free(client);
- idevice_free(device);
- return -1;
- }
- int product_version = ((product_version_major & 0xFF) << 16) | ((product_version_minor & 0xFF) << 8) | (product_version_patch & 0xFF);
+ unsigned int device_version = idevice_get_device_version(device);
lockdownd_error_t lerr = lockdownd_start_service(client, MISAGENT_SERVICE_NAME, &service);
if (lerr != LOCKDOWN_E_SUCCESS) {
@@ -546,7 +527,7 @@ int main(int argc, char *argv[])
{
plist_t profiles = NULL;
misagent_error_t merr;
- if (product_version < 0x090300) {
+ if (device_version < IDEVICE_DEVICE_VERSION(9,3,0)) {
merr = misagent_copy(mis, &profiles);
} else {
merr = misagent_copy_all(mis, &profiles);
@@ -631,7 +612,7 @@ int main(int argc, char *argv[])
/* remove all provisioning profiles */
plist_t profiles = NULL;
misagent_error_t merr;
- if (product_version < 0x090300) {
+ if (device_version < IDEVICE_DEVICE_VERSION(9,3,0)) {
merr = misagent_copy(mis, &profiles);
} else {
merr = misagent_copy_all(mis, &profiles);
diff --git a/tools/idevicescreenshot.c b/tools/idevicescreenshot.c
index 0e694c7..bfaa059 100644
--- a/tools/idevicescreenshot.c
+++ b/tools/idevicescreenshot.c
@@ -32,7 +32,7 @@
#include <errno.h>
#include <time.h>
#include <unistd.h>
-#ifndef WIN32
+#ifndef _WIN32
#include <signal.h>
#endif
@@ -142,7 +142,7 @@ int main(int argc, char **argv)
{ NULL, 0, NULL, 0}
};
-#ifndef WIN32
+#ifndef _WIN32
signal(SIGPIPE, SIG_IGN);
#endif
/* parse cmdline args */
diff --git a/tools/idevicesetlocation.c b/tools/idevicesetlocation.c
index 69fbaf5..dca8830 100644
--- a/tools/idevicesetlocation.c
+++ b/tools/idevicesetlocation.c
@@ -113,7 +113,7 @@ int main(int argc, char **argv)
if ((argc > 2) || (argc < 1)) {
print_usage(argc+optind, argv-optind, 1);
- return -1;
+ return 1;
}
if (argc == 2) {
@@ -123,7 +123,7 @@ int main(int argc, char **argv)
mode = RESET_LOCATION;
} else {
print_usage(argc+optind, argv-optind, 1);
- return -1;
+ return 1;
}
}
@@ -135,19 +135,30 @@ int main(int argc, char **argv)
} else {
printf("ERROR: No device found!\n");
}
- return -1;
+ return 1;
}
- lockdownd_client_t lockdown;
- lockdownd_client_new_with_handshake(device, &lockdown, TOOL_NAME);
+ lockdownd_client_t lockdown = NULL;
+ lockdownd_error_t lerr = lockdownd_client_new_with_handshake(device, &lockdown, TOOL_NAME);
+ if (lerr != LOCKDOWN_E_SUCCESS) {
+ idevice_free(device);
+ printf("ERROR: Could not connect to lockdownd: %s (%d)\n", lockdownd_strerror(lerr), lerr);
+ return 1;
+ }
lockdownd_service_descriptor_t svc = NULL;
- lockdownd_error_t lerr = lockdownd_start_service(lockdown, DT_SIMULATELOCATION_SERVICE, &svc);
+ lerr = lockdownd_start_service(lockdown, DT_SIMULATELOCATION_SERVICE, &svc);
if (lerr != LOCKDOWN_E_SUCCESS) {
+ unsigned int device_version = idevice_get_device_version(device);
lockdownd_client_free(lockdown);
idevice_free(device);
- printf("ERROR: Could not start the simulatelocation service: %s\nMake sure a developer disk image is mounted!\n", lockdownd_strerror(lerr));
- return -1;
+ printf("ERROR: Could not start the simulatelocation service: %s\n", lockdownd_strerror(lerr));
+ if (device_version >= IDEVICE_DEVICE_VERSION(17,0,0)) {
+ printf("Note: This tool is currently not supported on iOS 17+\n");
+ } else {
+ printf("Make sure a developer disk image is mounted!\n");
+ }
+ return 1;
}
lockdownd_client_free(lockdown);
@@ -158,10 +169,9 @@ int main(int argc, char **argv)
lockdownd_service_descriptor_free(svc);
if (serr != SERVICE_E_SUCCESS) {
- lockdownd_client_free(lockdown);
idevice_free(device);
printf("ERROR: Could not connect to simulatelocation service (%d)\n", serr);
- return -1;
+ return 1;
}
uint32_t l;
diff --git a/tools/idevicesyslog.c b/tools/idevicesyslog.c
index a0e641d..88af4c1 100644
--- a/tools/idevicesyslog.c
+++ b/tools/idevicesyslog.c
@@ -33,8 +33,9 @@
#include <stdlib.h>
#include <unistd.h>
#include <getopt.h>
+#include <time.h>
-#ifdef WIN32
+#ifdef _WIN32
#include <windows.h>
#define sleep(x) Sleep(x*1000)
#endif
@@ -42,10 +43,12 @@
#include <libimobiledevice/libimobiledevice.h>
#include <libimobiledevice/syslog_relay.h>
#include <libimobiledevice-glue/termcolors.h>
+#include <libimobiledevice/ostrace.h>
static int quit_flag = 0;
static int exit_on_disconnect = 0;
static int show_device_name = 0;
+static int force_syslog_relay = 0;
static char* udid = NULL;
static char** proc_filters = NULL;
@@ -58,6 +61,9 @@ static int num_pid_filters = 0;
static char** msg_filters = NULL;
static int num_msg_filters = 0;
+static char** msg_reverse_filters = NULL;
+static int num_msg_reverse_filters = 0;
+
static char** trigger_filters = NULL;
static int num_trigger_filters = 0;
static char** untrigger_filters = NULL;
@@ -66,11 +72,16 @@ static int triggered = 0;
static idevice_t device = NULL;
static syslog_relay_client_t syslog = NULL;
+static ostrace_client_t ostrace = NULL;
static const char QUIET_FILTER[] = "CircleJoinRequested|CommCenter|HeuristicInterpreter|MobileMail|PowerUIAgent|ProtectedCloudKeySyncing|SpringBoard|UserEventAgent|WirelessRadioManagerd|accessoryd|accountsd|aggregated|analyticsd|appstored|apsd|assetsd|assistant_service|backboardd|biometrickitd|bluetoothd|calaccessd|callservicesd|cloudd|com.apple.Safari.SafeBrowsing.Service|contextstored|corecaptured|coreduetd|corespeechd|cdpd|dasd|dataaccessd|distnoted|dprivacyd|duetexpertd|findmydeviced|fmfd|fmflocatord|gpsd|healthd|homed|identityservicesd|imagent|itunescloudd|itunesstored|kernel|locationd|maild|mDNSResponder|mediaremoted|mediaserverd|mobileassetd|nanoregistryd|nanotimekitcompaniond|navd|nsurlsessiond|passd|pasted|photoanalysisd|powerd|powerlogHelperd|ptpd|rapportd|remindd|routined|runningboardd|searchd|sharingd|suggestd|symptomsd|timed|thermalmonitord|useractivityd|vmd|wifid|wirelessproxd";
static int use_network = 0;
+static long long start_time = -1;
+static long long size_limit = -1;
+static long long age_limit = -1;
+
static char *line = NULL;
static int line_buffer_size = 0;
static int lp = 0;
@@ -129,6 +140,70 @@ static int find_char(char c, char** p, const char* end)
static void stop_logging(void);
+static int message_filter_matching(const char* message)
+{
+ if (num_msg_filters > 0) {
+ int found = 0;
+ int i;
+ for (i = 0; i < num_msg_filters; i++) {
+ if (strstr(message, msg_filters[i])) {
+ found = 1;
+ break;
+ }
+ }
+ if (!found) {
+ return 0;
+ }
+ }
+ if (num_msg_reverse_filters > 0) {
+ int found = 0;
+ int i;
+ for (i = 0; i < num_msg_reverse_filters; i++) {
+ if (strstr(message, msg_reverse_filters[i])) {
+ found = 1;
+ break;
+ }
+ }
+ if (found) {
+ return 0;
+ }
+ }
+ return 1;
+}
+
+static int process_filter_matching(int pid, const char* process_name, int process_name_length)
+{
+ int proc_matched = 0;
+ if (num_pid_filters > 0) {
+ int found = proc_filter_excluding;
+ int i = 0;
+ for (i = 0; i < num_pid_filters; i++) {
+ if (pid == pid_filters[i]) {
+ found = !proc_filter_excluding;
+ break;
+ }
+ }
+ if (found) {
+ proc_matched = 1;
+ }
+ }
+ if (num_proc_filters > 0 && !proc_matched) {
+ int found = proc_filter_excluding;
+ int i = 0;
+ for (i = 0; i < num_proc_filters; i++) {
+ if (!proc_filters[i]) continue;
+ if (strncmp(proc_filters[i], process_name, process_name_length) == 0) {
+ found = !proc_filter_excluding;
+ break;
+ }
+ }
+ if (found) {
+ proc_matched = 1;
+ }
+ }
+ return proc_matched;
+}
+
static void syslog_callback(char c, void *user_data)
{
if (lp >= line_buffer_size-1) {
@@ -202,20 +277,9 @@ static void syslog_callback(char c, void *user_data)
}
/* check message filters */
- if (num_msg_filters > 0) {
- int found = 0;
- int i;
- for (i = 0; i < num_msg_filters; i++) {
- if (strstr(device_name_end+1, msg_filters[i])) {
- found = 1;
- break;
- }
- }
- if (!found) {
- shall_print = 0;
- break;
- }
- shall_print = 1;
+ shall_print = message_filter_matching(device_name_end+1);
+ if (!shall_print) {
+ break;
}
/* process name */
@@ -235,39 +299,10 @@ static void syslog_callback(char c, void *user_data)
proc_name_end = p;
p++;
- int proc_matched = 0;
- if (num_pid_filters > 0) {
- char* endp = NULL;
- int pid_value = (int)strtol(pid_start, &endp, 10);
- if (endp && (*endp == ']')) {
- int found = proc_filter_excluding;
- int i = 0;
- for (i = 0; i < num_pid_filters; i++) {
- if (pid_value == pid_filters[i]) {
- found = !proc_filter_excluding;
- break;
- }
- }
- if (found) {
- proc_matched = 1;
- }
- }
- }
- if (num_proc_filters > 0 && !proc_matched) {
- int found = proc_filter_excluding;
- int i = 0;
- for (i = 0; i < num_proc_filters; i++) {
- if (!proc_filters[i]) continue;
- if (strncmp(proc_filters[i], process_name_start, process_name_end-process_name_start) == 0) {
- found = !proc_filter_excluding;
- break;
- }
- }
- if (found) {
- proc_matched = 1;
- }
- }
- if (proc_matched) {
+ /* match pid / process name */
+ char* endp = NULL;
+ int pid_value = (int)strtol(pid_start, &endp, 10);
+ if (process_filter_matching(pid_value, process_name_start, process_name_end-process_name_start)) {
shall_print = 1;
} else {
if (num_pid_filters > 0 || num_proc_filters > 0) {
@@ -331,7 +366,7 @@ static void syslog_callback(char c, void *user_data)
}
} while (0);
- if ((num_msg_filters == 0 && num_proc_filters == 0 && num_pid_filters == 0 && num_trigger_filters == 0 && num_untrigger_filters == 0) || shall_print) {
+ if ((num_msg_filters == 0 && num_msg_reverse_filters == 0 && num_proc_filters == 0 && num_pid_filters == 0 && num_trigger_filters == 0 && num_untrigger_filters == 0) || shall_print) {
fwrite(linep, 1, lp, stdout);
cprintf(COLOR_RESET);
fflush(stdout);
@@ -345,12 +380,231 @@ static void syslog_callback(char c, void *user_data)
}
}
-static int start_logging(void)
+static void ostrace_syslog_callback(const void* buf, size_t len, void* user_data)
{
- idevice_error_t ret = idevice_new_with_options(&device, udid, (use_network) ? IDEVICE_LOOKUP_NETWORK : IDEVICE_LOOKUP_USBMUX);
- if (ret != IDEVICE_E_SUCCESS) {
- fprintf(stderr, "Device with udid %s not found!?\n", udid);
- return -1;
+ if (len < 0x81) {
+ fprintf(stderr, "Error: not enough data in callback function?!\n");
+ return;
+ }
+
+ struct ostrace_packet_header_t *trace_hdr = (struct ostrace_packet_header_t*)buf;
+
+ if (trace_hdr->marker != 2 || (trace_hdr->type != 8 && trace_hdr->type != 2)) {
+ fprintf(stderr, "unexpected packet data %02x %08x\n", trace_hdr->marker, trace_hdr->type);
+ }
+
+ const char* dataptr = (const char*)buf + trace_hdr->header_size;
+ const char* process_name = dataptr;
+ const char* image_name = (trace_hdr->imagepath_len > 0) ? dataptr + trace_hdr->procpath_len : NULL;
+ const char* message = (trace_hdr->message_len > 0) ? dataptr + trace_hdr->procpath_len + trace_hdr->imagepath_len : NULL;
+ //const char* subsystem = (trace_hdr->subsystem_len > 0) ? dataptr + trace_hdr->procpath_len + trace_hdr->imagepath_len + trace_hdr->message_len : NULL;
+ //const char* category = (trace_hdr->category_len > 0) ? dataptr + trace_hdr->procpath_len + trace_hdr->imagepath_len + trace_hdr->message_len + trace_hdr->subsystem_len : NULL;
+
+ int shall_print = 1;
+ int trigger_off = 0;
+ const char* process_name_short = (process_name) ? strrchr(process_name, '/') : "";
+ process_name_short = (process_name_short) ? process_name_short+1 : process_name;
+ const char* image_name_short = (image_name) ? strrchr(image_name, '/') : NULL;
+ image_name_short = (image_name_short) ? image_name_short+1 : process_name;
+ if (image_name_short && !strcmp(image_name_short, process_name_short)) {
+ image_name_short = NULL;
+ }
+
+ do {
+ /* check if we have any triggers/untriggers */
+ if (num_untrigger_filters > 0 && triggered) {
+ int found = 0;
+ int i;
+ for (i = 0; i < num_untrigger_filters; i++) {
+ if (strstr(message, untrigger_filters[i])) {
+ found = 1;
+ break;
+ }
+ }
+ if (!found) {
+ shall_print = 1;
+ } else {
+ shall_print = 1;
+ trigger_off = 1;
+ }
+ } else if (num_trigger_filters > 0 && !triggered) {
+ int found = 0;
+ int i;
+ for (i = 0; i < num_trigger_filters; i++) {
+ if (strstr(message, trigger_filters[i])) {
+ found = 1;
+ break;
+ }
+ }
+ if (!found) {
+ shall_print = 0;
+ break;
+ }
+ triggered = 1;
+ shall_print = 1;
+ } else if (num_trigger_filters == 0 && num_untrigger_filters > 0 && !triggered) {
+ shall_print = 0;
+ quit_flag++;
+ break;
+ }
+
+ /* check message filters */
+ shall_print = message_filter_matching(message);
+ if (!shall_print) {
+ break;
+ }
+
+ /* check process filters */
+ if (process_filter_matching(trace_hdr->pid, process_name_short, strlen(process_name_short))) {
+ shall_print = 1;
+ } else {
+ if (num_pid_filters > 0 || num_proc_filters > 0) {
+ shall_print = 0;
+ }
+ }
+ if (!shall_print) {
+ break;
+ }
+ } while (0);
+
+ if (!shall_print) {
+ return;
+ }
+
+ const char* level_str = "Unknown";
+ const char* level_color = FG_YELLOW;
+ switch (trace_hdr->level) {
+ case 0:
+ level_str = "Notice";
+ level_color = FG_GREEN;
+ break;
+ case 0x01:
+ level_str = "Info";
+ level_color = FG_WHITE;
+ break;
+ case 0x02:
+ level_str = "Debug";
+ level_color = FG_MAGENTA;
+ break;
+ case 0x10:
+ level_str = "Error";
+ level_color = FG_RED;
+ break;
+ case 0x11:
+ level_str = "Fault";
+ level_color = FG_RED;
+ default:
+ break;
+ }
+
+ char datebuf[24];
+ struct tm *tp;
+ time_t time_sec = (time_t)trace_hdr->time_sec;
+#ifdef HAVE_LOCALTIME_R
+ struct tm tp_ = {0, };
+ tp = localtime_r(&time_sec, &tp_);
+#else
+ tp = localtime(&time_sec);
+#endif
+#ifdef _WIN32
+ strftime(datebuf, 16, "%b %#d %H:%M:%S", tp);
+#else
+ strftime(datebuf, 16, "%b %e %H:%M:%S", tp);
+#endif
+ snprintf(datebuf+15, 9, ".%06u", trace_hdr->time_usec);
+
+ /* write date and time */
+ cprintf(FG_LIGHT_GRAY "%s ", datebuf);
+
+ if (show_device_name) {
+ /* write device name TODO do we need this? */
+ //cprintf(FG_DARK_YELLOW "%s ", device_name);
+ }
+
+ /* write process name */
+ cprintf(FG_BRIGHT_CYAN "%s" FG_CYAN, process_name_short);
+ if (image_name_short) {
+ cprintf("(%s)", image_name_short);
+ }
+ cprintf("[%d]" COLOR_RESET " ", trace_hdr->pid);
+
+ /* write log level */
+ cprintf(level_color);
+ cprintf("<%s>:" COLOR_RESET " ", level_str);
+
+ /* write message */
+ cprintf(FG_WHITE);
+ cprintf("%s" COLOR_RESET "\n", message);
+ fflush(stdout);
+
+ if (trigger_off) {
+ triggered = 0;
+ }
+}
+
+static plist_t get_pid_list()
+{
+ plist_t list = NULL;
+ ostrace_client_t ostrace_tmp = NULL;
+ ostrace_client_start_service(device, &ostrace_tmp, TOOL_NAME);
+ if (ostrace_tmp) {
+ ostrace_get_pid_list(ostrace_tmp, &list);
+ ostrace_client_free(ostrace_tmp);
+ }
+ return list;
+}
+
+static int pid_valid(int pid)
+{
+ plist_t list = get_pid_list();
+ if (!list) return 0;
+ char valbuf[16];
+ snprintf(valbuf, 16, "%d", pid);
+ return (plist_dict_get_item(list, valbuf)) ? 1 : 0;
+}
+
+static int pid_for_proc(const char* procname)
+{
+ int result = -1;
+ plist_t list = get_pid_list();
+ if (!list) {
+ return result;
+ }
+ plist_dict_iter iter = NULL;
+ plist_dict_new_iter(list, &iter);
+ if (iter) {
+ plist_t node = NULL;
+ do {
+ char* key = NULL;
+ node = NULL;
+ plist_dict_next_item(list, iter, &key, &node);
+ if (!key) {
+ break;
+ }
+ if (PLIST_IS_DICT(node)) {
+ plist_t pname = plist_dict_get_item(node, "ProcessName");
+ if (PLIST_IS_STRING(pname)) {
+ if (!strcmp(plist_get_string_ptr(pname, NULL), procname)) {
+ result = (int)strtol(key, NULL, 10);
+ }
+ }
+ }
+ free(key);
+ } while (node);
+ plist_mem_free(iter);
+ }
+ plist_free(list);
+ return result;
+}
+
+static int connect_service(int ostrace_required)
+{
+ if (!device) {
+ idevice_error_t ret = idevice_new_with_options(&device, udid, (use_network) ? IDEVICE_LOOKUP_NETWORK : IDEVICE_LOOKUP_USBMUX);
+ if (ret != IDEVICE_E_SUCCESS) {
+ fprintf(stderr, "Device with udid %s not found!?\n", udid);
+ return -1;
+ }
}
lockdownd_client_t lockdown = NULL;
@@ -361,14 +615,28 @@ static int start_logging(void)
device = NULL;
return -1;
}
-
- /* start syslog_relay service */
lockdownd_service_descriptor_t svc = NULL;
- lerr = lockdownd_start_service(lockdown, SYSLOG_RELAY_SERVICE_NAME, &svc);
+
+ const char* service_name = OSTRACE_SERVICE_NAME;
+ int use_ostrace = 1;
+ if (idevice_get_device_version(device) < IDEVICE_DEVICE_VERSION(9,0,0) || force_syslog_relay) {
+ service_name = SYSLOG_RELAY_SERVICE_NAME;
+ use_ostrace = 0;
+ }
+ if (ostrace_required && !use_ostrace) {
+ fprintf(stderr, "ERROR: This operation requires iOS 9 or later.\n");
+ lockdownd_client_free(lockdown);
+ idevice_free(device);
+ device = NULL;
+ return -1;
+ }
+
+ /* start syslog_relay/os_trace_relay service */
+ lerr = lockdownd_start_service(lockdown, service_name, &svc);
if (lerr == LOCKDOWN_E_PASSWORD_PROTECTED) {
fprintf(stderr, "*** Device is passcode protected, enter passcode on the device to continue ***\n");
while (!quit_flag) {
- lerr = lockdownd_start_service(lockdown, SYSLOG_RELAY_SERVICE_NAME, &svc);
+ lerr = lockdownd_start_service(lockdown, service_name, &svc);
if (lerr != LOCKDOWN_E_PASSWORD_PROTECTED) {
break;
}
@@ -376,32 +644,84 @@ static int start_logging(void)
}
}
if (lerr != LOCKDOWN_E_SUCCESS) {
- fprintf(stderr, "ERROR: Could not connect to lockdownd: %d\n", lerr);
+ fprintf(stderr, "ERROR: Could not start %s service: %s (%d)\n", service_name, lockdownd_strerror(lerr), lerr);
idevice_free(device);
device = NULL;
return -1;
}
lockdownd_client_free(lockdown);
- /* connect to syslog_relay service */
- syslog_relay_error_t serr = SYSLOG_RELAY_E_UNKNOWN_ERROR;
- serr = syslog_relay_client_new(device, svc, &syslog);
- lockdownd_service_descriptor_free(svc);
- if (serr != SYSLOG_RELAY_E_SUCCESS) {
- fprintf(stderr, "ERROR: Could not start service com.apple.syslog_relay.\n");
- idevice_free(device);
- device = NULL;
+ if (use_ostrace) {
+ /* connect to os_trace_relay service */
+ ostrace_error_t serr = OSTRACE_E_UNKNOWN_ERROR;
+ serr = ostrace_client_new(device, svc, &ostrace);
+ lockdownd_service_descriptor_free(svc);
+ if (serr != OSTRACE_E_SUCCESS) {
+ fprintf(stderr, "ERROR: Could not connect to %s service (%d)\n", service_name, serr);
+ idevice_free(device);
+ device = NULL;
+ return -1;
+ }
+ } else {
+ /* connect to syslog_relay service */
+ syslog_relay_error_t serr = SYSLOG_RELAY_E_UNKNOWN_ERROR;
+ serr = syslog_relay_client_new(device, svc, &syslog);
+ lockdownd_service_descriptor_free(svc);
+ if (serr != SYSLOG_RELAY_E_SUCCESS) {
+ fprintf(stderr, "ERROR: Could not connect to %s service (%d)\n", service_name, serr);
+ idevice_free(device);
+ device = NULL;
+ return -1;
+ }
+ }
+ return 0;
+}
+
+static int start_logging(void)
+{
+ if (connect_service(0) < 0) {
return -1;
}
/* start capturing syslog */
- serr = syslog_relay_start_capture_raw(syslog, syslog_callback, NULL);
- if (serr != SYSLOG_RELAY_E_SUCCESS) {
- fprintf(stderr, "ERROR: Unable tot start capturing syslog.\n");
- syslog_relay_client_free(syslog);
- syslog = NULL;
- idevice_free(device);
- device = NULL;
+ if (ostrace) {
+ plist_t options = plist_new_dict();
+ if (num_proc_filters == 0 && num_pid_filters == 1 && !proc_filter_excluding) {
+ if (pid_filters[0] > 0) {
+ if (!pid_valid(pid_filters[0])) {
+ fprintf(stderr, "NOTE: A process with pid doesn't exists!\n");
+ }
+ }
+ plist_dict_set_item(options, "Pid", plist_new_int(pid_filters[0]));
+ } else if (num_proc_filters == 1 && num_pid_filters == 0 && !proc_filter_excluding) {
+ int pid = pid_for_proc(proc_filters[0]);
+ if (!strcmp(proc_filters[0], "kernel")) {
+ pid = 0;
+ }
+ if (pid >= 0) {
+ plist_dict_set_item(options, "Pid", plist_new_int(pid));
+ }
+ }
+ ostrace_error_t serr = ostrace_start_activity(ostrace, options, ostrace_syslog_callback, NULL);
+ if (serr != OSTRACE_E_SUCCESS) {
+ fprintf(stderr, "ERROR: Unable to start capturing syslog.\n");
+ ostrace_client_free(ostrace);
+ ostrace = NULL;
+ idevice_free(device);
+ device = NULL;
+ return -1;
+ }
+ } else if (syslog) {
+ syslog_relay_error_t serr = syslog_relay_start_capture_raw(syslog, syslog_callback, NULL);
+ if (serr != SYSLOG_RELAY_E_SUCCESS) {
+ fprintf(stderr, "ERROR: Unable to start capturing syslog.\n");
+ syslog_relay_client_free(syslog);
+ syslog = NULL;
+ idevice_free(device);
+ device = NULL;
+ return -1;
+ }
+ } else {
return -1;
}
@@ -419,6 +739,11 @@ static void stop_logging(void)
syslog_relay_client_free(syslog);
syslog = NULL;
}
+ if (ostrace) {
+ ostrace_stop_activity(ostrace);
+ ostrace_client_free(ostrace);
+ ostrace = NULL;
+ }
if (device) {
idevice_free(device);
@@ -426,6 +751,77 @@ static void stop_logging(void)
}
}
+static int write_callback(const void* buf, size_t len, void *user_data)
+{
+ FILE* f = (FILE*)user_data;
+ ssize_t res = fwrite(buf, 1, len, f);
+ if (res < 0) {
+ return -1;
+ }
+ if (quit_flag > 0) {
+ return -1;
+ }
+ return 0;
+}
+
+static void print_sorted_pidlist(plist_t list)
+{
+ struct listelem;
+ struct listelem {
+ int val;
+ struct listelem *next;
+ };
+ struct listelem* sortedlist = NULL;
+
+ plist_dict_iter iter = NULL;
+ plist_dict_new_iter(list, &iter);
+ if (iter) {
+ plist_t node = NULL;
+ do {
+ char* key = NULL;
+ node = NULL;
+ plist_dict_next_item(list, iter, &key, &node);
+ if (key) {
+ int pidval = (int)strtol(key, NULL, 10);
+ struct listelem* elem = (struct listelem*)malloc(sizeof(struct listelem));
+ elem->val = pidval;
+ elem->next = NULL;
+ struct listelem* prev = NULL;
+ struct listelem* curr = sortedlist;
+
+ while (curr && pidval > curr->val) {
+ prev = curr;
+ curr = curr->next;
+ }
+
+ elem->next = curr;
+ if (prev == NULL) {
+ sortedlist = elem;
+ } else {
+ prev->next = elem;
+ }
+ free(key);
+ }
+ } while (node);
+ plist_mem_free(iter);
+ }
+ struct listelem *listp = sortedlist;
+ char pidstr[16];
+ while (listp) {
+ snprintf(pidstr, 16, "%d", listp->val);
+ plist_t node = plist_dict_get_item(list, pidstr);
+ if (PLIST_IS_DICT(node)) {
+ plist_t pname = plist_dict_get_item(node, "ProcessName");
+ if (PLIST_IS_STRING(pname)) {
+ printf("%d %s\n", listp->val, plist_get_string_ptr(pname, NULL));
+ }
+ }
+ struct listelem *curr = listp;
+ listp = listp->next;
+ free(curr);
+ }
+}
+
static void device_event_cb(const idevice_event_t* event, void* userdata)
{
if (use_network && event->conn_type != CONNECTION_NETWORK) {
@@ -435,7 +831,7 @@ static void device_event_cb(const idevice_event_t* event, void* userdata)
return;
}
if (event->event == IDEVICE_DEVICE_ADD) {
- if (!syslog) {
+ if (!syslog && !ostrace) {
if (!udid) {
udid = strdup(event->udid);
}
@@ -446,7 +842,7 @@ static void device_event_cb(const idevice_event_t* event, void* userdata)
}
}
} else if (event->event == IDEVICE_DEVICE_REMOVE) {
- if (syslog && (strcmp(udid, event->udid) == 0)) {
+ if ((syslog || ostrace) && (strcmp(udid, event->udid) == 0)) {
stop_logging();
fprintf(stdout, "[disconnected:%s]\n", udid);
if (exit_on_disconnect) {
@@ -484,9 +880,20 @@ static void print_usage(int argc, char **argv, int is_error)
" -o, --output FILE write to FILE instead of stdout\n"
" (existing FILE will be overwritten)\n"
" --colors force writing colored output, e.g. for --output\n"
+ " --syslog-relay force use of syslog_relay service\n"
+ "\n"
+ "COMMANDS:\n"
+ " pidlist Print pid and name of all running processes.\n"
+ " archive PATH Request a logarchive and write it to PATH.\n"
+ " Output can be piped to another process using - as PATH.\n"
+ " The file data will be in .tar format.\n"
+ " --start-time VALUE start time of the log data as UNIX timestamp\n"
+ " --age-limit VALUE maximum age of the log data\n"
+ " --size-limit VALUE limit the size of the archive\n"
"\n"
"FILTER OPTIONS:\n"
" -m, --match STRING only print messages that contain STRING\n"
+ " -M, --unmatch STRING print messages that not contain STRING\n"
" -t, --trigger STRING start logging when matching STRING\n"
" -T, --untrigger STRING stop logging when matching STRING\n"
" -p, --process PROCESS only print messages from matching process(es)\n"
@@ -530,6 +937,12 @@ int main(int argc, char *argv[])
{ "quiet-list", no_argument, NULL, 1 },
{ "no-colors", no_argument, NULL, 2 },
{ "colors", no_argument, NULL, 3 },
+ { "syslog_relay", no_argument, NULL, 4 },
+ { "syslog-relay", no_argument, NULL, 4 },
+ { "legacy", no_argument, NULL, 4 },
+ { "start-time", required_argument, NULL, 5 },
+ { "size-limit", required_argument, NULL, 6 },
+ { "age-limit", required_argument, NULL, 7 },
{ "output", required_argument, NULL, 'o' },
{ "version", no_argument, NULL, 'v' },
{ NULL, 0, NULL, 0}
@@ -537,12 +950,12 @@ int main(int argc, char *argv[])
signal(SIGINT, clean_exit);
signal(SIGTERM, clean_exit);
-#ifndef WIN32
+#ifndef _WIN32
signal(SIGQUIT, clean_exit);
signal(SIGPIPE, SIG_IGN);
#endif
- while ((c = getopt_long(argc, argv, "dhu:nxt:T:m:e:p:qkKo:v", longopts, NULL)) != -1) {
+ while ((c = getopt_long(argc, argv, "dhu:nxt:T:m:M:e:p:qkKo:v", longopts, NULL)) != -1) {
switch (c) {
case 'd':
idevice_set_debug_level(1);
@@ -593,6 +1006,22 @@ int main(int argc, char *argv[])
num_msg_filters++;
}
break;
+ case 'M':
+ if (!*optarg) {
+ fprintf(stderr, "ERROR: reverse message filter string must not be empty!\n");
+ print_usage(argc, argv, 1);
+ return 2;
+ } else {
+ char **new_msg_filters = realloc(msg_reverse_filters, sizeof(char*) * (num_msg_reverse_filters+1));
+ if (!new_msg_filters) {
+ fprintf(stderr, "ERROR: realloc() failed\n");
+ exit(EXIT_FAILURE);
+ }
+ msg_reverse_filters = new_msg_filters;
+ msg_reverse_filters[num_msg_reverse_filters] = strdup(optarg);
+ num_msg_reverse_filters++;
+ }
+ break;
case 't':
if (!*optarg) {
fprintf(stderr, "ERROR: trigger filter string must not be empty!\n");
@@ -647,6 +1076,18 @@ int main(int argc, char *argv[])
case 3:
force_colors = 1;
break;
+ case 4:
+ force_syslog_relay = 1;
+ break;
+ case 5:
+ start_time = strtoll(optarg, NULL, 10);
+ break;
+ case 6:
+ size_limit = strtoll(optarg, NULL, 10);
+ break;
+ case 7:
+ age_limit = strtoll(optarg, NULL, 10);
+ break;
case 'o':
if (!*optarg) {
fprintf(stderr, "ERROR: --output option requires an argument!\n");
@@ -719,14 +1160,92 @@ int main(int argc, char *argv[])
argc -= optind;
argv += optind;
+ if (argc > 0) {
+ if (!strcmp(argv[0], "pidlist")) {
+ if (connect_service(1) < 0) {
+ return 1;
+ }
+ plist_t list = NULL;
+ ostrace_get_pid_list(ostrace, &list);
+ ostrace_client_free(ostrace);
+ ostrace = NULL;
+ idevice_free(device);
+ device = NULL;
+ if (!list) {
+ return 1;
+ }
+ print_sorted_pidlist(list);
+ plist_free(list);
+ return 0;
+ } else if (!strcmp(argv[0], "archive")) {
+ if (force_syslog_relay) {
+ force_syslog_relay = 0;
+ }
+ if (argc < 2) {
+ fprintf(stderr, "Please specify an output filename.\n");
+ return 1;
+ }
+ FILE* outf = NULL;
+ if (!strcmp(argv[1], "-")) {
+ if (isatty(1)) {
+ fprintf(stderr, "Refusing to directly write to stdout. Pipe the output to another process.\n");
+ return 1;
+ }
+ outf = stdout;
+ } else {
+ outf = fopen(argv[1], "w");
+ }
+ if (!outf) {
+ fprintf(stderr, "Failed to open %s: %s\n", argv[1], strerror(errno));
+ return 1;
+ }
+ if (connect_service(1) < 0) {
+ if (outf != stdout) {
+ fclose(outf);
+ }
+ return 1;
+ }
+ plist_t options = plist_new_dict();
+ if (start_time > 0) {
+ plist_dict_set_item(options, "StartTime", plist_new_int(start_time));
+ }
+ if (size_limit > 0) {
+ plist_dict_set_item(options, "SizeLimit", plist_new_int(size_limit));
+ }
+ if (age_limit > 0) {
+ plist_dict_set_item(options, "AgeLimit", plist_new_int(age_limit));
+ }
+ ostrace_create_archive(ostrace, options, write_callback, outf);
+ ostrace_client_free(ostrace);
+ ostrace = NULL;
+ idevice_free(device);
+ device = NULL;
+ if (outf != stdout) {
+ fclose(outf);
+ }
+ return 0;
+ } else {
+ fprintf(stderr, "Unknown command '%s'. See --help for valid commands.\n", argv[0]);
+ return 1;
+ }
+ }
+
int num = 0;
idevice_info_t *devices = NULL;
idevice_get_device_list_extended(&devices, &num);
+ int count = 0;
+ for (int i = 0; i < num; i++) {
+ if (devices[i]->conn_type == CONNECTION_NETWORK && use_network) {
+ count++;
+ } else if (devices[i]->conn_type == CONNECTION_USBMUXD) {
+ count++;
+ }
+ }
idevice_device_list_extended_free(devices);
- if (num == 0) {
+ if (count == 0) {
if (!udid) {
- fprintf(stderr, "No device found. Plug in a device or pass UDID with -u to wait for device to be available.\n");
- return -1;
+ fprintf(stderr, "No device found. Plug in a device or pass UDID with -u to wait for device to become available.\n");
+ return 1;
}
fprintf(stderr, "Waiting for device with UDID %s to become available...\n", udid);
@@ -761,6 +1280,13 @@ int main(int argc, char *argv[])
}
free(msg_filters);
}
+ if (num_msg_reverse_filters > 0) {
+ int i;
+ for (i = 0; i < num_msg_reverse_filters; i++) {
+ free(msg_reverse_filters[i]);
+ }
+ free(msg_reverse_filters);
+ }
if (num_trigger_filters > 0) {
int i;
for (i = 0; i < num_trigger_filters; i++) {