Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > comp.lang.c > #162313 > unrolled thread

How can a daemon process open() the stdout of someone who signals it?

Started byJohn Forkosh <forkosh@panix.com>
First post2021-08-11 13:53 +0000
Last post2021-08-12 03:17 +0000
Articles 7 — 4 participants

Back to article view | Back to comp.lang.c


Contents

  How can a daemon process open() the stdout of someone who signals it? John Forkosh <forkosh@panix.com> - 2021-08-11 13:53 +0000
    Re: How can a daemon process open() the stdout of someone who signals it? Lew Pitcher <lew.pitcher@digitalfreehold.ca> - 2021-08-11 15:41 +0000
      Re: How can a daemon process open() the stdout of someone who signals it? John Forkosh <forkosh@panix.com> - 2021-08-12 03:11 +0000
      Re: How can a daemon process open() the stdout of someone who signals it? Vir Campestris <vir.campestris@invalid.invalid> - 2021-08-13 21:30 +0100
        Re: How can a daemon process open() the stdout of someone who signals it? Lew Pitcher <lew.pitcher@digitalfreehold.ca> - 2021-08-14 14:25 +0000
    Re: How can a daemon process open() the stdout of someone who signals it? Kaz Kylheku <563-365-8930@kylheku.com> - 2021-08-11 18:10 +0000
      Re: How can a daemon process open() the stdout of someone who signals it? John Forkosh <forkosh@panix.com> - 2021-08-12 03:17 +0000

#162313 — How can a daemon process open() the stdout of someone who signals it?

FromJohn Forkosh <forkosh@panix.com>
Date2021-08-11 13:53 +0000
SubjectHow can a daemon process open() the stdout of someone who signals it?
Message-ID<sf0kp8$ki0$1@reader1.panix.com>
Specifically, below's the little test program I wrote.
It works fine, but isn't programmed to do exactly what I want
because I can't figure out how to program that.

It runs as a daemon, just sitting on a pause() until
it catches a signal, via  killall -SIGINT testdaemon
and then it just writes a dummy line with date/time and
#running processes to a log file. Just a "Hello, World" test.

But what I want is to write some stuff to the stdout
of the process that sent the signal to testdaemon.
Note that the testhandler() function has an
if(0)fprintf(stdout,etc).  So my question is,
what do I do to open()/fopen()/whatever the user's
(whoever sent testdaemon the sigint) stdout?
And then close it after the test handler completes,
just before the daemon goes back to pause().

So, for definiteness, here's the little testdaemon code,

/* ---
 * usage:  nohup ./testdaemon
 *   then:  killall -SIGINT testdaemon
 * -------------------------------------- */
/* --- standard headers --- */
#include <stdio.h>
#include <stdlib.h>
/* --- required for daemonize() --- */
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
/* --- required for signals --- */
#include <signal.h>
/* --- additional data --- */
#if !defined(LOGFILE)
  #define LOGFILE "/home/username/testdaemon.log"
#endif

/* --- entry point --- */
void testhand ( int signal ) {  /* test handler for all signals */
  if(0) fprintf(stdout,"testhand> trapped signal#%d\n",signal);
  switch(signal) {
    default: break;
    case SIGINT:
      system("echo `date` ... ps ax = `ps ax|wc` >> " LOGFILE);
      break;
    } /* --- end-of-switch(signal) --- */
  return;
  } /* --- end-of-function testhand() --- */

/* --- entry point --- */
int  main ( int argc, char *argv[] ) {
  /* --- allocations and declarations --- */
  int   signal=SIGINT, sethandler();    /* signal to send daemon */
  void  (*handler)() = testhand;        /* set up test handler */
  int   sleepsecs = 9;                  /* log info every sleepsecs seconds */
  int   daemonize(), isdaemon=daemonize(); /* run process as daemon */
  if ( isdaemon )                       /* running as daemon */
    if ( sethandler(signal,handler) == 0 ) /* established handler */
      while ( 1 ) pause();              /* killall -SIGINT testdaemon */
  exit ( 0 );
  } /* --- end-of-function main() --- */

And, if you're interested, or want to use them,
here are the more general daemonize() and sethandler() functions

/*===========================================================================
 * Function:    sethandler ( signal, handler )
 * Purpose:     Set handler() function for signal.
 * --------------------------------------------------------------------------
 * Arguments:   signal (I)      int containing signal (e.g., SIGALRM)
 *                              to be trapped by handler.
 *              handler (I)     function pointer to handler function.
 * --------------------------------------------------------------------------
 * Returns:     (int)           sigaction() return value.
 * --------------------------------------------------------------------------
 * Notes:     o
 *=========================================================================*/
