Hey Linuxers!					-*-Text-*-

Well, I've got this stuff up and running on my own linux box -- it
works just fine.  Unfortunately, Linux doesn't yet support the ac_mem
or ac_io statistics in its STRUCT ACCT, so you won't see that info
coming out in sa (I just threw in a few ifdefs...).

The patches I'm including in this file are based on those by Marco van
Wieringen <mvw@mercury.mcs.nl.mugnet.org>, which, in turn, look like
they're based on those of Juha Virtanen <Juha.Virtanen@hut.fi>, which
are ports of the code of Alan Cox <iiitac@pyr.swan.ac.uk>, who hacked
all of this together in the first place (whew!).  Thanks to everyone
for the great work done so far!

My changes:

* port to Linux v1.0
* added initializers for other object file formats (COFF and ELF)
* added support for the AC_IO field
* made the sys_acct call properly release the file if called
  with a NULL argument (so you don't get a "/dev/hd??: device busy"
  message when umounting your drives for shutdown/reboot)
* removed page fault information AC_MINFLT and AC_MAXFLT, as well as
  AC_EXITCODE (no other acct that I've ever seen has kept this kind of
  information) -- it will save a bunch of disk space in the long run

Right now, I'm working on the AC_MEM field.  I'm debating whether or
not to go with the standard definition (which tries to come up with an
average memory-use per tick) versus one that's easier to understand:
what if AC_MEM was just the size of the process (data + code +
shared)?  Hopefully I'll have something to show in a while.

Also, I'm considering the usefulness of changing the types of AC_MEM
and AC_IO to COMP_T (a strange floating-point number).  Any
suggestions?  More overhead in the kernel, ubdoubtedly.

TO PATCH YOUR KERNEL, cd into the top level source directory and do
something like "patch -p1 < <wherever-this-is>/README-LINUX" to apply
the diffs.  Make sure to do a "make dep; make clean; make" because I
added a field to the STRUCT TASK_STRUCT.

IMPORTANT NOTE: if you have the Quota patches, there may be a
rejection in exec.c (see below).  You'll also have to change the
#undef QUOTA_PATCHES at the top of the file...

Just ignore the memory information that you get from sa for now.
After I've figured out what to do about AC_MEM, I'll fix it.  Please
pound on everything else, however.  savacct and useracct support
coming soon!

WEIRDNESS: After you've got the patches installed and these programs
running, try doing a configure in the source directory and then check
out the output of lastcomm.  Does anyone know why shell scripts like
configure seem to keep getting reparented and running under different
PIDs?  We really don't want to record these transients -- just the
last entry that has the total CPU and IO.

----------------------------------------------------------------------
Here's Marco's original README:
----------------------------------------------------------------------

The patches here are for Linux-0.99pl15. Apply them with
"patch -p1 < kernel_diffs" in the /usr/src/linux dir. So

1) cd /usr/src/linux
2) patch -p1 < <where_ever_you_put_it>/kernel_diffs

Check for rejections and do somthing about it.
3) find . -name \*.rej

If you installed the quota-1.3 package you should have gotten one rejection
file, fs/exec.c.rej this is because its already in the quota-1.3 package.

IMPORTANT: If you don't install the quota-1.3 package make sure you edit
kernel/sys.c and comment out the vfs_open_filp call and the include of
fileio.h.

Now do a make and that should make a new kernel. When you finished make the
utils. Now all should be running. Add something like 
"/usr/sbin/accton /var/adm/acct" to your rc startup script. And somthing
like "/usr/sbin/accton" to the rc-script that shuts your system down.

Have fun.

Marco.

M. van Wieringen <v892273@si.hhs.nl> <mvw@mercury.mcs.nl.mugnet.org>

----------------------------------------------------------------------
Here's Juha's original README file:
----------------------------------------------------------------------
AcctKit 0.99.12

This kit of pieces adds process accounting to the standard Linux
kernel. It contains a diff file relative to the 0.99.12, a new include
file and ports of the BSD NET2 accton and lastcomm commands.

