xref: /original-bsd/sys/kern/kern_acct.c (revision 753853ba)
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.21 (Berkeley) 02/03/92
8  */
9 
10 #include "param.h"
11 #include "systm.h"
12 #include "namei.h"
13 #include "resourcevar.h"
14 #include "proc.h"
15 #include "ioctl.h"
16 #include "termios.h"
17 #include "tty.h"
18 #include "vnode.h"
19 #include "mount.h"
20 #include "kernel.h"
21 #include "file.h"
22 #include "acct.h"
23 #include "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 struct	timeval chk = { 15, 0 };/* frequency to check space for accounting */
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 sysacct(p, uap, retval)
51 	struct proc *p;
52 	struct args {
53 		char	*fname;
54 	} *uap;
55 	int *retval;
56 {
57 	register struct vnode *vp;
58 	extern int acctwatch();
59 	struct vnode *oacctp;
60 	int error;
61 	struct nameidata nd;
62 
63 	if (error = suser(p->p_ucred, &p->p_acflag))
64 		return (error);
65 	if (savacctp) {
66 		acctp = savacctp;
67 		savacctp = NULL;
68 	}
69 	if (uap->fname == NULL) {
70 		if (vp = acctp) {
71 			acctp = NULL;
72 			error = vn_close(vp, FWRITE, p->p_ucred, p);
73 			untimeout(acctwatch, (caddr_t)&chk);
74 		}
75 		return (error);
76 	}
77 	NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, uap->fname, p);
78 	if (error = vn_open(&nd, FWRITE, 0644))
79 		return (error);
80 	vp = nd.ni_vp;
81 	VOP_UNLOCK(vp);
82 	if (vp->v_type != VREG) {
83 		(void) vn_close(vp, FWRITE, p->p_ucred, p);
84 		return (EACCES);
85 	}
86 	oacctp = acctp;
87 	acctp = vp;
88 	if (oacctp)
89 		error = vn_close(oacctp, FWRITE, p->p_ucred, p);
90 	acctwatch(&chk);
91 	return (error);
92 }
93 
94 /*
95  * Periodically check the file system to see if accounting
96  * should be turned on or off.
97  */
98 acctwatch(resettime)
99 	struct timeval *resettime;
100 {
101 	struct statfs sb;
102 
103 	if (savacctp) {
104 		(void)VFS_STATFS(savacctp->v_mount, &sb, (struct proc *)0);
105 		if (sb.f_bavail > acctresume * sb.f_blocks / 100) {
106 			acctp = savacctp;
107 			savacctp = NULL;
108 			log(LOG_NOTICE, "Accounting resumed\n");
109 			return;
110 		}
111 	}
112 	if (acctp == NULL)
113 		return;
114 	(void)VFS_STATFS(acctp->v_mount, &sb, (struct proc *)0);
115 	if (sb.f_bavail <= acctsuspend * sb.f_blocks / 100) {
116 		savacctp = acctp;
117 		acctp = NULL;
118 		log(LOG_NOTICE, "Accounting suspended\n");
119 	}
120 	timeout(acctwatch, (caddr_t)resettime, hzto(resettime));
121 }
122 
123 /*
124  * This routine calculates an accounting record for a process and,
125  * if accounting is enabled, writes it to the accounting file.
126  */
127 acct(p)
128 	register struct proc *p;
129 {
130 	register struct rusage *ru;
131 	struct vnode *vp;
132 	struct timeval t, ut, st;
133 	int i, s;
134 	struct acct acctbuf;
135 	register struct acct *ap = &acctbuf;
136 
137 	if ((vp = acctp) == NULL)
138 		return (0);
139 	bcopy(p->p_comm, ap->ac_comm, sizeof(ap->ac_comm));
140 	ru = &p->p_stats->p_ru;
141 	s = splclock();
142 	ut = p->p_utime;
143 	st = p->p_stime;
144 	t = time;
145 	splx(s);
146 	ap->ac_utime = compress(ut.tv_sec, ut.tv_usec);
147 	ap->ac_stime = compress(st.tv_sec, st.tv_usec);
148 	timevalsub(&t, &p->p_stats->p_start);
149 	ap->ac_etime = compress(t.tv_sec, t.tv_usec);
150 	ap->ac_btime = p->p_stats->p_start.tv_sec;
151 	ap->ac_uid = p->p_cred->p_ruid;
152 	ap->ac_gid = p->p_cred->p_rgid;
153 	t = st;
154 	timevaladd(&t, &ut);
155 	if (i = t.tv_sec * hz + t.tv_usec / tick)
156 		ap->ac_mem = (ru->ru_ixrss + ru->ru_idrss + ru->ru_isrss) / i;
157 	else
158 		ap->ac_mem = 0;
159 	ap->ac_io = compress(ru->ru_inblock + ru->ru_oublock, (long)0);
160 	if (p->p_flag&SCTTY && p->p_session->s_ttyp)
161 		ap->ac_tty = p->p_session->s_ttyp->t_dev;
162 	else
163 		ap->ac_tty = NODEV;
164 	ap->ac_flag = p->p_acflag;
165 	return (vn_rdwr(UIO_WRITE, vp, (caddr_t)ap, sizeof (acctbuf), (off_t)0,
166 		UIO_SYSSPACE, IO_UNIT|IO_APPEND, p->p_ucred, (int *)0,
167 		(struct proc *)0));
168 }
169 
170 /*
171  * Produce a pseudo-floating point representation
172  * with 3 bits base-8 exponent, 13 bits fraction.
173  */
174 compress(t, ut)
175 	register long t;
176 	long ut;
177 {
178 	register exp = 0, round = 0;
179 
180 	t = t * AHZ;  /* compiler will convert only this format to a shift */
181 	if (ut)
182 		t += ut / (1000000 / AHZ);
183 	while (t >= 8192) {
184 		exp++;
185 		round = t&04;
186 		t >>= 3;
187 	}
188 	if (round) {
189 		t++;
190 		if (t >= 8192) {
191 			t >>= 3;
192 			exp++;
193 		}
194 	}
195 	return ((exp<<13) + t);
196 }
197