Passing Arguments wrongly? C Question - c

When my TimerExpire function is finally called when the timer ticks out, it prints out gibberish. Anyone know why? But my printk function in IOCTL_MAKE_TIMER prints out correctly, so I think it's because I'm passing in the data wrong.
setup_timer() works by setting up the timer in the first argument, telling it to call the function specified by the second argument, and passes the data (which is the third argument), to that function.
In my case, it is calling the TimerExpire(char* data) function, passing to it final_arg, which is a char* to kern_arg. I even tried passing kern_arg directly to the function... also gave me gibberish.
Previously (yesterday), I had char* kern_arg, instead of char kern_arg[], and that worked out perfectly, but I think it was unsafe.
If anyone could provide some insight, that would be amazing! Thanks!
//Necessary Includes For Device Drivers.
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/proc_fs.h>
#include <asm/uaccess.h>
#include <linux/timer.h>
#include <linux/ioctl.h>
#define DEVICE_NAME "mytimer"
#define DEVICE_FILE_NAME "mytimer"
#define MAJOR_NUM 61
#define MINOR_NUM 0
#define SUCCESS 0
#define IOCTL_MAKE_TIMER _IOWR(MAJOR_NUM, 0, int)
#define IOCTL_SET_TIMER _IOWR(MAJOR_NUM, 1, int)
#define IOCTL_GET_TIMER _IOWR(MAJOR_NUM, 2, int)
//Module License
MODULE_LICENSE("Dual BSD/GPL");
//Initialize timer structure.
static struct timer_list my_timer;
//Forward Declarations for File Operation Functions and Other Functions.
static int mytimer_open(struct inode *inode, struct file *file);
static int mytimer_release(struct inode *inode, struct file *file);
int mytimer_ioctl(struct inode *inode, struct file *file, unsigned int ioctl_num, unsigned long args);
void TimerExpire(char* data);
//Syscall Operations for the module.
struct file_operations FileOps =
{
.owner = THIS_MODULE,
.open = mytimer_open,
.release = mytimer_release,
.ioctl = mytimer_ioctl
};
//Syscall function for opening the module.
static int mytimer_open(struct inode *inode, struct file *file)
{
try_module_get(THIS_MODULE);
return SUCCESS;
}
//Syscall function for releasing the module.
static int mytimer_release(struct inode *inode, struct file *file)
{
module_put(THIS_MODULE);
return SUCCESS;
}
//Syscall function for controlling the module through IOCTLs.
int mytimer_ioctl(struct inode *inode, struct file *file, unsigned int fcn, unsigned long args)
{
//Copies the function parameters from userspace to kernel space in order to use them in the kernel module.
char* user_arg = args;
char kern_arg[strlen_user(user_arg)];
copy_from_user(kern_arg, user_arg, strlen_user(user_arg));
char* final_arg = kern_arg;
//If there is a timer, and the command is to make a new one, the old timer will be removed so a new one can be setup.
if (timer_pending(&my_timer) && fcn == IOCTL_MAKE_TIMER)
{
del_timer_sync(&my_timer);
printk("Timer already exists. Deleting old timer and setting new timer.\n");
}
//Switch function that serves the function that is called.
//Note that the make and set timer functions are separate. This is because only 1 arg is passed via ioctl at a time, so I had to make two different ioctl calls.
switch (fcn)
{
//Make a new timer.
case IOCTL_MAKE_TIMER:
setup_timer(&my_timer, TimerExpire, final_arg);
printk("Made timer with message: %s\n", final_arg);
break;
//Set the timer made above.
case IOCTL_SET_TIMER:
mod_timer(&my_timer, jiffies + msecs_to_jiffies(args * 1000));
printk("Armed timer for %d seconds.\n", args);
break;
//Print the current timer, if any.
case IOCTL_GET_TIMER:
if (!timer_pending(&my_timer))
{
printk("No timer currently set.\n");
}
else
{
printk("Time left in timer: %u seconds\n", jiffies_to_msecs(my_timer.expires - jiffies)/1000);
printk("Message in timer is: %s\n", my_timer.data);
}
break;
}
return SUCCESS;
}
//Function to perform when timer expires.
void TimerExpire(char* data)
{
printk("%s\n", data);
}
//Module Init and Exit Functions.
int init_module(void)
{
printk("Loading MyTimer Kernel Module...\n");
//Register the device with the system to obtain the major number and register the file operations for syscall functionality.
int initResult = register_chrdev(MAJOR_NUM, "mytimer", &FileOps);
//If we couldn't register the device, print the error.
if (initResult < 0)
{
printk("Cannot obtain major number %d\n", MAJOR_NUM);
return initResult;
}
printk("Please create device file using:\n\tmknod /dev/mytimer c 61 0\n");
return SUCCESS;
}
void cleanup_module(void)
{
//Unregister the device with the system to free the major number.
printk("Unloading MyTimer Kernel Module...\n");
unregister_chrdev(MAJOR_NUM, "mytimer");
printk("MyTimer Kernel Module Unloaded.\n");
}

