Subject: Re: stopping childs (patch included)
To: matthew green <mrg@eterna.com.au>
From: Emmanuel Dreyfus <manu@netbsd.org>
List: tech-kern
Date: 11/05/2002 23:16:59
> i dunno about emmanuel, but for me the fact that the child runs
> for an indeterminate amount of time is not enough.  it may run
> to completion even, before the parent gets scheduled again...
> this feature i would like.

I reworked a bit the patch, by splitting the stop on fork and stop on
exec. We now have two short int values that can be changed by sysctl,
one for stopping on fork and one for stopping on exec. 

The values are short instead of int to save memory in struct proc. Since
this is a debug feature, no one will load it with big values. I even
wonder is just sticking to a char instead of a short would not be better
(that would leave 2 bytes of padding that could be used for something
else)

Here is the new patch. Comment, opinions, before I commit it?

Oh, yes: I wonder if this feature would not be better integrated into
systrace, than using two sysctl. The interface would be more generic,
but on the other hand, stopping just after the system call is a need
really specific to fork and exec...


Index: sys/sys/proc.h
===================================================================
RCS file: /cvsroot/syssrc/sys/sys/proc.h,v
retrieving revision 1.147
diff -U4 -r1.147 proc.h
--- sys/sys/proc.h      2002/10/23 09:14:59     1.147
+++ sys/sys/proc.h      2002/11/05 22:14:56
@@ -186,8 +186,10 @@
  */
 #define        p_startzero     p_opptr
 
        struct proc     *p_opptr;       /* Save parent during ptrace. */
+       u_short         p_stopfork;     /* Stop child on fork */
+       u_short         p_stopexec;     /* Stop on exec */
        int             p_dupfd;        /* Sideways return value from
filedescopen. XXX */
 
        /* Scheduling */
        u_int           p_estcpu;       /* Time averaged value of
p_cpticks. XXX belongs in p_startcopy section */
Index: sys/sys/sysctl.h
===================================================================
RCS file: /cvsroot/syssrc/sys/sys/sysctl.h,v
retrieving revision 1.78
diff -U4 -r1.78 sysctl.h
--- sys/sys/sysctl.h    2002/08/26 13:09:39     1.78
+++ sys/sys/sysctl.h    2002/11/05 22:14:58
@@ -75,8 +75,9 @@
 #define        CTLTYPE_INT     2       /* name describes an integer */
 #define        CTLTYPE_STRING  3       /* name describes a string */
 #define        CTLTYPE_QUAD    4       /* name describes a 64-bit
number */
 #define        CTLTYPE_STRUCT  5       /* name describes a structure */
+#define        CTLTYPE_SHORT   6       /* name describes a short
integer */
 
 /*
  * Top-level identifiers
  */
@@ -589,14 +590,18 @@
  * (rlimit.<type>.{hard,soft}, int).
  */
 #define        PROC_PID_CORENAME       1
 #define        PROC_PID_LIMIT          2
-#define        PROC_PID_MAXID          3
+#define        PROC_PID_STOPFORK       3
+#define        PROC_PID_STOPEXEC       4
+#define        PROC_PID_MAXID          5
 
 #define        PROC_PID_NAMES { \
        { 0, 0 }, \
        { "corename", CTLTYPE_STRING }, \
        { "rlimit", CTLTYPE_NODE }, \
+       { "stopfork", CTLTYPE_SHORT }, \
+       { "stopexec", CTLTYPE_SHORT }, \
 }
 
 /* Limit types from <sys/resources.h> */
 #define        PROC_PID_LIMIT_CPU      (RLIMIT_CPU+1)
@@ -682,8 +687,10 @@
  */
 typedef int (sysctlfn)
     (int *, u_int, void *, size_t *, void *, size_t, struct proc *);
 
+int sysctl_short(void *, size_t *, void *, size_t, short *);
+int sysctl_rdshort(void *, size_t *, void *, short);
 int sysctl_int(void *, size_t *, void *, size_t, int *);
 int sysctl_rdint(void *, size_t *, void *, int);
 int sysctl_quad(void *, size_t *, void *, size_t, quad_t *);
 int sysctl_rdquad(void *, size_t *, void *, quad_t);
Index: sys/kern/kern_sysctl.c
===================================================================
RCS file: /cvsroot/syssrc/sys/kern/kern_sysctl.c,v
retrieving revision 1.114
diff -U4 -r1.114 kern_sysctl.c
--- sys/kern/kern_sysctl.c      2002/11/02 07:25:21     1.114
+++ sys/kern/kern_sysctl.c      2002/11/05 22:15:02
@@ -743,11 +743,28 @@
                        if (i == p->p_ucred->cr_ngroups)
                                return EPERM;
                }
        }
