4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
22 * Copyright (c) 1992, 2010, Oracle and/or its affiliates. All rights reserved.
25 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
26 /* All Rights Reserved */
29 #include <sys/types.h>
30 #include <sys/sysmacros.h>
31 #include <sys/param.h>
32 #include <sys/systm.h>
36 #include <sys/errno.h>
38 #include <sys/vnode.h>
39 #include <sys/debug.h>
41 #include <sys/resource.h>
42 #include <sys/session.h>
43 #include <sys/modctl.h>
44 #include <sys/syscall.h>
45 #include <sys/policy.h>
48 #include <sys/msacct.h>
52 * Each zone has its own accounting settings (on or off) and associated
53 * file. The global zone is not special in this aspect; it will only
54 * generate records for processes that ran in the global zone. We could
55 * allow the global zone to record all activity on the system, but there
56 * would be no way of knowing the zone in which the processes executed.
57 * sysacct() is thus virtualized to only act on the caller's zone.
67 * We need a list of all accounting settings for all zones, so we can
68 * accurately determine if a file is in use for accounting (possibly by
71 static zone_key_t acct_zone_key
;
72 static list_t acct_list
;
73 kmutex_t acct_list_lock
;
75 static struct sysent acctsysent
= {
77 SE_NOUNLOAD
| SE_ARGC
| SE_32RVAL1
,
81 static struct modlsys modlsys
= {
82 &mod_syscallops
, "acct(2) syscall", &acctsysent
85 #ifdef _SYSCALL32_IMPL
86 static struct modlsys modlsys32
= {
87 &mod_syscallops32
, "32-bit acct(2) syscall", &acctsysent
91 static struct modlinkage modlinkage
= {
94 #ifdef _SYSCALL32_IMPL
102 acct_init(zoneid_t zoneid
)
104 struct acct_globals
*ag
;
106 ag
= kmem_alloc(sizeof (*ag
), KM_SLEEP
);
107 bzero(&ag
->acctbuf
, sizeof (ag
->acctbuf
));
108 mutex_init(&ag
->aclock
, NULL
, MUTEX_DEFAULT
, NULL
);
111 mutex_enter(&acct_list_lock
);
112 list_insert_tail(&acct_list
, ag
);
113 mutex_exit(&acct_list_lock
);
119 acct_shutdown(zoneid_t zoneid
, void *arg
)
121 struct acct_globals
*ag
= arg
;
123 mutex_enter(&ag
->aclock
);
126 * This needs to be done as a shutdown callback, otherwise this
127 * held vnode may cause filesystems to be busy, and the zone
128 * shutdown operation to fail.
130 (void) fop_close(ag
->acctvp
, FWRITE
, 1, (offset_t
)0, kcred
,
135 mutex_exit(&ag
->aclock
);
140 acct_fini(zoneid_t zoneid
, void *arg
)
142 struct acct_globals
*ag
= arg
;
144 mutex_enter(&acct_list_lock
);
145 list_remove(&acct_list
, ag
);
146 mutex_exit(&acct_list_lock
);
148 mutex_destroy(&ag
->aclock
);
149 kmem_free(ag
, sizeof (*ag
));
157 mutex_init(&acct_list_lock
, NULL
, MUTEX_DEFAULT
, NULL
);
158 list_create(&acct_list
, sizeof (struct acct_globals
),
159 offsetof(struct acct_globals
, aclink
));
161 * Using an initializer here wastes a bit of memory for zones that
162 * don't use accounting, but vastly simplifies the locking.
164 zone_key_create(&acct_zone_key
, acct_init
, acct_shutdown
, acct_fini
);
165 if ((error
= mod_install(&modlinkage
)) != 0) {
166 (void) zone_key_delete(acct_zone_key
);
167 list_destroy(&acct_list
);
168 mutex_destroy(&acct_list_lock
);
174 _info(struct modinfo
*modinfop
)
176 return (mod_info(&modlinkage
, modinfop
));
180 * acct() is a "weak stub" routine called from exit().
181 * Once this module has been loaded, we refuse to allow
182 * it to unload - otherwise accounting would quietly
183 * cease. See 1211661. It's possible to make this module
184 * unloadable but it's substantially safer not to bother.
193 * See if vp is in use by the accounting system on any zone. This does a deep
194 * comparison of vnodes such that a file and a lofs "shadow" node of it will
195 * appear to be the same.
197 * If 'compare_vfs' is true, the function will do a comparison of vfs_t's
198 * instead (ie, is the vfs_t on which the vnode resides in use by the
199 * accounting system in any zone).
201 * Returns 1 if found (in use), 0 otherwise.
204 acct_find(vnode_t
*vp
, boolean_t compare_vfs
)
206 struct acct_globals
*ag
;
209 ASSERT(MUTEX_HELD(&acct_list_lock
));
212 if (fop_realvp(vp
, &realvp
, NULL
))
214 for (ag
= list_head(&acct_list
); ag
!= NULL
;
215 ag
= list_next(&acct_list
, ag
)) {
217 boolean_t found
= B_FALSE
;
219 mutex_enter(&ag
->aclock
);
220 if (ag
->acctvp
== NULL
) {
221 mutex_exit(&ag
->aclock
);
224 if (fop_realvp(ag
->acctvp
, &racctvp
, NULL
))
225 racctvp
= ag
->acctvp
;
227 if (racctvp
->v_vfsp
== realvp
->v_vfsp
)
230 if (VN_CMP(realvp
, racctvp
))
233 mutex_exit(&ag
->aclock
);
241 * Returns 1 if the vfs that vnode resides on is in use for the accounting
242 * subsystem, 0 otherwise.
245 acct_fs_in_use(vnode_t
*vp
)
251 mutex_enter(&acct_list_lock
);
252 found
= acct_find(vp
, B_TRUE
);
253 mutex_exit(&acct_list_lock
);
258 * Perform process accounting functions.
263 struct acct_globals
*ag
;
267 if (secpolicy_acct(CRED()) != 0)
268 return (set_errno(EPERM
));
270 ag
= zone_getspecific(acct_zone_key
, curproc
->p_zone
);
275 * Close the file and stop accounting.
277 mutex_enter(&ag
->aclock
);
280 mutex_exit(&ag
->aclock
);
282 error
= fop_close(vp
, FWRITE
, 1, (offset_t
)0, CRED(),
286 return (error
== 0 ? 0 : set_errno(error
));
290 * Either (a) open a new file and begin accounting -or- (b)
291 * switch accounting from an old to a new file.
293 * (Open the file without holding aclock in case it
294 * sleeps (holding the lock prevents process exit).)
296 if ((error
= vn_open(fname
, UIO_USERSPACE
, FWRITE
,
297 0, &vp
, (enum create
)0, 0)) != 0) {
298 /* SVID compliance */
301 return (set_errno(error
));
304 if (vp
->v_type
!= VREG
) {
307 mutex_enter(&acct_list_lock
);
308 if (acct_find(vp
, B_FALSE
)) {
311 mutex_enter(&ag
->aclock
);
316 * close old acctvp, and point acct()
317 * at new file by swapping vp and acctvp
324 * no existing file, start accounting ..
329 mutex_exit(&ag
->aclock
);
331 mutex_exit(&acct_list_lock
);
335 (void) fop_close(vp
, FWRITE
, 1, (offset_t
)0, CRED(), NULL
);
338 return (error
== 0 ? 0 : set_errno(error
));
342 * Produce a pseudo-floating point representation
343 * with 3 bits base-8 exponent, 13 bits fraction.
346 acct_compress(ulong_t t
)
348 int exp
= 0, round
= 0;
364 /* prevent wraparound */
369 return ((exp
<< 13) + t
);
373 * On exit, write a record on the accounting file.
385 struct acct_globals
*ag
;
388 * If sysacct module is loaded when zone is in down state then
389 * the following function can return NULL.
391 ag
= zone_getspecific(acct_zone_key
, curproc
->p_zone
);
395 mutex_enter(&ag
->aclock
);
396 if ((vp
= ag
->acctvp
) == NULL
) {
397 mutex_exit(&ag
->aclock
);
402 * This only gets called from exit after all lwp's have exited so no
403 * cred locking is needed.
407 bcopy(ua
->u_comm
, ag
->acctbuf
.ac_comm
, sizeof (ag
->acctbuf
.ac_comm
));
408 ag
->acctbuf
.ac_btime
= ua
->u_start
.tv_sec
;
409 ag
->acctbuf
.ac_utime
= acct_compress(NSEC_TO_TICK(p
->p_acct
[LMS_USER
]));
410 ag
->acctbuf
.ac_stime
= acct_compress(
411 NSEC_TO_TICK(p
->p_acct
[LMS_SYSTEM
] + p
->p_acct
[LMS_TRAP
]));
412 ag
->acctbuf
.ac_etime
= acct_compress(ddi_get_lbolt() - ua
->u_ticks
);
413 ag
->acctbuf
.ac_mem
= acct_compress((ulong_t
)ua
->u_mem
);
414 ag
->acctbuf
.ac_io
= acct_compress((ulong_t
)p
->p_ru
.ioch
);
415 ag
->acctbuf
.ac_rw
= acct_compress((ulong_t
)(p
->p_ru
.inblock
+
418 ag
->acctbuf
.ac_uid
= crgetruid(cr
);
419 ag
->acctbuf
.ac_gid
= crgetrgid(cr
);
420 (void) cmpldev(&ag
->acctbuf
.ac_tty
, cttydev(p
));
421 ag
->acctbuf
.ac_stat
= st
;
422 ag
->acctbuf
.ac_flag
= (ua
->u_acflag
| AEXPND
);
425 * Save the size. If the write fails, reset the size to avoid
426 * corrupted acct files.
428 * Large Files: We deliberately prevent accounting files from
429 * exceeding the 2GB limit as none of the accounting commands are
430 * currently large file aware.
432 va
.va_mask
= AT_SIZE
;
433 if (fop_getattr(vp
, &va
, 0, kcred
, NULL
) == 0) {
434 error
= vn_rdwr(UIO_WRITE
, vp
, (caddr_t
)&ag
->acctbuf
,
435 sizeof (ag
->acctbuf
), 0LL, UIO_SYSSPACE
, FAPPEND
,
436 (rlim64_t
)MAXOFF32_T
, kcred
, &resid
);
438 (void) fop_setattr(vp
, &va
, 0, kcred
, NULL
);
440 mutex_exit(&ag
->aclock
);