In this code, a call to ioctl(fd,IOCTL_MAKE_TIMER,...) passes setup_timer() a pointer to an array located on the kernel stack, then returns. By the point that the timer expires, the memory that used to hold that array has probably been reused.
You need to keep the memory around until after the timer expires. You could do this by allocating a buffer on the kernel heap (e.g. kmalloc()) or using static/global data.

Related

How to properly disable SMAP from a linux module?

I am following a tutorial from here.
I have the following code:
#include <linux/init.h> // Macros used to mark up functions e.g. __init __exit
#include <linux/module.h> // Core header for loading LKMs into the kernel
#include <linux/device.h> // Header to support the kernel Driver Model
#include <linux/kernel.h> // Contains types, macros, functions for the kernel
#include <linux/fs.h> // Header for the Linux file system support
#include <linux/uaccess.h> // Required for the copy to user function
#define DEVICE_NAME "ebbchar" ///< The device will appear at /dev/ebbchar using this value
#define CLASS_NAME "ebb" ///< The device class -- this is a character device driver
MODULE_LICENSE("GPL"); ///< The license type -- this affects available functionality
MODULE_AUTHOR("Derek Molloy"); ///< The author -- visible when you use modinfo
MODULE_DESCRIPTION("A simple Linux char driver for the BBB"); ///< The description -- see modinfo
MODULE_VERSION("0.1"); ///< A version number to inform users
static int majorNumber; ///< Stores the device number -- determined automatically
static char message[256] = {0}; ///< Memory for the string that is passed from userspace
static short size_of_message; ///< Used to remember the size of the string stored
static int numberOpens = 0; ///< Counts the number of times the device is opened
static struct class* ebbcharClass = NULL; ///< The device-driver class struct pointer
static struct device* ebbcharDevice = NULL; ///< The device-driver device struct pointer
static int dev_open(struct inode *, struct file *);
static int dev_release(struct inode *, struct file *);
static ssize_t dev_read(struct file *, char *, size_t, loff_t *);
static ssize_t dev_write(struct file *, const char *, size_t, loff_t *);
static struct file_operations fops =
{
.open = dev_open,
.read = dev_read,
.write = dev_write,
.release = dev_release,
};
static int __init ebbchar_init(void){
printk(KERN_INFO "EBBChar: Initializing the EBBChar LKM\n");
// Try to dynamically allocate a major number for the device -- more difficult but worth it
majorNumber = register_chrdev(0, DEVICE_NAME, &fops);
if (majorNumber<0){
printk(KERN_ALERT "EBBChar failed to register a major number\n");
return majorNumber;
}
printk(KERN_INFO "EBBChar: registered correctly with major number %d\n", majorNumber);
// Register the device class
ebbcharClass = class_create(THIS_MODULE, CLASS_NAME);
if (IS_ERR(ebbcharClass)){ // Check for error and clean up if there is
unregister_chrdev(majorNumber, DEVICE_NAME);
printk(KERN_ALERT "Failed to register device class\n");
return PTR_ERR(ebbcharClass); // Correct way to return an error on a pointer
}
printk(KERN_INFO "EBBChar: device class registered correctly\n");
// Register the device driver
ebbcharDevice = device_create(ebbcharClass, NULL, MKDEV(majorNumber, 0), NULL, DEVICE_NAME);
if (IS_ERR(ebbcharDevice)){ // Clean up if there is an error
class_destroy(ebbcharClass); // Repeated code but the alternative is goto statements
unregister_chrdev(majorNumber, DEVICE_NAME);
printk(KERN_ALERT "Failed to create the device\n");
return PTR_ERR(ebbcharDevice);
}
printk(KERN_INFO "EBBChar: device class created correctly\n"); // Made it! device was initialized
return 0;
}
static void __exit ebbchar_exit(void){
device_destroy(ebbcharClass, MKDEV(majorNumber, 0)); // remove the device
class_unregister(ebbcharClass); // unregister the device class
class_destroy(ebbcharClass); // remove the device class
unregister_chrdev(majorNumber, DEVICE_NAME); // unregister the major number
printk(KERN_INFO "EBBChar: Goodbye from the LKM!\n");
}
static int dev_open(struct inode *inodep, struct file *filep){
numberOpens++;
printk(KERN_INFO "EBBChar: Device has been opened %d time(s)\n", numberOpens);
return 0;
}
static ssize_t dev_read(struct file *filep, char *buffer, size_t len, loff_t *offset){
int error_count = 0;
// copy_to_user has the format ( * to, *from, size) and returns 0 on success
error_count = copy_to_user(buffer, message, size_of_message);
if (error_count==0){ // if true then have success
printk(KERN_INFO "EBBChar: Sent %d characters to the user\n", size_of_message);
return (size_of_message=0); // clear the position to the start and return 0
}
else {
printk(KERN_INFO "EBBChar: Failed to send %d characters to the user\n", error_count);
return -EFAULT; // Failed -- return a bad address message (i.e. -14)
}
}
static ssize_t dev_write(struct file *filep, const char *buffer, size_t len, loff_t *offset){
sprintf(message, "%s(%zu letters)", buffer, len); // appending received string with its length
size_of_message = strlen(message); // store the length of the stored message
printk(KERN_INFO "EBBChar: Received %zu characters from the user\n", len);
return len;
}
static int dev_release(struct inode *inodep, struct file *filep){
printk(KERN_INFO "EBBChar: Device successfully closed\n");
return 0;
}
module_init(ebbchar_init);
module_exit(ebbchar_exit);
I have a small testing file as well from the tutorial. The problem is that when the testing code runs, the process ends up being killed. The logs files say it is due to Supervisor Mode access and that a page fault exception was thrown.
After some research and looking in log files It came down to compatibility problems with Supervisor Mode Access Prevention, where kernel code can't access user code due to the new SMAP feature of some CPUs.
After disabling SMAP at boot time with the nosmap option the testing code works just fine.
I am looking for a way to disable/circumvent SMAP properly in module code. Since this application could run on multiple CPUs, I don't think that changing the CR4 register is the proper way.
I think the copy_to_user() function is a good lead. The problem arises when write is called. Could anyone point to me what is the proper way to code the write() function for this module?
If you are having a problem, disabling SMAP won't solve it, it will only hide it. The fact that SMAP kills your process is good and it should stay that way, it's a security measure of the Linux kernel and it should not be disabled only to make a buggy module work.
Your error is here:
sprintf(message, "%s(%zu letters)", buffer, len);
you are reading user space memory from kernel space, which is wrong, and SMAP prevents this generating a fault.
You should use copy_from_user(), since you are dealing with a user space buffer:
static ssize_t dev_write(struct file *filep, const char __user *buffer, size_t len, loff_t *offset){
unsigned long remaining;
// NEVER copy more than your message buffer size.
if (len > 256)
len = 256;
// Ensure that the additional string fits (XXX because len is at most 3 chars).
if (len + strlen(" (XXX letters)") >= 256) {
pr_info("User buffer is too big (%zu).\n", len);
return -EINVAL;
}
remaining = copy_from_user(message, buffer, len);
if (remaining > 0) {
pr_info("Failed to copy %lu characters from the user.\n", remaining);
return -EFAULT;
}
sprintf(message + len, " (%zu letters)", len);
size_of_message = len + strlen(message + len);
pr_info("Received %zu characters from the user.\n", len);
return len;
}
A few other tips:
error_count should be an unsigned long instead of an int since copy_to_user() returns that type.
Your dev_read() and dev_write() functions take pointers from user space. Any time a kernel function takes a pointer that comes from user space, that pointer should be declared using the __user annotation, like I did in the function above.
You can use the macro pr_info() instead of printk(KERN_INFO ...), also like I did above.
You can avoid writing the module name (EBBChar:) every single time at the beginning of each line simply by re-defining the pr_fmt macro like this:
// This will make any pr_* function (except pr_cont) prepend the module name to each message.
// KBUILD_MODNAME is automatically generated when building and is your module name.
// Put this at the top of your module.
#ifdef pr_fmt
#undef pr_fmt
#endif
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt

