Main Page   File List   File Members  

elsacct.c

Go to the documentation of this file.
00001 /*
00002  *  driver/elsacct/elsacct.c
00003  * 
00004  *  ELSA - Enhanced Linux System Accounting
00005  *  Guillaume Thouvenin - 26/04/2004
00006  *
00007  *  This module implements Enhanced Linux System Accounting. 
00008  *  We implement a character driver to transfer data between 
00009  *  BANK that are in the kernel adress space and the user 
00010  *  adress space. 
00011  *
00012  *  This code is licenced under GPL.
00013  */
00014 
00015 #include <linux/module.h>
00016 #include <linux/config.h>
00017 #include <linux/init.h>
00018 #include <linux/errno.h>
00019 #include <linux/fs.h>
00020 #include <linux/proc_fs.h>
00021 #include <linux/seq_file.h>
00022 #include <linux/slab.h>
00023 #include <linux/string.h>
00024 #include <linux/blkdev.h>
00025 #include <linux/times.h>
00026 
00027 #include <linux/elsacct.h>
00028 #include <linux/bank.h>
00029 
00030 #include <asm/uaccess.h>
00031 
00032 #ifdef CONFIG_ELSACCT_DEBUG
00033 #define dprintk(format...) printk(format)
00034 #else
00035 #define dprintk(format...)
00036 #endif
00037 
00038 /* Currently, informations are stored in the same file and we cannot chose it */
00039 #define ELSACCT_FILE    "/var/log/bank"
00040 
00041 /* It's the head on the list of banks */
00042 static struct bank_root elsa_br = BANK_ROOT_INIT(elsa_br);
00043 
00044 /* this is the major number dynamically got when registered */
00045 static int elsa_major;
00046 
00047 /* lock used to protect bank's list modifications */
00048 static spinlock_t elsa_lock = SPIN_LOCK_UNLOCKED;
00049 
00059 #define MANTSIZE        13      /* 13 bit mantissa. */
00060 #define EXPSIZE         3       /* Base 8 (3 bit) exponent. */
00061 #define MAXFRACT        ((1 << MANTSIZE) - 1)   /* Maximum fractional value. */
00062 
00063 typedef __u16 comp_t;
00064 
00065 static comp_t encode_comp_t(unsigned long value)
00066 {
00067         int exp, rnd;
00068 
00069         exp = rnd = 0;
00070         while (value > MAXFRACT) {
00071                 rnd = value & (1 << (EXPSIZE - 1));     /* Round up? */
00072                 value >>= EXPSIZE;      /* Base 8 exponent == 3 bit shift. */
00073                 exp++;
00074         }
00075 
00076         /*
00077          * If we need to round up, do it (and handle overflow correctly).
00078          */
00079         if (rnd && (++value > MAXFRACT)) {
00080                 value >>= EXPSIZE;
00081                 exp++;
00082         }
00083 
00084         /*
00085          * Clean it up and polish it off.
00086          */
00087         exp <<= MANTSIZE;       /* Shift the exponent into place */
00088         exp += value;           /* and add on the mantissa. */
00089         return exp;
00090 }
00091 
00103 static int do_elsacct_bank(struct elsa_bank *b)
00104 {
00105         static struct file *filp;
00106         //static int irq_restore;
00107         struct elsa_acct *info = (struct elsa_acct *)b->info;
00108         mm_segment_t old_fs;
00109 
00110         if (!info)
00111                 return -EINVAL;
00112 
00113         /* Set bank ID */
00114         info->eac_bid = b->bid;
00115 
00116         /* Cannot open a file if irqs are disabled */
00117         //if (irqs_disabled()) {
00118         //      local_irq_enable();
00119         //      irq_restore = 1;
00120         //}
00121 
00122         /* Open file where accounting information will be written */
00123         filp = filp_open(ELSACCT_FILE, O_CREAT | O_RDWR | O_APPEND, 0666);
00124 
00125         if (IS_ERR(filp)) {
00126                 return (PTR_ERR(filp));
00127         }
00128 
00129         if (!filp->f_op->write) {
00130                 filp_close(filp, NULL);
00131                 return (-EIO);
00132         }
00133 
00134         /*
00135          * Kernel segment override to datasegment and write it
00136          * to the accounting file. 
00137          */
00138         old_fs = get_fs();
00139         set_fs(KERNEL_DS);
00140 
00141         filp->f_op->write(filp, (char *)info,
00142                           sizeof(struct elsa_acct), &filp->f_pos);
00143 
00144         set_fs(old_fs);
00145 
00146         filp_close(filp, NULL);
00147 
00148         //if (irq_restore)
00149         //      local_irq_disable();
00150 
00151         return 0;
00152 }
00153 
00162 static int do_elsacct_data(struct elsa_bank *b, struct elsa_data *d)
00163 {
00164         u64 elapsed;
00165         struct elsa_acct *info = (struct elsa_acct *)b->info;
00166         struct task_struct *p = d->process;
00167 
00168         if (!info)
00169                 return -EINVAL;
00170 
00171         /* One more process in the bank */
00172         info->eac_ptot++;
00173 
00174         /* elapsed time */
00175         elapsed = jiffies_64_to_clock_t(get_jiffies_64() - p->start_time);
00176         info->eac_etime += encode_comp_t(elapsed < (unsigned long)-1l ?
00177                                          (unsigned long)elapsed : (unsigned
00178                                                                    long)-1l);
00179 
00180         /* user time and system time */
00181         info->eac_utime += encode_comp_t(jiffies_to_clock_t(p->utime));
00182         info->eac_stime += encode_comp_t(jiffies_to_clock_t(p->stime));
00183 
00184         /* minor and major page faults */
00185         info->eac_minflt += encode_comp_t(p->min_flt);
00186         info->eac_majflt += encode_comp_t(p->maj_flt);
00187 
00188         return 0;
00189 }
00190 
00201 int do_elsacct(int opcode, struct elsa_bank *b, struct elsa_data *d)
00202 {
00203         int retval = 0;
00204 
00205         switch (opcode) {
00206         case ELSA_BANK_CALLBACK:
00207                 BUG_ON(b == NULL);
00208                 retval = do_elsacct_bank(b);
00209                 break;
00210         case ELSA_DATA_CALLBACK:
00211                 BUG_ON(b == NULL);
00212                 BUG_ON(d == NULL);
00213                 retval = do_elsacct_data(b, d);
00214                 break;
00215         default:
00216                 printk("do_elsacct: unknown opcode\n");
00217                 retval = -ENOIOCTLCMD;
00218                 break;
00219         }
00220 
00221         return retval;
00222 }
00223 
00224 /* Used internally by kernel/fork.c */
00225 void elsacct_process_copy(struct task_struct *from, struct task_struct *to)
00226 {
00227         static struct list_head *entry; /* entry == NULL */
00228         static struct elsa_bank *b;     /* b == NULL */
00229         static struct elsa_data *d;     /* d == NULL */
00230         static struct elsa_data *new_d; /* d == NULL */
00231 
00232         /* 
00233          * First, initialize to->bank_head otherwise, if 
00234          * from->bank_head is NULL it won't be initialize
00235          */
00236         write_lock_irq(&tasklist_lock);
00237         INIT_LIST_HEAD(&(to->bank_head));
00238         write_unlock_irq(&tasklist_lock);
00239 
00240         list_for_each(entry, &from->bank_head) {
00241                 read_lock_irq(&tasklist_lock);
00242                 d = list_entry(entry, struct elsa_data, bank_list);
00243                 read_unlock_irq(&tasklist_lock);
00244 
00245                 new_d = elsa_data_alloc();
00246                 if (new_d) {
00247                         spin_lock_irq(&elsa_lock);
00248                         b = elsa_get_bank(&elsa_br, d->bid);
00249                         if (!b) {
00250                                 /* release memory */
00251                                 elsa_data_free(new_d);
00252                         } else {
00253                                 new_d->process = to;
00254                                 if (!elsa_data_add(b, new_d))
00255                                         elsa_data_free(new_d);
00256                         }
00257                         spin_unlock_irq(&elsa_lock);
00258                 }
00259         }
00260 }
00261 
00262 /* Used internally by kernel/exit.c */
00263 void elsacct_process_remove(struct task_struct *p)
00264 {
00265         static struct elsa_data *d;     /* d == NULL */
00266         static struct elsa_bank *b;     /* b == NULL */
00267         int empty = 0;
00268 
00269         while (!empty) {
00270                 int bid;
00271 
00272                 spin_lock_irq(&elsa_lock);
00273 
00274                 read_lock(&tasklist_lock);
00275                 if (list_empty(&p->bank_head)) {
00276                         d = NULL;
00277                 } else {
00278                         d = list_entry((p->bank_head).next,
00279                                        struct elsa_data, bank_list);
00280                 }
00281                 read_unlock(&tasklist_lock);
00282 
00283                 if (d != NULL) {
00284                         /* We don't need to check the return value */
00285                         b = elsa_get_bank(&elsa_br, d->bid);
00286                         bid = elsa_data_remove(b, d);
00287 
00288                         elsa_data_free(d);
00289 
00290                         if (bid) {
00291                                 /* bank is empty */
00292                                 elsa_bank_remove(b);
00293                                 /* 
00294                                  * unlock before releasing memory used by 
00295                                  * the bank
00296                                  */
00297                                 spin_unlock_irq(&elsa_lock);
00298                                 kfree(b->info);
00299                                 elsa_bank_free(b);
00300                         }
00301                 } else {
00302                         empty = 1;
00303                         spin_unlock_irq(&elsa_lock);
00304                 }
00305 
00306         }
00307 }
00308 
00309  /**************************************************************
00310  * functions used to manipulate the device                     *
00311  *                                                             *
00312  * The enhanced linux system accounting device is /dev/elsacct *
00313  * and the major number is dynamically given by OS             *
00314  **************************************************************/
00315 
00316 /* 
00317  * The process context, represented as a typical driver method - ioctl(), must 
00318  * use spin_lock_irq() because it knows that interrupts are always enabled 
00319  * while executing the device ioctl() method.
00320  */
00321 
00332 int elsacct_ioctl(struct inode *inode, struct file *filp, unsigned int cmd,
00333                   unsigned long arg)
00334 {
00335         static int retval;      /* retval == 0 */
00336         static struct elsa_ioctl_args iarg;
00337         static struct task_struct *p;
00338         static struct elsa_bank *b;
00339         static struct elsa_data *d;
00340 
00341         /* We can make some checks */
00342         if (_IOC_TYPE(cmd) != ELSACCT_MAGIC)
00343                 return -ENOTTY;
00344         if (_IOC_NR(cmd) > ELSACCT_MAXNR)
00345                 return -ENOTTY;
00346         if ((cmd != ELSACCT_PROCESS_ADD) && (cmd != ELSACCT_PROCESS_REMOVE) &&
00347             (cmd != ELSACCT_PROCESS_REMOVE_ALL) && (cmd != ELSACCT_BANK_CLEAN))
00348                 return -ENOTTY;
00349 
00350         if (!access_ok(VERIFY_READ, (void *)arg, _IOC_SIZE(cmd)))
00351                 return -EFAULT;
00352 
00353         /* Recover ioctl parameters */
00354         if (copy_from_user(&iarg, (struct elsa_ioctl_args *)arg,
00355                            sizeof(struct elsa_ioctl_args))) {
00356                 return -EFAULT;
00357         }
00358 
00359         dprintk("ELSAIOCTL: iarg = {bid#%d - pid#%d}\n", iarg.bid, iarg.pid);
00360 
00361         switch (cmd) {
00362         case ELSACCT_PROCESS_ADD:
00363                 /*
00364                  * To add a process to a bank we need to perform 
00365                  * following actions:
00366                  *     1) Allocate memory space to data (container)
00367                  *     2) Allocate memory space to a bank or get a 
00368                  *        pointer to an existing bank if BID != 0
00369                  *     3) Update process in data field
00370                  *     4) Add the container to the bank
00371                  */
00372                 /* 
00373                  * We need the pointer to task_struct. We catch it now so if 
00374                  * it fails we will not allocate memory for new data
00375                  */
00376                 read_lock(&tasklist_lock);
00377                 p = find_task_by_pid(iarg.pid);
00378                 read_unlock(&tasklist_lock);
00379                 if (!p) {
00380                         dprintk("elsacct_ioct: PID#%d not found\n", iarg.pid);
00381                         return -EAGAIN;
00382                 }
00383 
00384                 d = elsa_data_alloc();
00385                 if (!d) {
00386                         return -ENOMEM;
00387                 }
00388 
00389                 if (iarg.bid == 0) {
00390                         struct elsa_acct *acctinfo;
00391                         int bid;
00392 
00393                         acctinfo = (struct elsa_acct *)
00394                             kmalloc(sizeof(struct elsa_acct), GFP_KERNEL);
00395                         if (!acctinfo) {
00396                                 printk
00397                                     ("ELSA: cannot allocate space for accounting \n");
00398                                 return -ENOMEM;
00399                         }
00400 
00401                         b = elsa_bank_alloc();
00402                         if (!b) {
00403                                 dprintk("elsacct_ioct: cannot create BID\n");
00404                                 /* release memory */
00405                                 elsa_data_free(d);
00406                                 kfree(acctinfo);
00407                                 return -ENOMEM;
00408                         }
00409 
00410                         /* Macro to initialize accounting informations */
00411                         elsa_acct_init(acctinfo);
00412 
00413                         spin_lock_irq(&elsa_lock);
00414                         bid = elsa_bank_add(&elsa_br, b, &do_elsacct, acctinfo);
00415                         spin_unlock_irq(&elsa_lock);
00416 
00417                         if (!bid) {
00418                                 dprintk
00419                                     ("elsacct_ioct: error during bank creation\n");
00420                                 /* release memory */
00421                                 elsa_data_free(d);
00422                                 elsa_bank_free(b);
00423                                 kfree(acctinfo);
00424                                 return -EFAULT;
00425                         }
00426                 } else {
00427                         b = elsa_get_bank(&elsa_br, iarg.bid);
00428                         if (!b) {
00429                                 dprintk("elsacct_ioct: BID#%d not found\n",
00430                                         iarg.bid);
00431                                 /* release memory */
00432                                 elsa_data_free(d);
00433                                 return -EINVAL;
00434                         }
00435                 }
00436 
00437                 /* At this point, b cannot be NULL */
00438 
00439                 d->process = p;
00440 
00441                 spin_lock_irq(&elsa_lock);
00442                 retval = elsa_data_add(b, d);
00443                 spin_unlock_irq(&elsa_lock);
00444                 break;
00445 
00446         case ELSACCT_PROCESS_REMOVE:
00447                 /*
00448                  * We want to remove a process from one given bank
00449                  * Steps are:
00450                  *      1) get a pointer to the given bank
00451                  *      2) get a pointer to the container of the process
00452                  *      3) remove the process
00453                  *      4) free memory used by it
00454                  *      5) release bank if empty
00455                  */
00456                 spin_lock_irq(&elsa_lock);
00457 
00458                 b = elsa_get_bank(&elsa_br, iarg.bid);
00459                 if (!b) {
00460                         spin_unlock_irq(&elsa_lock);
00461                         dprintk("EBR: bank not found\n");
00462                         return -EINVAL;
00463                 }
00464 
00465                 d = elsa_get_data(iarg.pid, iarg.bid);
00466                 if (!d) {
00467                         spin_unlock_irq(&elsa_lock);
00468                         dprintk("EBR: data not found\n");
00469                         return -EINVAL;
00470                 }
00471 
00472                 retval = elsa_data_remove(b, d);
00473 
00474                 elsa_data_free(d);
00475 
00476                 if (retval) {
00477                         dprintk("EBR: Bank is now empty\n");
00478 
00479                         elsa_bank_remove(b);
00480 
00481                         dprintk("EBR: bank release\n");
00482 
00483                         spin_unlock_irq(&elsa_lock);
00484                         kfree(b->info);
00485                         elsa_bank_free(b);
00486                 } else {
00487                         spin_unlock_irq(&elsa_lock);
00488                 }
00489 
00490                 retval = 0;
00491                 break;
00492 
00493         case ELSACCT_PROCESS_REMOVE_ALL:
00494                 /*
00495                  * remove a process from all banks that it
00496                  * belongs. To achieve this we need to:
00497                  *      1) get a pointer to the process
00498                  *      2) get the first data 
00499                  *      3) remove it from the bank
00500                  *      4) release the data
00501                  *      5) if necessary, remove and release bank
00502                  *
00503                  */
00504                 read_lock(&tasklist_lock);
00505                 p = find_task_by_pid(iarg.pid);
00506                 read_unlock(&tasklist_lock);
00507                 if (!p) {
00508                         dprintk("elsacct_ioct: PID#%d not found\n", iarg.pid);
00509                         return -EAGAIN;
00510                 }
00511 
00512                 retval = 1;
00513                 while (retval) {
00514                         int bid;
00515 
00516                         spin_lock_irq(&elsa_lock);
00517 
00518                         read_lock(&tasklist_lock);
00519                         if (list_empty(&p->bank_head)) {
00520                                 d = NULL;
00521                         } else {
00522                                 d = list_entry((p->bank_head).next,
00523                                                struct elsa_data, bank_list);
00524                                 dprintk("EPRA: remove ...");
00525                                 BUG_ON(d == NULL);
00526                                 dprintk(" from bank #%d ...", d->bid);
00527                                 dprintk(" pid #%d\n", d->process->pid);
00528                         }
00529                         read_unlock(&tasklist_lock);
00530 
00531                         if (d != NULL) {
00532                                 /* We don't need to check the return value */
00533                                 b = elsa_get_bank(&elsa_br, d->bid);
00534                                 bid = elsa_data_remove(b, d);
00535 
00536                                 elsa_data_free(d);
00537 
00538                                 if (bid) {
00539                                         /* bank is empty */
00540                                         elsa_bank_remove(b);
00541                                         /* 
00542                                          * unlock before releasing memory 
00543                                          * occupied by the bank 
00544                                          */
00545                                         spin_unlock_irq(&elsa_lock);
00546                                         kfree(b->info);
00547                                         elsa_bank_free(b);
00548                                 }
00549                         } else {
00550                                 dprintk
00551                                     ("EPRA: Process removed from all banks\n");
00552                                 retval = 0;
00553                                 spin_unlock_irq(&elsa_lock);
00554                         }
00555 
00556                 }
00557                 break;
00558 
00559         case ELSACCT_BANK_CLEAN:
00560                 /*
00561                  * remove all process from one given bank
00562                  * Steps are:
00563                  *      1) get a pointer to the given bank
00564                  *      2) while there is data in the bank
00565                  *              -> remove the data
00566                  *      3) release bank
00567                  */
00568                 spin_lock_irq(&elsa_lock);
00569                 b = elsa_get_bank(&elsa_br, iarg.bid);
00570 
00571                 if (!b) {
00572                         spin_unlock_irq(&elsa_lock);
00573                         dprintk("EBC: bank not found\n");
00574                         return -EINVAL;
00575                 }
00576 
00577                 retval = 1;
00578                 while (retval) {
00579 
00580                         if (list_empty(&b->data_head)) {
00581                                 d = NULL;
00582                         } else {
00583                                 d = list_entry((b->data_head).next,
00584                                                struct elsa_data, data_list);
00585                         }
00586 
00587                         if (d) {
00588                                 dprintk("EBC: Remove pid#%d from bank#%d\n",
00589                                         d->process->pid, b->bid);
00590                                 /* We don't need to check the return value */
00591                                 elsa_data_remove(b, d);
00592                                 elsa_data_free(d);
00593                         } else {
00594                                 retval = 0;
00595                                 dprintk("EBC: Bank#%d is now empty\n", b->bid);
00596                         }
00597                 }
00598 
00599                 /* bank is empty */
00600                 elsa_bank_remove(b);
00601 
00602                 spin_unlock_irq(&elsa_lock);
00603                 kfree(b->info);
00604                 elsa_bank_free(b);
00605 
00606                 break;
00607 
00608         default:
00609                 /*
00610                  * The POSIX standard, states that if an inappropriate ioctl command 
00611                  * has been issued, then -ENOTTY should be returned.
00612                  */
00613                 retval = -ENOTTY;
00614         };
00615 
00616         return retval;
00617 }
00618 
00619 struct file_operations elsa_fops = {
00620         .owner = THIS_MODULE,
00621         .ioctl = elsacct_ioctl,
00622 };
00623 
00631 static int __init elsacct_init(void)
00632 {
00633         int retval = 0;
00634         struct proc_dir_entry *entry;
00635 
00636         /* register character device with a dynamic major number */
00637         elsa_major = register_chrdev(0, "elsacct", &elsa_fops);
00638         if (!elsa_major) {
00639                 dprintk(KERN_WARNING
00640                         "elsacct_init: can't get major %d\n", elsa_major);
00641                 return -EIO;
00642         }
00643 
00644         /* 
00645          * To get the major number we use a script that parse /proc/devices
00646          * and get the correct major number. We don't use elsa_major except
00647          * to test if the device is registered.
00648          */
00649         dprintk(KERN_INFO "ELSACCT: device driver is recorded\n");
00650         /*
00651          * From here, dev is registered. Thus, if there is a problem
00652          * we must unregister the device.
00653          */
00654 
00655 #ifdef CONFIG_PROC_FS
00656         /* create /proc entry */
00657         entry = create_proc_entry("bankinfo", 0, NULL);
00658         if (entry) {
00659                 entry->proc_fops = &proc_bankinfo_ops;
00660         } else {
00661                 unregister_chrdev(elsa_major, "elsacct");
00662                 return -EIO;
00663         }
00664 #endif
00665 
00666         /* succeed */
00667         return retval;
00668 }
00669 
00670  /*********************************
00671  * functions used to manage /proc *
00672  *                                *
00673  * The entry is /proc/bankinfo    *
00674  *********************************/
00675 
00676 /*
00677  * Add an entry in /proc to get informations concerning
00678  * banks. This entry is called /proc/bankinfo
00679  */
00680 #ifdef CONFIG_PROC_FS
00681 static void *b_start(struct seq_file *m, loff_t * pos)
00682 {
00683         loff_t n = *pos;
00684         struct list_head *p;
00685 
00686         /* Header is displaying just during the first called */
00687         if (!n) {
00688                 seq_puts(m, "# - bankinfo -\n");
00689                 seq_puts(m, "# bankid:\t<process> <process> ...\n");
00690         }
00691 
00692         if (list_empty(&elsa_br.bank_head))
00693                 return NULL;
00694 
00695         p = elsa_br.bank_head.next;
00696         while (n--) {
00697                 p = p->next;
00698                 if (p == &elsa_br.bank_head)
00699                         return NULL;
00700         }
00701 
00702         /* we return a pointer to the data in the bank */
00703         return list_entry(p, struct elsa_bank, bank_list);
00704 
00705 }
00706 
00707 static void b_stop(struct seq_file *m, void *v)
00708 {
00709 }
00710 
00711 static void *b_next(struct seq_file *m, void *v, loff_t * pos)
00712 {
00713         struct elsa_bank *bank = v;
00714 
00715         ++*pos;
00716         return bank->bank_list.next == &elsa_br.bank_head ? NULL :
00717             list_entry(bank->bank_list.next, struct elsa_bank, bank_list);
00718 }
00719 
00720 static int show_bankinfo(struct seq_file *m, void *v)
00721 {
00722         struct elsa_bank *bank = v;
00723         struct elsa_data *data;
00724 
00725         if (!bank) {
00726                 seq_printf(m, "There is no banks\n");
00727         } else {
00728                 /* display bank identifier */
00729                 seq_printf(m, "%d:", bank->bid);
00730                 /* add a tabulation */
00731                 seq_printf(m, "\t");
00732                 /* display list of processus */
00733                 if (list_empty(&(bank->data_head))) {
00734                         seq_printf(m, "Empty");
00735                 } else {
00736                         list_for_each_entry(data, &(bank->data_head), data_list)
00737                             seq_printf(m, "%d ", data->process->pid);
00738                 }
00739                 /* add EOL */
00740                 seq_printf(m, "\n");
00741         }
00742 
00743         return 0;
00744 }
00745 
00746 /* 
00747  * bankinfo_op - iterator that generates /proc/bankinfo
00748  * Output layout is:
00749  *     bankID   pid ...
00750  */
00751 struct seq_operations bankinfo_op = {
00752         .start = b_start,
00753         .stop = b_stop,
00754         .next = b_next,
00755         .show = show_bankinfo,
00756 };
00757 
00758 int bankinfo_open(struct inode *inode, struct file *file)
00759 {
00760         return seq_open(file, &bankinfo_op);
00761 }
00762 
00763 struct file_operations proc_bankinfo_ops = {
00764         .open = bankinfo_open,
00765         .read = seq_read,
00766         .llseek = seq_lseek,
00767         .release = seq_release,
00768 };
00769 
00770 EXPORT_SYMBOL(proc_bankinfo_ops);
00771 
00772 #endif
00773 
00774  /************************
00775  * Module initialization *
00776  ************************/
00777 
00784 static int __init elsacct_init_module(void)
00785 {
00786         dprintk(KERN_INFO "ELSA accounting started\n");
00787         return elsacct_init();
00788 }
00789 
00795 static void __exit elsacct_cleanup_module(void)
00796 {
00797         /* release the major number when module is unloaded */
00798         if (unregister_chrdev(elsa_major, "elsacct"))
00799                 dprintk(KERN_WARNING
00800                         "elsacct_cleanup: cannot unregister blkdev\n");
00801         else
00802                 dprintk(KERN_INFO "elsacct_cleanup: accounting terminated\n");
00803 }
00804 
00805 module_init(elsacct_init_module);
00806 module_exit(elsacct_cleanup_module);
00807 
00808 MODULE_DESCRIPTION("Enhanced Linux System Accounting.");
00809 MODULE_AUTHOR("Guillaume Thouvenin <guillaume.thouvenin@bull.net>");
00810 
00811 MODULE_LICENSE("GPL");

Generated on Wed Jul 7 08:31:37 2004 for Enhanced Linux System Accounting by doxygen1.2.18