3

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".

  • Do you mean you want to be able to pass commands to the daemon when you start it the first time, or do you mean _after_ you've started a daemon and it's running, successive calls to "mydaemon" send instructions to the already running process? – Matt Nov 06 '12 at 03:32
  • 1
    I meant successive calls to "mydaemon" send instructions to the already running process. – user1801900 Nov 06 '12 at 03:36

2 Answers2

1

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...

Community
  • 1
  • 1
Matt
  • 10,434
  • 1
  • 36
  • 45
  • Thanks, I have one more question here, lets say I want to return the new request "mydaemon yyy" an indication that your request has been accepted can I write stuff in the named pipe and read at the other end too? – user1801900 Nov 06 '12 at 04:13
  • I don't think one pipe will do it - you could do it with multiple - but you may be better served by using a Unix socket http://beej.us/guide/bgipc/output/html/multipage/unixsock.html - in particular, look at the end of the article where `socketpair()` is described. Using Unix sockets feels more like using full network sockets, which may be an advantage - it's nearly trivial to port your daemon to be a networked service - and (if you do it right) you've got full bidirectional asynchronous communication between your daemon and any other program. – Matt Nov 06 '12 at 06:44
0

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.

Jack G
  • 4,553
  • 2
  • 41
  • 50