summaryrefslogtreecommitdiff
path: root/content/posts/2017/2017-09-28-perl6-creating-a-background-service.md
diff options
context:
space:
mode:
Diffstat (limited to 'content/posts/2017/2017-09-28-perl6-creating-a-background-service.md')
-rw-r--r--content/posts/2017/2017-09-28-perl6-creating-a-background-service.md157
1 files changed, 157 insertions, 0 deletions
diff --git a/content/posts/2017/2017-09-28-perl6-creating-a-background-service.md b/content/posts/2017/2017-09-28-perl6-creating-a-background-service.md
new file mode 100644
index 0000000..4f94bb6
--- /dev/null
+++ b/content/posts/2017/2017-09-28-perl6-creating-a-background-service.md
@@ -0,0 +1,157 @@
+---
+date: 2017-09-28
+title: Perl 6 - Creating a background service
+tags:
+- Tutorial
+- Perl6
+- Programming
+- Raku
+---
+
+I've recently made some progress on
+[Shinrin](https://github.com/scriptkitties/perl6-Shinrin) a centralized logging
+system in Perl 6. This has to run as service, which means that for most service
+managers it has to be able to run in the background.
+
+{{< admonition title="Note" >}}
+If you just want to get to the solution and don't care for the details, just
+head straight to [the full script](#the-final-solution).
+{{< / admonition >}}
+
+## It's not possible!
+
+After a lot of trying and talking with the folks at
+[#perl6](irc://chat.freenode.net:6697/#perl6) I was told that it is not possible
+to do this in pure Perl 6, explained by people with more knowledge than I have
+on the internals:
+
+{{< quote attribution="jnthn" >}}
+(jnthn suspects fork + multi-threaded VM = pain) Since fork only clones one
+thread - the one that called it. So suddenly you've got an instance of the VM
+missing most of its threads.
+{{< / quote >}}
+
+{{< quote attribution="geekosaur" >}}
+The most common failure mode is that some thread is holding e.g. a mutex (or a
+userspace lock) during the fork. The thread goes away but the lock is process
+level and remains, with nothing around to know to unlock it. So then things
+work until something else needs that lock and suddenly you deadlock.
+{{< / quote >}}
+
+Not much later, `jnthn` [pushed a
+commit](https://github.com/perl6/doc/commit/8f9443c3ac) to update the docs to
+clarify that a `fork` call through `NativeCall` will probably not give the
+result you were hoping for.
+
+## Or is it?
+
+Luckily, the same people were able to think up of a work-around, which can be
+made in POSIX sh, so it's usable on any decent OS. The workaround is to let a
+little shell script fork into the background, and let that run the Perl
+application.
+
+### A first example
+This is fairly simple to create, as in this example to launch `shinrind` in the
+background:
+
+```sh
+#! /usr/bin/env sh
+
+main()
+{
+ perl6 -Ilib bin/shinrind "$@"
+}
+
+main "$@" &
+```
+
+This works just fine if the working directory is correct. This means you need
+to be in the parent directory to `lib` and `bin` of the program to make it
+work.
+
+## Improving the forking script
+
+While that short script works fine to show a proof of concept, in order to make
+it viable for real-world scenarios, it can use some improvements. After all, it
+would be annoying if you'd have to `cd` to a specific directory any time you
+want to start your application.
+
+### Ensure you are in the directory you should be in
+
+So for starters, let's make sure that you can run it from anywhere on your
+system. For this, you should set the working directory for the script, so you
+don't have to do it manually. Because the script runs in its own subshell, the
+shell you're working from remains unaffected.
+
+A POSIX compliant way to get the directory the script is stored in is as
+follows:
+
+```sh
+DIR=$(CDPATH="" cd -- "$(dirname -- "$0")" && pwd)
+```
+
+This will set `$DIR` to the path of the directory the shell script is stored
+in. You can simply `cd` to that and be assured you're in the right directory.
+
+In Perl 6, it is expected for executable files to live in the `bin` directory
+of your project repository. So you should actually be in the parent of the
+directory holding your script. Furthermore, you should check the `cd` command
+executed correctly, just to be safe.
+
+```sh
+cd -- "${DIR}/.." || exit
+```
+
+### Disable `STDOUT` and `STDERR`
+
+A started service should not be polluting your interactive shell, so you should
+disable (or otherwise redirect) `STDOUT` and `STDERR`. This is done in the
+shell using a small bit of code behind whatever you want to redirect:
+
+```sh
+> /dev/null 2>&1
+```
+
+This will set `STDOUT` to `/dev/null`, and set `STDERR` to the same stream as
+`STDOUT`, which in effect will make all output go to `/dev/null`. If you want
+to log everything to a single file, you can replace `/dev/null` with another
+file of your choice. If you don't want logs to be overwritten on each start,
+use a `>>` instead of a single `>` at the start.
+
+If you want to log errors and output in different files, you can use the
+following:
+
+```sh
+> /var/log/service.log 2> /var/log/service.err
+```
+
+This will put standard output in `/var/log/service.log` and errors in
+`/var/log/service.err`.
+
+### Fork just the Perl 6 program
+
+In the initial example, I put the `&` behind the `main` call, at the bottom of
+the script. While this works just fine for most simple usage, if you want to do
+additional chores, like creating a pidfile after starting the Perl 6 program,
+you're out of luck. If you were to only fork the Perl 6 application, you could
+handle some other cases in the shell script.
+
+### The final solution
+
+For those eager to just get going with this, here is the complete example
+script to just fork your Perl program into the background:
+
+```sh
+#! /usr/bin/env sh
+
+readonly DIR=$(CDPATH="" cd -- "$(dirname -- "$0")" && pwd)
+
+main()
+{
+ cd -- "${DIR}/.." || exit
+
+ perl6 -Ilib bin/shinrind "$@" > /dev/null >2&1 &
+}
+
+main "$@"
+```