/* * linux/libgtop/module.c * Copyright (C) 1999 Martin Baulig */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include EXPORT_NO_SYMBOLS; static int system_ctl_handler (ctl_table *table, int *name, int nlen, void *oldval, size_t *oldlenp, void *newval, size_t newlen, void **context); static int proc_ctl_handler (ctl_table *table, int *name, int nlen, void *oldval, size_t *oldlenp, void *newval, size_t newlen, void **context); static int libgtop_sysctl_version = 1; static int libgtop_update_expensive = 5000; static struct ctl_table_header *libgtop_sysctl_header = NULL; static libgtop_stat_t libgtop_stat; static unsigned int libgtop_mem_timestamp = 0; static libgtop_mem_t libgtop_mem; static unsigned int libgtop_swap_timestamp = 0; static libgtop_swap_t libgtop_swap; static libgtop_proclist_t libgtop_proclist; static libgtop_proc_state_t libgtop_proc_state; static ctl_table libgtop_table[]; static ctl_table libgtop_root_table[] = { {CTL_LIBGTOP, "libgtop", NULL, 0, 0555, libgtop_table}, {0} }; #ifdef MODULE static #endif ctl_table libgtop_table[] = { {LIBGTOP_VERSION, "version", &libgtop_sysctl_version, sizeof (int), 0444, NULL, &proc_dointvec}, {LIBGTOP_UPDATE_EXPENSIVE, "update_expensive", &libgtop_update_expensive, sizeof (int), 0664, NULL, &proc_dointvec}, {LIBGTOP_STAT, NULL, &libgtop_stat, sizeof (libgtop_stat), 0444, NULL, NULL, &system_ctl_handler}, {LIBGTOP_MEM, NULL, &libgtop_mem, sizeof (libgtop_mem), 0444, NULL, NULL, &system_ctl_handler}, {LIBGTOP_SWAP, NULL, &libgtop_swap, sizeof (libgtop_swap), 0444, NULL, NULL, &system_ctl_handler}, {LIBGTOP_PROCLIST, NULL, &libgtop_proclist, sizeof (libgtop_proclist), 0444, NULL, NULL, &system_ctl_handler}, {LIBGTOP_PROC_STATE, NULL, &libgtop_proc_state, sizeof (libgtop_proc_state), 0444, NULL, NULL, &proc_ctl_handler}, {0} }; #ifdef MODULE static void libgtop_sysctl_register(void) { static int initialized = 0; if (initialized == 1) return; libgtop_sysctl_header = register_sysctl_table(libgtop_root_table, 1); initialized = 1; } static void libgtop_sysctl_unregister(void) { unregister_sysctl_table(libgtop_sysctl_header); } int init_module(void) { libgtop_sysctl_register(); return 0; } void cleanup_module(void) { libgtop_sysctl_unregister(); } #endif /* MODULE */ #if defined(__i386__) # define KSTK_EIP(tsk) (((unsigned long *)(4096+(unsigned long)(tsk)))[1019]) # define KSTK_ESP(tsk) (((unsigned long *)(4096+(unsigned long)(tsk)))[1022]) #elif defined(__alpha__) /* * See arch/alpha/kernel/ptrace.c for details. */ # define PT_REG(reg) (PAGE_SIZE - sizeof(struct pt_regs) \ + (long)&((struct pt_regs *)0)->reg) # define KSTK_EIP(tsk) \ (*(unsigned long *)(PT_REG(pc) + PAGE_SIZE + (unsigned long)(tsk))) # define KSTK_ESP(tsk) ((tsk) == current ? rdusp() : (tsk)->tss.usp) #elif defined(CONFIG_ARM) # define KSTK_EIP(tsk) (((unsigned long *)(4096+(unsigned long)(tsk)))[1022]) # define KSTK_ESP(tsk) (((unsigned long *)(4096+(unsigned long)(tsk)))[1020]) #elif defined(__mc68000__) #define KSTK_EIP(tsk) \ ({ \ unsigned long eip = 0; \ if ((tsk)->tss.esp0 > PAGE_SIZE && \ MAP_NR((tsk)->tss.esp0) < max_mapnr) \ eip = ((struct pt_regs *) (tsk)->tss.esp0)->pc; \ eip; }) #define KSTK_ESP(tsk) ((tsk) == current ? rdusp() : (tsk)->tss.usp) #elif defined(__powerpc__) #define KSTK_EIP(tsk) ((tsk)->tss.regs->nip) #define KSTK_ESP(tsk) ((tsk)->tss.regs->gpr[1]) #elif defined (__sparc_v9__) # define KSTK_EIP(tsk) ((tsk)->tss.kregs->tpc) # define KSTK_ESP(tsk) ((tsk)->tss.kregs->u_regs[UREG_FP]) #elif defined(__sparc__) # define KSTK_EIP(tsk) ((tsk)->tss.kregs->pc) # define KSTK_ESP(tsk) ((tsk)->tss.kregs->u_regs[UREG_FP]) #endif static int libgtop_sysctl (ctl_table *table, int nlen, int *name) { extern unsigned long total_forks; int index, tindex, tty, which, arg; libgtop_stat_t *lstat; libgtop_mem_t *mem; libgtop_swap_t *swap; libgtop_proclist_t *proclist; struct task_struct *tsk = NULL; struct sysinfo i; switch (table->ctl_name) { case LIBGTOP_STAT: lstat = table->data; lstat->cpu.total = jiffies; lstat->cpu.user = kstat.cpu_user; lstat->cpu.nice = kstat.cpu_nice; lstat->cpu.sys = kstat.cpu_system; lstat->cpu.idle = jiffies*smp_num_cpus - (lstat->cpu.user + lstat->cpu.nice + lstat->cpu.sys); #ifdef __SMP__ for (i = 0; i < smp_num_cpus; i++) { lstat->xcpu[i].user = kstat.per_cpu_user[cpu_logical_map(i)]; lstat->xcpu[i].nice = kstat.per_cpu_nice[cpu_logical_map(i)]; lstat->xcpu[i].sys = kstat.per_cpu_system[cpu_logical_map(i)]; lstat->xcpu[i].idle = jiffies - (lstat->xcpu[i].user + lstat->xcpu[i].nice + lstat->xcpu[i].sys); } lstat->ncpu = smp_num_cpus; #else lstat->ncpu = 0; #endif lstat->frequency = HZ; lstat->loadavg [0] = (double) avenrun [0] / (1 << FSHIFT); lstat->loadavg [1] = (double) avenrun [1] / (1 << FSHIFT); lstat->loadavg [2] = (double) avenrun [2] / (1 << FSHIFT); lstat->pgpgin = kstat.pgpgin; lstat->pgpgout = kstat.pgpgout; lstat->pswpin = kstat.pswpin; lstat->pswpout = kstat.pswpout; lstat->context_swtch = kstat.context_swtch; lstat->boot_time = xtime.tv_sec - jiffies / HZ; lstat->total_forks = total_forks; break; case LIBGTOP_MEM: if (jiffies - libgtop_mem_timestamp < libgtop_update_expensive) return 0; libgtop_mem_timestamp = jiffies; mem = table->data; si_meminfo (&i); mem->totalram = i.totalram; mem->freeram = i.freeram; mem->sharedram = i.sharedram; mem->bufferram = i.bufferram; #if 0 mem->cachedram = page_cache_size * PAGE_SIZE; #endif return 0; case LIBGTOP_SWAP: if (jiffies - libgtop_swap_timestamp < libgtop_update_expensive) return 0; libgtop_swap_timestamp = jiffies; swap = table->data; si_swapinfo (&i); swap->totalswap = i.totalswap; swap->freeswap = i.freeswap; return 0; case LIBGTOP_PROCLIST: proclist = table->data; if (nlen == 1) { which = 0; arg = 0; } else if (nlen == 2) { which = name [1]; arg = 0; } else if (nlen == 3) { which = name [1]; arg = name [2]; } else { return -EINVAL; } tsk = task [0]; read_lock (&tasklist_lock); for (index = tindex = 0; index < nr_tasks; index++, tsk = tsk->next_task) { if (tsk->pid == 0) continue; switch (which & LIBGTOP_PROCLIST_MASK) { case LIBGTOP_PROCLIST_PID: if (tsk->pid != arg) continue; break; case LIBGTOP_PROCLIST_PGRP: if (tsk->pgrp != arg) continue; break; case LIBGTOP_PROCLIST_SESSION: if (tsk->session != arg) continue; break; case LIBGTOP_PROCLIST_TTY: tty = tsk->tty ? kdev_t_to_nr (tsk->tty->device) : 0; if (tty != arg) continue; break; case LIBGTOP_PROCLIST_UID: if (tsk->uid != arg) continue; break; case LIBGTOP_PROCLIST_RUID: if (tsk->euid != arg) continue; break; } if ((which & LIBGTOP_EXCLUDE_IDLE) && (tsk->state != 0)) continue; if ((which & LIBGTOP_EXCLUDE_NOTTY) && (tsk->tty == NULL)) continue; proclist->pids [tindex++] = tsk->pid; } proclist->count = tindex; proclist->nr_running = nr_running; proclist->last_pid = last_pid; proclist->nr_tasks = tindex; read_unlock(&tasklist_lock); return 0; default: return -EINVAL; } return 0; } static int libgtop_sysctl_proc (ctl_table *table, int nlen, int *name, struct task_struct *tsk) { libgtop_proc_state_t *proc_state; int i; switch (table->ctl_name) { case LIBGTOP_PROC_STATE: proc_state = table->data; proc_state->uid = tsk->uid; proc_state->gid = tsk->gid; proc_state->state = tsk->state; proc_state->flags = tsk->flags; memcpy (proc_state->comm, tsk->comm, sizeof (proc_state->comm)); proc_state->uid = tsk->uid; proc_state->euid = tsk->euid; proc_state->suid = tsk->suid; proc_state->fsuid = tsk->fsuid; proc_state->gid = tsk->gid; proc_state->egid = tsk->egid; proc_state->sgid = tsk->sgid; proc_state->fsgid = tsk->fsgid; proc_state->pid = tsk->pid; proc_state->pgrp = tsk->pgrp; proc_state->ppid = tsk->p_pptr->pid; proc_state->session = tsk->session; proc_state->tty = tsk->tty ? kdev_t_to_nr (tsk->tty->device) : 0; proc_state->tpgid = tsk->tty ? tsk->tty->pgrp : -1; proc_state->priority = tsk->priority; proc_state->counter = tsk->counter; proc_state->def_priority = DEF_PRIORITY; proc_state->utime = tsk->times.tms_utime; proc_state->stime = tsk->times.tms_stime; proc_state->cutime = tsk->times.tms_cutime; proc_state->cstime = tsk->times.tms_cstime; proc_state->start_time = tsk->start_time; #ifdef __SMP__ for (i = 0; i < NR_CPUS; i++) { proc_state->per_cpu_utime [i] = tsk->per_cpu_utime [i]; proc_state->per_cpu_stime [i] = tsk->per_cpu_stime [i]; } #endif #if 0 proc_state->policy = tsk->policy; proc_state->rt_priority = tsk->rt_priority; proc_state->it_real_value = tsk->it_real_value; proc_state->it_prof_value = tsk->it_prof_value; proc_state->it_virt_value = tsk->it_virt_value; proc_state->it_real_incr = tsk->it_real_incr; proc_state->it_prof_incr = tsk->it_prof_incr; proc_state->it_virt_incr = tsk->it_virt_incr; #endif proc_state->min_flt = tsk->min_flt; proc_state->cmin_flt = tsk->cmin_flt; proc_state->maj_flt = tsk->maj_flt; proc_state->cmaj_flt = tsk->cmaj_flt; proc_state->nswap = tsk->nswap; proc_state->cnswap = tsk->cnswap; proc_state->kesp = KSTK_ESP(tsk); proc_state->keip = KSTK_EIP(tsk); break; default: return -EINVAL; } return 0; } static int system_ctl_handler (ctl_table *table, int *name, int nlen, void *oldval, size_t *oldlenp, void *newval, size_t newlen, void **context) { int ret, len, len_name; if (!table->data || !table->maxlen) return -ENOTDIR; if (!oldval || !oldlenp || get_user(len, oldlenp)) return -EFAULT; if (!name || !nlen || get_user(len_name, name)) return -EFAULT; if (len != table->maxlen) return -EFAULT; ret = libgtop_sysctl (table, nlen, name); if (ret) return ret; if(copy_to_user(oldval, table->data, len)) return -EFAULT; return 1; } static int proc_ctl_handler (ctl_table *table, int *name, int nlen, void *oldval, size_t *oldlenp, void *newval, size_t newlen, void **context) { struct task_struct *tsk = NULL; int ret, len, len_name; if (!table->data || !table->maxlen) return -ENOTDIR; if (!oldval || !oldlenp || get_user(len, oldlenp)) return -EFAULT; if (!name || !nlen || get_user(len_name, name)) return -EFAULT; if (len != table->maxlen) return -EFAULT; if (nlen != 2) return -EFAULT; read_lock (&tasklist_lock); tsk = find_task_by_pid (name [1]); /* FIXME!! This should be done after the last use */ read_unlock(&tasklist_lock); if (tsk == NULL) return -ESRCH; ret = libgtop_sysctl_proc (table, nlen, name, tsk); if (ret) return ret; if(copy_to_user(oldval, table->data, len)) return -EFAULT; return 1; }