/* --- entry point --- */
int sethandler ( int signal, void (*handler)() ) {
  /* --- allocations and declarations --- */
  struct sigaction newaction;           /* new action for signal */
  int   action = (-1);                  /* sigaction() return value */
  /* --- establish signal handler by calling sigaction() --- */
  newaction.sa_handler = handler;       /* set handler address */
  newaction.sa_flags = SA_RESTART;      /* resume after running handler */
  sigemptyset(&newaction.sa_mask);      /* no additional signals blocked */
  action = sigaction(signal, &newaction, NULL); /* 0=success, -1=failure */
  return(action);                       /* back to caller with result */
  } /* --- end-of-function sethandler() --- */

/* ==========================================================================
 * Function:    int daemonize ( void )
 * Purpose:     runs process as daemon
 * --------------------------------------------------------------------------
 * Arguments:   -none
 * --------------------------------------------------------------------------
 * Returns:     ( int )         1=success, 0=failure
 * --------------------------------------------------------------------------
 * Notes:     o
 * ======================================================================= */
/* --- entry point --- */
int daemonize ( void ) {
  /* --- allocations and declarations --- */
  int     status = 0;                   /* init to signal failure */
  pid_t   pid, sid;                     /* process, session id */
  /* --- fork off the parent process --- */
  if ( (pid = fork()) < 0 ) goto end_of_job; /* failed to fork */
  if ( pid > 0 ) exit(EXIT_SUCCESS);    /* exit parent process */
  /* --- change the file mode mask --- */
  umask(0);
  /* --- create a new sid for the child process --- */
  if ( (sid = setsid()) < 0 ) goto end_of_job;
  /* --- change the current working directory --- */
  if ( chdir("/") < 0 ) goto end_of_job;
  /* --- close out the standard file descriptors --- */
  close(STDIN_FILENO);
  close(STDOUT_FILENO);
  close(STDERR_FILENO);
  /* --- back to caller as daemon --- */
  status = 1;                           /* signal success to caller */
  end_of_job:
    return status;
  } /* --- end-of-function daemonize() --- */

-- 
John Forkosh  ( mailto:  j@f.com  where j=john and f=forkosh )

[toc] | [next] | [standalone]


#162319

FromLew Pitcher <lew.pitcher@digitalfreehold.ca>
Date2021-08-11 15:41 +0000
Message-ID<sf0r3g$ksq$1@dont-email.me>
In reply to#162313
On Wed, 11 Aug 2021 13:53:44 +0000, John Forkosh wrote:

> Specifically, below's the little test program I wrote.
> It works fine, but isn't programmed to do exactly what I want
> because I can't figure out how to program that.
> 
> It runs as a daemon,

Just to be clear, comp.lang.c is not the correct forum to discuss
daemons and system-specific development. You probably want
one of the comp.os.unix or comp.os.linux groups for a comprehensive
answer.

> just sitting on a pause() until
> it catches a signal, via  killall -SIGINT testdaemon
> and then it just writes a dummy line with date/time and
> #running processes to a log file. Just a "Hello, World" test.
> 
> But what I want is to write some stuff to the stdout
> of the process that sent the signal to testdaemon.

In your daemon, you will want to
 - establish which process signalled,
 - establish which file the signalling process uses as stdout
 - open that file (if possible), write your data, and then close it

You can get the PID of the signalling process from the
siginfo_t si_pid value.

With the signaller's PID, you /may/ be able to obtain the path of the
signaller's stdout file from the symlink named by /proc/<PID>/fd/1.
Note that individual /proc files are usually read-protected so that
only the relevant process's UID and GID can read them.

Once you retrieve the path of the signaller's stdout file, you can try
to open it. It may not exist as a filesystem entity (it may be a pipe,
for instance) or it may be write-protected, but if you can open it for
write access (correction: /append/ access), you can write your data
and close the file.

> Note that the testhandler() function has an
> if(0)fprintf(stdout,etc).  So my question is,
> what do I do to open()/fopen()/whatever the user's
> (whoever sent testdaemon the sigint) stdout?
> And then close it after the test handler completes,
> just before the daemon goes back to pause().
> 
> So, for definiteness, here's the little testdaemon code,
[code snipped]

HTH
-- 
Lew Pitcher
"In Skills, We Trust"

[toc] | [prev] | [next] | [standalone]


#162348

