prim-ctrl 0.6.0__tar.gz → 0.6.1__tar.gz

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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: prim-ctrl
3
- Version: 0.6.0
3
+ Version: 0.6.1
4
4
  Summary: Primitive Ctrl - Remote control of your phone's Primitive FTPd Android SFTP server and optionally Tailscale VPN.
5
5
  Home-page: https://github.com/lmagyar/prim-ctrl
6
6
  License: Apache-2.0
@@ -62,6 +62,15 @@ class Logger(logging.Logger):
62
62
  if self.level == logging.NOTSET:
63
63
  self.setLevel(logging.WARNING if silent else logging.INFO)
64
64
 
65
+ def exception_or_error(self, e: Exception, args):
66
+ if not args or args.debug:
67
+ logger.exception(e)
68
+ else:
69
+ if hasattr(e, '__notes__'):
70
+ logger.error("%s: %s", LazyStr(repr, e), LazyStr(", ".join, e.__notes__))
71
+ else:
72
+ logger.error(LazyStr(repr, e))
73
+
65
74
  def error(self, msg, *args, **kwargs):
66
75
  self.exitcode = 1
67
76
  super().error(msg, *args, **kwargs)
@@ -171,8 +180,11 @@ class Pingable:
171
180
  logger.debug("Waiting for %s to be %s (timeout is %ds)", LazyStr(self.get_class_name), LazyStr(Pingable.get_state_name, available), int(timeout))
172
181
  async with asyncio.timeout(timeout):
173
182
  while await self.ping(available) != available:
174
- if not available:
175
- await asyncio.sleep(1)
183
+ await self._sleep_while_wait(available)
184
+
185
+ async def _sleep_while_wait(self, available: bool):
186
+ if not available:
187
+ await asyncio.sleep(1)
176
188
 
177
189
  class Manager:
178
190
  @abstractmethod
@@ -653,6 +665,9 @@ class LocalTailscale(Manageable):
653
665
  logger.debug("Pinging %s", LazyStr(self.get_class_name))
654
666
  return await async_tailscale_is_online()
655
667
 
668
+ async def _sleep_while_wait(self, available: bool):
669
+ await asyncio.sleep(0.250)
670
+
656
671
  ########
657
672
 
658
673
  class WideHelpFormatter(argparse.RawTextHelpFormatter):
@@ -715,18 +730,30 @@ class Control:
715
730
  raise ValueError("The --restore-state option can be enabled only for the stop intent")
716
731
 
717
732
  async def execute(self, args, local: Local, phone: Phone):
718
- async def _stop(restore_state: dict | None):
733
+ async def _stop(restore_state: dict | None, stop_only_started: bool = False):
719
734
  if local.vpn and phone.vpn and phone.remote_sftp and await local.vpn.test() and await phone.vpn.test():
720
- if (restore_state is None or not restore_state.get(Control.PHONE_SFTP, False)) and await phone.remote_sftp.test():
721
- await phone.remote_sftp.stop(10, 30)
722
- if restore_state is None or not restore_state.get(Control.PHONE_VPN, False):
723
- await phone.vpn.stop(10, 60)
735
+ if (restore_state is None or not restore_state.get(Control.PHONE_SFTP, stop_only_started)) and await phone.remote_sftp.test():
736
+ try:
737
+ await phone.remote_sftp.stop(10, 30)
738
+ except Exception as e:
739
+ logger.exception_or_error(e, args)
740
+ if restore_state is None or not restore_state.get(Control.PHONE_VPN, stop_only_started):
741
+ try:
742
+ await phone.vpn.stop(10, 60)
743
+ except Exception as e:
744
+ logger.exception_or_error(e, args)
724
745
  else:
725
- if (restore_state is None or not restore_state.get(Control.PHONE_SFTP, False)):
726
- await phone.zeroconf_sftp.stop(10, 30)
746
+ if (restore_state is None or not restore_state.get(Control.PHONE_SFTP, stop_only_started)):
747
+ try:
748
+ await phone.zeroconf_sftp.stop(10, 30)
749
+ except Exception as e:
750
+ logger.exception_or_error(e, args)
727
751
  if local.vpn:
728
- if restore_state is None or not restore_state.get(Control.LOCAL_VPN, False):
729
- await local.vpn.stop(10, 30)
752
+ if restore_state is None or not restore_state.get(Control.LOCAL_VPN, stop_only_started):
753
+ try:
754
+ await local.vpn.stop(10, 30)
755
+ except Exception as e:
756
+ logger.exception_or_error(e, args)
730
757
  match args.intent:
731
758
  case 'test':
732
759
  if local.vpn and phone.vpn and phone.remote_sftp and phone.state:
@@ -739,29 +766,31 @@ class Control:
739
766
  case 'start':
740
767
  if local.vpn and phone.vpn and phone.remote_sftp:
741
768
  state = dict()