-       if (name[1] == PROC_PID_CORENAME) {
+       switch(name[1]) {
+       case PROC_PID_STOPFORK: 
                if (namelen != 2)
                        return EINVAL;
+               error = sysctl_short(oldp, oldlenp, newp, 
+                   newlen, &ptmp->p_stopfork);
+               return error;
+               break;
+       
+       case PROC_PID_STOPEXEC: 
+               if (namelen != 2)
+                       return EINVAL;
+               error = sysctl_short(oldp, oldlenp, newp, 
+                   newlen, &ptmp->p_stopexec);
+               return error;
+               break;
+
+       case PROC_PID_CORENAME:
+               if (namelen != 2)
+                       return EINVAL;
                /*
                 * Can't use sysctl_string() here because we may malloc
a new
                 * area during the process, so we have to do it by hand.
                 */
@@ -813,10 +830,11 @@
 cleanup:
                if (tmps)
                        free(tmps, M_TEMP);
                return (error);
-       }
-       if (name[1] == PROC_PID_LIMIT) {
+               break;
+
+       case PROC_PID_LIMIT:
                if (namelen != 4 || name[2] >= PROC_PID_LIMIT_MAXID)
                        return EINVAL;
                memcpy(&alim, &ptmp->p_rlimit[name[2] - 1],
sizeof(alim));
                if (name[3] == PROC_PID_LIMIT_TYPE_HARD)
@@ -834,9 +852,15 @@
                if (newp)
                        error = dosetrlimit(ptmp, p->p_cred,
                            name[2] - 1, &alim);
                return error;
+               break;
+
+       default:
+               return (EINVAL);
+               break;
        }
+       /* NOTREACHED */
        return (EINVAL);
 }
 
 int
@@ -939,8 +963,42 @@
                        if (error == 0)                 \
                                error = err2;           \
                }                                       \
        }
+
+/*
+ * Validate parameters and get old / set new parameters
+ * for a short integer-valued sysctl function.
+ */
+int
+sysctl_short(void *oldp, size_t *oldlenp, void *newp, 
+    size_t newlen, short *valp)
+{
+       int error = 0;
+
+       SYSCTL_SCALAR_NEWPCHECK_TYP(newp, newlen, short)
+       SYSCTL_SCALAR_CORE_TYP(oldp, oldlenp, valp, short)
+       SYSCTL_SCALAR_NEWPCOP_TYP(newp, valp, short)
+
+       return (error);
+}
+
+
+/*
+ * As above, but read-only.
+ */
+int
+sysctl_rdshort(void *oldp, size_t *oldlenp, void *newp, short val)
+{
+       int error = 0;
+
+       if (newp)
+               return (EPERM);
+
+       SYSCTL_SCALAR_CORE_TYP(oldp, oldlenp, &val, short)
+
+       return (error);
+}
 
 /*
  * Validate parameters and get old / set new parameters
  * for an integer-valued sysctl function.
Index: sys/kern/kern_fork.c
===================================================================
RCS file: /cvsroot/syssrc/sys/kern/kern_fork.c,v
retrieving revision 1.96
diff -U4 -r1.96 kern_fork.c
--- sys/kern/kern_fork.c        2002/10/23 09:14:17     1.96
+++ sys/kern/kern_fork.c        2002/11/05 22:15:03
@@ -470,15 +470,23 @@
         */
        proclist_unlock_write(s);
 
        /*
-        * Make child runnable, set start time, and add to run queue.
+        * Make child runnable, set start time, and add to run queue
+        * except if the parent requested the child to start in SSTOP
state.
         */
        SCHED_LOCK(s);
        p2->p_stats->p_start = time;
        p2->p_acflag = AFORK;
