diff options
author | neodarz <neodarz@neodarz.net> | 2019-09-28 19:30:41 +0200 |
---|---|---|
committer | neodarz <neodarz@neodarz.net> | 2019-09-28 19:30:41 +0200 |
commit | d70390197ee3e06ee1fecbc14d1ed48ba4040c94 (patch) | |
tree | 325267c8d64ed2d7282d1d3166b0a651b9594ddc | |
download | pykeepalive-d70390197ee3e06ee1fecbc14d1ed48ba4040c94.tar.xz pykeepalive-d70390197ee3e06ee1fecbc14d1ed48ba4040c94.zip |
-rw-r--r-- | .gitignore | 131 | ||||
-rw-r--r-- | README.md | 7 | ||||
-rw-r--r-- | commands.py | 91 | ||||
-rw-r--r-- | config.py | 24 | ||||
-rw-r--r-- | file.py | 8 | ||||
-rw-r--r-- | log.py | 20 | ||||
-rw-r--r-- | pykeepalive.py | 42 | ||||
-rw-r--r-- | tools.py | 37 |
8 files changed, 360 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..01495c0 --- /dev/null +++ b/.gitignore @@ -0,0 +1,131 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +pip-wheel-metadata/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +.python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# celery beat schedule file +celerybeat-schedule + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# Vim tmp file +.pykeepalive.py.swp + +# Log file +log.txt diff --git a/README.md b/README.md new file mode 100644 index 0000000..64f40cd --- /dev/null +++ b/README.md @@ -0,0 +1,7 @@ +# PyKeepAlive + +Rewrite of +["openwrt-lte-keep-alive"](https://github.com/mchsk/openwrt-lte-keep-alive) +for fun, do the same. + +It ping my server and a FDN DNS server, it can be changes in `config.py`. diff --git a/commands.py b/commands.py new file mode 100644 index 0000000..6ddb940 --- /dev/null +++ b/commands.py @@ -0,0 +1,91 @@ +#/bin/python3 + +import sys + +import tailhead + +import platform +import subprocess + +import datetime, time + +import json + +def _make_gen(reader): + b = reader(1024 * 1024) + while b: + yield b + b = reader(1024*1024) + +def file_len(filename): + """ More opti than wc on turis """ + f = open(filename, 'rb') + f_gen = _make_gen(f.raw.read) + return sum( buf.count(b'\n') for buf in f_gen ) + +def multi_ping(hosts, interface): + """ + Returns True if host (list of str) responds to a ping request. + Remember that a host may not respond to a ping (ICMP) request even if the host name is valid. + """ + + # Option for the number of packets as a function of + count = '-n' if platform.system().lower()=='windows' else '-c' + source = '-S' if platform.system().lower()=='windows' else '-I' + + if type(hosts) is list: + procs = [] + for host in hosts: + command = ['ping', count, '1', source, interface, host] + proc = subprocess.Popen(command, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) + procs.append(proc) + + procs_done = [] + while True: + for proc in procs: + if proc.poll() is not None: + procs_done.append(proc.poll()) + procs.pop(procs.index(proc)) + if len(procs) == 0: + #print("o") + break + time.sleep(1) + + if 0 in procs_done: + return 0 + else: + return 1 + + else: + print("fuck you host must be a list") + sys.exit() + + +def log_write(status): + with open(LOG_PATH_FILE, 'a') as f: + f.write("{} {}\n".format(tools.date(), status)) + +def uqmi_current_settings(): + command = ['uqmi', '-d', '/dev/cdc-wdm0', '--get-current-settings'] + proc = subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + datas = json.loads(proc.stdout) + return datas["ipv4"]["ip"], datas["ipv4"]["gateway"], datas["ipv4"]["subnet"] + +def ifconfig_update(interface, ip, subnet): + command = ['ifconfig', interface, ip, 'netmask', subnet, 'up'] + proc = subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + +def route_update(gateway): + command = ['route', 'add', 'default', 'gw', gateway] + proc = subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + +def tail(file, lines): + return tailhead.tail(open(file, 'rb'), lines) + + +def date(): + # Calculate the offset taking into account daylight saving time + utc_offset_sec = time.altzone if time.localtime().tm_isdst else time.timezone + utc_offset = datetime.timedelta(seconds=-utc_offset_sec) + return datetime.datetime.now().replace(tzinfo=datetime.timezone(offset=utc_offset)).isoformat() + diff --git a/config.py b/config.py new file mode 100644 index 0000000..c9cc4dc --- /dev/null +++ b/config.py @@ -0,0 +1,24 @@ +#/bin/python3 + +import os +import sys +from pathlib import Path, PurePath + +import tools +import commands + +DIR=os.path.dirname(os.path.abspath( __file__ )) +LOG_FILE=PurePath("log.txt") +LOG_PATH_FILE=Path(DIR / LOG_FILE) +if not os.path.exists(LOG_PATH_FILE): + os.mknod(LOG_PATH_FILE) + +OFFLINE_COUNT=tools.offline_counts(LOG_PATH_FILE) +OFFLINE_COUNT_TRESHOLD=4 + +LINES_MAX=11000 +LINES_MIN=6000 +LINES_COUNT=commands.file_len(LOG_PATH_FILE) + +HOSTS = ["80.67.169.12", "neodarz.net"] +INTERFACE = "wwan0" @@ -0,0 +1,8 @@ +#/bin/python3 + +from config import * +import commands + +def log_write(status): + with open(LOG_PATH_FILE, 'a') as f: + f.write("{} {}\n".format(commands.date(), status)) @@ -0,0 +1,20 @@ +#/bin/python3 + +import platform +import subprocess + +from config import * +import commands + +def tail_log(): + if commands.file_len(LOG_PATH_FILE) > LINES_MAX: + if platform.system().lower()=='windows': + datas = commands.tail(LOG_PATH_FILE, LINES_MIN) + with open(LOG_PATH_FILE, "wb") as f: + f.write(b' \n'.join(datas)) + else: + command = ["tail", "-"+str(LINES_MIN), str(LOG_PATH_FILE)] + proc = subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + datas = proc.stdout.decode('utf-8') + with open(LOG_PATH_FILE, "w") as f: + f.write("".join(datas)) diff --git a/pykeepalive.py b/pykeepalive.py new file mode 100644 index 0000000..e4d54af --- /dev/null +++ b/pykeepalive.py @@ -0,0 +1,42 @@ +#/bin/python3 + +import sys, time +import tailhead + +from config import * + +import tools +import commands +import file +import log + +if len(sys.argv) == 1: + log.tail_log() + if commands.multi_ping(HOSTS, INTERFACE) == 0: + file.log_write("ONLINE") + else: + print("Ooops, we're offline!") + file.log_write("OFFLINE >> RESETING INTERFACE [{}]".format(INTERFACE)) + print(">> Reseting interface [{}]...".format(INTERFACE)) + ip, gateway, subnet = commands.uqmi_current_settings() + commands.ifconfig_update(INTERFACE, ip, subnet) + commands.route_update(gateway) +elif len(sys.argv) == 2: + if sys.argv[1] == "tail": + log.tail_log() + for line in tailhead.follow_path(LOG_PATH_FILE): + if line is not None: + print(tools.status_color(line)) + else: + time.sleep(1) + elif sys.argv[1] == "cat": + log.tail_log() + with open(LOG_PATH_FILE) as f: + for line in f: + print(tools.status_color(line), end="") + else: + tools.show_help() +else: + tools.show_help() + + diff --git a/tools.py b/tools.py new file mode 100644 index 0000000..862f15d --- /dev/null +++ b/tools.py @@ -0,0 +1,37 @@ +#/bin/python3 + +import sys +import re +import colorama +from termcolor import colored +colorama.init() + +from collections import Counter + +import commands + +def offline_counts(file): + counts = Counter() + for sentence in commands.tail(file, 4): + counts.update(word.strip('.,?!"\'').upper() for word in sentence.decode('utf-8').split()) + return counts["OFFLINE"] + +def show_help(): + print("{} [command]".format(sys.argv[0])) + print("{} Check internet status, log status in file and, if\n {}needed, restart interface.".format(sys.argv[0], " " * len(sys. argv[0]))) + print("{} tail Follow log file like tail command".format(sys.argv[0])) + print("{} cat Like cat command".format(sys.argv[0])) + +offline = re.compile(r'.*OFFLINE.*', re.IGNORECASE) +online = re.compile(r'.*ONLINE.*', re.IGNORECASE) + +def status_color(str_line): + if type(str_line) is bytes: + str_line = str_line.decode('utf-8') + if offline.search(str_line): + return colored(str_line, 'red') + elif online.search(str_line): + return colored(str_line, 'green') + else: + return str_line + |