FromJohn Forkosh <forkosh@panix.com>
Date2021-08-12 03:11 +0000
Message-ID<sf23hf$jle$1@reader1.panix.com>
In reply to#162319
Lew Pitcher <lew.pitcher@digitalfreehold.ca> wrote:
> John Forkosh wrote:
> 
>> Specifically, below's the little test program I wrote.
>> It works fine, but isn't programmed to do exactly what I want
>> because I can't figure out how to program that.
>> 
>> It runs as a daemon,
> 
> Just to be clear, comp.lang.c is not the correct forum to discuss
> daemons and system-specific development. You probably want
> one of the comp.os.unix or comp.os.linux groups for a comprehensive
> answer.

My bad if it's the wrong ng. That thought had occurred to me before
posting here, but it's obviously both a C-related and Unix-related
question, and I felt that (a)the C-related part dominated, and that
(b)C developers were more likely familiar with Unix than vice versa.

Anyway, thanks for the answer and suggestions. Definitely gives me
some possible approaches to pursue. I'd tried googling, looking
through my copy of Stevens, etc, but failed to find anything useful.
But your remarks are definitely leading to some useful googling,
filling in the details (although, like you say, it's still not
clear whether or not it can actually work as intended). Thanks again.

>> just sitting on a pause() until
>> it catches a signal, via  killall -SIGINT testdaemon
>> and then it just writes a dummy line with date/time and
>> #running processes to a log file. Just a "Hello, World" test.
>> 
>> But what I want is to write some stuff to the stdout
>> of the process that sent the signal to testdaemon.
> 
> In your daemon, you will want to
>  - establish which process signalled,
>  - establish which file the signalling process uses as stdout
>  - open that file (if possible), write your data, and then close it
> 
> You can get the PID of the signalling process from the
> siginfo_t si_pid value.
> 
> With the signaller's PID, you /may/ be able to obtain the path of the
> signaller's stdout file from the symlink named by /proc/<PID>/fd/1.
> Note that individual /proc files are usually read-protected so that
> only the relevant process's UID and GID can read them.
> 
> Once you retrieve the path of the signaller's stdout file, you can try
> to open it. It may not exist as a filesystem entity (it may be a pipe,
> for instance) or it may be write-protected, but if you can open it for
> write access (correction: /append/ access), you can write your data
> and close the file.
> 
>> Note that the testhandler() function has an
>> if(0)fprintf(stdout,etc).  So my question is,
>> what do I do to open()/fopen()/whatever the user's
>> (whoever sent testdaemon the sigint) stdout?
>> And then close it after the test handler completes,
>> just before the daemon goes back to pause().
>> 
>> So, for definiteness, here's the little testdaemon code,
> [code snipped]
> 
> HTH

-- 
John Forkosh  ( mailto:  j@f.com  where j=john and f=forkosh )

[toc] | [prev] | [next] | [standalone]


#162375

FromVir Campestris <vir.campestris@invalid.invalid>
Date2021-08-13 21:30 +0100
Message-ID<sf6kpl$2u4$2@dont-email.me>
In reply to#162319
On 11/08/2021 16:41, Lew Pitcher wrote:
> Once you retrieve the path of the signaller's stdout file, you can try
> to open it. It may not exist as a filesystem entity (it may be a pipe,
> for instance) or it may be write-protected, but if you can open it for
> write access (correction:/append/  access), you can write your data
> and close the file.

Yeah... but... the original process will then carry on writing its data 
to that file, and will write it at the location that it "knows" the end 
is. Which is where it was before the daemon touched it. And is quite 
possibly nothing to do with the data last written by the signaller, 
which will be in the buffers of its internal file handling code.

Kaz seems to be on the right path. Good luck!

Andy

[toc] | [prev] | [next] | [standalone]


#162376

FromLew Pitcher <lew.pitcher@digitalfreehold.ca>
Date2021-08-14 14:25 +0000
Message-ID<sf8jp7$dao$1@dont-email.me>
In reply to#162375
On Fri, 13 Aug 2021 21:30:45 +0100, Vir Campestris wrote:

> On 11/08/2021 16:41, Lew Pitcher wrote:
>> Once you retrieve the path of the signaller's stdout file, you can try
>> to open it. It may not exist as a filesystem entity (it may be a pipe,
>> for instance) or it may be write-protected, but if you can open it for
>> write access (correction:/append/  access), you can write your data
>> and close the file.
> 
> Yeah... but... the original process will then carry on writing its data 
> to that file, and will write it at the location that it "knows" the end 
> is. Which is where it was before the daemon touched it. And is quite 
> possibly nothing to do with the data last written by the signaller, 
> which will be in the buffers of its internal file handling code.

