xref: /original-bsd/sys/kern/kern_acct.c (revision a296d2cb)
1 /*-
2  * Copyright (c) 1982, 1986, 1989 The Regents of the University of California.
3  * All rights reserved.
4  *
5  * %sccs.include.proprietary.c%
6  *
7  *	@(#)kern_acct.c	7.26 (Berkeley) 10/11/92
8  */
9 
10 #include <sys/param.h>
11 #include <sys/systm.h>
12 #include <sys/namei.h>
13 #include <sys/resourcevar.h>
14 #include <sys/proc.h>
15 #include <sys/ioctl.h>
16 #include <sys/termios.h>
17 #include <sys/tty.h>
18 #include <sys/vnode.h>
19 #include <sys/mount.h>
20 #include <sys/kernel.h>
21 #include <sys/file.h>
22 #include <sys/acct.h>
23 #include <sys/syslog.h>
24 
25 /*
26  * Values associated with enabling and disabling accounting
27  */
28 int	acctsuspend = 2;	/* stop accounting when < 2% free space left */
29 int	acctresume = 4;		/* resume when free space risen to > 4% */
30 int	chk = 15;		/* frequency (in seconds) to check space */
31 
32 /*
33  * SHOULD REPLACE THIS WITH A DRIVER THAT CAN BE READ TO SIMPLIFY.
34  */
35 struct	vnode *acctp;
36 struct	vnode *savacctp;
37 
38 /*
39  * Enable or disable process accounting.
40  *
41  * If a non-null filename is given, that file is used to store accounting
42  * records on process exit. If a null filename is given process accounting
43  * is suspended. If accounting is enabled, the system checks the amount
44  * of freespace on the filesystem at timeval intervals. If the amount of
45  * freespace is below acctsuspend percent, accounting is suspended. If
46  * accounting has been suspended, and freespace rises above acctresume,
47  * accounting is resumed.
48  */
49 /* ARGSUSED */
50 struct sysacct_args {
51 	char	*fname;
52 };
53 sysacct(p, uap, retval)
54 	struct proc *p;
55 	struct sysacct_args *uap;
56 	int *retval;
57 {
58 	register struct vnode *vp;
59 	extern void acctwatch __P((void *));
60 	struct vnode *oacctp;
61 	int error;
62 	struct nameidata nd;
63 
64 	if (error = suser(p->p_ucred, &p->p_acflag))
65 		return (error);
66 	if (savacctp) {
67 		acctp = savacctp;
68 		savacctp = NULL;
69 	}
70 	if (uap->fname == NULL) {
71 		if (vp = acctp) {
72 			acctp = NULL;
73 			error = vn_close(vp, FWRITE, p->p_ucred, p);
74 			untimeout(acctwatch, NULL);
75 		}
76 		return (error);
77 	}
78 	NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, uap->fname, p);
79 	if (error = vn_open(&nd, FWRITE, 0644))
80 		return (error);
81 	vp = nd.ni_vp;
82 	VOP_UNLOCK(vp);
83 	if (vp->v_type != VREG) {
84 		(void) vn_close(vp, FWRITE, p->p_ucred, p);
85 		return (EACCES);
86 	}
87 	oacctp = acctp;
88 	acctp = vp;
89 	if (oacctp)
90 		error = vn_close(oacctp, FWRITE, p->p_ucred, p);
91 	acctwatch(NULL);
92 	return (error);
93 }
94 
95 /*
96  * Periodically check the file system to see if accounting
97  * should be turned on or off.
98  */
99 /* ARGSUSED */
100 void
101 acctwatch(a)
102 	void *a;
103 {
104 	struct statfs sb;
105 
106 	if (savacctp) {
107 		(void)VFS_STATFS(savacctp->v_mount, &sb, (struct proc *)0);
108 		if (sb.f_bavail > acctresume * sb.f_blocks / 100) {
109 			acctp = savacctp;
110 			savacctp = NULL;
111 			log(LOG_NOTICE, "Accounting resumed\n");
112 			return;
113 		}
114 	}
115 	if (acctp == NULL)
116 		return;
117 	(void)VFS_STATFS(acctp->v_mount, &sb, (struct proc *)0);
118 	if (sb.f_bavail <= acctsuspend * sb.f_blocks / 100) {
119 		savacctp = acctp;
120 		acctp = NULL;
121 		log(LOG_NOTICE, "Accounting suspended\n");
122 	}
123 	timeout(acctwatch, NULL, chk * hz);
124 }
125 
126 /*
127  * This routine calculates an accounting record for a process and,
128  * if accounting is enabled, writes it to the accounting file.
129  */
130 acct(p)
131 	register struct proc *p;
132 {
133 	register struct rusage *ru;
134 	struct vnode *vp;
135 	struct timeval t, ut, st;
136 	int i, s;
137 	struct acct acctbuf;
138 	register struct acct *ap = &acctbuf;
139 
140 	if ((vp = acctp) == NULL)
141 		return (0);
142 	bcopy(p->p_comm, ap->ac_comm, sizeof(ap->ac_comm));
143 	ru = &p->p_stats->p_ru;
144 	calcru(p, &ut, &st, NULL);
145 	s = splclock();
146 	t = time;
147 	splx(s);
148 	ap->ac_utime = compress(ut.tv_sec, ut.tv_usec);
149 	ap->ac_stime = compress(st.tv_sec, st.tv_usec);
150 	timevalsub(&t, &p->p_stats->p_start);
151 	ap->ac_etime = compress(t.tv_sec, t.tv_usec);
152 	ap->ac_btime = p->p_stats->p_start.tv_sec;
153 	ap->ac_uid = p->p_cred->p_ruid;
154 	ap->ac_gid = p->p_cred->p_rgid;
155 	t = st;
156 	timevaladd(&t, &ut);
157 	if (i = t.tv_sec * hz + t.tv_usec / tick)
158 		ap->ac_mem = (ru->ru_ixrss + ru->ru_idrss + ru->ru_isrss) / i;
159 	else
160 		ap->ac_mem = 0;
161 	ap->ac_io = compress(ru->ru_inblock + ru->ru_oublock, (long)0);
162 	if (p->p_flag&SCTTY && p->p_session->s_ttyp)
163 		ap->ac_tty = p->p_session->s_ttyp->t_dev;
164 	else
165 		ap->ac_tty = NODEV;
166 	ap->ac_flag = p->p_acflag;
167 	return (vn_rdwr(UIO_WRITE, vp, (caddr_t)ap, sizeof (acctbuf), (off_t)0,
168 		UIO_SYSSPACE, IO_UNIT|IO_APPEND, p->p_ucred, (int *)0,
169 		(struct proc *)0));
170 }
171 
172 /*
173  * Produce a pseudo-floating point representation
174  * with 3 bits base-8 exponent, 13 bits fraction.
175  */
176 compress(t, ut)
177 	register long t;
178 	long ut;
179 {
180 	register exp = 0, round = 0;
181 
182 	t = t * AHZ;  /* compiler will convert only this format to a shift */
183 	if (ut)
184 		t += ut / (1000000 / AHZ);
185 	while (t >= 8192) {
186 		exp++;
187 		round = t&04;
188 		t >>= 3;
189 	}
190 	if (round) {
191 		t++;
192 		if (t >= 8192) {
193 			t >>= 3;
194 			exp++;
195 		}
196 	}
197 	return ((exp<<13) + t);
198 }
199