742
- zeroconf_accessible = False
743
- remote_accessible = False
744
-
745
- # gather local state info
746
- local_vpn_state = await local.vpn.test()
747
- state[Control.LOCAL_VPN] = local_vpn_state
748
- # start changing local state - we need a local vpn to be able to access the state of the remote vpn and optionally the phone
749
- await local.vpn.start(10, 30)
750
-
751
- # gather remote state info
752
- if phone.state:
753
- phone_state, phone_vpn_state = await gather_with_taskgroup(phone.state.get(10, 30), phone.vpn.test())
754
- state[Control.PHONE_WIFI] = phone_state[PhoneState.WIFI]
755
- state[Control.PHONE_VPN] = phone_vpn_state
756
- state[Control.PHONE_SFTP] = phone_state[PhoneState.PFTPD]
757
- if not state[Control.PHONE_WIFI] and not args.accept_cellular:
758
- raise RuntimeError(f"Phone is not on Wi-Fi network")
759
- else:
760
- state[Control.PHONE_VPN] = await phone.vpn.test()
761
- if state[Control.PHONE_VPN]:
762
- state[Control.PHONE_SFTP] = remote_accessible = await phone.remote_sftp.test()
763
- # start changing remote state
764
769
  try:
770
+ # gather local state info
771
+ local_vpn_state = await local.vpn.test()
772
+ state[Control.LOCAL_VPN] = local_vpn_state
773
+
774
+ # start changing local state - we need a local vpn to be able to access the state of the remote vpn and optionally the phone
775
+ await local.vpn.start(10, 30)
776
+
777
+ zeroconf_accessible = False
778
+ remote_accessible = False
779
+
780
+ # gather phone state info
781
+ if phone.state:
782
+ phone_state, phone_vpn_state = await gather_with_taskgroup(phone.state.get(10, 30), phone.vpn.test())
783
+ state[Control.PHONE_WIFI] = phone_state[PhoneState.WIFI]
784
+ state[Control.PHONE_VPN] = phone_vpn_state
785
+ state[Control.PHONE_SFTP] = phone_state[PhoneState.PFTPD]
786
+ if not state[Control.PHONE_WIFI] and not args.accept_cellular:
787
+ raise RuntimeError(f"Phone is not on Wi-Fi network")
788
+ else:
789
+ state[Control.PHONE_VPN] = await phone.vpn.test()
790
+ if state[Control.PHONE_VPN]:
791
+ state[Control.PHONE_SFTP] = remote_accessible = await phone.remote_sftp.test()
792
+
793
+ # start changing phone state
765
794
  if phone.state:
766
795
  if not state[Control.PHONE_SFTP]:
767
796
  if not state[Control.PHONE_VPN]:
@@ -799,20 +828,22 @@ class Control:
799
828
  zeroconf_accessible = await phone.zeroconf_sftp.test()
800
829
  if not zeroconf_accessible and not remote_accessible:
801
830
  raise RuntimeError(f"Even when {phone.vpn.get_class_name()} and {phone.remote_sftp.get_class_name()} is started, {phone.remote_sftp.get_class_name()} is still not accessible")
831
+
832
+ # print out result on stdout
833
+ if not args.backup_state:
834
+ state = dict()
835
+ state[Control.CONNECTED] = Control.ZEROCONF if zeroconf_accessible else Control.REMOTE
836
+ print(StateSerializer.dumps(state))
802
837
  except:
803
- await _stop(state)
838
+ await _stop(state, stop_only_started = True)
804
839
  raise
805
- if not args.backup_state:
806
- state = dict()
807
- state[Control.CONNECTED] = Control.ZEROCONF if zeroconf_accessible else Control.REMOTE
808
- print(StateSerializer.dumps(state))
809
840
  else:
810
- if not await phone.zeroconf_sftp.test():
811
- try:
841
+ try:
842
+ if not await phone.zeroconf_sftp.test():
812
843
  await phone.zeroconf_sftp.start(10, 30)
813
- except:
814
- await _stop(None)
815
- raise
844
+ except:
845
+ await _stop(None)
846
+ raise
816
847
  case 'stop':
817
848
  await _stop(StateSerializer.loads(args.restore_state) if args.restore_state else None)
818
849
 
@@ -912,13 +943,7 @@ async def main():
912
943
  await args.ctor().run(args)
913
944
 
914
945
  except Exception as e:
915
- if not args or args.debug:
916
- logger.exception(e)
917
- else:
918
- if hasattr(e, '__notes__'):
919
- logger.error("%s: %s", LazyStr(repr, e), LazyStr(", ".join, e.__notes__))
920
- else:
921
- logger.error(LazyStr(repr, e))
946
+ logger.exception_or_error(e, args)
922
947
 
923
948
  return logger.exitcode
924
949
 
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "prim-ctrl"
3
- version = "0.6.0"
3
+ version = "0.6.1"
4
4
  description = "Primitive Ctrl - Remote control of your phone's Primitive FTPd Android SFTP server and optionally Tailscale VPN."
5
5
  license = "Apache-2.0"
6
6
  authors = ["Laszlo Magyar <lmagyar1973@gmail.com>"]
File without changes
File without changes