in reply to Systemd socket activated service in perl
Thanks to everyone for the help here, especially tybalt89.
Bringing it all together and testing it on an Ubuntu 22.04 VM, I have this simplified perl script:
#!/usr/bin/perl # Minimal systemd socket activated service # Put together based on responses from: https://www.perlmonks.org/?nod +e_id=11157093 use strict; use warnings; use English; $OUTPUT_AUTOFLUSH = 1; # AKA $| # Important. We need to know the remote IP address. my $remote_addr = $ENV{REMOTE_ADDR}; printf "Greetings %s what is your command?\n", $remote_addr; # printf "%s = %s\n", $_, $ENV{$_} foreach sort keys %ENV; # Us +ed to get the rest of the environment. # Code here to check that $remote_addr is authorised to connect, and w +hich remote commands it may call. # Need to close the socket & refuse futher commands $remote_addr is no +t authorised. while( my $cmd = <> ) { if( $cmd =~ m/^sleep (\d+)/ ) { my $snooze_time = $1; printf "Will sleep for $snooze_time seconds\n"; sleep $snooze_time; print "... ZZZ ... Yawn ... What next oh master?\n"; } # NB: In real life there are lots of other commands and many take +some time to process. else { print "I did not understand that. What next oh master?\n"; } }
With two systemd unit files:
The socket:
/etc/systemd/system/pfs.socket
[Unit] Description=perl forking socket PartOf=pfs.service [Socket] ListenStream=2345 BindIPv6Only=both Accept=yes MaxConnections=12 [Install] WantedBy=sockets.target
And the service:
/etc/systemd/system/pfs@.service
[Unit] Description=perl forking server After=network.target nss-user-lookup.target Requires=pfs.socket [Service] ExecStart=/root/path/to/minimal_systemd_socket_activated_service.pl StandardInput=socket [Install] Also=pfs.socket WantedBy=multi-user.target
Once all three files are installed on the sever:
It should then be possible to test the service:
echo "sleep 30" | nc localhost 2345 -NCNotes and observations:
Like a CGI script, when the socket activated service is running, STDIN and STDOUT are the socket to the remote client, so printf debugging and logging does not work as expected.
Similar to a cron job, the socket activated service gets very few environment variables set. It is possible to add Environment="variable=value" entries into the Service stanza of the pfs@.service file.
The service file should have an @ symbol in the name. It is not a mistake. In other types of systemd units, the @ symbol means that the service is parameterised, and we should expect to see a %i placeholder in the service unit file. For the socket activated service, the socket sets up the parameters when it invokes the service.
I connected to the service to start a lengthy sleep, then ran systemctl status pfs.socket
This gave an extra Triggers entry that is not show when the socket is idle.
root@Ubuntu:~# systemctl status pfs.socket
● pfs.socket - perl forking socket
Loaded: loaded (/etc/systemd/system/pfs.socket; enabled; vendor preset: enabled)
Active: active (listening) since Sun 2024-01-21 10:18:43 GMT; 9min ago
Triggers: ● pfs@5-127.0.0.1:2345-127.0.0.1:39394.service
Listen: :::2345 (Stream)
Accepted: 6; Connected: 1;
Tasks: 0 (limit: 9361)
Memory: 8.0K
CPU: 1ms
CGroup: /system.slice/pfs.socket
I was then able to get the status of the temporary service:
root@Ubuntu:~# systemctl status pfs@5-127.0.0.1:2345-127.0.0.1:39394.service
● pfs@5-127.0.0.1:2345-127.0.0.1:39394.service - perl forking server (127.0.0.1:39394)
Loaded: loaded (/etc/systemd/system/pfs@.service; disabled; vendor preset: enabled)
Active: active (running) since Sun 2024-01-21 10:27:42 GMT; 11s ago
TriggeredBy: ● pfs.socket
Main PID: 3225 (minimal_systemd)
Tasks: 1 (limit: 9361)
Memory: 1.2M
CPU: 5ms
CGroup: /system.slice/system-pfs.slice/pfs@5-127.0.0.1:2345-127.0.0.1:39394.service
└─3225 /usr/bin/perl /root/path/to/minimal_systemd_socket_activated_service.pl
And investigate the environment variables of that service
root@Ubuntu:~# cat /proc/3225/environ | xargs -0 -n1 LANG=en_GB.UTF-8 LANGUAGE=en_GB:en PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/snap/bin REMOTE_ADDR=127.0.0.1 REMOTE_PORT=39394 INVOCATION_ID=73c1fa9b1e8b45c0a557538dce370fd6 SYSTEMD_EXEC_PID=3225
|
|---|