***Important: The lastcomm and accton programs from this kit are part
of the BSD NET2 release. See lastcomm.c and accton.c for the UCB
licensing conditions


To install:

Unpack the source with

gzip -d acctkit-0.99.12.tar.gz | tar -xzvvf -

Note that the linux source is assumed to lie in /usr/src/linux
subdirectory tree. After patching copy .../acctkit-0.99.11/acct.h
to your /usr/src/linux/include/linux directory.

cd /usr/src
patch < acctkit-0.99.12/diffs-0.99.12
cp acctkit-0.99.12/acct.h /usr/src/linux/include/linux/acct.h
cd acctkit-0.99.12
mv accton /etc/accton
mv lastcomm /usr/bin/lastcomm
cp lastcomm.1 /usr/man/man1/lastcomm.1
touch /usr/adm/acct

Build a new kernel.

With this new kernel you can enable accounting with command
'/etc/accton /usr/adm/acct'. Disable it with '/etc/accton', and look
at command histories with 'lastcomm [user]'.

If you upgraded from older kernel than 0.99.11 and already had
accounting running, zap the old accounting file to 0 size before
starting accounting with new kernel. For example, say:

/etc/accton
cp /dev/null /usr/adm/acct

and then reboot. After that everything should be okay again.


Limitations
-----------

I don't implement memory, pagefault or disk usage logging, just
commands and whether it ran as root. Its enough for my purposes with
our computer society system.

LICENSE
--------
All original code in this kit is placed under the GNU public license.
All other material carries its own copyright messages which you should
check to ensure they are suitable.


THANKS and miscellaneus notes
------------------------------
I found this package originally as acctkit-0.99.8.tar.Z from
nic.funet.fi.  Alan Cox <iiitac@pyr.swan.ac.uk> had done most of the
work this far; Thanks a lot to him.

I just cleaned up the code a little, set up a makefile and made new
diffs against stock linux 0.99.12 kernel release.