I need help in understanding the following kernel module written in C [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 7 years ago.
Improve this question
As an assignment I need to complete the following C code in order to produce a kernel module able to act as a memory, but from how it's written I can't understand how it works and why many variables are not used but just declared. I have already tried looking on the teaching material they gave me, and it's even more confusing, plus I can't find on the web a good site where to find documentation about these functions.
The code is the following:
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <asm/uaccess.h>
#define DEVICE_NAME "my_device"
#define MAJOR_DEVICE_NUMBER 60
#define MINOR_DEVICE_NUMBER 0
#define BUF_LEN 1024
static char msg[BUF_LEN];
static char *msg_ptr; // I'm pretty sure this should become msg_reading_offset
static int major;
MODULE_AUTHOR("<YOUR NAME>");
MODULE_LICENSE("GPL");
static ssize_t my_read (
struct file *filp, char __user *buf,
size_t length, loff_t *offset);
static ssize_t my_write (
struct file *filp, const char __user *buf,
size_t length, loff_t *offset);
static int my_open (struct inode *inode,
struct file *filp);
static int my_close (struct inode *inode,
struct file *filp);
static int __init my_init (void);
static void __exit my_cleanup (void);
static struct file_operations fops = {
.read = my_read,
.write = my_write,
.open = my_open,
.release = my_close,
};
// I need to implement this function
static int my_open (struct inode *inode,
struct file *filp)
{
return 0;
}
// and this function
static int my_close (struct inode *inode,
struct file *filp)
{
return 0;
}
static ssize_t my_read (
struct file *filp, char __user *buf,
size_t length, loff_t *offset)
{
int nc = 0;
// if no more "valid" bytes can be read, stop
if (*msg_reading_offset == 0) return 0;
// no-negative values allowed
if (length < 0)
return -EINVAL;
// read the whole msg, nothing more
if (length > strlen(msg)) {
length = strlen(msg);
}
nc = copy_to_user(buf, msg_reading_offset, length);
/*
updates the current reading offset pointer so that a
recursive call due to not original
full length will get a 0 (nothing to read)
*/
msg_reading_offset += sizeof(char) * (length-nc);
// returns the number of REAL bytes read.
return length - nc;
}
static ssize_t my_write (
struct file *filp, const char __user *buf,
size_t length, loff_t *offset)
{
int nc = 0;
if (length > BUF_LEN)
return BUF_LEN-length;
nc = copy_from_user(msg,buf,length);
msg_ptr = msg;
return length - nc;
}
static int __init my_init (void)
{
register_chrdev (MAJOR_DEVICE_NUMBER,
DEVICE_NAME,
&fops);
}
module_init(my_init);
static void __exit my_cleanup (void)
{
unregister_chrdev (major, DEVICE_NAME);
}
module_exit(my_cleanup);
At the moment these are my biggest problems:
Where are all the *inode, *filp variables going? Am I supposed to use them?
How is this program even working? I know I need to compile it with a makefile I've been give, but then how am I supposed to access these functions?
Is this supposed to be a real program executed by the kernel or is it just a collections of functions I should use in another C program?
I'm sorry if the questions may seem stupid, but I am at a loss to know how the hell I'm supposed to approach this.
Your Q is a bit broad, but I'll try to give you some hints.
Where are all the *inode, *filp variables going? Am I supposed to use them?
First read an example of how a typical character device is implemented, eg, here.
How is this program even working? I know I need to compile it with a makefile I've been give, but then how am I supposed to access these functions?
Is this supposed to be a real program executed by the kernel or is it just a collections of functions I should use in another C program?
This is NOT a normal executable program. As you are writing a kernel module, you are extending the kernel functionalities. You need to tell the kernel about that, normally via an insmod call. Eg.,
insmod chardev.ko
Then create the corresponding character device:
mknod /dev/chardev c 60 0 # 60 being your MAJOR_DEVICE_NUMBER
Then you can create your own program to read and write to your character device. Alternatively, you can use existing user-space tools:
echo "12345678" > /dev/chardev # write to the device
and,
cat /dev/chardev # read from the device

Why does printk() work only in the init/exit method of a kernel module? (Priority should be fine)

My goal is to write a kernel-module. I am following the memory tutorial of the freesoftware magazine.
The tutorial works fine. I am able to compile the code. When loaded with insmod, the kernel prints <1>Inserting memory module as expected. When I remove the module using rmmod the kernel prints <1>Removing memory module.
For debugging purposes, I am trying to add printk() to the other methods. But they are never printed.
The priority of all the messages is <1>.
I write into the device by: echo -n test1234 > /dev/memory
And use cat /dev/memory to get back the data.
cat /var/log/messages and dmesg donĀ“t print anymore information
[ 5550.651221] <1>Inserting memory module
[ 5550.655396] <1>Inserting memory module !!!!!!!!!!!!!!!
[12230.130847] <1>Removing memory module
cat /proc/sys/kernel/printk
7 4 1 7
uname- a
Linux generic-armv7a-hf 3.14.0-163850-g775a3df-dirty #2 SMP Mon Jan 12 13:53:50 CET 2015 armv7l GNU/Linux
Why does printk() only work in the init and exit method?
Is there any (better) way to print variable values than printk()?
Here the code:
/* Necessary includes for device drivers */
#include <linux/init.h>
//#include <linux/config.h>
#include <linux/module.h>
#include <linux/kernel.h> /* printk() */
#include <linux/slab.h> /* kmalloc() */
#include <linux/fs.h> /* everything... */
#include <linux/errno.h> /* error codes */
#include <linux/types.h> /* size_t */
#include <linux/proc_fs.h>
#include <linux/fcntl.h> /* O_ACCMODE */
#include <asm/system.h> /* cli(), *_flags */
#include <asm/uaccess.h> /* copy_from/to_user */
MODULE_LICENSE("Dual BSD/GPL");
/* Declaration of memory.c functions */
int memory_open(struct inode *inode, struct file *filp);
int memory_release(struct inode *inode, struct file *filp);
ssize_t memory_read(struct file *filp, char *buf, size_t count, loff_t *f_pos);
ssize_t memory_write(struct file *filp, char *buf, size_t count, loff_t *f_pos);
void memory_exit(void);
int memory_init(void);
/* Structure that declares the usual file */
/* access functions */
struct file_operations memory_fops = {
read: memory_read,
write: memory_write,
open: memory_open,
release: memory_release
};
/* Declaration of the init and exit functions */
module_init(memory_init);
module_exit(memory_exit);
/* Global variables of the driver */
/* Major number */
int memory_major = 60;
/* Buffer to store data */
char *memory_buffer;
int memory_init(void) {
int result;
/* Registering device */
result = register_chrdev(memory_major, "memory", &memory_fops);
if (result < 0) {
printk(
"<1>memory: cannot obtain major number %d\n", memory_major);
return result;
}
/* Allocating memory for the buffer */
memory_buffer = kmalloc(1, GFP_KERNEL);
if (!memory_buffer) {
result = -ENOMEM;
goto fail;
}
memset(memory_buffer, 0, 1);
printk("<1>Inserting memory module\n"); ///this works fine
printk("<1>Inserting memory module !!!!!!!!!!!!!!!\n"); ///this works fine too
return 0;
fail:
memory_exit();
return result;
}
void memory_exit(void) {
/* Freeing the major number */
unregister_chrdev(memory_major, "memory");
/* Freeing buffer memory */
if (memory_buffer) {
kfree(memory_buffer);
}
printk("<1>Removing memory module\n"); //never printed
}
int memory_open(struct inode *inode, struct file *filp) {
printk("<1>memory open\n"); //never printed
/* Success */
return 0;
}
int memory_release(struct inode *inode, struct file *filp) {
printk("<1>memory_release\n"); //never printed
/* Success */
return 0;
}
ssize_t memory_read(struct file *filp, char *buf,
size_t count, loff_t *f_pos) {
printk("<1>mem read\n"); //never printed
/* Transfering data to user space */
copy_to_user(buf,memory_buffer,1);
/* Changing reading position as best suits */
if (*f_pos == 0) {
*f_pos+=1;
return 1;
} else {
return 0;
}
}
ssize_t memory_write( struct file *filp, char *buf,
size_t count, loff_t *f_pos) {
printk("<1>mem write\n"); //never printed
char *tmp;
tmp=buf+count-1;
copy_from_user(memory_buffer,tmp,1);
return 1;
}
Your driver seems fine, but you aren't actually talking to it with your test commands, so the functions with printks aren't being called. Once the module is loaded, it registers a major and minor number, 60 and 0 in your case. (Down the road you should update the module to request an available major number instead of using a hard-coded one.)
You need to create a file system node with mknod in order to actually use the driver. This will create the /dev/memory node and connect it to the module you have loaded. Then when it is opened, closed, read from, or written to, the file_operations in your module will be called, and the printks will work.
For your module, you should be able to use
mknod /dev/memory c 60 0
You can also chmod 666 /dev/memory to allow any user to use the device, rather than running as root all the time.
Here's a script based on the one I use with modules I develop:
#!/bin/sh
device="memory"
mode="666"
major=$(awk "\$2==\"$device\" {print \$1}" /proc/devices}
mknod /dev/${device} c $major 0
chmod $mode /dev/${device}
It will look up the major number associated with your module and create a file system node for it automatically.
Once you have loaded the module and run mknod or the script, you should be able to use the driver. You will know that it is working becase cat will only return the last character written to the device - your driver only has a one character buffer, and it is automatically overwritten each time a new character comes in. Then dmesg should show the printk's associated with the functions in your module.
The reason your driver seemed to work is because you were creating a regular file with your echo command, which cat happily printed right back to you. It's the same thing that would have happened if you ran those commands on a file in your home directory, you just happened to be in /dev instead.

FreeBSD kldload: can't load, No such file or directory

I am new to kernel and KLD programming. I am looking to modify the example file in FreeBSD for system call module. My question is, is it possible to fork or exec inside system call function? Like in the following example?
#include <sys/types.h>
#include <sys/param.h>
#include <sys/proc.h>
#include <sys/module.h>
#include <sys/sysent.h>
#include <sys/kernel.h>
#include <sys/systm.h>
/*
* The function for implementing the syscall.
*/
static int hello (struct thread *td, void *arg)
{
printf("Running...\n");
/******************************************************/
/*Something like this?*/
/******************************************************/
execl("/bin/pwd", "pwd", NULL);
return 0;
}
/*
* The `sysent' for the new syscall
*/
static struct sysent hello_sysent = {
0, /* sy_narg */
hello /* sy_call */
};
/*
* The offset in sysent where the syscall is allocated.
*/
static int offset = NO_SYSCALL;
/*
* The function called at load/unload.
*/
static int
load (struct module *module, int cmd, void *arg)
{
int error = 0;
switch (cmd) {
case MOD_LOAD :
uprintf ("syscall loaded at %d\n", offset);
break;
case MOD_UNLOAD :
uprintf ("syscall unloaded from %d\n", offset);
break;
default :
uprintf("There was some error!");
error = EINVAL;
break;
}
return error;
}
SYSCALL_MODULE(syscall, &offset, &hello_sysent, load, NULL);
There is no compilation error (syscall), but while loading it using kldload, it returns an error:
kldload: can't load ./syscall.ko: No such file or directory
Is there something I can read and know more about why is this happening and what can I do about it?
When kldload returns "No such file or directory", or some other weird error, first do "dmesg" and look for any errors at the bottom. In this case it's probably due to a missing symbol "execl". That's because execl is a userspace API (man 3 execl), and you're trying to use it in kernel.
What you're trying to do doesn't seem to be a good idea, but it's possible. Look at sys/kern/kern_exec.c:kern_execve().

Using system calls from C, how do I get the utilization of the CPU(s)?

In C on FreeBSD, how does one access the CPU utilization?
I am writing some code to handle HTTP redirects. If the CPU load goes above a threshold on a FReeBSD system, I want to redirect client requests. Looking over the man pages, kvm_getpcpu() seems to be the right answer, but the man pages (that I read) don't document the usage.
Any tips or pointers would be welcome - thanks!
After reading the answers here, I was able to come up with the below. Due to the poor documentation, I'm not 100% sure it is correct, but top seems to agree. Thanks to everyone who answered.
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/sysctl.h>
#include <unistd.h>
#define CP_USER 0
#define CP_NICE 1
#define CP_SYS 2
#define CP_INTR 3
#define CP_IDLE 4
#define CPUSTATES 5
int main()
{
long cur[CPUSTATES], last[CPUSTATES];
size_t cur_sz = sizeof cur;
int state, i;
long sum;
double util;
memset(last, 0, sizeof last);
for (i=0; i<6; i++)
{
if (sysctlbyname("kern.cp_time", &cur, &cur_sz, NULL, 0) < 0)
{
printf ("Error reading kern.cp_times sysctl\n");
return -1;
}
sum = 0;
for (state = 0; state<CPUSTATES; state++)
{
long tmp = cur[state];
cur[state] -= last[state];
last[state] = tmp;
sum += cur[state];
}
util = 100.0L - (100.0L * cur[CP_IDLE] / (sum ? (double) sum : 1.0L));
printf("cpu utilization: %7.3f\n", util);
sleep(1);
}
return 0;
}
From the MAN pages
NAME
kvm_getmaxcpu, kvm_getpcpu -- access per-CPU data
LIBRARY
Kernel Data Access Library (libkvm, -lkvm)
SYNOPSIS
#include <sys/param.h>
#include <sys/pcpu.h>
#include <sys/sysctl.h>
#include <kvm.h>
int
kvm_getmaxcpu(kvm_t *kd);
void *
kvm_getpcpu(kvm_t *kd, int cpu);
DESCRIPTION
The kvm_getmaxcpu() and kvm_getpcpu() functions are used to access the
per-CPU data of active processors in the kernel indicated by kd. The
kvm_getmaxcpu() function returns the maximum number of CPUs supported by
the kernel. The kvm_getpcpu() function returns a buffer holding the per-
CPU data for a single CPU. This buffer is described by the struct pcpu
type. The caller is responsible for releasing the buffer via a call to
free(3) when it is no longer needed. If cpu is not active, then NULL is
returned instead.
CACHING
These functions cache the nlist values for various kernel variables which
are reused in successive calls. You may call either function with kd set
to NULL to clear this cache.
RETURN VALUES
On success, the kvm_getmaxcpu() function returns the maximum number of
CPUs supported by the kernel. If an error occurs, it returns -1 instead.
On success, the kvm_getpcpu() function returns a pointer to an allocated
buffer or NULL. If an error occurs, it returns -1 instead.
If either function encounters an error, then an error message may be
retrieved via kvm_geterr(3.)
EDIT
Here's the kvm_t struct:
struct __kvm {
/*
* a string to be prepended to error messages
* provided for compatibility with sun's interface
* if this value is null, errors are saved in errbuf[]
*/
const char *program;
char *errp; /* XXX this can probably go away */
char errbuf[_POSIX2_LINE_MAX];
#define ISALIVE(kd) ((kd)->vmfd >= 0)
int pmfd; /* physical memory file (or crashdump) */
int vmfd; /* virtual memory file (-1 if crashdump) */
int unused; /* was: swap file (e.g., /dev/drum) */
int nlfd; /* namelist file (e.g., /kernel) */
struct kinfo_proc *procbase;
char *argspc; /* (dynamic) storage for argv strings */
int arglen; /* length of the above */
char **argv; /* (dynamic) storage for argv pointers */
int argc; /* length of above (not actual # present) */
char *argbuf; /* (dynamic) temporary storage */
/*
* Kernel virtual address translation state. This only gets filled
* in for dead kernels; otherwise, the running kernel (i.e. kmem)
* will do the translations for us. It could be big, so we
* only allocate it if necessary.
*/
struct vmstate *vmst;
};
I believe you want to look into 'man sysctl'.
I don't know the exact library, command, or system call; however, if you really get stuck, download the source code to top. It displays per-cpu stats when you use the "-P" flag, and it has to get that information from somewhere.

Resources