Writing a unix daemon - unix

I'm trying to code a daemon in Unix. I understand the part how to make a daemon up and running . Now I want the daemon to respond when I type commands in the shell if they are targeted to the daemon.
For example:
Let us assume the daemon name is "mydaemon"
In terminal 1 I type mydaemon xxx.
In terminal 2 I type mydaemon yyy.
"mydaemon" should be able to receive the argument "xxx" and "yyy".

If I interpret your question correctly, then you have to do this as an application-level construct. That is, this is something specific to your program you're going to have to code up yourself.
The approach I would take is to write "mydaemon" with the idea of it being a wrapper: it checks the process table or a pid file to see if a "mydaemon" is already running. If not, then fork/exec your new daemon. If so, then send the arguments to it.
For "send the arguments to it", I would use named pipes, like are explained here: What are named pipes? Essentially, you can think of named pipes as being like "stdin", except they appear as a file to the rest of the system, so you can open them in your running "mydaemon" and check them for inputs.
Finally, it should be noted that all of this check-if-running-send-to-pipe stuff can either be done in your daemon program, using the API of the *nix OS, or it can be done in a script by using e.g. 'ps', 'echo', etc...

The easiest, most common, and most robust way to do this in Linux is using a systemd socket service.
Example contents of /usr/lib/systemd/system/yoursoftware.socket:
[Unit]
Description=This is a description of your software
Before=yoursoftware.service
[Socket]
ListenStream=/run/yoursoftware.sock
Service=yourservicename.service
# E.x.: use SocketMode=0666 to give rw access to everyone
# E.x.: use SocketMode=0640 to give rw access to root and read-only to SocketGroup
SocketMode=0660
SocketUser=root
# Use socket group to grant access only to specific processes
SocketGroup=root
[Install]
WantedBy=sockets.target
NOTE: If you are creating a local-user daemon instead of a root daemon, then your systemd files go in /usr/lib/systemd/user/ (see pulseaudio.socket for example) or ~/.config/systemd/user/ and your socket is at /run/usr/$(id -u)/yoursoftware.sock (note that you can't actually use command substitution in pathnames in systemd.)
Example contents of /lib/systemd/system/yoursoftware.service
[Unit]
Description=This is a description of your software
Requires=yoursoftware.socket
[Service]
ExecStart=/usr/local/bin/yoursoftware --daemon --yourarg yourvalue
KillMode=process
Restart=on-failure
[Install]
WantedBy=multi-user.target
Also=yoursoftware.socket
Run systemctl daemon-reload && systemctl enable yoursoftware.socket yoursoftware.service as root
Use systemctl --user daemon-reload && systemctl --user enable yoursoftware.socket yoursoftware.service if you're creating the service to run as a local-user
A functional example of the software in C would be way too long, so here's an example in NodeJS. Here is /usr/local/bin/yoursoftware:
#!/usr/bin/env node
var SOCKET_PATH = "/run/yoursoftware.sock";
function errorHandle(e) {
if (e) console.error(e), process.exit(1);
}
if (process.argv[0] === "--daemon") {
var logFile = require("fs").createWriteStream(
"/var/log/yoursoftware.log", {flags: "a"});
require('net').createServer(errorHandle)
.listen(SOCKET_PATH, s => s.pipe(logFile));
} else process.stdin.pipe(
require('net')
.createConnection(SOCKET_PATH, errorHandle)
);
In the example above, you can run many yoursoftware instances at the same time, and the stdin of each of the instances will be piped through to the daemon, which appends all the stuff it receives to a log file.
For non-Linux OSes and distros without systemd, you would use the (typically shell-scripted) startup system to begin your process at boot and the user would receive an error like could not connect to socket /run/yoursoftware.sock when something goes wrong with your daemon.

Related

Using systemd to start vpnc results in 'unknown host'?

I'm trying to connect to vpnc using a systemd service file. The service file runs a script, myscript.sh, which, among other things, runs:
sudo vpnc myhost
Upon booting the device, the other commands are correctly executed, but the vpn is not connected, and gives me the error message:
vpnc: unknown host `myhost.com'
However, if I run the service file manually using
systemctl start myservice.service
then the vpn is successfully started.
My service file looks like this:
[Unit]
Description=VPN Start
Wants=network-online.target
After=network.target network-online.target
[Service]
Environment=DISPLAY=:0.0
Environment=XAUTHORITY=/home/pi/.Xauthority
Type=forking
ExecStart=/bin/bash /home/pi/myscript.sh
Restart=on-abort
User=pi
Group=pi
[Install]
WantedBy=multi-user.target
systemctl status myservice.service
includes this message:
pi: TTY=unknown ; PWD=/home/pi ; USER=root ; COMMAND=/usr/sbin/vpnc myhost
I have already done:
systemctl enable systemd-networkd-wait-online
and that hasn't appeared to help.
It might be to late, but maybe anyone else stumbles upon this.
I had the same issue although I configured the VPN through the GUI.
I found out eventually that my /etc/resolv.conf was a symlink to a configuration file some third party VPN software I sometimes use installed. Although the entries in that file looked fine it only worked, when I deleted the /etc/resolv.conf symlink and created a new normal file.
TL;DR:
Backup /etc/resolv.conf
Remove symlink
Create new /etc/resolv.conf and populate it with your preferred configuration
After that my VPN worked like a charm.

Running nobody (or dynamic user) with CAP_NET_RAW in systemd

I want to run service with cap_net_raw capabilities but with no any interaction with filesystem and/or other processes. My program will use raw sockets and normal sockets (for API), stdout/err for logging and that's all.
I want to write systemd.service file to do this, but I couldn't produce a proper combination for DynamicUser, User and CapabilityBoundingSet.
My (non-working) unit looks like this:
[Unit]
Description=my daemon (%I)
ConditionFileNotEmpty=/etc/daemon/%i.conf
Wants=network-online.target
BindsTo=daemon.target
[Service]
Type=simple
WorkingDirectory=/etc/daemon
EnvironmentFile=/etc/daemon/%i.conf
ExecStart=/usr/bin/daemon ${OPTIONS}
CapabilityBoundingSet=CAP_NET_RAW
ProtectSystem=true
ProtectHome=true
RestartSec=5s
Restart=on-failure
User=daemon-%i
Group=nobody
DynamicUser=true
[Install]
WantedBy=daemon.target
How can I configure dynamic user 'nobody' together with CAP_NET_RAW?
You also need:
AmbientCapabilities=CAP_NET_RAW
See this question about the difference between AmbientCapabilities and CapabilityBoundingSet, as well as the documentation.

Camera (/dev/video0) dependencies in systemd service Ubuntu 16.04

I need to run some services on boot up which I have successfully accomplished using systemd services. (Lots of answers already available).
Now, one of my service requires access to /dev/video0 while bootup when a certain user is logged in. (I am doing auto login which is working fine).
So how do I check that whether the /dev/video0 is available before starting my systemd service while bootup.
I came across something called udev for doing this, I followed this link
but I am not getting desired output as after editing /lib/udev/rules.d/99-systemd.rules files as mentioned in the link and starting my service manually it's not starting, any help is appreciated.
Finally after struggling for a day I found the answer -
I made a script in /etc/systemd/system which contains
[Unit]
Description='some description of my file write according to you'
[Service]
Type=forking
ExecStart='path to script'
[Install]
WantedBy=multi-user.target
and It executes a script which contains
#!/bin/bash
modprobe uvcvideo
Now after rebooting all the services are running properly
mod probe uvcvideo command check for running video driver and enable it at the time of bootup so that It is available for my systemd process
Thanks

Can MPI_Publish_name be used for two separately started applications?

I write an OpenMPI application which consists of a server and a client part which are launched separately:
me#server1:~> mpirun server
and
me#server2:~> mpirun client
server creates a port using MPI_Open_port. The question is: Does OpenMPI have a mechanism to communicate the port to client? I suppose that MPI_Publish_name and MPI_Lookup_name doesn't work here because server wouldn't know to which other computer the information should be sent.
To me, it looks like only processes which were started using a single mpirun can communicate with MPI_Publish_name.
I also found ompi-server, but the documentation is too minimalistic for me to understand this. Does anyone know how this is used?
Related: MPICH: How to publish_name such that a client application can lookup_name it? and https://stackoverflow.com/questions/9263458/client-server-example-using-ompi-does-not-work
MPI_Publish_name is supplied with an MPI info object, which could have an Open MPI specific boolean key ompi_global_scope. If this key is set to true, then the name would be published to the global scope, i.e. to an already running instance of ompi-server. MPI_Lookup_name by default first does a global name lookup if the URI of the ompi-server was provided.
With a dedicated Open MPI server
The process involves several steps:
1) Start the ompi-server somewhere in the cluster where it could be accessed from all nodes. For debugging purposes you may pass it the --no-daemonize -r + argument. It would start and print to the standard output an URI similar to this one:
$ ompi-server --no-daemonize -r +
1221656576.0;tcp://10.1.13.164:36351;tcp://192.168.221.41:36351
2) In the server, build an MPI info object and set the ompi_global_scope key to true:
MPI_Info info;
MPI_Info_create(&info);
MPI_Info_set(info, "ompi_global_scope", "true");
Then pass the info object to MPI_Publish_name:
MPI_Publish_name("server", info, port_name);
3) In the client, the call to MPI_Lookup_name would automatically do the lookup in the global context first (this could be changed by providing the proper key in the MPI info object, but in your case the default behaviour should suffice).
In order for both client and server code to know where the ompi-server is located, you have to give its URI to both mpirun commands with the --ompi-server 1221656576.0;tcp://10.1.13.164:36351;tcp://192.168.221.41:36351 option.
Another option is to have ompi-server write the URI to a file, which can then be read on the node(s) where mpirun is to be run. For example, if you start the server on the same node where both mpirun commands are executed, then you could use a file in /tmp. If you start the ompi-server on a different node, then a shared file system (NFS, Lustre, etc.) would do. Either way, the set of commands would be:
$ ompi-server [--no-daemonize] -r file:/path/to/urifile
...
$ mpirun --ompi-server file:/path/to/urifile server
...
$ mpirun --ompi-server file:/path/to/urifile client
Serverless method
If run both mpirun's on the same node, the --ompi-server could also specify the PID of an already running mpirun instance to be used as a name server. It allows you to use local name publishing in the server (i.e. skip the "run an ompi-server" and "make an info object" parts). The sequence of commands would be:
head-node$ mpirun --report-pid server
[ note the PID of this mpirun instance ]
...
head-node$ mpirun --ompi-server pid:12345 client
where 12345 should be replaced by the real PID of the server's mpirun.
You can also have the server's mpirun print its URI and pass that URI to the client's mpirun:
$ mpirun --report-uri + server
[ note the URI ]
...
$ mpirun --ompi-server URI client
You could also have the URI written to a file if you specify /path/to/file (note: no file: prefix here) instead of + after the --report-uri option:
$ mpirun --report-uri /path/to/urifile server
...
$ mpirun --ompi-server file:/path/to/urifile client
Note that the URI returned by mpirun has the same format as that of an ompi-server, i.e. it includes the host IP address, so it also works if the second mpirun is executed on a different node, which is able to talk to the first node via TCP/IP (and /path/to/urifile lives on a shared file system).
I tested all of the above with Open MPI 1.6.1. Some of the variant might not work with earlier versions.

virsh console hangs at the escape character "^]"

I am trying to kickstart a newly built VM. I am stuck with the following.
Want to start with a console so that I can include username and other info for this VM:
#vmhost02 ~]$ sudo virsh start --console testengine
Domain testengine started
Connected to domain testengine
Escape character is ^]
It hangs up in there and doesn't listen to any keys except "^]"
Let me know if you need more information for any ideas...
Thanks very much.
1)
You can try to edit /etc/default/grub in the guest, and make sure you have:
GRUB_TERMINAL=serial
GRUB_SERIAL_COMMAND="serial --unit=0 --speed=115200 --word=8 --parity=no --stop=1"
Then execute:
# update-grub
# reboot
2)
If that does not work, try to replace quiet with console=ttyS0 in GRUB_CMDLINE_LINUX_DEFAULT in /etc/default/grub:
GRUB_CMDLINE_LINUX_DEFAULT="... console=ttyS0"
Then again:
# update-grub
# reboot
3)
You may still need to try:
# systemctl enable serial-getty#ttyS0.service
# systemctl start serial-getty#ttyS0.service
# reboot
You would need to define a tty to be used as a virtual console. In case you have access to your vm either using vnc or ssh create the following file
vi /etc/init/ttyS0.conf
The content should be something like
start on stopped rc RUNLEVEL=[2345]
stop on runlevel [!2345]
respawn
exec /sbin/getty -L 38400 ttyS0 vt102 # This is your term type vt102
Save these settings and subsequently from your host machine
virsh destroy [vm-name]; service libvirtd stop; service libvirtd start; virsh start [vm-name]
I'm doing here a stop/start of libvirt, because it sometimes tend to not send a SIGTERM to libvirt.
Finally try
virsh console [vm-name]
May be simpler than the solution of val0x00ff, you shall add the console=ttyS0 at the end of the kernel lines in the /boot/grub2/grub.cfg file of the VM (this is not done by default it seems):
(vm)$> grubby --update-kernel=ALL --args="console=ttyS0"
(vm)$> reboot
Then virsh console shall work as expected.

Resources