]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/kern/kern_acct.c
This commit was generated by cvs2svn to compensate for changes in r164146,
[FreeBSD/FreeBSD.git] / sys / kern / kern_acct.c
1 /*-
2  * Copyright (c) 1982, 1986, 1989, 1993
3  *      The Regents of the University of California.  All rights reserved.
4  * (c) UNIX System Laboratories, Inc.
5  * All or some portions of this file are derived from material licensed
6  * to the University of California by American Telephone and Telegraph
7  * Co. or Unix System Laboratories, Inc. and are reproduced herein with
8  * the permission of UNIX System Laboratories, Inc.
9  *
10  * Copyright (c) 1994 Christopher G. Demetriou
11  * Copyright (c) 2005 Robert N. M. Watson
12  *
13  * Redistribution and use in source and binary forms, with or without
14  * modification, are permitted provided that the following conditions
15  * are met:
16  * 1. Redistributions of source code must retain the above copyright
17  *    notice, this list of conditions and the following disclaimer.
18  * 2. Redistributions in binary form must reproduce the above copyright
19  *    notice, this list of conditions and the following disclaimer in the
20  *    documentation and/or other materials provided with the distribution.
21  * 3. All advertising materials mentioning features or use of this software
22  *    must display the following acknowledgement:
23  *      This product includes software developed by the University of
24  *      California, Berkeley and its contributors.
25  * 4. Neither the name of the University nor the names of its contributors
26  *    may be used to endorse or promote products derived from this software
27  *    without specific prior written permission.
28  *
29  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
30  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
31  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
32  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
33  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
34  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
35  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
36  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
37  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
38  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
39  * SUCH DAMAGE.
40  *
41  *      @(#)kern_acct.c 8.1 (Berkeley) 6/14/93
42  */
43
44 #include <sys/cdefs.h>
45 __FBSDID("$FreeBSD$");
46
47 #include "opt_mac.h"
48
49 #include <sys/param.h>
50 #include <sys/systm.h>
51 #include <sys/acct.h>
52 #include <sys/fcntl.h>
53 #include <sys/kernel.h>
54 #include <sys/kthread.h>
55 #include <sys/lock.h>
56 #include <sys/mount.h>
57 #include <sys/mutex.h>
58 #include <sys/namei.h>
59 #include <sys/priv.h>
60 #include <sys/proc.h>
61 #include <sys/resourcevar.h>
62 #include <sys/sched.h>
63 #include <sys/sx.h>
64 #include <sys/sysctl.h>
65 #include <sys/sysent.h>
66 #include <sys/syslog.h>
67 #include <sys/sysproto.h>
68 #include <sys/tty.h>
69 #include <sys/vnode.h>
70
71 #include <security/mac/mac_framework.h>
72
73 /*
74  * The routines implemented in this file are described in:
75  *      Leffler, et al.: The Design and Implementation of the 4.3BSD
76  *          UNIX Operating System (Addison Welley, 1989)
77  * on pages 62-63.
78  *
79  * Arguably, to simplify accounting operations, this mechanism should
80  * be replaced by one in which an accounting log file (similar to /dev/klog)
81  * is read by a user process, etc.  However, that has its own problems.
82  */
83
84 /*
85  * Internal accounting functions.
86  * The former's operation is described in Leffler, et al., and the latter
87  * was provided by UCB with the 4.4BSD-Lite release
88  */
89 static comp_t   encode_comp_t(u_long, u_long);
90 static void     acctwatch(void);
91 static void     acct_thread(void *);
92 static int      acct_disable(struct thread *);
93
94 /*
95  * Accounting vnode pointer, saved vnode pointer, and flags for each.
96  * acct_sx protects against changes to the active vnode and credentials
97  * while accounting records are being committed to disk.
98  */
99 static int               acct_configured;
100 static int               acct_suspended;
101 static struct vnode     *acct_vp;
102 static struct ucred     *acct_cred;
103 static int               acct_flags;
104 static struct sx         acct_sx;
105
106 SX_SYSINIT(acct, &acct_sx, "acct_sx");
107
108 /*
109  * State of the accounting kthread.
110  */
111 static int               acct_state;
112
113 #define ACCT_RUNNING    1       /* Accounting kthread is running. */
114 #define ACCT_EXITREQ    2       /* Accounting kthread should exit. */
115
116 /*
117  * Values associated with enabling and disabling accounting
118  */
119 static int acctsuspend = 2;     /* stop accounting when < 2% free space left */
120 SYSCTL_INT(_kern, OID_AUTO, acct_suspend, CTLFLAG_RW,
121         &acctsuspend, 0, "percentage of free disk space below which accounting stops");
122
123 static int acctresume = 4;      /* resume when free space risen to > 4% */
124 SYSCTL_INT(_kern, OID_AUTO, acct_resume, CTLFLAG_RW,
125         &acctresume, 0, "percentage of free disk space above which accounting resumes");
126
127 static int acctchkfreq = 15;    /* frequency (in seconds) to check space */
128
129 static int
130 sysctl_acct_chkfreq(SYSCTL_HANDLER_ARGS)
131 {
132         int error, value;
133
134         /* Write out the old value. */
135         error = SYSCTL_OUT(req, &acctchkfreq, sizeof(int));
136         if (error || req->newptr == NULL)
137                 return (error);
138
139         /* Read in and verify the new value. */
140         error = SYSCTL_IN(req, &value, sizeof(int));
141         if (error)
142                 return (error);
143         if (value <= 0)
144                 return (EINVAL);
145         acctchkfreq = value;
146         return (0);
147 }
148 SYSCTL_PROC(_kern, OID_AUTO, acct_chkfreq, CTLTYPE_INT|CTLFLAG_RW,
149     &acctchkfreq, 0, sysctl_acct_chkfreq, "I",
150     "frequency for checking the free space");
151
152 SYSCTL_INT(_kern, OID_AUTO, acct_configured, CTLFLAG_RD, &acct_configured, 0,
153         "Accounting configured or not");
154
155 SYSCTL_INT(_kern, OID_AUTO, acct_suspended, CTLFLAG_RD, &acct_suspended, 0,
156         "Accounting suspended or not");
157
158 /*
159  * Accounting system call.  Written based on the specification and
160  * previous implementation done by Mark Tinguely.
161  *
162  * MPSAFE
163  */
164 int
165 acct(struct thread *td, struct acct_args *uap)
166 {
167         struct nameidata nd;
168         int error, flags, vfslocked;
169
170         error = priv_check(td, PRIV_ACCT);
171         if (error)
172                 return (error);
173
174         /*
175          * If accounting is to be started to a file, open that file for
176          * appending and make sure it's a 'normal'.
177          */
178         if (uap->path != NULL) {
179                 NDINIT(&nd, LOOKUP, NOFOLLOW | MPSAFE | AUDITVNODE1,
180                     UIO_USERSPACE, uap->path, td);
181                 flags = FWRITE | O_APPEND;
182                 error = vn_open(&nd, &flags, 0, -1);
183                 if (error)
184                         return (error);
185                 vfslocked = NDHASGIANT(&nd);
186                 NDFREE(&nd, NDF_ONLY_PNBUF);
187 #ifdef MAC
188                 error = mac_check_system_acct(td->td_ucred, nd.ni_vp);
189                 if (error) {
190                         VOP_UNLOCK(nd.ni_vp, 0, td);
191                         vn_close(nd.ni_vp, flags, td->td_ucred, td);
192                         VFS_UNLOCK_GIANT(vfslocked);
193                         return (error);
194                 }
195 #endif
196                 VOP_UNLOCK(nd.ni_vp, 0, td);
197                 if (nd.ni_vp->v_type != VREG) {
198                         vn_close(nd.ni_vp, flags, td->td_ucred, td);
199                         VFS_UNLOCK_GIANT(vfslocked);
200                         return (EACCES);
201                 }
202                 VFS_UNLOCK_GIANT(vfslocked);
203 #ifdef MAC
204         } else {
205                 error = mac_check_system_acct(td->td_ucred, NULL);
206                 if (error)
207                         return (error);
208 #endif
209         }
210
211         /*
212          * Disallow concurrent access to the accounting vnode while we swap
213          * it out, in order to prevent access after close.
214          */
215         sx_xlock(&acct_sx);
216
217         /*
218          * If accounting was previously enabled, kill the old space-watcher,
219          * close the file, and (if no new file was specified, leave).  Reset
220          * the suspended state regardless of whether accounting remains
221          * enabled.
222          */
223         acct_suspended = 0;
224         if (acct_vp != NULL) {
225                 vfslocked = VFS_LOCK_GIANT(acct_vp->v_mount);
226                 error = acct_disable(td);
227                 VFS_UNLOCK_GIANT(vfslocked);
228         }
229         if (uap->path == NULL) {
230                 if (acct_state & ACCT_RUNNING) {
231                         acct_state |= ACCT_EXITREQ;
232                         wakeup(&acct_state);
233                 }
234                 sx_xunlock(&acct_sx);
235                 return (error);
236         }
237
238         /*
239          * Save the new accounting file vnode, and schedule the new
240          * free space watcher.
241          */
242         acct_vp = nd.ni_vp;
243         acct_cred = crhold(td->td_ucred);
244         acct_flags = flags;
245         if (acct_state & ACCT_RUNNING)
246                 acct_state &= ~ACCT_EXITREQ;
247         else {
248                 /*
249                  * Try to start up an accounting kthread.  We may start more
250                  * than one, but if so the extras will commit suicide as
251                  * soon as they start up.
252                  */
253                 error = kthread_create(acct_thread, NULL, NULL, 0, 0,
254                     "accounting");
255                 if (error) {
256                         vfslocked = VFS_LOCK_GIANT(acct_vp->v_mount);
257                         (void) vn_close(acct_vp, acct_flags, acct_cred, td);
258                         VFS_UNLOCK_GIANT(vfslocked);
259                         crfree(acct_cred);
260                         acct_configured = 0;
261                         acct_vp = NULL;
262                         acct_cred = NULL;
263                         acct_flags = 0;
264                         sx_xunlock(&acct_sx);
265                         log(LOG_NOTICE, "Unable to start accounting thread\n");
266                         return (error);
267                 }
268         }
269         acct_configured = 1;
270         sx_xunlock(&acct_sx);
271         log(LOG_NOTICE, "Accounting enabled\n");
272         return (error);
273 }
274
275 /*
276  * Disable currently in-progress accounting by closing the vnode, dropping
277  * our reference to the credential, and clearing the vnode's flags.
278  */
279 static int
280 acct_disable(struct thread *td)
281 {
282         int error;
283
284         sx_assert(&acct_sx, SX_XLOCKED);
285         error = vn_close(acct_vp, acct_flags, acct_cred, td);
286         crfree(acct_cred);
287         acct_configured = 0;
288         acct_vp = NULL;
289         acct_cred = NULL;
290         acct_flags = 0;
291         log(LOG_NOTICE, "Accounting disabled\n");
292         return (error);
293 }
294
295 /*
296  * Write out process accounting information, on process exit.
297  * Data to be written out is specified in Leffler, et al.
298  * and are enumerated below.  (They're also noted in the system
299  * "acct.h" header file.)
300  */
301 int
302 acct_process(struct thread *td)
303 {
304         struct acct acct;
305         struct timeval ut, st, tmp;
306         struct plimit *newlim, *oldlim;
307         struct proc *p;
308         struct rusage *r;
309         int t, ret, vfslocked;
310
311         /*
312          * Lockless check of accounting condition before doing the hard
313          * work.
314          */
315         if (acct_vp == NULL || acct_suspended)
316                 return (0);
317
318         sx_slock(&acct_sx);
319
320         /*
321          * If accounting isn't enabled, don't bother.  Have to check again
322          * once we own the lock in case we raced with disabling of accounting
323          * by another thread.
324          */
325         if (acct_vp == NULL || acct_suspended) {
326                 sx_sunlock(&acct_sx);
327                 return (0);
328         }
329
330         p = td->td_proc;
331
332         /*
333          * Get process accounting information.
334          */
335
336         PROC_LOCK(p);
337         /* (1) The name of the command that ran */
338         bcopy(p->p_comm, acct.ac_comm, sizeof acct.ac_comm);
339
340         /* (2) The amount of user and system time that was used */
341         calcru(p, &ut, &st);
342         acct.ac_utime = encode_comp_t(ut.tv_sec, ut.tv_usec);
343         acct.ac_stime = encode_comp_t(st.tv_sec, st.tv_usec);
344
345         /* (3) The elapsed time the command ran (and its starting time) */
346         tmp = boottime;
347         timevaladd(&tmp, &p->p_stats->p_start);
348         acct.ac_btime = tmp.tv_sec;
349         microuptime(&tmp);
350         timevalsub(&tmp, &p->p_stats->p_start);
351         acct.ac_etime = encode_comp_t(tmp.tv_sec, tmp.tv_usec);
352
353         /* (4) The average amount of memory used */
354         r = &p->p_stats->p_ru;
355         tmp = ut;
356         timevaladd(&tmp, &st);
357         t = tmp.tv_sec * hz + tmp.tv_usec / tick;
358         if (t)
359                 acct.ac_mem = (r->ru_ixrss + r->ru_idrss + r->ru_isrss) / t;
360         else
361                 acct.ac_mem = 0;
362
363         /* (5) The number of disk I/O operations done */
364         acct.ac_io = encode_comp_t(r->ru_inblock + r->ru_oublock, 0);
365
366         /* (6) The UID and GID of the process */
367         acct.ac_uid = p->p_ucred->cr_ruid;
368         acct.ac_gid = p->p_ucred->cr_rgid;
369
370         /* (7) The terminal from which the process was started */
371         SESS_LOCK(p->p_session);
372         if ((p->p_flag & P_CONTROLT) && p->p_pgrp->pg_session->s_ttyp)
373                 acct.ac_tty = dev2udev(p->p_pgrp->pg_session->s_ttyp->t_dev);
374         else
375                 acct.ac_tty = NODEV;
376         SESS_UNLOCK(p->p_session);
377
378         /* (8) The boolean flags that tell how the process terminated, etc. */
379         acct.ac_flag = p->p_acflag;
380         PROC_UNLOCK(p);
381
382         /*
383          * Eliminate any file size rlimit.
384          */
385         newlim = lim_alloc();
386         PROC_LOCK(p);
387         oldlim = p->p_limit;
388         lim_copy(newlim, oldlim);
389         newlim->pl_rlimit[RLIMIT_FSIZE].rlim_cur = RLIM_INFINITY;
390         p->p_limit = newlim;
391         PROC_UNLOCK(p);
392         lim_free(oldlim);
393
394         /*
395          * Write the accounting information to the file.
396          */
397         vfslocked = VFS_LOCK_GIANT(acct_vp->v_mount);
398         VOP_LEASE(acct_vp, td, acct_cred, LEASE_WRITE);
399         ret = vn_rdwr(UIO_WRITE, acct_vp, (caddr_t)&acct, sizeof (acct),
400             (off_t)0, UIO_SYSSPACE, IO_APPEND|IO_UNIT, acct_cred, NOCRED,
401             (int *)0, td);
402         VFS_UNLOCK_GIANT(vfslocked);
403         sx_sunlock(&acct_sx);
404         return (ret);
405 }
406
407 /*
408  * Encode_comp_t converts from ticks in seconds and microseconds
409  * to ticks in 1/AHZ seconds.  The encoding is described in
410  * Leffler, et al., on page 63.
411  */
412
413 #define MANTSIZE        13                      /* 13 bit mantissa. */
414 #define EXPSIZE         3                       /* Base 8 (3 bit) exponent. */
415 #define MAXFRACT        ((1 << MANTSIZE) - 1)   /* Maximum fractional value. */
416
417 static comp_t
418 encode_comp_t(u_long s, u_long us)
419 {
420         int exp, rnd;
421
422         exp = 0;
423         rnd = 0;
424         s *= AHZ;
425         s += us / (1000000 / AHZ);      /* Maximize precision. */
426
427         while (s > MAXFRACT) {
428         rnd = s & (1 << (EXPSIZE - 1)); /* Round up? */
429                 s >>= EXPSIZE;          /* Base 8 exponent == 3 bit shift. */
430                 exp++;
431         }
432
433         /* If we need to round up, do it (and handle overflow correctly). */
434         if (rnd && (++s > MAXFRACT)) {
435                 s >>= EXPSIZE;
436                 exp++;
437         }
438
439         /* Clean it up and polish it off. */
440         exp <<= MANTSIZE;               /* Shift the exponent into place */
441         exp += s;                       /* and add on the mantissa. */
442         return (exp);
443 }
444
445 /*
446  * Periodically check the filesystem to see if accounting
447  * should be turned on or off.  Beware the case where the vnode
448  * has been vgone()'d out from underneath us, e.g. when the file
449  * system containing the accounting file has been forcibly unmounted.
450  */
451 /* ARGSUSED */
452 static void
453 acctwatch(void)
454 {
455         struct statfs sb;
456         int vfslocked;
457
458         sx_assert(&acct_sx, SX_XLOCKED);
459
460         /*
461          * If accounting was disabled before our kthread was scheduled,
462          * then acct_vp might be NULL.  If so, just ask our kthread to
463          * exit and return.
464          */
465         if (acct_vp == NULL) {
466                 acct_state |= ACCT_EXITREQ;
467                 return;
468         }
469
470         /*
471          * If our vnode is no longer valid, tear it down and signal the
472          * accounting thread to die.
473          */
474         vfslocked = VFS_LOCK_GIANT(acct_vp->v_mount);
475         if (acct_vp->v_type == VBAD) {
476                 (void) acct_disable(NULL);
477                 VFS_UNLOCK_GIANT(vfslocked);
478                 acct_state |= ACCT_EXITREQ;
479                 return;
480         }
481
482         /*
483          * Stopping here is better than continuing, maybe it will be VBAD
484          * next time around.
485          */
486         if (VFS_STATFS(acct_vp->v_mount, &sb, curthread) < 0) {
487                 VFS_UNLOCK_GIANT(vfslocked);
488                 return;
489         }
490         VFS_UNLOCK_GIANT(vfslocked);
491         if (acct_suspended) {
492                 if (sb.f_bavail > (int64_t)(acctresume * sb.f_blocks /
493                     100)) {
494                         acct_suspended = 0;
495                         log(LOG_NOTICE, "Accounting resumed\n");
496                 }
497         } else {
498                 if (sb.f_bavail <= (int64_t)(acctsuspend * sb.f_blocks /
499                     100)) {
500                         acct_suspended = 1;
501                         log(LOG_NOTICE, "Accounting suspended\n");
502                 }
503         }
504 }
505
506 /*
507  * The main loop for the dedicated kernel thread that periodically calls
508  * acctwatch().
509  */
510 static void
511 acct_thread(void *dummy)
512 {
513         u_char pri;
514
515         /* This is a low-priority kernel thread. */
516         pri = PRI_MAX_KERN;
517         mtx_lock_spin(&sched_lock);
518         sched_prio(curthread, pri);
519         mtx_unlock_spin(&sched_lock);
520
521         /* If another accounting kthread is already running, just die. */
522         sx_xlock(&acct_sx);
523         if (acct_state & ACCT_RUNNING) {
524                 sx_xunlock(&acct_sx);
525                 kthread_exit(0);
526         }
527         acct_state |= ACCT_RUNNING;
528
529         /* Loop until we are asked to exit. */
530         while (!(acct_state & ACCT_EXITREQ)) {
531
532                 /* Perform our periodic checks. */
533                 acctwatch();
534
535                 /*
536                  * We check this flag again before sleeping since the
537                  * acctwatch() might have shut down accounting and asked us
538                  * to exit.
539                  */
540                 if (!(acct_state & ACCT_EXITREQ)) {
541                         sx_xunlock(&acct_sx);
542                         tsleep(&acct_state, pri, "-", acctchkfreq * hz);
543                         sx_xlock(&acct_sx);
544                 }
545         }
546
547         /*
548          * Acknowledge the exit request and shutdown.  We clear both the
549          * exit request and running flags.
550          */
551         acct_state = 0;
552         sx_xunlock(&acct_sx);
553         kthread_exit(0);
554 }