Hello Guus, hello Ziga

I found the problem.

Guus Sliepen schrieb am Tue 27. Mar, 12:46 (+0200):
> On Thu, Mar 22, 2007 at 05:23:36PM +0100, Jörg Sommer wrote:
> 
> > > The patch didn't work. I then reinstalled the original script, but
> > > removed all redirections to /dev/null. It still complains about not
> > > being able to open /dev/null.
> > 
> > Oh, can you add a set -x before the PATH assignment and than look, if you
> > can see between which lines the message is printed. BTW: You can stop the
> > output with ctrl+s.
> 
> I see the following:
> 
> [...]
> + [ ! -f /proc/stat ]
> + sleep 0.2
> /sbin/bootchartd: 1: Can't open /dev/null
> /sbin/bootchartd: 1: Can't open /dev/null
> /sbin/bootchartd: 1: + [ -n yes ]
> /sbin/bootchartd: 1: Can't open /dev/null
> Can't open /dev/null

The lines above belong to these lines:

  while [ ! -f /proc/stat ]; do sleep $SAMPLE_PERIOD; done
  sleep $SAMPLE_PERIOD

  # Run loggers in background
  log_output "cat /proc/stat" proc_stat.log &

  # /proc/diskstats is available in 2.6 kernels
  [ -f /proc/diskstats ] && log_output "cat /proc/diskstats" proc_diskstats.log 
&

  log_output "cat /proc/*/stat" proc_ps.log &

This is a short form of the problamatical code:

#!/bin/dash

foo()
{
    echo $!
    /bin/ls -l /proc/$$/fd
    /bin/sleep 2
}

foo &
foo

If you use strace, you can find these lines:

[pid 17961] clone(Process 17964 attached
child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, 
child_tidptr=0x3001fd68) = 17964
[pid 17961] wait4(-1, Process 17961 suspended
 <unfinished ...>
[pid 17963] close(10)                   = 0
[pid 17963] rt_sigaction(SIGINT, {SIG_IGN}, {0x100115b0, ~[KILL STOP RTMIN 
RT_1], 0}, 8) = 0
[pid 17963] rt_sigaction(SIGQUIT, {SIG_IGN}, {SIG_DFL}, 8) = 0
[pid 17963] close(0)                    = 0
[pid 17963] open("/dev/null", O_RDONLY) = 0
[pid 17963] write(1, "\n", 1
)           = 1

And there you see, dash tries to open /dev/null. The corresponding code
is the following: (dash-0.5.3/src/jobs.c)

/*
 * Fork off a subshell.  If we are doing job control, give the subshell its
 * own process group.  Jp is a job structure that the job is to be added to.
 * N is the command that will be evaluated by the child.  Both jp and n may
 * be NULL.  The mode parameter can be one of the following:
 *      FORK_FG - Fork off a foreground process.
 *      FORK_BG - Fork off a background process.
 *      FORK_NOJOB - Like FORK_FG, but don't give the process its own
 *                   process group even if job control is on.
 *
 * When job control is turned off, background processes have their standard
 * input redirected to /dev/null (except for the second and later processes
 * in a pipeline).
 *
 * Called with interrupts off.
 */

STATIC inline void
forkchild(struct job *jp, union node *n, int mode)
{
        int oldlvl;

        TRACE(("Child shell %d\n", getpid()));
        oldlvl = shlvl;
        shlvl++;

        closescript();
        clear_traps();
#if JOBS
        /* do job control only in root shell */
        jobctl = 0;
        if (mode != FORK_NOJOB && jp->jobctl && !oldlvl) {
                pid_t pgrp;

                if (jp->nprocs == 0)
                        pgrp = getpid();
                else
                        pgrp = jp->ps[0].pid;
                /* This can fail because we are doing it in the parent also */
                (void)setpgid(0, pgrp);
                if (mode == FORK_FG)
                        xtcsetpgrp(ttyfd, pgrp);
                setsignal(SIGTSTP);
                setsignal(SIGTTOU);
        } else
#endif
        if (mode == FORK_BG) {
                ignoresig(SIGINT);
                ignoresig(SIGQUIT);
                if (jp->nprocs == 0) {
                        close(0);
                        if (open(_PATH_DEVNULL, O_RDONLY) != 0)
                                sh_error("Can't open %s", _PATH_DEVNULL);
                }
        }

Three lines above you can see the problamatical line of code. The path is
hard coded and there's (no) escape. The only way I found out is job
control.

#!/bin/dash

exec 3>&1 <>/dev/null >&0

set -m

foo()
{
    echo $!
    /bin/ls -l /proc/$$/fd
    /bin/sleep 2
}

echo hello >&3

foo &
foo

echo bye >&3

For job control bootchart needs a heavy rewrite, but I also think it can
gain something. The other way is to try to redirect the shell error to
/dev/null.

What do you think Ziga?

Bye, Jörg.
-- 
“Perl—the only language that looks the same
 before and after RSA encryption.”           (Keith Bostic)

Attachment: pgpzB7eE2nZ3Y.pgp
Description: PGP signature

Reply via email to