pymobiledevice3 6.2.0__py3-none-any.whl → 7.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pymobiledevice3/__main__.py +136 -44
- pymobiledevice3/_version.py +2 -2
- pymobiledevice3/bonjour.py +19 -20
- pymobiledevice3/cli/activation.py +24 -22
- pymobiledevice3/cli/afc.py +49 -41
- pymobiledevice3/cli/amfi.py +13 -18
- pymobiledevice3/cli/apps.py +71 -65
- pymobiledevice3/cli/backup.py +134 -93
- pymobiledevice3/cli/bonjour.py +31 -29
- pymobiledevice3/cli/cli_common.py +179 -232
- pymobiledevice3/cli/companion_proxy.py +12 -12
- pymobiledevice3/cli/crash.py +95 -52
- pymobiledevice3/cli/developer/__init__.py +62 -0
- pymobiledevice3/cli/developer/accessibility/__init__.py +65 -0
- pymobiledevice3/cli/developer/accessibility/settings.py +43 -0
- pymobiledevice3/cli/developer/arbitration.py +50 -0
- pymobiledevice3/cli/developer/condition.py +33 -0
- pymobiledevice3/cli/developer/core_device.py +294 -0
- pymobiledevice3/cli/developer/debugserver.py +244 -0
- pymobiledevice3/cli/developer/dvt/__init__.py +387 -0
- pymobiledevice3/cli/developer/dvt/core_profile_session.py +295 -0
- pymobiledevice3/cli/developer/dvt/simulate_location.py +56 -0
- pymobiledevice3/cli/developer/dvt/sysmon/__init__.py +69 -0
- pymobiledevice3/cli/developer/dvt/sysmon/process.py +188 -0
- pymobiledevice3/cli/developer/fetch_symbols.py +108 -0
- pymobiledevice3/cli/developer/simulate_location.py +51 -0
- pymobiledevice3/cli/diagnostics/__init__.py +75 -0
- pymobiledevice3/cli/diagnostics/battery.py +47 -0
- pymobiledevice3/cli/idam.py +18 -22
- pymobiledevice3/cli/lockdown.py +70 -75
- pymobiledevice3/cli/mounter.py +99 -57
- pymobiledevice3/cli/notification.py +38 -26
- pymobiledevice3/cli/pcap.py +36 -20
- pymobiledevice3/cli/power_assertion.py +15 -16
- pymobiledevice3/cli/processes.py +11 -17
- pymobiledevice3/cli/profile.py +120 -75
- pymobiledevice3/cli/provision.py +27 -26
- pymobiledevice3/cli/remote.py +108 -99
- pymobiledevice3/cli/restore.py +134 -129
- pymobiledevice3/cli/springboard.py +50 -50
- pymobiledevice3/cli/syslog.py +138 -74
- pymobiledevice3/cli/usbmux.py +66 -27
- pymobiledevice3/cli/version.py +2 -5
- pymobiledevice3/cli/webinspector.py +149 -103
- pymobiledevice3/remote/remote_service_discovery.py +11 -10
- pymobiledevice3/restore/device.py +28 -4
- pymobiledevice3/service_connection.py +1 -1
- pymobiledevice3/services/mobilebackup2.py +4 -1
- pymobiledevice3/services/screenshot.py +2 -2
- pymobiledevice3/services/web_protocol/automation_session.py +4 -2
- pymobiledevice3/services/web_protocol/cdp_screencast.py +2 -1
- pymobiledevice3/services/web_protocol/element.py +3 -3
- {pymobiledevice3-6.2.0.dist-info → pymobiledevice3-7.0.0.dist-info}/METADATA +3 -2
- {pymobiledevice3-6.2.0.dist-info → pymobiledevice3-7.0.0.dist-info}/RECORD +58 -45
- pymobiledevice3/cli/completions.py +0 -50
- pymobiledevice3/cli/developer.py +0 -1645
- pymobiledevice3/cli/diagnostics.py +0 -110
- {pymobiledevice3-6.2.0.dist-info → pymobiledevice3-7.0.0.dist-info}/WHEEL +0 -0
- {pymobiledevice3-6.2.0.dist-info → pymobiledevice3-7.0.0.dist-info}/entry_points.txt +0 -0
- {pymobiledevice3-6.2.0.dist-info → pymobiledevice3-7.0.0.dist-info}/licenses/LICENSE +0 -0
- {pymobiledevice3-6.2.0.dist-info → pymobiledevice3-7.0.0.dist-info}/top_level.txt +0 -0
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
from
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
from typing import Annotated, Literal
|
|
2
3
|
|
|
3
|
-
import click
|
|
4
4
|
import IPython
|
|
5
|
+
import typer
|
|
6
|
+
from typer_injector import InjectingTyper
|
|
5
7
|
|
|
6
|
-
from pymobiledevice3.cli.cli_common import
|
|
7
|
-
from pymobiledevice3.lockdown import LockdownClient
|
|
8
|
-
from pymobiledevice3.lockdown_service_provider import LockdownServiceProvider
|
|
8
|
+
from pymobiledevice3.cli.cli_common import ServiceProviderDep, print_json
|
|
9
9
|
from pymobiledevice3.services.springboard import SpringBoardServicesService
|
|
10
10
|
|
|
11
11
|
SHELL_USAGE = """
|
|
@@ -13,32 +13,28 @@ Use `service` to access the service features
|
|
|
13
13
|
"""
|
|
14
14
|
|
|
15
15
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
16
|
+
cli = InjectingTyper(
|
|
17
|
+
name="springboard",
|
|
18
|
+
help="Interact with SpringBoard UI (icons, wallpapers, orientation, shell).",
|
|
19
|
+
no_args_is_help=True,
|
|
20
|
+
)
|
|
21
|
+
state_cli = InjectingTyper(
|
|
22
|
+
name="state",
|
|
23
|
+
help="Icon state operations.",
|
|
24
|
+
no_args_is_help=True,
|
|
25
|
+
)
|
|
26
|
+
cli.add_typer(state_cli)
|
|
19
27
|
|
|
20
28
|
|
|
21
|
-
@
|
|
22
|
-
def
|
|
23
|
-
"""
|
|
24
|
-
pass
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
@springboard.group()
|
|
28
|
-
def state():
|
|
29
|
-
"""icons state options"""
|
|
30
|
-
pass
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
@state.command("get", cls=Command)
|
|
34
|
-
def state_get(service_provider: LockdownClient):
|
|
35
|
-
"""get icon state"""
|
|
29
|
+
@state_cli.command("get")
|
|
30
|
+
def state_get(service_provider: ServiceProviderDep) -> None:
|
|
31
|
+
"""Fetch the current icon layout/state."""
|
|
36
32
|
print_json(SpringBoardServicesService(lockdown=service_provider).get_icon_state())
|
|
37
33
|
|
|
38
34
|
|
|
39
|
-
@
|
|
40
|
-
def springboard_shell(service_provider:
|
|
41
|
-
"""
|
|
35
|
+
@cli.command("shell")
|
|
36
|
+
def springboard_shell(service_provider: ServiceProviderDep) -> None:
|
|
37
|
+
"""Open an IPython shell bound to SpringBoardServicesService."""
|
|
42
38
|
service = SpringBoardServicesService(lockdown=service_provider)
|
|
43
39
|
IPython.embed(
|
|
44
40
|
header=SHELL_USAGE,
|
|
@@ -48,43 +44,47 @@ def springboard_shell(service_provider: LockdownClient):
|
|
|
48
44
|
)
|
|
49
45
|
|
|
50
46
|
|
|
51
|
-
@
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
"""get application's icon"""
|
|
56
|
-
out.write(SpringBoardServicesService(lockdown=service_provider).get_icon_pngdata(bundle_id))
|
|
47
|
+
@cli.command("icon")
|
|
48
|
+
def springboard_icon(service_provider: ServiceProviderDep, bundle_id: str, out: Path) -> None:
|
|
49
|
+
"""Save an app's icon PNG to the given path."""
|
|
50
|
+
out.write_bytes(SpringBoardServicesService(lockdown=service_provider).get_icon_pngdata(bundle_id))
|
|
57
51
|
|
|
58
52
|
|
|
59
|
-
@
|
|
60
|
-
def springboard_orientation(service_provider:
|
|
61
|
-
"""
|
|
53
|
+
@cli.command("orientation")
|
|
54
|
+
def springboard_orientation(service_provider: ServiceProviderDep) -> None:
|
|
55
|
+
"""Print current screen orientation."""
|
|
62
56
|
print(SpringBoardServicesService(lockdown=service_provider).get_interface_orientation())
|
|
63
57
|
|
|
64
58
|
|
|
65
|
-
@
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
out.write(SpringBoardServicesService(lockdown=service_provider).get_wallpaper_pngdata())
|
|
59
|
+
@cli.command("wallpaper-home-screen")
|
|
60
|
+
def springboard_wallpaper_home_screen(service_provider: ServiceProviderDep, out: Path) -> None:
|
|
61
|
+
"""Save the homescreen wallpaper PNG to the given path."""
|
|
62
|
+
out.write_bytes(SpringBoardServicesService(lockdown=service_provider).get_wallpaper_pngdata())
|
|
70
63
|
|
|
71
64
|
|
|
72
|
-
@
|
|
73
|
-
@click.argument("wallpaper-name", type=click.Choice(["homescreen", "lockscreen"]))
|
|
74
|
-
@click.argument("out", type=click.File("wb"))
|
|
75
|
-
@click.option("-r", "--reload", is_flag=True, help="reload icon state before fetching image")
|
|
65
|
+
@cli.command("wallpaper-preview-image")
|
|
76
66
|
def springboard_wallpaper_preview_image(
|
|
77
|
-
service_provider:
|
|
67
|
+
service_provider: ServiceProviderDep,
|
|
68
|
+
wallpaper_name: Literal["homescreen", "lockscreen"],
|
|
69
|
+
out: Path,
|
|
70
|
+
reload: Annotated[
|
|
71
|
+
bool,
|
|
72
|
+
typer.Option(
|
|
73
|
+
"--reload",
|
|
74
|
+
"-r",
|
|
75
|
+
help="reload icon state before fetching image",
|
|
76
|
+
),
|
|
77
|
+
] = False,
|
|
78
78
|
) -> None:
|
|
79
|
-
"""
|
|
79
|
+
"""Save the preview image for the homescreen or lockscreen wallpaper (optionally reload state first)."""
|
|
80
80
|
with SpringBoardServicesService(lockdown=service_provider) as springboard_service:
|
|
81
81
|
if reload:
|
|
82
82
|
springboard_service.reload_icon_state()
|
|
83
|
-
out.
|
|
83
|
+
out.write_bytes(springboard_service.get_wallpaper_preview_image(wallpaper_name))
|
|
84
84
|
|
|
85
85
|
|
|
86
|
-
@
|
|
87
|
-
def springboard_homescreen_icon_metrics(service_provider:
|
|
88
|
-
"""
|
|
86
|
+
@cli.command("homescreen-icon-metrics")
|
|
87
|
+
def springboard_homescreen_icon_metrics(service_provider: ServiceProviderDep) -> None:
|
|
88
|
+
"""Print homescreen icon spacing/metrics."""
|
|
89
89
|
with SpringBoardServicesService(lockdown=service_provider) as springboard_service:
|
|
90
90
|
print_json(springboard_service.get_homescreen_icon_metrics())
|
pymobiledevice3/cli/syslog.py
CHANGED
|
@@ -2,38 +2,41 @@ import logging
|
|
|
2
2
|
import os
|
|
3
3
|
import posixpath
|
|
4
4
|
import re
|
|
5
|
-
from
|
|
6
|
-
|
|
7
|
-
import
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
from
|
|
5
|
+
from contextlib import nullcontext
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from typing import Annotated, Optional, TextIO
|
|
8
|
+
|
|
9
|
+
import typer
|
|
10
|
+
from typer_injector import InjectingTyper
|
|
11
|
+
|
|
12
|
+
from pymobiledevice3.cli.cli_common import (
|
|
13
|
+
ServiceProviderDep,
|
|
14
|
+
get_last_used_terminal_formatting,
|
|
15
|
+
user_requested_colored_output,
|
|
16
|
+
)
|
|
11
17
|
from pymobiledevice3.lockdown_service_provider import LockdownServiceProvider
|
|
12
|
-
from pymobiledevice3.services.os_trace import OsTraceService, SyslogLogLevel
|
|
18
|
+
from pymobiledevice3.services.os_trace import OsTraceService, SyslogEntry, SyslogLogLevel
|
|
13
19
|
from pymobiledevice3.services.syslog import SyslogService
|
|
14
20
|
|
|
15
21
|
logger = logging.getLogger(__name__)
|
|
16
22
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
@cli.group()
|
|
24
|
-
def syslog() -> None:
|
|
25
|
-
"""Watch syslog messages"""
|
|
26
|
-
pass
|
|
23
|
+
cli = InjectingTyper(
|
|
24
|
+
name="syslog",
|
|
25
|
+
help="Watch syslog messages",
|
|
26
|
+
no_args_is_help=True,
|
|
27
|
+
)
|
|
27
28
|
|
|
28
29
|
|
|
29
|
-
@
|
|
30
|
-
def syslog_live_old(service_provider:
|
|
30
|
+
@cli.command("live-old")
|
|
31
|
+
def syslog_live_old(service_provider: ServiceProviderDep) -> None:
|
|
31
32
|
"""view live syslog lines in raw bytes form from old relay"""
|
|
32
33
|
for line in SyslogService(service_provider=service_provider).watch():
|
|
33
34
|
print(line)
|
|
34
35
|
|
|
35
36
|
|
|
36
|
-
def format_line(
|
|
37
|
+
def format_line(
|
|
38
|
+
color: bool, pid: int, syslog_entry: SyslogEntry, include_label: bool, image_offset: bool = False
|
|
39
|
+
) -> Optional[str]:
|
|
37
40
|
log_level_colors = {
|
|
38
41
|
SyslogLogLevel.NOTICE.name: "white",
|
|
39
42
|
SyslogLogLevel.INFO.name: "white",
|
|
@@ -60,17 +63,17 @@ def format_line(color, pid, syslog_entry, include_label: bool, image_offset: boo
|
|
|
60
63
|
label = f"[{syslog_entry.label.subsystem}][{syslog_entry.label.category}]"
|
|
61
64
|
|
|
62
65
|
if color:
|
|
63
|
-
timestamp =
|
|
64
|
-
process_name =
|
|
66
|
+
timestamp = typer.style(str(timestamp), "green")
|
|
67
|
+
process_name = typer.style(process_name, "magenta")
|
|
65
68
|
if len(image_name) > 0:
|
|
66
|
-
image_name =
|
|
69
|
+
image_name = typer.style(image_name, "magenta")
|
|
67
70
|
if image_offset:
|
|
68
|
-
image_offset_str =
|
|
69
|
-
syslog_pid =
|
|
71
|
+
image_offset_str = typer.style(image_offset_str, "blue")
|
|
72
|
+
syslog_pid = typer.style(syslog_pid, "cyan")
|
|
70
73
|
log_level_color = log_level_colors[level]
|
|
71
|
-
level =
|
|
72
|
-
label =
|
|
73
|
-
message =
|
|
74
|
+
level = typer.style(level, log_level_color)
|
|
75
|
+
label = typer.style(label, "cyan")
|
|
76
|
+
message = typer.style(message, log_level_color)
|
|
74
77
|
|
|
75
78
|
line_format = "{timestamp} {process_name}{{{image_name}{image_offset_str}}}[{pid}] <{level}>: {message}"
|
|
76
79
|
|
|
@@ -93,7 +96,7 @@ def format_line(color, pid, syslog_entry, include_label: bool, image_offset: boo
|
|
|
93
96
|
def syslog_live(
|
|
94
97
|
service_provider: LockdownServiceProvider,
|
|
95
98
|
out: Optional[TextIO],
|
|
96
|
-
pid:
|
|
99
|
+
pid: int,
|
|
97
100
|
process_name: Optional[str],
|
|
98
101
|
match: list[str],
|
|
99
102
|
match_insensitive: list[str],
|
|
@@ -106,9 +109,9 @@ def syslog_live(
|
|
|
106
109
|
match_regex += [re.compile(f".*({r}).*", re.IGNORECASE | re.DOTALL) for r in insensitive_regex]
|
|
107
110
|
|
|
108
111
|
def replace(m):
|
|
109
|
-
if len(m.groups()):
|
|
110
|
-
return line.replace(m.group(1),
|
|
111
|
-
return
|
|
112
|
+
if len(m.groups()) and line:
|
|
113
|
+
return line.replace(m.group(1), typer.style(m.group(1), bold=True, underline=True))
|
|
114
|
+
return ""
|
|
112
115
|
|
|
113
116
|
for syslog_entry in OsTraceService(lockdown=service_provider).syslog(pid=pid):
|
|
114
117
|
if process_name and posixpath.basename(syslog_entry.filename) != process_name:
|
|
@@ -117,6 +120,9 @@ def syslog_live(
|
|
|
117
120
|
line_no_style = format_line(False, pid, syslog_entry, include_label, image_offset)
|
|
118
121
|
line = format_line(user_requested_colored_output(), pid, syslog_entry, include_label, image_offset)
|
|
119
122
|
|
|
123
|
+
if line_no_style is None or line is None:
|
|
124
|
+
continue
|
|
125
|
+
|
|
120
126
|
skip = False
|
|
121
127
|
|
|
122
128
|
if match is not None:
|
|
@@ -127,7 +133,7 @@ def syslog_live(
|
|
|
127
133
|
break
|
|
128
134
|
else:
|
|
129
135
|
if user_requested_colored_output():
|
|
130
|
-
match_line = match_line.replace(m,
|
|
136
|
+
match_line = match_line.replace(m, typer.style(m, bold=True, underline=True))
|
|
131
137
|
line = match_line
|
|
132
138
|
|
|
133
139
|
if match_insensitive is not None:
|
|
@@ -143,7 +149,7 @@ def syslog_live(
|
|
|
143
149
|
last_color_formatting = get_last_used_terminal_formatting(line[:start])
|
|
144
150
|
line = (
|
|
145
151
|
line[:start]
|
|
146
|
-
+
|
|
152
|
+
+ typer.style(line[start:end], bold=True, underline=True)
|
|
147
153
|
+ last_color_formatting
|
|
148
154
|
+ line[end:]
|
|
149
155
|
)
|
|
@@ -168,50 +174,108 @@ def syslog_live(
|
|
|
168
174
|
print(line_no_style, file=out, flush=True)
|
|
169
175
|
|
|
170
176
|
|
|
171
|
-
@
|
|
172
|
-
@click.option("-o", "--out", type=click.File("wt"), help="log file")
|
|
173
|
-
@click.option("--pid", type=click.INT, default=-1, help="pid to filter. -1 for all")
|
|
174
|
-
@click.option("-pn", "--process-name", help="process name to filter")
|
|
175
|
-
@click.option("-m", "--match", multiple=True, help="match expression")
|
|
176
|
-
@click.option("-mi", "--match-insensitive", multiple=True, help="insensitive match expression")
|
|
177
|
-
@click.option("include_label", "--label", is_flag=True, help="should include label")
|
|
178
|
-
@click.option("-e", "--regex", multiple=True, help="filter only lines matching given regex")
|
|
179
|
-
@click.option("-ei", "--insensitive-regex", multiple=True, help="filter only lines matching given regex (insensitive)")
|
|
180
|
-
@click.option("-io", "--image-offset", is_flag=True, help="Include image offset in log line")
|
|
177
|
+
@cli.command("live")
|
|
181
178
|
def cli_syslog_live(
|
|
182
|
-
service_provider:
|
|
183
|
-
out:
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
179
|
+
service_provider: ServiceProviderDep,
|
|
180
|
+
out: Annotated[
|
|
181
|
+
Optional[Path],
|
|
182
|
+
typer.Option(
|
|
183
|
+
"--out",
|
|
184
|
+
"-o",
|
|
185
|
+
help="log file",
|
|
186
|
+
),
|
|
187
|
+
] = None,
|
|
188
|
+
pid: Annotated[
|
|
189
|
+
int,
|
|
190
|
+
typer.Option(help="pid to filter. -1 for all"),
|
|
191
|
+
] = -1,
|
|
192
|
+
process_name: Annotated[
|
|
193
|
+
Optional[str],
|
|
194
|
+
typer.Option(
|
|
195
|
+
"--process-name",
|
|
196
|
+
"-pn",
|
|
197
|
+
help="process name to filter",
|
|
198
|
+
),
|
|
199
|
+
] = None,
|
|
200
|
+
match: Annotated[
|
|
201
|
+
Optional[list[str]],
|
|
202
|
+
typer.Option(
|
|
203
|
+
"--match",
|
|
204
|
+
"-m",
|
|
205
|
+
help="match expression",
|
|
206
|
+
),
|
|
207
|
+
] = None,
|
|
208
|
+
match_insensitive: Annotated[
|
|
209
|
+
Optional[list[str]],
|
|
210
|
+
typer.Option(
|
|
211
|
+
"--match-insensitive",
|
|
212
|
+
"-mi",
|
|
213
|
+
help="case-insensitive match expression",
|
|
214
|
+
),
|
|
215
|
+
] = None,
|
|
216
|
+
include_label: Annotated[
|
|
217
|
+
bool,
|
|
218
|
+
typer.Option(
|
|
219
|
+
"--label",
|
|
220
|
+
help="should include label",
|
|
221
|
+
),
|
|
222
|
+
] = False,
|
|
223
|
+
regex: Annotated[
|
|
224
|
+
Optional[list[str]],
|
|
225
|
+
typer.Option(
|
|
226
|
+
"--regex",
|
|
227
|
+
"-e",
|
|
228
|
+
help="filter only lines matching given regex",
|
|
229
|
+
),
|
|
230
|
+
] = None,
|
|
231
|
+
insensitive_regex: Annotated[
|
|
232
|
+
Optional[list[str]],
|
|
233
|
+
typer.Option(
|
|
234
|
+
"--insensitive-regex",
|
|
235
|
+
"-ei",
|
|
236
|
+
help="filter only lines matching given regex (insensitive)",
|
|
237
|
+
),
|
|
238
|
+
] = None,
|
|
239
|
+
image_offset: Annotated[
|
|
240
|
+
bool,
|
|
241
|
+
typer.Option(
|
|
242
|
+
"--image-offset",
|
|
243
|
+
"-io",
|
|
244
|
+
help="Include image offset in log line",
|
|
245
|
+
),
|
|
246
|
+
] = False,
|
|
192
247
|
) -> None:
|
|
193
248
|
"""view live syslog lines"""
|
|
194
249
|
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
250
|
+
with out.open("wt") if out else nullcontext() as out_file:
|
|
251
|
+
syslog_live(
|
|
252
|
+
service_provider,
|
|
253
|
+
out_file,
|
|
254
|
+
pid,
|
|
255
|
+
process_name,
|
|
256
|
+
match or [],
|
|
257
|
+
match_insensitive or [],
|
|
258
|
+
include_label,
|
|
259
|
+
regex or [],
|
|
260
|
+
insensitive_regex or [],
|
|
261
|
+
)
|
|
207
262
|
|
|
208
263
|
|
|
209
|
-
@
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
264
|
+
@cli.command("collect")
|
|
265
|
+
def syslog_collect(
|
|
266
|
+
service_provider: ServiceProviderDep,
|
|
267
|
+
out: Annotated[
|
|
268
|
+
Path,
|
|
269
|
+
typer.Argument(
|
|
270
|
+
exists=False,
|
|
271
|
+
dir_okay=True,
|
|
272
|
+
file_okay=False,
|
|
273
|
+
),
|
|
274
|
+
],
|
|
275
|
+
size_limit: int,
|
|
276
|
+
age_limit: int,
|
|
277
|
+
start_time: int,
|
|
278
|
+
) -> None:
|
|
215
279
|
"""
|
|
216
280
|
Collect the system logs into a .logarchive that can be viewed later with tools such as log or Console.
|
|
217
281
|
If the filename doesn't exist, system_logs.logarchive will be created in the given directory.
|
|
@@ -219,12 +283,12 @@ def syslog_collect(service_provider: LockdownClient, out, size_limit, age_limit,
|
|
|
219
283
|
if not os.path.exists(out):
|
|
220
284
|
os.makedirs(out)
|
|
221
285
|
|
|
222
|
-
if
|
|
286
|
+
if out.suffix != ".logarchive":
|
|
223
287
|
logger.warning(
|
|
224
288
|
"given out path doesn't end with a .logarchive - consider renaming to be able to view "
|
|
225
289
|
"the file with the likes of the Console.app and the `log show` utilities"
|
|
226
290
|
)
|
|
227
291
|
|
|
228
292
|
OsTraceService(lockdown=service_provider).collect(
|
|
229
|
-
out, size_limit=size_limit, age_limit=age_limit, start_time=start_time
|
|
293
|
+
str(out), size_limit=size_limit, age_limit=age_limit, start_time=start_time
|
|
230
294
|
)
|
pymobiledevice3/cli/usbmux.py
CHANGED
|
@@ -1,35 +1,53 @@
|
|
|
1
1
|
import logging
|
|
2
2
|
import tempfile
|
|
3
|
+
from typing import Annotated, Optional
|
|
3
4
|
|
|
4
|
-
import
|
|
5
|
+
import typer
|
|
6
|
+
from typer_injector import InjectingTyper
|
|
5
7
|
|
|
6
8
|
from pymobiledevice3 import usbmux
|
|
7
|
-
from pymobiledevice3.cli.cli_common import USBMUX_OPTION_HELP,
|
|
9
|
+
from pymobiledevice3.cli.cli_common import USBMUX_OPTION_HELP, print_json
|
|
8
10
|
from pymobiledevice3.lockdown import create_using_usbmux
|
|
9
11
|
from pymobiledevice3.tcp_forwarder import UsbmuxTcpForwarder
|
|
10
12
|
|
|
11
13
|
logger = logging.getLogger(__name__)
|
|
12
14
|
|
|
13
15
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
16
|
+
cli = InjectingTyper(
|
|
17
|
+
name="usbmux",
|
|
18
|
+
help="Inspect usbmuxd-connected devices and forward TCP ports to them.",
|
|
19
|
+
no_args_is_help=True,
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@cli.command("forward")
|
|
24
|
+
def usbmux_forward(
|
|
25
|
+
src_port: Annotated[
|
|
26
|
+
int,
|
|
27
|
+
typer.Argument(min=1, max=0xFFFF),
|
|
28
|
+
],
|
|
29
|
+
dst_port: Annotated[
|
|
30
|
+
int,
|
|
31
|
+
typer.Argument(min=1, max=0xFFFF),
|
|
32
|
+
],
|
|
33
|
+
*,
|
|
34
|
+
usbmux_address: Annotated[
|
|
35
|
+
Optional[str],
|
|
36
|
+
typer.Option(
|
|
37
|
+
"--usbmux",
|
|
38
|
+
help=USBMUX_OPTION_HELP,
|
|
39
|
+
),
|
|
40
|
+
] = None,
|
|
41
|
+
serial: Annotated[
|
|
42
|
+
str,
|
|
43
|
+
typer.Option(help="Device serial/UDID to forward traffic to."),
|
|
44
|
+
],
|
|
45
|
+
daemonize: Annotated[
|
|
46
|
+
bool,
|
|
47
|
+
typer.Option("--daemonize", "-d", help="Run the forwarder in the background."),
|
|
48
|
+
] = False,
|
|
49
|
+
) -> None:
|
|
50
|
+
"""Forward a local TCP port to the device via usbmuxd."""
|
|
33
51
|
forwarder = UsbmuxTcpForwarder(serial, dst_port, src_port, usbmux_address=usbmux_address)
|
|
34
52
|
|
|
35
53
|
if daemonize:
|
|
@@ -45,12 +63,33 @@ def usbmux_forward(usbmux_address: str, src_port: int, dst_port: int, serial: st
|
|
|
45
63
|
forwarder.start()
|
|
46
64
|
|
|
47
65
|
|
|
48
|
-
@
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
66
|
+
@cli.command("list")
|
|
67
|
+
def usbmux_list(
|
|
68
|
+
usbmux_address: Annotated[
|
|
69
|
+
Optional[str],
|
|
70
|
+
typer.Option(
|
|
71
|
+
"--usbmux",
|
|
72
|
+
help=USBMUX_OPTION_HELP,
|
|
73
|
+
),
|
|
74
|
+
] = None,
|
|
75
|
+
usb: Annotated[
|
|
76
|
+
bool,
|
|
77
|
+
typer.Option(
|
|
78
|
+
"--usb",
|
|
79
|
+
"-u",
|
|
80
|
+
help="show only USB devices",
|
|
81
|
+
),
|
|
82
|
+
] = False,
|
|
83
|
+
network: Annotated[
|
|
84
|
+
bool,
|
|
85
|
+
typer.Option(
|
|
86
|
+
"--network",
|
|
87
|
+
"-n",
|
|
88
|
+
help="show only network devices",
|
|
89
|
+
),
|
|
90
|
+
] = False,
|
|
91
|
+
) -> None:
|
|
92
|
+
"""List devices known to usbmuxd (USB and Wi-Fi)."""
|
|
54
93
|
connected_devices = []
|
|
55
94
|
for device in usbmux.list_devices(usbmux_address=usbmux_address):
|
|
56
95
|
udid = device.serial
|