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