~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~ [ freetext search ] ~ [ file search ] ~

Linux Cross Reference
Linux/kernel/acct.c

Version: ~ [ 2.4.0 ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  1 /*
  2  *  linux/kernel/acct.c
  3  *
  4  *  BSD Process Accounting for Linux
  5  *
  6  *  Author: Marco van Wieringen <mvw@planets.elm.net>
  7  *
  8  *  Some code based on ideas and code from:
  9  *  Thomas K. Dyas <tdyas@eden.rutgers.edu>
 10  *
 11  *  This file implements BSD-style process accounting. Whenever any
 12  *  process exits, an accounting record of type "struct acct" is
 13  *  written to the file specified with the acct() system call. It is
 14  *  up to user-level programs to do useful things with the accounting
 15  *  log. The kernel just provides the raw accounting information.
 16  *
 17  * (C) Copyright 1995 - 1997 Marco van Wieringen - ELM Consultancy B.V.
 18  *
 19  *  Plugged two leaks. 1) It didn't return acct_file into the free_filps if
 20  *  the file happened to be read-only. 2) If the accounting was suspended
 21  *  due to the lack of space it happily allowed to reopen it and completely
 22  *  lost the old acct_file. 3/10/98, Al Viro.
 23  *
 24  *  Now we silently close acct_file on attempt to reopen. Cleaned sys_acct().
 25  *  XTerms and EMACS are manifestations of pure evil. 21/10/98, AV.
 26  *
 27  *  Fixed a nasty interaction with with sys_umount(). If the accointing
 28  *  was suspeneded we failed to stop it on umount(). Messy.
 29  *  Another one: remount to readonly didn't stop accounting.
 30  *      Question: what should we do if we have CAP_SYS_ADMIN but not
 31  *  CAP_SYS_PACCT? Current code does the following: umount returns -EBUSY
 32  *  unless we are messing with the root. In that case we are getting a
 33  *  real mess with do_remount_sb(). 9/11/98, AV.
 34  *
 35  *  Fixed a bunch of races (and pair of leaks). Probably not the best way,
 36  *  but this one obviously doesn't introduce deadlocks. Later. BTW, found
 37  *  one race (and leak) in BSD implementation.
 38  *  OK, that's better. ANOTHER race and leak in BSD variant. There always
 39  *  is one more bug... 10/11/98, AV.
 40  *
 41  *      Oh, fsck... Oopsable SMP race in do_process_acct() - we must hold
 42  * ->mmap_sem to walk the vma list of current->mm. Nasty, since it leaks
 43  * a struct file opened for write. Fixed. 2/6/2000, AV.
 44  */
 45 
 46 #include <linux/config.h>
 47 #include <linux/errno.h>
 48 #include <linux/kernel.h>
 49 
 50 #ifdef CONFIG_BSD_PROCESS_ACCT
 51 #include <linux/mm.h>
 52 #include <linux/slab.h>
 53 #include <linux/acct.h>
 54 #include <linux/smp_lock.h>
 55 #include <linux/file.h>
 56 
 57 #include <asm/uaccess.h>
 58 
 59 /*
 60  * These constants control the amount of freespace that suspend and
 61  * resume the process accounting system, and the time delay between
 62  * each check.
 63  * Turned into sysctl-controllable parameters. AV, 12/11/98
 64  */
 65 
 66 int acct_parm[3] = {4, 2, 30};
 67 #define RESUME          (acct_parm[0])  /* >foo% free space - resume */
 68 #define SUSPEND         (acct_parm[1])  /* <foo% free space - suspend */
 69 #define ACCT_TIMEOUT    (acct_parm[2])  /* foo second timeout between checks */
 70 
 71 /*
 72  * External references and all of the globals.
 73  */
 74 
 75 static volatile int acct_active;
 76 static volatile int acct_needcheck;
 77 static struct file *acct_file;
 78 static struct timer_list acct_timer;
 79 static void do_acct_process(long, struct file *);
 80 
 81 /*
 82  * Called whenever the timer says to check the free space.
 83  */
 84 static void acct_timeout(unsigned long unused)
 85 {
 86         acct_needcheck = 1;
 87 }
 88 
 89 /*
 90  * Check the amount of free space and suspend/resume accordingly.
 91  */
 92 static int check_free_space(struct file *file)
 93 {
 94         struct statfs sbuf;
 95         int res;
 96         int act;
 97 
 98         lock_kernel();
 99         res = acct_active;
100         if (!file || !acct_needcheck)
101                 goto out;
102         unlock_kernel();
103 
104         /* May block */
105         if (vfs_statfs(file->f_dentry->d_inode->i_sb, &sbuf))
106                 return res;
107 
108         if (sbuf.f_bavail <= SUSPEND * sbuf.f_blocks / 100)
109                 act = -1;
110         else if (sbuf.f_bavail >= RESUME * sbuf.f_blocks / 100)
111                 act = 1;
112         else
113                 act = 0;
114 
115         /*
116          * If some joker switched acct_file under us we'ld better be
117          * silent and _not_ touch anything.
118          */
119         lock_kernel();
120         if (file != acct_file) {
121                 if (act)
122                         res = act>0;
123                 goto out;
124         }
125 
126         if (acct_active) {
127                 if (act < 0) {
128                         acct_active = 0;
129                         printk(KERN_INFO "Process accounting paused\n");
130                 }
131         } else {
132                 if (act > 0) {
133                         acct_active = 1;
134                         printk(KERN_INFO "Process accounting resumed\n");
135                 }
136         }
137 
138         del_timer(&acct_timer);
139         acct_needcheck = 0;
140         acct_timer.expires = jiffies + ACCT_TIMEOUT*HZ;
141         add_timer(&acct_timer);
142         res = acct_active;
143 out:
144         unlock_kernel();
145         return res;
146 }
147 
148 /*
149  *  sys_acct() is the only system call needed to implement process
150  *  accounting. It takes the name of the file where accounting records
151  *  should be written. If the filename is NULL, accounting will be
152  *  shutdown.
153  */
154 asmlinkage long sys_acct(const char *name)
155 {
156         struct file *file = NULL, *old_acct = NULL;
157         char *tmp;
158         int error;
159 
160         if (!capable(CAP_SYS_PACCT))
161                 return -EPERM;
162 
163         if (name) {
164                 tmp = getname(name);
165                 error = PTR_ERR(tmp);
166                 if (IS_ERR(tmp))
167                         goto out;
168                 /* Difference from BSD - they don't do O_APPEND */
169                 file = filp_open(tmp, O_WRONLY|O_APPEND, 0);
170                 putname(tmp);
171                 if (IS_ERR(file)) {
172                         error = PTR_ERR(file);
173                         goto out;
174                 }
175                 error = -EACCES;
176                 if (!S_ISREG(file->f_dentry->d_inode->i_mode)) 
177                         goto out_err;
178 
179                 error = -EIO;
180                 if (!file->f_op->write) 
181                         goto out_err;
182         }
183 
184         error = 0;
185         lock_kernel();
186         if (acct_file) {
187                 old_acct = acct_file;
188                 del_timer(&acct_timer);
189                 acct_active = 0;
190                 acct_needcheck = 0;
191                 acct_file = NULL;
192         }
193         if (name) {
194                 acct_file = file;
195                 acct_needcheck = 0;
196                 acct_active = 1;
197                 /* It's been deleted if it was used before so this is safe */
198                 init_timer(&acct_timer);
199                 acct_timer.function = acct_timeout;
200                 acct_timer.expires = jiffies + ACCT_TIMEOUT*HZ;
201                 add_timer(&acct_timer);
202         }
203         unlock_kernel();
204         if (old_acct) {
205                 do_acct_process(0,old_acct);
206                 filp_close(old_acct, NULL);
207         }
208 out:
209         return error;
210 out_err:
211         filp_close(file, NULL);
212         goto out;
213 }
214 
215 void acct_auto_close(kdev_t dev)
216 {
217         lock_kernel();
218         if (acct_file && acct_file->f_dentry->d_inode->i_dev == dev)
219                 sys_acct(NULL);
220         unlock_kernel();
221 }
222 
223 /*
224  *  encode an unsigned long into a comp_t
225  *
226  *  This routine has been adopted from the encode_comp_t() function in
227  *  the kern_acct.c file of the FreeBSD operating system. The encoding
228  *  is a 13-bit fraction with a 3-bit (base 8) exponent.
229  */
230 
231 #define MANTSIZE        13                      /* 13 bit mantissa. */
232 #define EXPSIZE         3                       /* Base 8 (3 bit) exponent. */
233 #define MAXFRACT        ((1 << MANTSIZE) - 1)   /* Maximum fractional value. */
234 
235 static comp_t encode_comp_t(unsigned long value)
236 {
237         int exp, rnd;
238 
239         exp = rnd = 0;
240         while (value > MAXFRACT) {
241                 rnd = value & (1 << (EXPSIZE - 1));     /* Round up? */
242                 value >>= EXPSIZE;      /* Base 8 exponent == 3 bit shift. */
243                 exp++;
244         }
245 
246         /*
247          * If we need to round up, do it (and handle overflow correctly).
248          */
249         if (rnd && (++value > MAXFRACT)) {
250                 value >>= EXPSIZE;
251                 exp++;
252         }
253 
254         /*
255          * Clean it up and polish it off.
256          */
257         exp <<= MANTSIZE;               /* Shift the exponent into place */
258         exp += value;                   /* and add on the mantissa. */
259         return exp;
260 }
261 
262 /*
263  *  Write an accounting entry for an exiting process
264  *
265  *  The acct_process() call is the workhorse of the process
266  *  accounting system. The struct acct is built here and then written
267  *  into the accounting file. This function should only be called from
268  *  do_exit().
269  */
270 
271 /*
272  *  do_acct_process does all actual work. Caller holds the reference to file.
273  */
274 static void do_acct_process(long exitcode, struct file *file)
275 {
276         struct acct ac;
277         mm_segment_t fs;
278         unsigned long vsize;
279 
280         /*
281          * First check to see if there is enough free_space to continue
282          * the process accounting system.
283          */
284         if (!check_free_space(file))
285                 return;
286 
287         /*
288          * Fill the accounting struct with the needed info as recorded
289          * by the different kernel functions.
290          */
291         memset((caddr_t)&ac, 0, sizeof(struct acct));
292 
293         strncpy(ac.ac_comm, current->comm, ACCT_COMM);
294         ac.ac_comm[ACCT_COMM - 1] = '\0';
295 
296         ac.ac_btime = CT_TO_SECS(current->start_time) + (xtime.tv_sec - (jiffies / HZ));
297         ac.ac_etime = encode_comp_t(jiffies - current->start_time);
298         ac.ac_utime = encode_comp_t(current->times.tms_utime);
299         ac.ac_stime = encode_comp_t(current->times.tms_stime);
300         ac.ac_uid = current->uid;
301         ac.ac_gid = current->gid;
302         ac.ac_tty = (current->tty) ? kdev_t_to_nr(current->tty->device) : 0;
303 
304         ac.ac_flag = 0;
305         if (current->flags & PF_FORKNOEXEC)
306                 ac.ac_flag |= AFORK;
307         if (current->flags & PF_SUPERPRIV)
308                 ac.ac_flag |= ASU;
309         if (current->flags & PF_DUMPCORE)
310                 ac.ac_flag |= ACORE;
311         if (current->flags & PF_SIGNALED)
312                 ac.ac_flag |= AXSIG;
313 
314         vsize = 0;
315         if (current->mm) {
316                 struct vm_area_struct *vma;
317                 down(&current->mm->mmap_sem);
318                 vma = current->mm->mmap;
319                 while (vma) {
320                         vsize += vma->vm_end - vma->vm_start;
321                         vma = vma->vm_next;
322                 }
323                 up(&current->mm->mmap_sem);
324         }
325         vsize = vsize / 1024;
326         ac.ac_mem = encode_comp_t(vsize);
327         ac.ac_io = encode_comp_t(0 /* current->io_usage */);    /* %% */
328         ac.ac_rw = encode_comp_t(ac.ac_io / 1024);
329         ac.ac_minflt = encode_comp_t(current->min_flt);
330         ac.ac_majflt = encode_comp_t(current->maj_flt);
331         ac.ac_swaps = encode_comp_t(current->nswap);
332         ac.ac_exitcode = exitcode;
333 
334         /*
335          * Kernel segment override to datasegment and write it
336          * to the accounting file.
337          */
338         fs = get_fs();
339         set_fs(KERNEL_DS);
340         file->f_op->write(file, (char *)&ac,
341                                sizeof(struct acct), &file->f_pos);
342         set_fs(fs);
343 }
344 
345 /*
346  * acct_process - now just a wrapper around do_acct_process
347  */
348 int acct_process(long exitcode)
349 {
350         struct file *file = NULL;
351         lock_kernel();
352         if (acct_file) {
353                 file = acct_file;
354                 get_file(file);
355                 unlock_kernel();
356                 do_acct_process(exitcode, acct_file);
357                 fput(file);
358         } else
359                 unlock_kernel();
360         return 0;
361 }
362 
363 #else
364 /*
365  * Dummy system call when BSD process accounting is not configured
366  * into the kernel.
367  */
368 
369 asmlinkage long sys_acct(const char * filename)
370 {
371         return -ENOSYS;
372 }
373 #endif
374 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~ [ freetext search ] ~ [ file search ] ~

This page was automatically generated by the LXR engine.
Visit the LXR main site for more information.