The daemon post
This commit is contained in:
parent
9ca7d28cca
commit
ba4f0cb206
186
daemons.py
Normal file
186
daemons.py
Normal file
@ -0,0 +1,186 @@
|
||||
#!/usr/bin/env python3
|
||||
## This Blog post is executable python code, it requires the gevent modules to run
|
||||
# pip as gevent, Debian as python3-gevent.
|
||||
|
||||
import os
|
||||
import sys
|
||||
import gevent
|
||||
import gevent.socket as socket
|
||||
import signal
|
||||
|
||||
|
||||
# A long time ago circa 2010ish when i was taking the network programing class in
|
||||
# college there was a blog post that explained in great detail,
|
||||
# how to "fork into the background" e.g make a daemon on a Unix
|
||||
# system. It was called Demystifing Daemons. By some guy on Blogspot,
|
||||
# I think they went by Tom. I can no longer find this post.
|
||||
# I get back more and more AI slop every time I look.
|
||||
# This trick is not something i see often anymore.
|
||||
# Therefore let's recreate a classic.
|
||||
# Daemon's Demystified 2.0 starts right now
|
||||
|
||||
# Sometimes programs need to run unattended, without tying up
|
||||
# a terminal session. This is less of a problem now today
|
||||
# then 30 years ago with our new fangled terminal multiplexers
|
||||
# and the like. But i find that there's value in learning archane
|
||||
# knowledge. Unix has had the concept of daemons since AT&T version 7
|
||||
# I think, but I'm finding that a decreasing number of people are
|
||||
# familiar with this once basic knowledge.
|
||||
|
||||
# To that end this post will show how to create a Disk and Execution Monitor
|
||||
# a daemon, also called a service by the systemd kids.
|
||||
|
||||
# In order to have a service we must first have something to serve.
|
||||
# Here's one of my favorite prank services. RickRoll over http
|
||||
|
||||
# you know it, you love it, It's a valid HTTP response
|
||||
|
||||
RICKROLL_LYRICS = """
|
||||
HTTP/1.1 200 OK
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Last-Modified: Mon, 27 July 1987 00:00 GMT
|
||||
Content-Length: 982
|
||||
|
||||
|
||||
We're no strangers to love
|
||||
You know the rules and so do I
|
||||
A full commitment's what I'm thinkin' of
|
||||
You wouldn't get this from any other guy
|
||||
|
||||
I just wanna tell you how I'm feeling
|
||||
Gotta make you understand
|
||||
|
||||
Never gonna give you up, never gonna let you down
|
||||
Never gonna run around and desert you
|
||||
Never gonna make you cry, never gonna say goodbye
|
||||
Never gonna tell a lie and hurt you
|
||||
|
||||
We've known each other for so long
|
||||
Your heart's been aching, but you're too shy to say it
|
||||
Inside, we both know what's been going on
|
||||
We know the game and we're gonna play it
|
||||
|
||||
And if you ask me how I'm feeling
|
||||
Don't tell me you're too blind to see
|
||||
|
||||
Never gonna give you up, never gonna let you down
|
||||
Never gonna run around and desert you
|
||||
Never gonna make you cry, never gonna say goodbye
|
||||
Never gonna tell a lie and hurt you
|
||||
|
||||
Never gonna give you up, never gonna let you down
|
||||
Never gonna run around and desert you
|
||||
Never gonna make you cry, never gonna say goodbye
|
||||
Never gonna tell a lie and hurt you
|
||||
|
||||
"""
|
||||
|
||||
client_procs = []
|
||||
|
||||
svr_proc = None
|
||||
|
||||
|
||||
# more on this later
|
||||
class NullDevice:
|
||||
def write(self, s):
|
||||
pass
|
||||
|
||||
|
||||
# See ln 104 and following
|
||||
def hup_handle(sig, fr):
|
||||
sys.exit()
|
||||
|
||||
|
||||
# just standard Unix Network Programing Stuff
|
||||
# Only interesting bit is if using INET6 in python at least
|
||||
# IPV4 comes for free so no need to use AF_UNSPEC.
|
||||
# See Refs 1 and 2 for more info
|
||||
|
||||
# By the way i hate python 3's concurrency stuff so we'll be using
|
||||
# gevent.
|
||||
|
||||
|
||||
def server_handler():
|
||||
serversock = socket.socket(socket.AF_INET6, socket.SOCK_STREAM)
|
||||
serversock.bind(("", 1337))
|
||||
serversock.listen(10)
|
||||
while True:
|
||||
client, addr = serversock.accept()
|
||||
print(addr)
|
||||
client_procs.append(gevent.spawn(client_handler, client))
|
||||
gevent.sleep(0)
|
||||
|
||||
serversock.close()
|
||||
return
|
||||
|
||||
|
||||
# This is a simple infinite rickroll http server
|
||||
# it only responds to GET requests. and no matter what
|
||||
# will give you a rickroll back.
|
||||
#
|
||||
# This is somewhat useful in elfing with ai scraper bots.
|
||||
# There are better methods if you wanna try that
|
||||
def client_handler(sock):
|
||||
print("Client handler spawn")
|
||||
while True:
|
||||
data = sock.recv(4096)
|
||||
dstring = data.decode("UTF-8")
|
||||
if dstring.startswith("GET"):
|
||||
break
|
||||
gevent.sleep(0)
|
||||
|
||||
sock.send(RICKROLL_LYRICS.encode("utf-8"))
|
||||
sock.close()
|
||||
return
|
||||
|
||||
|
||||
def daemon_main():
|
||||
svr_proc = gevent.spawn(server_handler)
|
||||
client_procs.append(svr_proc)
|
||||
gevent.joinall(client_procs)
|
||||
sys.exit(0)
|
||||
|
||||
|
||||
pid = os.fork() # Hmmm this looks an awful lot like... C
|
||||
# Yes it does per C fork(3) creates a nearly identical copy of the
|
||||
# calling process as a child of the calling process
|
||||
# returning it's pid to the caller. Asexual reproduction at it's finest
|
||||
|
||||
if pid:
|
||||
os._exit(0)
|
||||
# exit without running exit handlers, that might cause race condition
|
||||
# in the child
|
||||
|
||||
|
||||
# Per the fork manual the child begins execution at the point where fork
|
||||
# is called, as if the child had called it. The only difference being
|
||||
# is the child process gets a zero as return value, and so the else branch
|
||||
# of this if is followed.
|
||||
else:
|
||||
# It turns out my CS prof lied about the purpose of these two calls
|
||||
# the child process needs to be the process group leader, when parent
|
||||
# exits or it gets reaped by the init system
|
||||
os.setpgrp()
|
||||
os.umask(0)
|
||||
|
||||
print(os.getpid()) # to aid in stopping the server
|
||||
# We want to close our connection to the controlling terminal
|
||||
# to avoid accedentially spamming the use. And causing interactive processes
|
||||
# to be SIGSTOP'ed. I do this with a Null Device class.
|
||||
# You could just as easily do some sort of logging thing.
|
||||
sys.stdin.close()
|
||||
sys.stdout = NullDevice()
|
||||
sys.stderr = NullDevice()
|
||||
|
||||
# The last thing we do before handing things off to the daemon's main
|
||||
# function is set up the daemon's signal table how we want it
|
||||
# fork, may have initialized it with the default handlers
|
||||
# depending on implementation
|
||||
signal.signal(signal.SIGHUP, hup_handle)
|
||||
signal.signal(signal.SIGTERM, hup_handle)
|
||||
daemon_main()
|
||||
|
||||
|
||||
# References
|
||||
# 1. Beej's guide to Network Programing https://beej.us/guide/bgnet/
|
||||
# 2. Foundations of Python Network Programming 2ed Rhodes and Goerzen
|
Loading…
x
Reference in New Issue
Block a user