New in 0.99.11
---------------
Accounting now supports full length filenames. This leads into
incompatibility with older version and you must get rid of old
accounting information before starting accounting.  I did also some
cleanup and added c++ support (even I don't know any c++ ;-) among
other things.

New in 0.99.12
---------------
New diffs...


Juha Virtanen, email <Juha.Virtanen@hut.fi>


----------------------------------------------------------------------
			T H E   P A T C H E S
----------------------------------------------------------------------

diff --recursive --new-file --context --exclude=.*depend --exclude=make.out --exclude=.config --exclude=.version --exclude=bootsect --exclude=*.o --exclude=*.S --exclude=*.s --exclude=config.in --exclude=config.old --exclude=*.a --exclude=setup --exclude=ksyms.lst --exclude=autoconf.h --exclude=build --exclude=version.h --exclude=zSystem.map --exclude=*~ --exclude=zImage --exclude=zSystem --exclude=piggyback --exclude=xtract linux-1.0-orig/fs/binfmt_coff.c linux-1.0-mine/fs/binfmt_coff.c
*** linux-1.0-orig/fs/binfmt_coff.c	Mon Feb 14 11:41:55 1994
--- linux-1.0-mine/fs/binfmt_coff.c	Sat Mar 19 01:34:37 1994
***************
*** 455,460 ****
--- 455,462 ----
  /*
   *  Do the end processing once the stack has been constructed
   */
+ 	    current->flags &= ~PF_FORKNOEXEC; /* accounting flags */
+ 	    current->io_usage = 0;
  	    current->start_code  = text_vaddr & PAGE_MASK;
  	    current->end_code    = text_vaddr + text_size;
  	    current->end_data    = data_vaddr + data_size;
diff --recursive --new-file --context --exclude=.*depend --exclude=make.out --exclude=.config --exclude=.version --exclude=bootsect --exclude=*.o --exclude=*.S --exclude=*.s --exclude=config.in --exclude=config.old --exclude=*.a --exclude=setup --exclude=ksyms.lst --exclude=autoconf.h --exclude=build --exclude=version.h --exclude=zSystem.map --exclude=*~ --exclude=zImage --exclude=zSystem --exclude=piggyback --exclude=xtract linux-1.0-orig/fs/binfmt_elf.c linux-1.0-mine/fs/binfmt_elf.c
*** linux-1.0-orig/fs/binfmt_elf.c	Thu Feb 17 02:07:27 1994
--- linux-1.0-mine/fs/binfmt_elf.c	Sat Mar 19 01:34:28 1994
***************
*** 420,425 ****
--- 420,427 ----
  	current->end_code = 0;
  	current->start_mmap = ELF_START_MMAP;
  	current->mmap = NULL;
+ 	current->flags &= ~PF_FORKNOEXEC; /* accounting flags */
+ 	current->io_usage = 0;
  	elf_entry = (unsigned int) elf_ex.e_entry;
  	
  	/* Do this so that we can load the interpreter, if need be.  We will
diff --recursive --new-file --context --exclude=.*depend --exclude=make.out --exclude=.config --exclude=.version --exclude=bootsect --exclude=*.o --exclude=*.S --exclude=*.s --exclude=config.in --exclude=config.old --exclude=*.a --exclude=setup --exclude=ksyms.lst --exclude=autoconf.h --exclude=build --exclude=version.h --exclude=zSystem.map --exclude=*~ --exclude=zImage --exclude=zSystem --exclude=piggyback --exclude=xtract linux-1.0-orig/fs/exec.c linux-1.0-mine/fs/exec.c
*** linux-1.0-orig/fs/exec.c	Fri Mar  4 07:10:38 1994
--- linux-1.0-mine/fs/exec.c	Sat Mar 19 01:34:50 1994
***************
*** 160,165 ****
--- 160,166 ----
  	if (!file.f_op->write)
  		goto close_coredump;
  	has_dumped = 1;
+ 	current->flags |= PF_DUMPCORE; /* accounting flags */
  /* changed the size calculations - should hopefully work better. lbt */
  	dump.magic = CMAGIC;
  	dump.start_code = 0;
***************
*** 797,802 ****
--- 798,805 ----
  	current->mmap = NULL;
  	current->executable = NULL;  /* for OMAGIC files */
  	current->sgid = current->egid = bprm->e_gid;
+ 	current->flags &= ~PF_FORKNOEXEC; /* accounting flags */
+ 	current->io_usage = 0;
  	if (N_MAGIC(ex) == OMAGIC) {
  		do_mmap(NULL, 0, ex.a_text+ex.a_data,
  			PROT_READ|PROT_WRITE|PROT_EXEC,
diff --recursive --new-file --context --exclude=.*depend --exclude=make.out --exclude=.config --exclude=.version --exclude=bootsect --exclude=*.o --exclude=*.S --exclude=*.s --exclude=config.in --exclude=config.old --exclude=*.a --exclude=setup --exclude=ksyms.lst --exclude=autoconf.h --exclude=build --exclude=version.h --exclude=zSystem.map --exclude=*~ --exclude=zImage --exclude=zSystem --exclude=piggyback --exclude=xtract linux-1.0-orig/fs/read_write.c linux-1.0-mine/fs/read_write.c
*** linux-1.0-orig/fs/read_write.c	Wed Dec  1 07:44:15 1993
--- linux-1.0-mine/fs/read_write.c	Sat Mar 19 01:34:12 1994
***************
*** 84,89 ****
--- 84,94 ----
  	error = verify_area(VERIFY_WRITE,buf,count);
  	if (error)
  		return error;
+ 
+ 	/* ok, we KNOW we're going to read something */
+ 	
+ 	current->io_usage += count; /* accounting purposes */
+ 
  	return file->f_op->read(inode,file,buf,count);
  }
  
***************
*** 104,108 ****
--- 109,118 ----
  	error = verify_area(VERIFY_READ,buf,count);
  	if (error)
  		return error;
+ 
+ 	/* ok, we KNOW we're going to write something */
+ 	
+ 	current->io_usage += count; /* accounting purposes */
+ 
  	return file->f_op->write(inode,file,buf,count);
  }
diff --recursive --new-file --context --exclude=.*depend --exclude=make.out --exclude=.config --exclude=.version --exclude=bootsect --exclude=*.o --exclude=*.S --exclude=*.s --exclude=config.in --exclude=config.old --exclude=*.a --exclude=setup --exclude=ksyms.lst --exclude=autoconf.h --exclude=build --exclude=version.h --exclude=zSystem.map --exclude=*~ --exclude=zImage --exclude=zSystem --exclude=piggyback --exclude=xtract linux-1.0-orig/include/linux/acct.h linux-1.0-mine/include/linux/acct.h
*** linux-1.0-orig/include/linux/acct.h	Wed Dec 31 19:00:00 1969
--- linux-1.0-mine/include/linux/acct.h	Tue Mar 22 21:07:54 1994
***************
*** 0 ****
--- 1,28 ----
+ #ifndef __LINUX_ACCT_H
+ #define __LINUX_ACCT_H
+ 
+ #define ACCT_COMM 16
+ 
+ struct acct
+ {
+   char		   ac_comm[ACCT_COMM];	/* Accounting command name */
+   time_t	   ac_utime;		/* Accounting user time */
+   time_t	   ac_stime;		/* Accounting system time */
+   time_t           ac_etime;		/* Accounting elapsed time */
+   time_t           ac_btime;		/* Beginning time */
+   uid_t            ac_uid;		/* Accounting user ID */
+   gid_t            ac_gid;		/* Accounting group ID */
+   dev_t	           ac_tty;		/* controlling tty */
+   char	           ac_flag;		/* Accounting flag */
+   unsigned long    ac_mem;		/* Pages of memory used */
+   unsigned long    ac_io;               /* number of read/write operations */
+ };
+ 
+ #define AFORK	0001	/* has executed fork, but no exec */
+ #define ASU	0002	/* used super-user privileges */
+ #define ACORE	0004	/* dumped core */
+ #define AXSIG	0010	/* killed by a signal */
+ 
+ #define AHZ     100
+ 
+ #endif
diff --recursive --new-file --context --exclude=.*depend --exclude=make.out --exclude=.config --exclude=.version --exclude=bootsect --exclude=*.o --exclude=*.S --exclude=*.s --exclude=config.in --exclude=config.old --exclude=*.a --exclude=setup --exclude=ksyms.lst --exclude=autoconf.h --exclude=build --exclude=version.h --exclude=zSystem.map --exclude=*~ --exclude=zImage --exclude=zSystem --exclude=piggyback --exclude=xtract linux-1.0-orig/include/linux/kernel.h linux-1.0-mine/include/linux/kernel.h
*** linux-1.0-orig/include/linux/kernel.h	Tue Jan 25 04:35:03 1994
--- linux-1.0-mine/include/linux/kernel.h	Fri Mar 18 17:45:32 1994
***************
*** 50,64 ****
  asmlinkage int printk(const char * fmt, ...)
  	__attribute__ ((format (printf, 1, 2)));
  
- /*
-  * This is defined as a macro, but at some point this might become a
-  * real subroutine that sets a flag if it returns true (to do
-  * BSD-style accounting where the process is flagged if it uses root
-  * privs).  The implication of this is that you should do normal
-  * permissions checks first, and check suser() last.
-  */
- #define suser() (current->euid == 0)
- 
  #endif /* __KERNEL__ */
  
  #define SI_LOAD_SHIFT	16
--- 50,55 ----
diff --recursive --new-file --context --exclude=.*depend --exclude=make.out --exclude=.config --exclude=.version --exclude=bootsect --exclude=*.o --exclude=*.S --exclude=*.s --exclude=config.in --exclude=config.old --exclude=*.a --exclude=setup --exclude=ksyms.lst --exclude=autoconf.h --exclude=build --exclude=version.h --exclude=zSystem.map --exclude=*~ --exclude=zImage --exclude=zSystem --exclude=piggyback --exclude=xtract linux-1.0-orig/include/linux/sched.h linux-1.0-mine/include/linux/sched.h
*** linux-1.0-orig/include/linux/sched.h	Fri Mar  4 07:13:32 1994
--- linux-1.0-mine/include/linux/sched.h	Sat Mar 19 01:34:54 1994
***************
*** 210,215 ****
--- 210,216 ----
  	struct rlimit rlim[RLIM_NLIMITS]; 
  	unsigned short used_math;
  	unsigned short rss;	/* number of resident pages */
+ 	unsigned long io_usage;	/* num characters transferred via r/w */
  	char comm[16];
  	struct vm86_struct * vm86_info;
  	unsigned long screen_bitmap;
***************
*** 246,251 ****
--- 247,256 ----
  					/* Not implemented yet, only for 486*/
  #define PF_PTRACED	0x00000010	/* set if ptrace (0) has been called. */
  #define PF_TRACESYS	0x00000020	/* tracing system calls */
+ #define PF_FORKNOEXEC	0x00000040	/* forked but didn't exec */
+ #define PF_SUPERPREV	0x00000100	/* used super-user privileges */
+ #define PF_DUMPCORE	0x00000200	/* dumped core */
+ #define PF_SIGNALED	0x00000400	/* killed by a signal */
  
  /*
   * cloning flags:
***************
*** 277,282 ****
--- 282,288 ----
  		  {       0, LONG_MAX}, {LONG_MAX, LONG_MAX}}, \
  /* math */	0, \
  /* rss */	2, \
+ /* io_usage */	0, \
  /* comm */	"swapper", \
  /* vm86_info */	NULL, 0, \
  /* fs info */	0,-1,0022,NULL,NULL,NULL,NULL, \
***************
*** 397,402 ****
--- 403,421 ----
  
  #define set_base(ldt,base) _set_base( ((char *)&(ldt)) , base )
  #define set_limit(ldt,limit) _set_limit( ((char *)&(ldt)) , (limit-1)>>12 )
+ 
+ /*
+  * This has now become a routine instead of a macro, it sets a flag if
+  * it returns true (to do BSD-style accounting where the process is flagged
+  * if it uses root privs). The implication of this is that you should do
+  * normal permissions checks first, and check suser() last.
+  */
+ extern inline int suser(void)
+ {
+ 	if (current->euid == 0)
+ 		current->flags |= PF_SUPERPREV;
+ 	return (current->euid == 0);
+ }
  
  /*
   * The wait-queues are circular lists, and you have to be *very* sure
diff --recursive --new-file --context --exclude=.*depend --exclude=make.out --exclude=.config --exclude=.version --exclude=bootsect --exclude=*.o --exclude=*.S --exclude=*.s --exclude=config.in --exclude=config.old --exclude=*.a --exclude=setup --exclude=ksyms.lst --exclude=autoconf.h --exclude=build --exclude=version.h --exclude=zSystem.map --exclude=*~ --exclude=zImage --exclude=zSystem --exclude=piggyback --exclude=xtract linux-1.0-orig/kernel/exit.c linux-1.0-mine/kernel/exit.c
*** linux-1.0-orig/kernel/exit.c	Mon Jan  3 01:06:01 1994
--- linux-1.0-mine/kernel/exit.c	Fri Mar 18 17:45:32 1994
***************
*** 19,24 ****
--- 19,25 ----
  #include <asm/segment.h>
  extern void shm_exit (void);
  extern void sem_exit (void);
+ extern void acct_process (long exitcode);
  
  int getrusage(struct task_struct *, int, struct rusage *);
  
***************
*** 354,359 ****
--- 355,361 ----
  	int i;
  
  fake_volatile:
+ 	acct_process(code);
  	if (current->semun)
  		sem_exit();
  	if (current->shm)
diff --recursive --new-file --context --exclude=.*depend --exclude=make.out --exclude=.config --exclude=.version --exclude=bootsect --exclude=*.o --exclude=*.S --exclude=*.s --exclude=config.in --exclude=config.old --exclude=*.a --exclude=setup --exclude=ksyms.lst --exclude=autoconf.h --exclude=build --exclude=version.h --exclude=zSystem.map --exclude=*~ --exclude=zImage --exclude=zSystem --exclude=piggyback --exclude=xtract linux-1.0-orig/kernel/fork.c linux-1.0-mine/kernel/fork.c
*** linux-1.0-orig/kernel/fork.c	Fri Mar  4 07:11:01 1994
--- linux-1.0-mine/kernel/fork.c	Sat Mar 19 01:34:46 1994
***************
*** 139,145 ****
  	p->did_exec = 0;
  	p->kernel_stack_page = 0;
  	p->state = TASK_UNINTERRUPTIBLE;
! 	p->flags &= ~(PF_PTRACED|PF_TRACESYS);
  	p->pid = last_pid;
  	p->swappable = 1;
  	p->p_pptr = p->p_opptr = current;
--- 139,147 ----
  	p->did_exec = 0;
  	p->kernel_stack_page = 0;
  	p->state = TASK_UNINTERRUPTIBLE;
! 	p->flags &= ~(PF_PTRACED|PF_TRACESYS|PF_SUPERPREV);
! 	p->flags |= PF_FORKNOEXEC;
! 	p->io_usage = 0;	/* does NOT inherit parent's I/O usage */
  	p->pid = last_pid;
  	p->swappable = 1;
  	p->p_pptr = p->p_opptr = current;
diff --recursive --new-file --context --exclude=.*depend --exclude=make.out --exclude=.config --exclude=.version --exclude=bootsect --exclude=*.o --exclude=*.S --exclude=*.s --exclude=config.in --exclude=config.old --exclude=*.a --exclude=setup --exclude=ksyms.lst --exclude=autoconf.h --exclude=build --exclude=version.h --exclude=zSystem.map --exclude=*~ --exclude=zImage --exclude=zSystem --exclude=piggyback --exclude=xtract linux-1.0-orig/kernel/sys.c linux-1.0-mine/kernel/sys.c
*** linux-1.0-orig/kernel/sys.c	Fri Mar  4 10:10:58 1994
--- linux-1.0-mine/kernel/sys.c	Tue Mar 22 21:08:32 1994
***************
*** 17,23 ****
--- 17,38 ----
  #include <linux/ptrace.h>
  #include <linux/stat.h>
  #include <linux/mman.h>
+ #include <linux/fcntl.h>
+ #include <linux/acct.h>
  
+ #undef QUOTA_PATCHES		/* define if you've installed the patches */
+ 
+ #ifdef QUOTA_PATCHES
+ 
+ #include <linux/fileio.h>	/* new header file */
+ #define close_fp close_filp	/* name change (open.c) */
+ 
+ #else /* !QUOTA_PATCHES */
+ 
+ #define vfs_open_filp(x)	/* can't call this if we don't have quota */
+ 
+ #endif
+   
  #include <asm/segment.h>
  #include <asm/io.h>
  
***************
*** 290,299 ****
  		return -EPERM;
  	return 0;
  }
  
