2929from pymobiledevice3 .lockdown import create_using_usbmux
3030import plistlib
3131
32+ class IPCClient :
33+ def __init__ (self , sock , address ):
34+ self .sock = sock
35+ self .read_file = sock .makefile ('r' )
36+ self .write_file = sock .makefile ('w' )
37+ self .address = address
38+
39+ def close (self ):
40+ self .sock .close ()
41+ self .read_file .close ()
42+ self .write_file .close ()
43+
3244class WriteDispatcher :
3345 def __init__ (self ):
3446 self .write_queue = queue .Queue ()
@@ -54,11 +66,11 @@ def _write_worker(self):
5466 finally :
5567 self .shutdown_event .set ()
5668
57- def write_reply (self , writer , reply : dict ):
69+ def write_reply (self , ipc_client , reply : dict ):
5870 if self .shutdown_event .is_set ():
5971 return
60- print (" Sending packet: " + str (reply ) + " to " + str ( writer . fileno ()) )
61- self .write_queue .put ((writer , reply ))
72+ print (f" { ipc_client . address } : Sending packet: { str (reply )} " )
73+ self .write_queue .put ((ipc_client . write_file , reply ))
6274
6375 def shutdown (self ):
6476 self .write_queue .put (None )
@@ -67,9 +79,7 @@ def shutdown(self):
6779class ReadDispatcher :
6880 def __init__ (self ):
6981 self .socket_list = []
70- self .addresses = {}
71- self .read_files = {}
72- self .write_files = {}
82+ self .clients = {}
7383 self .shutdown_event = threading .Event ()
7484 self .read_thread = threading .Thread (target = self ._read_worker , daemon = True , name = "Python-ReadDispatcher" )
7585 self .read_thread .start ()
@@ -82,43 +92,38 @@ def _read_worker(self):
8292
8393 if self .shutdown_event .is_set ():
8494 for sock in self .socket_list :
85- self .remove_socket ( sock )
95+ self .remove_client ( self . clients [ sock ] )
8696 return
8797
8898 for exception_socket in exception_sockets :
89- self .remove_socket ( exception_socket )
99+ self .remove_client ( self . clients [ exception_socket ] )
90100
91101 for ready_socket in ready_sockets :
92- reader = self .read_files [ready_socket ]
93- writer = self .write_files [ready_socket ]
94- command = reader .readline ().strip ()
102+ ipc_client = self .clients [ready_socket ]
103+ command = ipc_client .read_file .readline ().strip ()
95104 if not command :
96- self .remove_socket ( ready_socket )
105+ self .remove_client ( ipc_client )
97106 continue
98- print (" Received command: {}" . format ( command ) )
107+ print (f" { ipc_client . address } : Received command: { command } " )
99108
100- handle_command (command , writer )
109+ handle_command (command , ipc_client )
101110
102111 except Exception as e :
103112 print (f"Read thread error: { e } " )
104113 continue
105114 finally :
106115 self .shutdown_event .set ()
107116
108- def remove_socket (self , sock ):
109- self .socket_list .remove (sock )
110- self .read_files .pop (sock ).close ()
111- self .write_files .pop (sock ).close ()
112- address = self .addresses .pop (sock )
113- sock .close ()
114- print (f"Disconnected { address } " )
117+ def remove_client (self , ipc_client ):
118+ self .socket_list .remove (ipc_client .sock )
119+ self .clients .pop (ipc_client .sock )
120+ ipc_client .close ()
121+ print (f"Disconnected { ipc_client .address } " )
115122
116- def add_socket (self , sock , address ):
117- self .read_files [sock ] = sock .makefile ('r' )
118- self .write_files [sock ] = sock .makefile ('w' )
119- self .addresses [sock ] = address
120- self .socket_list .append (sock )
121- print (f"Connected { address } " )
123+ def add_client (self , ipc_client ):
124+ self .clients [ipc_client .sock ] = ipc_client
125+ self .socket_list .append (ipc_client .sock )
126+ print (f"Connected { ipc_client .address } " )
122127
123128
124129 def shutdown (self ):
@@ -131,7 +136,7 @@ def shutdown(self):
131136read_dispatcher = ReadDispatcher ()
132137
133138
134- def list_devices (id , writer ):
139+ def list_devices (id , ipc_client ):
135140 devices = []
136141 for device in usbmux .list_devices ():
137142 udid = device .serial
@@ -141,33 +146,33 @@ def list_devices(id, writer):
141146
142147 reply = {"id" : id , "state" : "completed" , "result" : devices }
143148
144- write_dispatcher .write_reply (writer , reply )
149+ write_dispatcher .write_reply (ipc_client , reply )
145150
146- def list_devices_udid (id , writer ):
151+ def list_devices_udid (id , ipc_client ):
147152 devices = []
148153 for device in usbmux .list_devices ():
149154 devices .append (device .serial )
150155
151156 reply = {"id" : id , "state" : "completed" , "result" : devices }
152157
153- write_dispatcher .write_reply (writer , reply )
158+ write_dispatcher .write_reply (ipc_client , reply )
154159
155- def get_device (id , device_id , writer ):
160+ def get_device (id , device_id , ipc_client ):
156161 try :
157162 with create_using_usbmux (device_id , autopair = False ) as lockdown :
158163 reply = {"id" : id , "state" : "completed" , "result" : lockdown .short_info }
159- write_dispatcher .write_reply (writer , reply )
164+ write_dispatcher .write_reply (ipc_client , reply )
160165 except NoDeviceConnectedError | DeviceNotFoundError :
161166 reply = {"id" : id , "state" : "failed_expected" }
162- write_dispatcher .write_reply (writer , reply )
167+ write_dispatcher .write_reply (ipc_client , reply )
163168
164- def install_app (id , lockdown_client , path , mode , writer ):
169+ def install_app (id , lockdown_client , path , mode , ipc_client ):
165170 with InstallationProxyService (lockdown = lockdown_client ) as installer :
166171 options = {"PackageType" : "Developer" }
167172
168173 def progress_handler (progress , * args ):
169174 reply = {"id" : id , "state" : "progress" , "progress" : progress }
170- write_dispatcher .write_reply (writer , reply )
175+ write_dispatcher .write_reply (ipc_client , reply )
171176 return
172177
173178 if mode == "INSTALL" :
@@ -187,24 +192,24 @@ def progress_handler(progress, *args):
187192 res = installer .lookup (options = {"BundleIDs" : [bundle_identifier ]})
188193
189194 reply = {"id" : id , "state" : "completed" , "result" : res [bundle_identifier ]["Path" ]}
190- write_dispatcher .write_reply (writer , reply )
195+ write_dispatcher .write_reply (ipc_client , reply )
191196
192197 print ("Installed bundle: " + str (bundle_identifier ))
193198
194- def decode_plist (id , path , writer ):
199+ def decode_plist (id , path , ipc_client ):
195200 with open (path , 'rb' ) as f :
196201 plist_data = plistlib .load (f )
197202 reply = {"id" : id , "state" : "completed" , "result" : plist_data }
198- write_dispatcher .write_reply (writer , reply )
203+ write_dispatcher .write_reply (ipc_client , reply )
199204 return
200205
201- def auto_mount_image (id , lockdown , writer ):
206+ def auto_mount_image (id , lockdown , ipc_client ):
202207 try :
203208 asyncio .run (auto_mount (lockdown ))
204209 except AlreadyMountedError :
205210 pass
206211 reply = {"id" : id , "state" : "completed" }
207- write_dispatcher .write_reply (writer , reply )
212+ write_dispatcher .write_reply (ipc_client , reply )
208213
209214
210215def start_tunneld ():
@@ -261,14 +266,14 @@ def ensure_tunneld_running():
261266 else :
262267 print ("tunneld is already running" )
263268
264- def debugserver_connect (id , lockdown , port , writer ):
269+ def debugserver_connect (id , lockdown , port , ipc_client ):
265270 try :
266271 discovery_service = get_tunneld_device_by_udid (lockdown .udid )
267272 if not discovery_service :
268273 raise TunneldConnectionError ()
269274 except TunneldConnectionError :
270275 reply = {"id" :id , "state" : "failed_tunneld" }
271- write_dispatcher .write_reply (writer , reply )
276+ write_dispatcher .write_reply (ipc_client , reply )
272277 return
273278
274279 if Version (discovery_service .product_version ) < Version ('17.0' ):
@@ -293,10 +298,10 @@ def forwarder_thread():
293298 "host" : "127.0.0.1" ,
294299 "port" : selected_port
295300 }}
296- write_dispatcher .write_reply (writer , reply )
301+ write_dispatcher .write_reply (ipc_client , reply )
297302
298303
299- def debugserver_close (id , port , writer ):
304+ def debugserver_close (id , port , ipc_client ):
300305 forwarder , thread = active_debug_server .pop (port )
301306 forwarder .stop ()
302307
@@ -306,10 +311,10 @@ def debugserver_close(id, port, writer):
306311 print (f"Joining debugserver thread { port } timed out" )
307312
308313 reply = {"id" : id , "state" : "completed" }
309- write_dispatcher .write_reply (writer , reply )
314+ write_dispatcher .write_reply (ipc_client , reply )
310315
311316
312- def usbmux_forward_open (id , udid , remote_port , local_port , writer ):
317+ def usbmux_forward_open (id , udid , remote_port , local_port , ipc_client ):
313318 listen_event = threading .Event ()
314319
315320 forwarder = UsbmuxTcpForwarder (udid , remote_port , local_port , listening_event = listen_event )
@@ -330,10 +335,10 @@ def forwarder_thread():
330335 "local_port" : selected_port ,
331336 "remote_port" : remote_port
332337 }}
333- write_dispatcher .write_reply (writer , reply )
338+ write_dispatcher .write_reply (ipc_client , reply )
334339
335340
336- def usbmux_forward_close (id , local_port , writer ):
341+ def usbmux_forward_close (id , local_port , ipc_client ):
337342 forwarder , thread = active_usbmux_forwarder .pop (local_port )
338343 forwarder .stop ()
339344
@@ -343,69 +348,69 @@ def usbmux_forward_close(id, local_port, writer):
343348 print (f"Joining usbmux thread { local_port } timed out" )
344349
345350 reply = {"id" : id , "state" : "completed" }
346- write_dispatcher .write_reply (writer , reply )
351+ write_dispatcher .write_reply (ipc_client , reply )
347352
348353
349- def handle_command (command , writer ):
354+ def handle_command (command , ipc_client ):
350355 try :
351356 res = json .loads (command )
352357 id = res ['id' ]
353358
354359 command_type = res ['command' ]
355360 if command_type == "exit" :
356- print ("Exiting by request" )
361+ print (f "Exiting by request from { ipc_client . address } " )
357362 atexit ._run_exitfuncs ()
358363 os ._exit (0 )
359364 elif command_type == "list_devices" :
360- list_devices (id , writer )
365+ list_devices (id , ipc_client )
361366 return
362367 elif command_type == "list_devices_udid" :
363- list_devices_udid (id , writer )
368+ list_devices_udid (id , ipc_client )
364369 return
365370 elif command_type == "get_device" :
366371 device_id = res ['device_id' ] if 'device_id' in res else None
367- get_device (id , device_id , writer )
372+ get_device (id , device_id , ipc_client )
368373 return
369374 elif command_type == "decode_plist" :
370- decode_plist (id , res ['plist_path' ], writer )
375+ decode_plist (id , res ['plist_path' ], ipc_client )
371376 return
372377 elif command_type == "debugserver_close" :
373- debugserver_close (id , res ['port' ], writer )
378+ debugserver_close (id , res ['port' ], ipc_client )
374379 return
375380 elif command_type == "usbmux_forwarder_open" :
376- usbmux_forward_open (id , res ['device_id' ], res ['remote_port' ], res ['local_port' ], writer )
381+ usbmux_forward_open (id , res ['device_id' ], res ['remote_port' ], res ['local_port' ], ipc_client )
377382 return
378383 elif command_type == "usbmux_forwarder_close" :
379- usbmux_forward_close (id , res ['local_port' ], writer )
384+ usbmux_forward_close (id , res ['local_port' ], ipc_client )
380385 return
381386 elif command_type == "ensure_tunneld_running" :
382387 ensure_tunneld_running ()
383388 reply = {"id" : id , "state" : "completed" }
384- write_dispatcher .write_reply (writer , reply )
389+ write_dispatcher .write_reply (ipc_client , reply )
385390 return
386391 elif command_type == "is_tunneld_running" :
387392 res = is_tunneld_running ()
388393 reply = {"id" : id , "state" : "completed" , "result" : res }
389- write_dispatcher .write_reply (writer , reply )
394+ write_dispatcher .write_reply (ipc_client , reply )
390395 return
391396
392397 # Now come the device targetted functions
393398 device_id = res ['device_id' ]
394399 with create_using_usbmux (device_id ) as lockdown :
395400 if command_type == "install_app" :
396- install_app (id , lockdown , res ['app_path' ], res ['install_mode' ], writer )
401+ install_app (id , lockdown , res ['app_path' ], res ['install_mode' ], ipc_client )
397402 return
398403 elif command_type == "auto_mount_image" :
399- auto_mount_image (id , lockdown , writer )
404+ auto_mount_image (id , lockdown , ipc_client )
400405 return
401406 elif command_type == "debugserver_connect" :
402407 port = res ['port' ] if 'port' in res else 0
403- debugserver_connect (id , lockdown , port , writer )
408+ debugserver_connect (id , lockdown , port , ipc_client )
404409 return
405410
406411 except Exception as e :
407412 reply = {"request" : command , "state" : "failed" , "error" : repr (e ), "backtrace" : traceback .format_exc ()}
408- write_dispatcher .write_reply (writer , reply )
413+ write_dispatcher .write_reply (ipc_client , reply )
409414
410415
411416def main ():
@@ -430,6 +435,6 @@ def main():
430435 print (f"Start listening on port { port } " )
431436 while True :
432437 client_socket , client_address = server .accept ()
433- read_dispatcher .add_socket ( client_socket , client_address )
438+ read_dispatcher .add_client ( IPCClient ( client_socket , client_address ) )
434439
435440main ()
0 commit comments