Subject: SIGINFO and NOKERNINFO
To: None <tech-kern@netbsd.org>
From: Christos Zoulas <christos@zoulas.com>
List: tech-kern
Date: 06/02/2006 12:42:09
Chuck found a bug in ping where if ping was backgrounded, it would stop.
This exposed an issue in the SIGINFO functionality. Ping wants to trap
siginfo to print statistics, but at the same time it does not want the
kernel to print statistics. This is currently accomplished by ping
setting the tty flag NOKERNINFO. Unfortunately, in order to set this
flag we need to call tcsetattr, and this causes the process to stop
if it has a tty, but the tty is not in the current tty control group.

Well, one can detect if the process is in the current tty control group
and choose not to set the flag. This is not good enough, because the
process can start in the foreground, and then be placed in the background.
In this case, not restoring the flag will end up modifying the tty
state.

In the end, the more basic issue is that there should be a way to
avoid printing kerninfo in the current process, that does not modify
global state [the tty state] that persists after the process ends.

Chuck had a good idea, indicating this via a signal flag. The following
code does this.

Opinions?

christos

Index: kern/tty.c
===================================================================
RCS file: /cvsroot/src/sys/kern/tty.c,v
retrieving revision 1.182
diff -u -u -r1.182 tty.c
--- kern/tty.c	14 May 2006 21:15:11 -0000	1.182
+++ kern/tty.c	2 Jun 2006 16:20:34 -0000
@@ -606,7 +606,7 @@
 			 */
 			if (CCEQ(cc[VSTATUS], c)) {
 				if (!ISSET(lflag, NOKERNINFO))
-					ttyinfo(tp);
+					ttyinfo(tp, 1);
 				if (ISSET(lflag, ISIG))
 					pgsignal(tp->t_pgrp, SIGINFO, 1);
 				goto endcase;
@@ -1192,7 +1192,7 @@
 	case TIOCSTAT:			/* get load avg stats */
 		s = spltty();
 		TTY_LOCK(tp);
-		ttyinfo(tp);
+		ttyinfo(tp, 0);
 		TTY_UNLOCK(tp);
 		splx(s);
 		break;
@@ -2314,74 +2314,86 @@
  * Call with tty slock held.
  */
 void
-ttyinfo(struct tty *tp)
+ttyinfo(struct tty *tp, int fromsig)
 {
 	struct lwp	*l;
 	struct proc	*p, *pick;
 	struct timeval	utime, stime;
 	int		tmp;
+	const char	*msg;
 
 	if (ttycheckoutq_wlock(tp, 0) == 0)
 		return;
 
-	/* Print load average. */
-	tmp = (averunnable.ldavg[0] * 100 + FSCALE / 2) >> FSHIFT;
-	ttyprintf_nolock(tp, "load: %d.%02d ", tmp / 100, tmp % 100);
-
 	if (tp->t_session == NULL)
-		ttyprintf_nolock(tp, "not a controlling terminal\n");
+		msg = "not a controlling terminal\n";
 	else if (tp->t_pgrp == NULL)
-		ttyprintf_nolock(tp, "no foreground process group\n");
-	else if ((p = LIST_FIRST(&tp->t_pgrp->pg_members)) == 0)
-		ttyprintf_nolock(tp, "empty foreground process group\n");
+		msg = "no foreground process group\n";
+	else if ((p = LIST_FIRST(&tp->t_pgrp->pg_members)) == NULL)
+		msg = "empty foreground process group\n";
 	else {
 		/* Pick interesting process. */
 		for (pick = NULL; p != NULL; p = LIST_NEXT(p, p_pglist))
 			if (proc_compare(pick, p))
 				pick = p;
+		if (fromsig &&
+		    (SIGACTION_PS(pick->p_sigacts, SIGINFO).sa_flags &
+		    SA_NOKERNINFO))
+			return;
+		msg = NULL;
+	}
 
-		ttyprintf_nolock(tp, " cmd: %s %d [", pick->p_comm, pick->p_pid);
-		LIST_FOREACH(l, &pick->p_lwps, l_sibling)
-		    ttyprintf_nolock(tp, "%s%s",
-		    l->l_stat == LSONPROC ? "running" :
-		    l->l_stat == LSRUN ? "runnable" :
-		    l->l_wmesg ? l->l_wmesg : "iowait",
-			(LIST_NEXT(l, l_sibling) != NULL) ? " " : "] ");
-
-		calcru(pick, &utime, &stime, NULL);
-
-		/* Round up and print user time. */
-		utime.tv_usec += 5000;
-		if (utime.tv_usec >= 1000000) {
-			utime.tv_sec += 1;
-			utime.tv_usec -= 1000000;
-		}
-		ttyprintf_nolock(tp, "%ld.%02ldu ", (long int)utime.tv_sec,
-		    (long int)utime.tv_usec / 10000);
-
-		/* Round up and print system time. */
-		stime.tv_usec += 5000;
-		if (stime.tv_usec >= 1000000) {
-			stime.tv_sec += 1;
-			stime.tv_usec -= 1000000;
-		}
-		ttyprintf_nolock(tp, "%ld.%02lds ", (long int)stime.tv_sec,
-		    (long int)stime.tv_usec / 10000);
+	/* Print load average. */
+	tmp = (averunnable.ldavg[0] * 100 + FSCALE / 2) >> FSHIFT;
+	ttyprintf_nolock(tp, "load: %d.%02d ", tmp / 100, tmp % 100);
+
+	if (msg) {
+		ttyprintf_nolock(tp, msg);
+		tp->t_rocount = 0; /* so pending input will be retyped if BS */
+		return;
+	}
+
+	ttyprintf_nolock(tp, " cmd: %s %d [", pick->p_comm, pick->p_pid);
+	LIST_FOREACH(l, &pick->p_lwps, l_sibling)
+	    ttyprintf_nolock(tp, "%s%s",
+	    l->l_stat == LSONPROC ? "running" :
+	    l->l_stat == LSRUN ? "runnable" :
+	    l->l_wmesg ? l->l_wmesg : "iowait",
+		(LIST_NEXT(l, l_sibling) != NULL) ? " " : "] ");
+
+	calcru(pick, &utime, &stime, NULL);
+
+	/* Round up and print user time. */
+	utime.tv_usec += 5000;
+	if (utime.tv_usec >= 1000000) {
+		utime.tv_sec += 1;
+		utime.tv_usec -= 1000000;
+	}
+	ttyprintf_nolock(tp, "%ld.%02ldu ", (long int)utime.tv_sec,
+	    (long int)utime.tv_usec / 10000);
+
+	/* Round up and print system time. */
+	stime.tv_usec += 5000;
+	if (stime.tv_usec >= 1000000) {
+		stime.tv_sec += 1;
+		stime.tv_usec -= 1000000;
+	}
+	ttyprintf_nolock(tp, "%ld.%02lds ", (long int)stime.tv_sec,
+	    (long int)stime.tv_usec / 10000);
 
 #define	pgtok(a)	(((u_long) ((a) * PAGE_SIZE) / 1024))
-		/* Print percentage CPU. */
-		tmp = (pick->p_pctcpu * 10000 + FSCALE / 2) >> FSHIFT;
-		ttyprintf_nolock(tp, "%d%% ", tmp / 100);
-
-		/* Print resident set size. */
-		if (pick->p_stat == SIDL || P_ZOMBIE(pick))
-			tmp = 0;
-		else {
-			struct vmspace *vm = pick->p_vmspace;
-			tmp = pgtok(vm_resident_count(vm));
-		}
-		ttyprintf_nolock(tp, "%dk\n", tmp);
+	/* Print percentage CPU. */
+	tmp = (pick->p_pctcpu * 10000 + FSCALE / 2) >> FSHIFT;
+	ttyprintf_nolock(tp, "%d%% ", tmp / 100);
+
+	/* Print resident set size. */
+	if (pick->p_stat == SIDL || P_ZOMBIE(pick))
+		tmp = 0;
+	else {
+		struct vmspace *vm = pick->p_vmspace;
+		tmp = pgtok(vm_resident_count(vm));
 	}
+	ttyprintf_nolock(tp, "%dk\n", tmp);
 	tp->t_rocount = 0;	/* so pending input will be retyped if BS */
 }
 
Index: kern/tty_pty.c
===================================================================
RCS file: /cvsroot/src/sys/kern/tty_pty.c,v
retrieving revision 1.89
diff -u -u -r1.89 tty_pty.c
--- kern/tty_pty.c	14 May 2006 21:15:11 -0000	1.89
+++ kern/tty_pty.c	2 Jun 2006 16:20:34 -0000
@@ -1125,7 +1125,7 @@
 				ttyflush(tp, FREAD|FWRITE);
 			if ((sig == SIGINFO) &&
 			    (!ISSET(tp->t_lflag, NOKERNINFO)))
-				ttyinfo(tp);
+				ttyinfo(tp, 1);
 			TTY_UNLOCK(tp);
 			pgsignal(tp->t_pgrp, sig, 1);
 			return(0);
Index: sys/signal.h
===================================================================
RCS file: /cvsroot/src/sys/sys/signal.h,v
retrieving revision 1.62
diff -u -u -r1.62 signal.h
--- sys/signal.h	11 Dec 2005 12:25:21 -0000	1.62
+++ sys/signal.h	2 Jun 2006 16:20:34 -0000
@@ -156,8 +156,11 @@
     defined(_NETBSD_SOURCE)
 #define SA_SIGINFO	0x0040	/* take sa_sigaction handler */
 #endif /* (_POSIX_C_SOURCE - 0) >= 199309L || ... */
+#if defined(_NETBSD_SOURCE)
+#define	SA_NOKERNINFO	0x0080	/* siginfo does not print kernel info on tty */
+#endif /*_NETBSD_SOURCE */
 #ifdef _KERNEL
-#define	SA_ALLBITS	0x007f
+#define	SA_ALLBITS	0x00ff
 #endif
 
 /*
Index: sys/tty.h
===================================================================
RCS file: /cvsroot/src/sys/sys/tty.h,v
retrieving revision 1.70
diff -u -u -r1.70 tty.h
--- sys/tty.h	11 Dec 2005 12:25:21 -0000	1.70
+++ sys/tty.h	2 Jun 2006 16:20:34 -0000
@@ -235,7 +235,7 @@
 int	 ttycheckoutq(struct tty *, int);
 int	 ttyclose(struct tty *);
 void	 ttyflush(struct tty *, int);
-void	 ttyinfo(struct tty *);
+void	 ttyinfo(struct tty *, int);
 int	 ttyinput(int, struct tty *);
 int	 ttylclose(struct tty *, int);
 int	 ttylopen(dev_t, struct tty *);