-       p2->p_stat = SRUN;
-       setrunqueue(p2);
+       if (p1->p_stopfork) {
+               p2->p_stat = SSTOP;
+               p1->p_stopfork--;
+               p2->p_stopfork = p1->p_stopfork;
+               p2->p_stopexec = p1->p_stopexec;
+       } else {
+               p2->p_stat = SRUN;
+               setrunqueue(p2);
+       }
        SCHED_UNLOCK(s);
 
        /*
         * Now can be swapped.
Index: sys/kern/kern_exec.c
===================================================================
RCS file: /cvsroot/syssrc/sys/kern/kern_exec.c,v
retrieving revision 1.161
diff -U4 -r1.161 kern_exec.c
--- sys/kern/kern_exec.c        2002/11/01 19:27:05     1.161
+++ sys/kern/kern_exec.c        2002/11/05 22:15:05
@@ -737,8 +737,21 @@
 #ifdef LKM
        lockmgr(&exec_lock, LK_RELEASE, NULL);
 #endif
        p->p_flag &= ~P_INEXEC;
+
+       if (p->p_stopexec) {
+               int s;
+
+               sigminusset(&contsigmask, &p->p_sigctx.ps_siglist);
+               SCHED_LOCK(s);
+               p->p_stopexec--;
+               p->p_stat = SSTOP;
+               mi_switch(p, NULL);
+               SCHED_ASSERT_UNLOCKED();
+               splx(s);
+       }
+
        return (EJUSTRETURN);
 
  bad:
        p->p_flag &= ~P_INEXEC;
Index: lib/libc/gen/sysctl.3
===================================================================
RCS file: /cvsroot/basesrc/lib/libc/gen/sysctl.3,v
retrieving revision 1.99
diff -U4 -r1.99 sysctl.3
--- lib/libc/gen/sysctl.3       2002/10/01 16:59:47     1.99
+++ lib/libc/gen/sysctl.3       2002/11/05 22:15:09
@@ -1272,8 +1272,10 @@
 points to the current process, or the PID of the target process.
 .Bl -column "USER_COLL_WEIGHTS_MAXXXX" "integerXXX" "yes" -offset
indent
 .It Sy Pa Third level name     Type    Changeable
 .It PROC\_PID\_CORENAME        string  yes
+.It PROC\_STOPEXEC     short   yes
+.It PROC\_STOPFORK     short   yes
 .It PROC\_PID\_LIMIT   node    not applicable
 .El
 .Bl -tag -width "123456"
 .Pp
@@ -1329,8 +1331,24 @@
 .Pp
 The fifth level name is one of PROC_PID_LIMIT_TYPE_SOFT or
 PROC_PID_LIMIT_TYPE_HARD, to select respectively the soft or hard
limit.
 Both are of type integer.
+.It Li PROC_STOPEXEC
+If non zero, the process will be stopped on next 
+.Xr exec 2
+call, and PROC_STOPEXEC will be decreased by one at that time.
+The stopped process can be easily attached by a debugger such as
+.Xr gdb 1 .
+.It Li PROC_STOPFORK
+If non zero, the process' child will be stopped on next 
+.Xr fork 2
+call, and PROC_STOPFORK will be decreased by one at that time, for both
+the parent and the child.
+This also apply to emulation specific system calls that
+fork a new process, such as 
+.Fn sproc 
+or 
+.Fn clone .
 .El
 .Sh CTL_USER
 The string and integer information available for the CTL_USER level
 is detailed below.
Index: sbin/sysctl/sysctl.8
===================================================================
RCS file: /cvsroot/basesrc/sbin/sysctl/sysctl.8,v
retrieving revision 1.81
diff -U4 -r1.81 sysctl.8
--- sbin/sysctl/sysctl.8        2002/10/03 15:41:47     1.81
+++ sbin/sysctl/sysctl.8        2002/11/05 22:15:10
@@ -355,8 +355,10 @@
 .It proc.\*[Lt]pid\*[Gt].rlimit.memoryuse.hard integer yes
 .It proc.\*[Lt]pid\*[Gt].rlimit.memoryuse.soft integer yes
 .It proc.\*[Lt]pid\*[Gt].rlimit.stacksize.hard integer yes
 .It proc.\*[Lt]pid\*[Gt].rlimit.stacksize.soft integer yes
+.It proc.\*[Lt]pid\*[Gt].stopexec      short   yes
+.It proc.\*[Lt]pid\*[Gt].stopfork      short   yes
 .It user.bc_base_max   integer no
 .It user.bc_dim_max    integer no
 .It user.bc_scale_max  integer no
 .It user.bc_string_max integer no
Index: sbin/sysctl/sysctl.c
===================================================================
RCS file: /cvsroot/basesrc/sbin/sysctl/sysctl.c,v
retrieving revision 1.59
diff -U4 -r1.59 sysctl.c
--- sbin/sysctl/sysctl.c        2002/11/03 07:06:06     1.59
+++ sbin/sysctl/sysctl.c        2002/11/05 22:15:12
@@ -318,8 +318,9 @@
 {
        int indx, type, state, len;
        int special = 0;
        void *newval = 0;
+       short shortval;
        int intval, newsize = 0;
        quad_t quadval;
        size_t size;
        struct list *lp;
@@ -553,8 +554,14 @@
                return;
        }
        if (newsize > 0) {
                switch (type) {
+               case CTLTYPE_SHORT:
+                       shortval = (short)atoi(newval);
+                       newval = &shortval;
+                       newsize = sizeof shortval;
+                       break;
+
                case CTLTYPE_INT:
                        intval = atoi(newval);
                        newval = &intval;
                        newsize = sizeof intval;
@@ -647,8 +654,20 @@
                return;
        }
 
        switch (type) {
+       case CTLTYPE_SHORT:
+               if (newsize == 0) {
+                       if (!nflag)
+                               printf("%s = ", string);
+                       printf("%hd\n", *(short *)buf);
+               } else {
+                       if (!nflag)
+                               printf("%s: %hd -> ", string, *(short
*)buf);
+                       printf("%hd\n", *(short *)newval);
+               }
+               return;
+
        case CTLTYPE_INT:
                if (newsize == 0) {
                        if (!nflag)
                                printf("%s = ", string);

-- 
Emmanuel Dreyfus.
"Of course, it runs NetBSD" -- http://www.netbsd.org
manu@netbsd.org