Agreed. Even with "append", the "client" process may write over the
daemon's output.

FWIW, I do not believe that the OP can achieve the goal as stated in
the original post. Like Kaz, I believe that co-operative changes in both
client and daemon provide the only viable solution. /My/ post pointed
out the challenges (and roadblocks) that I saw in the OP's apparently
preferred solution.

> Kaz seems to be on the right path. Good luck!





-- 
Lew Pitcher
"In Skills, We Trust"

[toc] | [prev] | [next] | [standalone]


#162327

FromKaz Kylheku <563-365-8930@kylheku.com>
Date2021-08-11 18:10 +0000
Message-ID<20210811110000.589@kylheku.com>
In reply to#162313
On 2021-08-11, John Forkosh <forkosh@panix.com> wrote:
> Specifically, below's the little test program I wrote.
> It works fine, but isn't programmed to do exactly what I want
> because I can't figure out how to program that.
>
> It runs as a daemon, just sitting on a pause() until
> it catches a signal, via  killall -SIGINT testdaemon
> and then it just writes a dummy line with date/time and
> #running processes to a log file. Just a "Hello, World" test.
>
> But what I want is to write some stuff to the stdout
> of the process that sent the signal to testdaemon.

I wouldn't bang my head against the wall by doing it this way.

What you can do is implement a little management protocol for
your server that uses some inter-process communication mechanism,
like sockets.

Then write a CLI over the protocol that provides a command language
for doing things with the server.

The server can send output over the protocol, which the CLI can dump on
its standard output.

There are things you can try. In Unix, there are ways to send an open
file descriptor from one process to another. So it seems like it would
be possible to connect to the server somehow, and say "here is file
descriptor; please send printed output to it" (it happens to be my
standard output).

Ooen descriptors can be passed over Unix sockets (AF_UNIX == AF_LOCAL
address family) and SystemV streams and maybe other ways.

I don't think signal handling can pass an open descriptor, so it
couldn't literally work they way you want: get a signal from
some process, and then obtain a descriptor from it.

More like, process opens an AF_UNIX socket to the server,
and passes the desired file descriptor.

-- 
TXR Programming Language: http://nongnu.org/txr
Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal

[toc] | [prev] | [next] | [standalone]


#162349

FromJohn Forkosh <forkosh@panix.com>
Date2021-08-12 03:17 +0000
Message-ID<sf23rs$jle$2@reader1.panix.com>
In reply to#162327
Kaz Kylheku <563-365-8930@kylheku.com> wrote:
> John Forkosh <forkosh@panix.com> wrote:
>> Specifically, below's the little test program I wrote.
>> It works fine, but isn't programmed to do exactly what I want
>> because I can't figure out how to program that.
>>
>> It runs as a daemon, just sitting on a pause() until
>> it catches a signal, via  killall -SIGINT testdaemon
>> and then it just writes a dummy line with date/time and
>> #running processes to a log file. Just a "Hello, World" test.
>>
>> But what I want is to write some stuff to the stdout
>> of the process that sent the signal to testdaemon.
> 
> I wouldn't bang my head against the wall by doing it this way.
> 
> What you can do is implement a little management protocol for
> your server that uses some inter-process communication mechanism,
> like sockets.
> 
> Then write a CLI over the protocol that provides a command language
> for doing things with the server.
> 
> The server can send output over the protocol, which the CLI can dump on
> its standard output.
> 
> There are things you can try. In Unix, there are ways to send an open
> file descriptor from one process to another. So it seems like it would
> be possible to connect to the server somehow, and say "here is file
> descriptor; please send printed output to it" (it happens to be my
> standard output).
> 
> Open descriptors can be passed over Unix sockets (AF_UNIX == AF_LOCAL
> address family) and SystemV streams and maybe other ways.
> 
> I don't think signal handling can pass an open descriptor, so it
> couldn't literally work they way you want: get a signal from
> some process, and then obtain a descriptor from it.
> 
> More like, process opens an AF_UNIX socket to the server,
> and passes the desired file descriptor.

Thanks for the suggestions, Kaz. Sockets sounds like the easiest
and most straightforward approach, so I'll be checking that out
more carefully first.
-- 
John Forkosh  ( mailto:  j@f.com  where j=john and f=forkosh )

[toc] | [prev] | [standalone]


Back to top | Article view | comp.lang.c


csiph-web