Bert,

On Tue, Jul 11, 2006 at 08:08:09AM -0700, Bert Miemietz wrote:
> The idea of darrenm to copy the behaviour of sshd is good. First I
> could not believe that contracts have effects to standard unix
> functions like fork, system, popen etc. What sshd does is a pre-fork
> and post-fork processing depending on whether parent or child is
> effected. All other fork-related functions would need this too.

  We felt it was a simpler model if you could only create a contract as
  a new process, enter a contract by inheritance, and leave a contract
  by exiting.  (Among other things, it avoids circular dependencies
  between process contracts.)  The wide variety of process-creation
  interfaces on unix makes this a little complicated in practice,
  though.

> After some reading of manpages I've written the following which seems
> to work and results in our daemon and applications being in different
> contracts wich allows to start and stop the daemon service without
> applications getting killed. No pre-fork and post-fork processing is
> done. All applications share the same contract but can be killed
> independently because of empty event sets (as far as I understand
> it).
> 
> The following has been added to the daemonize section:
> 
> static int ctfd = -1;             /* contract fd needed for Solaris 10 */
> 
> #if !defined(__SunOS_5_7) && !defined(__SunOS_5_8) && !defined(__SunOS_5_9)
> 
>     ctfd = open64(CTFS_ROOT "/process/template", O_RDWR);
>     if (ctfd == -1) {
>         error_handling();
>     }
>     else {
>         int err = 0;
>         /* supress events, inheriting of contracts and allow orphaning. */
>         err = ct_tmpl_set_critical(ctfd, 0);
>         if (err == 0) err = ct_tmpl_set_informative(ctfd, 0);
>         if (err == 0) err = ct_pr_tmpl_set_fatal(ctfd, 0);
>         if (err == 0) err = ct_pr_tmpl_set_param(ctfd, 0);
>         if (err == 0) err = ct_tmpl_activate(ctfd);
>         if (err != 0) {
>             error_handling();
>         }
> 
>         close(ctfd);
>     }
>     
> #endif /* !defined(__SunOS_5_7) ... !defined(__SunOS_5_9) */

  Depending on when this is run, and what exactly your daemon does
  after it forks, this may or may not do what you think it does.
  
  The key idea here is one of an active template.  Active templates
  behave like many other things in processes in that they are inherited
  on fork but cleared on exec.  Within a process, they persist until
  they are explicitly cleared.

  Every fork which occurs after you activate your template will result
  in a new contract being created (with the terms you declared before
  activating the template) for the newly created process.  Those new
  processes, if they simply exec, will behave normally.  If you fork
  again within a forked process, you'll be creating more contracts.

  So if you run the above code before your daemon daemonizes, your
  daemon will be in one contract, and any process it forks and execs
  will be in its own individual contract.  If you run the above after
  you daemonize, only your children will be in newly created contracts
  (though they will each be in their own).  Assuming your children
  immediately exec, *their* children will be in the same contracts as
  their parents.

> On the system this looks as follows (daemon and applications with
> different contracts):
> 
> # ps -e -o pid,ppid,ctid,args
> 24262     1   330 /usr/openwin/bin/xclock
> 24165     7   323 dvamd -w
> 
> # ctstat -v -i 330
> CTID    ZONEID  TYPE    STATE   HOLDER  EVENTS  QTIME   NTIME   
> 330     0       process owned   24165   0       -       -       
>         cookie:                0
>         informative event set: none
>         critical event set:    none
>         fatal event set:       none
>         parameter set:         none
>         member processes:      24216 24262
>         inherited contracts:   none
> 
> # ctstat -v -i 323
> CTID    ZONEID  TYPE    STATE   HOLDER  EVENTS  QTIME   NTIME   
> 323     0       process owned   7       0       -       -       
>         cookie:                0x20
>         informative event set: none
>         critical event set:    hwerr empty
>         fatal event set:       hwerr
>         parameter set:         inherit pgrponly regent
>         member processes:      24165
>         inherited contracts:   none

  Judging by this output, it looks like the latter is what you are
  doing.

  This should be no cause for alarm; there isn't a huge cost associated
  with creating or managing contracts.  The one concern I would have is
  that if your daemon is long-lived and processes you start are
  regularly restarted by your daemon, you may end up leaking
  contracts.

  Because contracts need to reliably communicate the availability of
  their resources (in this case, processes), they aren't destroyed
  automatically when the your child processes exit.  Unless your daemon
  explicitly abandon these contracts (or exits, which implicitly
  abandons any contracts it holds), they will accumulate as you create
  more and more child processes.  Eventually, assuming you run long
  enough, you'll run into the per-project resource control for
  contracts and start failing your fork()s.

  There are two solutions to this problem.  The first is to abandon a
  contract when its "empties out".  This will require the most work,
  but means that people will still be able to use 'ctstat' and 'ptree
  -c' to see the contractual relationship between your daemon and the
  processes it starts.  The second is to abandon a contract as soon as
  you create it.  This is easy (though it does require a post-fork
  hook), but you lose the aforementioned visibility.  Most people
  choose the second pill.

  The contracts_post_fork_parent function in sshd:

    http://cvs.opensolaris.org/source/xref/on/usr/src/cmd/ssh/sshd/sshd.c#417

  does this.

  We realize there are many ways to make this simpler, including new
  contract terms for things like auto-abandoning on creation, as well
  as new interfaces (e.g. we have some private, unstable, not-to-be-
  used-outside-OpenSolaris-because-they-could-change-at-any-time
  abstractions in libcontract_priv:

    
http://cvs.opensolaris.org/source/xref/on/usr/src/lib/libcontract/common/libcontract_priv.c

  that should probably be made public, stable, etc.).  We appreciate
  any feedback you may have when working with the existing interfaces;
  it will help us decide how best to enhance them.

> Disabling the daemon service does no longer kill the applications:
> [ Jul  8 10:12:05 Executing stop method ("/lib/svc/method/dvamd stop") ]
> [ Jul  8 10:12:05 Method "stop" exited with status 0 ]
> 
> Now it is working, but I'm not that sure if I got everything right. 
> The manual page fork(2) says:
> The child process differs from the parent process in the following ways:
> o  The child process holds no contracts (see contract(4)).
> 
> Do we do anything wrong? Or do similar daemons really need to be
> changed in the same way like sshd. What if not fork() but popen() is used?

  If you use popen() (or any other system call that creates another
  process) you'll need to do the same thing.

  Dave


Reply via email to