! asmlinkage int sys_acct(void)
! {
! 	return -ENOSYS;
  }
  
  asmlinkage int sys_phys(void)
--- 305,430 ----
  		return -EPERM;
  	return 0;
  }
+   
+ static char acct_active = 0;
+ static struct file *acct_file;
+ 
+ int acct_process(long exitcode)
+ {
+    struct acct ac;
+    unsigned short fs;
+ 
+    if (acct_active) {
+       strncpy(ac.ac_comm, current->comm, ACCT_COMM);
+       ac.ac_comm[ACCT_COMM] = '\0';
+       ac.ac_utime = current->utime;
+       ac.ac_stime = current->stime;
+       ac.ac_btime = CT_TO_SECS(current->start_time) + (xtime.tv_sec - (jiffies / HZ));
+       ac.ac_etime = CURRENT_TIME - ac.ac_btime;
+       ac.ac_uid   = current->uid;
+       ac.ac_gid   = current->gid;
+       ac.ac_tty   = MKDEV(4, current->tty);
+       ac.ac_flag  = 0;
+       if (current->flags & PF_FORKNOEXEC)
+          ac.ac_flag |= AFORK;
+       if (current->flags & PF_SUPERPREV)
+          ac.ac_flag |= ASU;
+       if (current->flags & PF_DUMPCORE)
+          ac.ac_flag |= ACORE;
+       if (current->flags & PF_SIGNALED)
+          ac.ac_flag |= AXSIG;
+       ac.ac_io = current->io_usage;
+ 
+       /* Figure out the vsize of the current process and divide by the
+          page size to calculate AC_MEM.  This is the approved method
+          from the proc filesystem (array.c).*/
  
! #define	KSTK_ESP(stack)	(((unsigned long *)stack)[1022])
! 
!       {
! 	unsigned long vsize, esp;
! 	
! 	vsize = current->kernel_stack_page;
! 	if (vsize) {
! 	  esp = KSTK_ESP(vsize);
! 	  vsize = current->brk - current->start_code + PAGE_SIZE-1;
! 	  if (esp)
! 	    vsize += TASK_SIZE - esp;
! 	}
! 	/* now vsize contains the number of bytes used -- we want to
!            find out the number of pages, so divide it by the page size
!            and round up. */
! 	ac.ac_mem = (vsize / PAGE_SIZE) + ((vsize % PAGE_SIZE) != 0);
!       }
! 
!       /* Kernel segment override */
!       fs = get_fs();
!       set_fs(KERNEL_DS);
! 
!       acct_file->f_op->write(acct_file->f_inode, acct_file,
!                              (char *)&ac, sizeof(struct acct));
! 
!       set_fs(fs);
!    }
!    return 0;
! }
! 
! extern int close_fp (struct file *, int);
! 
! asmlinkage int sys_acct(const char *name)
! {
!    struct inode *inode = (struct inode *)0;
!    char *tmp;
!    int error;
! 
!    if (!suser())
!       return -EPERM;
! 
!    if (name == (char *)0) {
!       if (acct_active) {
! 	if ((error = close_fp (acct_file, 0)) != 0)
! 	  return (error);
! 	acct_active = 0;
!       }
!       return 0;
!    } else {
!       if (!acct_active) {
!          if ((error = getname(name, &tmp)) != 0)
!             return (error);
!          error = open_namei(tmp, O_RDWR, 0600, &inode, 0);
!          putname(tmp);
!          if (error)
!             return (error);
!          if (!S_ISREG(inode->i_mode)) {
!             iput(inode);
!             return -EACCES;
!          }
!          if (!inode->i_op || !inode->i_op->default_file_ops || 
!              !inode->i_op->default_file_ops->write) {
!             iput(inode);
!             return -EIO;
!          }
!          if ((acct_file = get_empty_filp()) == (struct file *)0)
!             return -EUSERS;
!          acct_file->f_mode = (O_WRONLY + 1) & O_ACCMODE;
!          acct_file->f_flags = O_WRONLY;
!          acct_file->f_inode = inode;
!          acct_file->f_pos = inode->i_size;
!          acct_file->f_reada = 0;
!          acct_file->f_op = inode->i_op->default_file_ops;
!          if (acct_file->f_op->open)
!             if (acct_file->f_op->open(acct_file->f_inode, acct_file)) {
!                iput(inode);
!                return -EIO;
!             }
! 
!          vfs_open_filp(acct_file); /* registers file with QUOTA routines */
! 
!          acct_active = 1;
!          return 0;
!       } else
!          return -EBUSY;
!    }
  }
  
  asmlinkage int sys_phys(void)
