Run as a daemon or service: Difference between revisions
Line 323: | Line 323: | ||
=={{header|Smalltalk}}== |
=={{header|Smalltalk}}== |
||
{{works with |Smalltalk/X}} |
{{works with |Smalltalk/X}} |
||
<lang smalltalk> |
<lang smalltalk>#! /usr/bin/env stx --script |
||
(pid := OperatingSystem fork) == 0 ifTrue:[ |
|||
Stdin close. |
Stdin close. |
||
Stdout := '/tmp/daemon.log' asFilename writeStream. |
Stdout := '/tmp/daemon.log' asFilename writeStream. |
||
Line 339: | Line 340: | ||
]</lang> |
]</lang> |
||
{{out}} |
{{out}} |
||
<pre>bash-3.2$ ./daemon.st |
|||
forked new process pid=77897; now exiting |
|||
bash-3.2$ |
|||
... after a few seconds: |
... after a few seconds: |
||
cat /tmp/daemon.log |
bash-3.2$ cat /tmp/daemon.log |
||
here is the child |
here is the child |
||
another hello on Transcript 1 |
another hello on Transcript 1 |
Revision as of 21:19, 25 December 2020
A daemon is a service that runs in the background independent of a users login session.
Demonstrate how a program disconnects from the terminal to run as a daemon in the background.
Write a small program that writes a message roughly once a second to its stdout which should be redirected to a file.
Note that in some language implementations it may not be possible to disconnect from the terminal, and instead the process needs to be started with stdout (and stdin) redirected to files before program start. If that is the case then a helper program to set up this redirection should be written in the language itself. A shell wrapper, as would be the usual solution on Unix systems, is not appropriate.
C
BSD provides a convenient daemon(3) function. GNU libc also provides daemon(3), but POSIX omits it, so it is not portable. Other BSDisms in this program are __progname and <err.h>.
The task also wants to redirect stdout. This program does so with dup2(2). Had we wanted to directly write to a file, we could open the file with file = fopen(argv[1], "a")
, and write to file instead of stdout.
<lang c>#include <err.h>
- include <errno.h>
- include <fcntl.h>
- include <stdlib.h>
- include <stdio.h>
- include <string.h>
- include <syslog.h>
- include <time.h>
- include <unistd.h>
int main(int argc, char **argv) { extern char *__progname; time_t clock; int fd;
if (argc != 2) { fprintf(stderr, "usage: %s file\n", __progname); exit(1); }
/* Open the file before becoming a daemon. */ fd = open(argv[1], O_WRONLY | O_APPEND | O_CREAT, 0666); if (fd < 0) err(1, argv[1]);
/* * Become a daemon. Lose terminal, current working directory, * stdin, stdout, stderr. */ if (daemon(0, 0) < 0) err(1, "daemon");
/* Redirect stdout. */ if (dup2(fd, STDOUT_FILENO) < 0) { syslog(LOG_ERR, "dup2: %s", strerror(errno)); exit(1); } close(fd);
/* Dump clock. */ for (;;) { time(&clock); fputs(ctime(&clock), stdout); if (fflush(stdout) == EOF) { syslog(LOG_ERR, "%s: %s", argv[1], strerror(errno)); exit(1); } sleep(1); /* Can wake early or drift late. */ } }</lang>
$ make dumper cc -O2 -pipe -o dumper dumper.c $ ./dumper dump $ tail -f dump Fri Nov 18 13:50:41 2011 Fri Nov 18 13:50:42 2011 Fri Nov 18 13:50:43 2011 Fri Nov 18 13:50:44 2011 Fri Nov 18 13:50:45 2011 ^C $ pkill -x dumper $ rm dump
Go
<lang go>package main
import (
"fmt" "github.com/sevlyar/go-daemon" "log" "os" "time"
)
func work() {
f, err := os.Create("daemon_output.txt") if err != nil { log.Fatal(err) } defer f.Close() ticker := time.NewTicker(time.Second) go func() { for t := range ticker.C { fmt.Fprintln(f, t) // writes time to file every second } }() time.Sleep(60 * time.Second) // stops after 60 seconds at most ticker.Stop() log.Print("ticker stopped")
}
func main() {
cntxt := &daemon.Context{ PidFileName: "pid", PidFilePerm: 0644, LogFileName: "log", LogFilePerm: 0640, WorkDir: "./", Umask: 027, Args: []string{"[Rosetta Code daemon example]"}, }
d, err := cntxt.Reborn() if err != nil { log.Fatal("Unable to run: ", err) } if d != nil { return } defer cntxt.Release()
log.Print("- - - - - - - - - - - - - - -") log.Print("daemon started")
work()
}</lang>
- Output:
Although the daemon will only run for a maximum of 60 seconds, we kill it after a few seconds so the output is not too long.
$ go build daemon.go $ ./daemon $ kill `cat pid` $ cat log 2019/01/31 22:14:50 - - - - - - - - - - - - - - - 2019/01/31 22:14:50 daemon started $ cat daemon_output.txt 2019-01-31 22:14:51.641498031 +0000 GMT m=+1.089305363 2019-01-31 22:14:52.641672923 +0000 GMT m=+2.089480618 2019-01-31 22:14:53.641724473 +0000 GMT m=+3.089531868 2019-01-31 22:14:54.641557151 +0000 GMT m=+4.089364696 2019-01-31 22:14:55.641543175 +0000 GMT m=+5.089350596
PARI/GP
GP scripts cannot run in this fashion directly, but can be compiled into PARI code with gp2c
. PARI code, whether from gp2c
or not, can be run as a daemon just as C would be.
PicoLisp
<lang PicoLisp>(unless (fork)
(out "file.log" (println *Pid) # First write the daemon's PID to the file (for N 3600 # Write count for about one hour (if not killed) (wait 1000) (println N) (flush) ) ) (bye) ) # Child terminates after one hour
(bye) # Parent terminates immediately</lang>
Pike
__FILE__
is a preprocessor definition that contains the current filename.
if the first argument is "daemon" the program will be restarted with stdout redirected to "foo".
<lang Pike>int main(int argc, array argv) {
if (sizeof(argv)>1 && argv[1] == "daemon") { Stdio.File newout = Stdio.File("foo", "wc"); Process.spawn_pike(({ __FILE__ }), ([ "stdout":newout ])); return 1; }
int i = 100; while(i--) { write(i+"\n"); sleep(0.1); }
}</lang>
Python
<lang python>
- !/usr/bin/python3
import posix import os import sys
pid = posix.fork() if pid != 0:
print("Child process detached with pid %s" % pid) sys.exit(0)
old_stdin = sys.stdin old_stdout = sys.stdout old_stderr = sys.stderr
sys.stdin = open('/dev/null', 'rt') sys.stdout = open('/tmp/dmn.log', 'wt') sys.stderr = sys.stdout
old_stdin.close() old_stdout.close() old_stderr.close()
posix.setsid()
import time t = time.time() while time.time() < t + 10:
print("timer running, %s seconds" % str(time.time() - t)) time.sleep(1)
</lang>
Racket
<lang racket>
- lang racket
(require ffi/unsafe) ((get-ffi-obj 'daemon #f (_fun _int _int -> _int)) 0 0) (with-output-to-file "/tmp/foo"
(λ() (for ([i 10]) (displayln (random 1000)) (flush-output) (sleep 1))))
</lang>
Raku
(formerly Perl 6) <lang perl6># Reference:
use v6; use UNIX::Daemonize; use File::Temp;
my ($output, $filehandle) = tempfile(:tempdir("/tmp"),:!unlink) or die;
say "Output now goes to ",$output;
daemonize();
loop {
sleep(1); spurt $output, DateTime.now.Str~"\n", :append;
}</lang>
- Output:
root@ubuntu:~# su - daviddavid@ubuntu:~$ ./dumper.p6 Output now goes to /tmp/x2ovx9JG8b david@ubuntu:~$ tail -f /tmp/x2ovx9JG8b 2018-12-11T20:20:01.510484+08:00 2018-12-11T20:20:02.513732+08:00 2018-12-11T20:20:03.517063+08:00 2018-12-11T20:20:04.520394+08:00 2018-12-11T20:20:05.524871+08:00 2018-12-11T20:20:06.528244+08:00 2018-12-11T20:20:07.531985+08:00 2018-12-11T20:20:08.537776+08:00 2018-12-11T20:20:09.541606+08:00 2018-12-11T20:20:10.545796+08:00 2018-12-11T20:20:11.549047+08:00 2018-12-11T20:20:12.552704+08:00 ^C david@ubuntu:~$ exit logout root@ubuntu:~# tail -f /tmp/x2ovx9JG8b 2018-12-11T20:20:28.623690+08:00 2018-12-11T20:20:29.626978+08:00 2018-12-11T20:20:30.634309+08:00 2018-12-11T20:20:31.637481+08:00 2018-12-11T20:20:32.640794+08:00 2018-12-11T20:20:33.643947+08:00 2018-12-11T20:20:34.647146+08:00 2018-12-11T20:20:35.651008+08:00 ^C root@ubuntu:~# su - david david@ubuntu:~$ tail -f /tmp/x2ovx9JG8b 2018-12-11T20:20:51.711357+08:00 2018-12-11T20:20:52.715044+08:00 2018-12-11T20:20:53.718921+08:00 2018-12-11T20:20:54.722134+08:00 2018-12-11T20:20:55.725970+08:00 2018-12-11T20:20:56.729160+08:00 2018-12-11T20:20:57.732376+08:00 2018-12-11T20:20:58.735409+08:00 2018-12-11T20:20:59.738886+08:00 2018-12-11T20:21:00.743045+08:00 2018-12-11T20:21:01.748113+08:00 2018-12-11T20:21:02.753204+08:00 2018-12-11T20:21:03.756665+08:00 2018-12-11T20:21:04.759902+08:00 ^C david@ubuntu:~$ pkill -c moar 1
Sidef
When the "daemon" argument is specified, a fork of the program is created with its STDOUT redirected into the file "foo.txt", and the main process is exited. <lang ruby>var block = {
for n in (1..100) { STDOUT.say(n) Sys.sleep(0.5) }
}
if (ARGV[0] == 'daemon') {
STDERR.say("Daemon mode") STDOUT{:fh} = %f'foo.txt'.open_w(){:fh} STDOUT.autoflush(true) block.fork STDERR.say("Exiting") Sys.exit(0)
}
STDERR.say("Normal mode") block.run</lang>
Smalltalk
<lang smalltalk>#! /usr/bin/env stx --script (pid := OperatingSystem fork) == 0 ifTrue:[
Stdin close. Stdout := '/tmp/daemon.log' asFilename writeStream. Stdout buffered:false. "so we can watch it growing" Transcript := Stderr := Stdout.
Stdout showCR: e'here is the child'. 1 to:5 do:[:n | Delay waitForSeconds:5. Transcript showCR: e'another hello on Transcript {n}'. ].
] ifFalse:[
Stdout showCR: e'forked new process pid={pid}; now exiting'. OperatingSystem exit:0
]</lang>
- Output:
bash-3.2$ ./daemon.st forked new process pid=77897; now exiting bash-3.2$ ... after a few seconds: bash-3.2$ cat /tmp/daemon.log here is the child another hello on Transcript 1 another hello on Transcript 2 another hello on Transcript 3 another hello on Transcript 4 another hello on Transcript 5
Tcl
Tcl doesn't come with tools for converting the process into a daemon, but can build them easily enough. Here's the BSD daemon function mapped into a Tcl command in a package.
<lang tcl>package provide daemon 1 package require critcl
critcl::ccode {
#include <stdlib.h>
} critcl::cproc daemon {Tcl_Interp* interp} ok {
if (daemon(0, 0) < 0) {
Tcl_AppendResult(interp, "cannot switch to daemon operation: ", Tcl_PosixError(interp), NULL); return TCL_ERROR;
} return TCL_OK;
}</lang> These tools can then be used to solve this task: <lang tcl>### Command line argument parsing if {$argc < 1} {
puts "usage: $argv0 file ?message...?" exit 1
} elseif {$argc == 1} {
set filename [lindex $argv 0] set message "Hi there!"
} else {
set message [join [lassign $argv filename]]
}
- Daemonize
package require daemon daemon close stdout; open $filename ;# Redirects stdout!
- Print the message to the file every second until killed
proc every {ms body} {eval $body; after $ms [info level 0]} every 1000 {puts "[clock format [clock seconds]]: $message"} vwait forever</lang> On Windows, there is a commercial extension to Tcl which allows a script to be installed as a service. Such a script would be much like the one above, but without the daemonization section as that has become a property of the runtime.