I am writing a kernel driver to send/receive data with a PCI Express device. For this first version of the driver I am creating a character device interface where the user can read data using a file.
Background
I want to implement a blocking read where the user requests data and the driver populates a user buffer. In order to block the user's read call, I am using a completion structure.
When the driver is loaded and the user requests a read the driver blocks as expected. If I were to finish the read then everything runs fine.
The problem
In order to be safe, whenever the module is removed I call the complete_all function, just in case someone removes the module or device in the middle of a read transaction.
Neither the remove or exit function is called and both the module and user application is blocked. I've tried the following three functions (shown with their associated result).
wait_completion(&dev->read_complete); //Blocks indefinitely, I need to reset the computer
retval = wait_for_completion_interruptible(&dev->read_complete); //I can kill the user application manually and then remove the driver
retval = wait_for_completion_killable(&dev->read_complete); //Same as interruptible
My expectation is that when the remove function is called I can call complete_all(&dev->read_complete) and the read function will return an error.
In order to remove external factors I've made a repo on github, so if anyone wants to see the behavior for themselves they just need to clone and follow the instructions:
Kernel Module Completion Test
The relevant parts of the module are here (/src/mymodule.c)
typedef struct {
struct cdev cdv;
struct class *cls;
struct device *dev;
struct completion complete;
} mymodule_t;
mymodule_t mymod;
//Sysfs 'mymodule_test' attribute (all but the actual function is left out for brevity)
static ssize_t mymodule_test_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
int retval = 0;
int value = 0;
if (sscanf(buf, "%d", &value) == 1)
{
retval = strlen(buf);
}
if (value)
{
printk("Value is: %d\n", value);
if (!completion_done(&mymod.complete))
{
complete(&mymod.complete);
}
printk("Sent Completion\n");
}
return retval;
}
//FOPS (all but 'read' function is left out for brevity)
ssize_t mymodule_read(struct file *filp, char * buf, size_t count, loff_t *f_pos)
{
printk("Read!\n");
if (completion_done(&mymod.complete))
{
reinit_completion(&mymod.complete);
}
printk("Wait for Completion\n");
wait_for_completion_interruptible(&mymod.complete);
printk("After Completion\n");
return 0;
}
static int __init mymodule_init(void)
{
...
//Register class and device
//Configure character driver with fops
init_completion(&mymod.complete);
...
}
static void __exit mymodule_exit(void)
{
...
if (!completion_done(&mymod.complete))
{
printk("Send a completion!\n");
complete(&mymod.complete);
}
//Clean up the rest of the module
...
}
module_init(mymodule_init);
module_exit(mymodule_exit);
Here is the userland application I use to exercise this:
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <termios.h>
#include "mymodule.h"
#define FILEPATH "/dev/mymodule0"
#define TEST_SIZE 10
int main(void)
{
int fn = -1;
char buf[TEST_SIZE];
printf("Attempting to open file module file...\n");
fn = open(FILEPATH, O_RDWR);
if (fn < 0)
{
printf("Failed to open file!\n");
return -1;
}
printf("Attempting to read from the file...\n");
read(fn, &buf, TEST_SIZE);
printf("Finished reading from file\n");
return 0;
}
Here is the dmesg output when I
load the module
run the user application (it opens the file, attempts to read 10 characters, then exits)
write '1' to the sysfs attribute
unload the module
[3217633.993937] Registering Driver
[3217633.993995] Driver Initialized!
[3217643.747791] Opened!
[3217643.747800] Read!
[3217643.747801] Wait for Completion
[3217646.436780] Value is: 1
[3217646.436792] Sent Completion
[3217646.436806] After Completion
[3217646.437010] Closed!
[3217727.378388] Cleanup Module
[3217727.378393] Check if we need to complete anything
[3217727.378395] Send a completion!
[3217727.378397] Unregistering Character Driver
[3217727.378400] Give back all the numbers we requested
[3217727.378402] Remove the class driver
[3217727.378571] Release the class
[3217727.378593] Finished Cleanup Module, Exiting
If I run the following commands:
load the module
run the user application
unload the module
[3218223.442777] Registering Driver
[3218223.442934] Driver Initialized!
[3218229.378396] Opened!
[3218229.378419] Read!
[3218229.378422] Wait for Completion
then the module doesn't unload. If this were a real device, like a USB hard drive, it is possible that the user could remove the device in the middle of a read transaction. It seems like something is wrong, or perhaps I'm missing something. Am I missing something?
While USB device can be removed at any time, its driver (e.g. kernel module) cannot be unloaded during certain operations with that device (e.g. reading). It is driver who reports to the upper level(e.g. filesystem) about absence of device.
Related
I have recently be reading up on how to write Linux Device Drivers and came across a Device Driver which basically "handles" IRQ1 (a keyboard event). The code itself is shown below:
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/interrupt.h>
#include <asm/io.h>
/* This function services keyboard interrupts */
irq_handler_t irq_handler (int irq, void *dev_id, struct pt_regs *regs) {
static unsigned char scancode;
/* Read keyboard status */
scancode = inb (0x60);
if ((scancode == 0x01) || (scancode == 0x81)) {
printk("You pressed Esc !\n");
}
return (irq_handler_t) IRQ_HANDLED;
}
/* Initialize the module and Register the IRQ handler */
static int __init keybrd_int_register(void) {
int result;
/* Request IRQ 1, the keyboard IRQ */
result = request_irq (
1,
(irq_handler_t) irq_handler,
IRQF_SHARED,
"keyboard_stats_irq",
(void *)(irq_handler)
);
if (result) {
printk(KERN_INFO "can't get shared interrupt for keyboard\n");
}
return result;
}
/* Remove the interrupt handler */
static void __exit keybrd_int_unregister(void) {
free_irq(1, (void *)(irq_handler)); /* can't pass NULL, this is a shared interrupt handler! */
}
MODULE_LICENSE ("GPL");
module_init(keybrd_int_register);
module_exit(keybrd_int_unregister);
I would like to understand whether this Device Driver is considered an "Interrupt Service Routine"? Correct me if I'm wrong, but the flow of events is as follows:
Press key on Keyboard
Keyboard Triggers IRQ1
Operating System looks up ALL the Interrupt Service Routines "registered" to IRQ1 (which includes the Device Driver we wrote above) and triggers the code in these Interrupt Service Routines (including the code in Device Driver above).
Does this mean tat the Device Driver IS the Interrupt Service Routine?
A follow up question would be, how would you actually write a real Keyboard Driver (not one that just naively prints out some value)? How do you tell the Operating System that a particular key was pressed and that it should inform the current running application that this key was pressed? What does the C code look like for this?
Hi i would like to know how is it possible to call/run the following function from user space.
static ssize_t lm70_sense_temp(struct device *dev,
struct device_attribute *attr, char *buf)
{
//some code
.
.
status = sprintf(buf, "%d\n", val); /* millidegrees Celsius */
.
.
//some code
}
This function is defined in lm70.c driver located in the kernel/drivers/hwmon folder of the linux source? Is it possible to pass the values of this functions internal variables to the user application? I would like to retrieve the value of val variable in the above function...
I don't know well the kernel internals. However, I grepped for lm70_sense_temp in the entire kernel source tree, and it appears only in the file linux-3.7.1/drivers/hwmon/lm70.c, first as a static function, then as the argument to DEVICE_ATTR.
Then I googled for linux kernel DEVICE_ATTR and found immediately device.txt which shows that you probably should read that thru the sysfs, i.e. under /sys; read sysfs-rules.txt; so a user application could very probably read something relevant under /sys/
I'm downvoting your question because I feel that you could have searched a few minutes like I did (and I am not a kernel expert).
You don't need to call this function from user space to get that value - it is already exported to you via sysfs.
You could use grep to find which hwmon device it is:
grep -rl "lm70" /sys/class/hwmon/*/name /sys/class/hwmon/*/*/name
Then you can read the temperature input from your user space program, e.g:
#include <stdio.h>
#include <fcntl.h>
#define SENSOR_FILE "/sys/class/hwmon/hwmon0/temp1_input"
int readSensor(void)
{
int fd, val = -1;
char buf[32];
fd = open(SENSOR_FILE, O_RDONLY);
if (fd < 0) {
printf("Failed to open %s\n", SENSOR_FILE);
return val;
}
if (read(fd, &buf, sizeof(buf)) > 0) {
val = atoi(buf);
printf("Sensor value = %d\n", val);
} else {
printf("Failed to read %s\n", SENSOR_FILE);
}
close(fd);
return val;
}
As others have already stated - you can't call kernel code from user space, thems the breaks.
You cannot call a driver function directly from user space.
If that function is exported with EXPORT_SYMBOL or EXPORT_SYMBOL_GPL then we can write a simple kernel module and call that function directly. The result can be sent to user space through FIFO or shared memory.
But in your case, this function is not exported. so you should not do in this way.
I have created an eventfd instance in a userspace program using eventfd(). Is there a way in which I can pass some reference (a pointer to its struct or pid+fd pair) to this created instance of eventfd to a kernel module so that it can update the counter value?
Here is what I want to do:
I am developing a userspace program which needs to exchange data and signals with a kernel space module which I have written.
For transferring data, I am already using ioctl. But I want the kernel module to be able to signal the userspace program whenever new data is ready for it to consume over ioctl.
To do this, my userspace program will create a few eventfds in various threads. These threads will wait on these eventfds using select() and whenever the kernel module updates the counts on these eventfds, they will go on to consume the data by requesting for it over ioctl.
The problem is, how do I resolve the "struct file *" pointers to these eventfds from kernelspace? What kind of information bout the eventfds can I sent to kernel modules so that it can get the pointers to the eventfds? what functions would I use in the kernel module to get those pointers?
Is there better way to signal events to userspace from kernelspace?
I cannot let go of using select().
I finally figured out how to do this. I realized that each open file on a system could be identified by the pid of one of the processes which opened it and the fd corresponding to that file (within that process's context). So if my kernel module knows the pid and fd, it can look up the struct * task_struct of the process and from that the struct * files and finally using the fd, it can acquire the pointer to the eventfd's struct * file. Then, using this last pointer, it can write to the eventfd's counter.
Here are the codes for the userspace program and the kernel module that I wrote up to demonstrate the concept (which now work):
Userspace C code (efd_us.c):
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h> //Definition of uint64_t
#include <sys/eventfd.h>
int efd; //Eventfd file descriptor
uint64_t eftd_ctr;
int retval; //for select()
fd_set rfds; //for select()
int s;
int main() {
//Create eventfd
efd = eventfd(0,0);
if (efd == -1){
printf("\nUnable to create eventfd! Exiting...\n");
exit(EXIT_FAILURE);
}
printf("\nefd=%d pid=%d",efd,getpid());
//Watch efd
FD_ZERO(&rfds);
FD_SET(efd, &rfds);
printf("\nNow waiting on select()...");
fflush(stdout);
retval = select(efd+1, &rfds, NULL, NULL, NULL);
if (retval == -1){
printf("\nselect() error. Exiting...");
exit(EXIT_FAILURE);
} else if (retval > 0) {
printf("\nselect() says data is available now. Exiting...");
printf("\nreturned from select(), now executing read()...");
s = read(efd, &eftd_ctr, sizeof(uint64_t));
if (s != sizeof(uint64_t)){
printf("\neventfd read error. Exiting...");
} else {
printf("\nReturned from read(), value read = %lld",eftd_ctr);
}
} else if (retval == 0) {
printf("\nselect() says that no data was available");
}
printf("\nClosing eventfd. Exiting...");
close(efd);
printf("\n");
exit(EXIT_SUCCESS);
}
Kernel Module C code (efd_lkm.c):
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/pid.h>
#include <linux/sched.h>
#include <linux/fdtable.h>
#include <linux/rcupdate.h>
#include <linux/eventfd.h>
//Received from userspace. Process ID and eventfd's File descriptor are enough to uniquely identify an eventfd object.
int pid;
int efd;
//Resolved references...
struct task_struct * userspace_task = NULL; //...to userspace program's task struct
struct file * efd_file = NULL; //...to eventfd's file struct
struct eventfd_ctx * efd_ctx = NULL; //...and finally to eventfd context
//Increment Counter by 1
static uint64_t plus_one = 1;
int init_module(void) {
printk(KERN_ALERT "~~~Received from userspace: pid=%d efd=%d\n",pid,efd);
userspace_task = pid_task(find_vpid(pid), PIDTYPE_PID);
printk(KERN_ALERT "~~~Resolved pointer to the userspace program's task struct: %p\n",userspace_task);
printk(KERN_ALERT "~~~Resolved pointer to the userspace program's files struct: %p\n",userspace_task->files);
rcu_read_lock();
efd_file = fcheck_files(userspace_task->files, efd);
rcu_read_unlock();
printk(KERN_ALERT "~~~Resolved pointer to the userspace program's eventfd's file struct: %p\n",efd_file);
efd_ctx = eventfd_ctx_fileget(efd_file);
if (!efd_ctx) {
printk(KERN_ALERT "~~~eventfd_ctx_fileget() Jhol, Bye.\n");
return -1;
}
printk(KERN_ALERT "~~~Resolved pointer to the userspace program's eventfd's context: %p\n",efd_ctx);
eventfd_signal(efd_ctx, plus_one);
printk(KERN_ALERT "~~~Incremented userspace program's eventfd's counter by 1\n");
eventfd_ctx_put(efd_ctx);
return 0;
}
void cleanup_module(void) {
printk(KERN_ALERT "~~~Module Exiting...\n");
}
MODULE_LICENSE("GPL");
module_param(pid, int, 0);
module_param(efd, int, 0);
To run this, carry out the following steps:
Compile the userspace program (efd_us.out) and the kernel module (efd_lkm.ko)
Run the userspace program (./efd_us.out) and note the pid and efd values that it print. (for eg. "pid=2803 efd=3". The userspace program will wait endlessly on select()
Open a new terminal window and insert the kernel module passing the pid and efd as params: sudo insmod efd_lkm.ko pid=2803 efd=3
Switch back to the userspace program window and you will see that the userspace program has broken out of select and exited.
Consult the kernel source here:
http://lxr.free-electrons.com/source/fs/eventfd.c
Basically, send your userspace file descriptor, as produced by eventfd(), to your module via ioctl() or some other path. From the kernel, call eventfd_ctx_fdget() to get an eventfd context, then eventfd_signal() on the resulting context. Don't forget eventfd_ctx_put() when you're done with the context.
how do I resolve the "struct file *" pointers to these eventfds from kernelspace
You must resolve those pointers into data structures that this interface you've created has published (create new types and read the fields you want from struct file into it).
Is there better way to signal events to userspace from kernelspace?
Netlink sockets are another convenient way for the kernel to communicate with userspace. "Better" is in the eye of the beholder.
I'm trying to write some code to communicate with wpa_supplicant using DBUS. As I'm working in an embedded system (ARM), I'd like to avoid the use of Python or the GLib. I'm wondering if I'm stupid because I really have the feeling that there is no nice and clear documentation about D-Bus. Even with the official one, I either find the documentation too high level, or the examples shown are using Glib! Documentation I've looked at: http://www.freedesktop.org/wiki/Software/dbus
I found a nice article about using D-Bus in C: http://www.matthew.ath.cx/articles/dbus
However, this article is pretty old and not complete enough! I also found the c++-dbus API but also here, I don't find ANY documentation! I've been digging into wpa_supplicant and NetworkManager source code but it's quite a nightmare! I've been looking into the "low-level D-Bus API" as well but this doesn't tell me how to extract a string parameter from a D-Bus message! http://dbus.freedesktop.org/doc/api/html/index.html
Here is some code I wrote to test a little but I really have trouble to extract string values. Sorry for the long source code but if someone want to try it ... My D-Bus configuration seems fine because it "already" catches "StateChanged" signals from wpa_supplicant but cannot print the state:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <string.h>
#include <dbus/dbus.h>
//#include "wpa_supp_dbus.h"
/* Content of wpa_supp_dbus.h */
#define WPAS_DBUS_SERVICE "fi.epitest.hostap.WPASupplicant"
#define WPAS_DBUS_PATH "/fi/epitest/hostap/WPASupplicant"
#define WPAS_DBUS_INTERFACE "fi.epitest.hostap.WPASupplicant"
#define WPAS_DBUS_PATH_INTERFACES WPAS_DBUS_PATH "/Interfaces"
#define WPAS_DBUS_IFACE_INTERFACE WPAS_DBUS_INTERFACE ".Interface"
#define WPAS_DBUS_NETWORKS_PART "Networks"
#define WPAS_DBUS_IFACE_NETWORK WPAS_DBUS_INTERFACE ".Network"
#define WPAS_DBUS_BSSIDS_PART "BSSIDs"
#define WPAS_DBUS_IFACE_BSSID WPAS_DBUS_INTERFACE ".BSSID"
int running = 1;
void stopLoop(int sig)
{
running = 0;
}
void sendScan()
{
// TODO !
}
void loop(DBusConnection* conn)
{
DBusMessage* msg;
DBusMessageIter args;
DBusMessageIter subArgs;
int argType;
int i;
int buffSize = 1024;
char strValue[buffSize];
const char* member = 0;
sendScan();
while (running)
{
// non blocking read of the next available message
dbus_connection_read_write(conn, 0);
msg = dbus_connection_pop_message(conn);
// loop again if we haven't read a message
if (!msg)
{
printf("No message received, waiting a little ...\n");
sleep(1);
continue;
}
else printf("Got a message, will analyze it ...\n");
// Print the message member
printf("Got message for interface %s\n",
dbus_message_get_interface(msg));
member = dbus_message_get_member(msg);
if(member) printf("Got message member %s\n", member);
// Check has argument
if (!dbus_message_iter_init(msg, &args))
{
printf("Message has no argument\n");
continue;
}
else
{
// Go through arguments
while(1)
{
argType = dbus_message_iter_get_arg_type(&args);
if (argType == DBUS_TYPE_STRING)
{
printf("Got string argument, extracting ...\n");
/* FIXME : got weird characters
dbus_message_iter_get_basic(&args, &strValue);
*/
/* FIXME : segmentation fault !
dbus_message_iter_get_fixed_array(
&args, &strValue, buffSize);
*/
/* FIXME : segmentation fault !
dbus_message_iter_recurse(&args, &subArgs);
*/
/* FIXME : deprecated!
if(dbus_message_iter_get_array_len(&args) > buffSize)
printf("message content to big for local buffer!");
*/
//printf("String value was %s\n", strValue);
}
else
printf("Arg type not implemented yet !\n");
if(dbus_message_iter_has_next(&args))
dbus_message_iter_next(&args);
else break;
}
printf("No more arguments!\n");
}
// free the message
dbus_message_unref(msg);
}
}
int main(int argc, char* argv[])
{
DBusError err;
DBusConnection* conn;
int ret;
char signalDesc[1024]; // Signal description as string
// Signal handling
signal(SIGKILL, stopLoop);
signal(SIGTERM, stopLoop);
// Initialize err struct
dbus_error_init(&err);
// connect to the bus
conn = dbus_bus_get(DBUS_BUS_SYSTEM, &err);
if (dbus_error_is_set(&err))
{
fprintf(stderr, "Connection Error (%s)\n", err.message);
dbus_error_free(&err);
}
if (!conn)
{
exit(1);
}
// request a name on the bus
ret = dbus_bus_request_name(conn, WPAS_DBUS_SERVICE, 0, &err);
if (dbus_error_is_set(&err))
{
fprintf(stderr, "Name Error (%s)\n", err.message);
dbus_error_free(&err);
}
/* Connect to signal */
// Interface signal ..
sprintf(signalDesc, "type='signal',interface='%s'",
WPAS_DBUS_IFACE_INTERFACE);
dbus_bus_add_match(conn, signalDesc, &err);
dbus_connection_flush(conn);
if (dbus_error_is_set(&err))
{
fprintf(stderr, "Match Error (%s)\n", err.message);
exit(1);
}
// Network signal ..
sprintf(signalDesc, "type='signal',interface='%s'",
WPAS_DBUS_IFACE_NETWORK);
dbus_bus_add_match(conn, signalDesc, &err);
dbus_connection_flush(conn);
if (dbus_error_is_set(&err))
{
fprintf(stderr, "Match Error (%s)\n", err.message);
exit(1);
}
// Bssid signal ..
sprintf(signalDesc, "type='signal',interface='%s'",
WPAS_DBUS_IFACE_BSSID);
dbus_bus_add_match(conn, signalDesc, &err);
dbus_connection_flush(conn);
if (dbus_error_is_set(&err))
{
fprintf(stderr, "Match Error (%s)\n", err.message);
exit(1);
}
// Do main loop
loop(conn);
// Main loop exited
printf("Main loop stopped, exiting ...\n");
dbus_connection_close(conn);
return 0;
}
Any pointer to any nice, complete, low-level C tutorial is strongly appreciated! I'm also planning to do some remote method call, so if the tutorial covers this subject it would be great! Saying I'm not very smart because I don't get it with the official tutorial is also appreciated :-p!
Or is there another way to communicate with wpa_supplicant (except using wpa_cli)?
EDIT 1:
Using 'qdbusviewer' and the introspection capabilty, this helped me a lot discovering what and how wpa_supplicant works using dbus. Hopping that this would help someone else!
Edit 2:
Will probably come when I'll find a way to read string values on D-Bus!
You have given up the tools that would help you to learn D-Bus more easily and are using the low level libdbus implementation, so maybe you deserve to be in pain. BTW, are you talking about ARM, like a cell phone ARM ? With maybe 500 Mhz and 256 MB RAM ? In this case the processor is well suited to using glib, Qt or even python. And D-Bus is most useful when you're writing asynchronous event driven code, with an integrated main loop, for example from glib, even when you're using the low level libdbus (it has functions to connect to the glib main loop, for example).
Since you're using the low level library, then documentation is what you already have:
http://dbus.freedesktop.org/doc/api/html/index.html
Also, libdbus source code is also part of the documentation:
http://dbus.freedesktop.org/doc/api/html/files.html
The main entry point for the documentation is the Modules page (in particular, the public API section):
http://dbus.freedesktop.org/doc/api/html/modules.html
For message handling, the section DBusMessage is the relevant one:
DBusMessage
There you have the documentation for functions that parse item values. In your case, you started with a dbus_message_iter_get_basic. As described in the docs, retrieving the string requires a const char ** variable, since the returned value will point to the pre-allocated string in the received message:
So for int32 it should be a "dbus_int32_t*" and for string a "const char**". The returned value is by reference and should not be freed.
So you can't define an array, because libdbus won't copy the text to your array. If you need to save the string, first get the constant string reference, then strcpy to your own array.
Then you tried to get a fixed array without moving the iterator. You need a call to the next iterator (dbus_message_iter_next) between the basic string and the fixed array. Same right before recursing into the sub iterator.
Finally, you don't call get_array_len to get the number of elements on the array. From the docs, it only returns byte counts. Instead you loop over the sub iterator using iter_next the same way you should have done with the main iterator. After you have iterated past the end of the array, dbus_message_iter_get_arg_type will return DBUS_TYPE_INVALID.
For more info, read the reference manual, don't look for a tutorial. Or just use a reasonable d-bus implementation:
https://developer.gnome.org/gio/2.36/gdbus-codegen.html
GIO's GDBus automatically creates wrappers for your d-bus calls.
http://qt-project.org/doc/qt-4.8/intro-to-dbus.html
http://dbus.freedesktop.org/doc/dbus-python/doc/tutorial.html
etc.
You don't need to use/understand working of dbus If you just need to write a C program to communicate with wpa_supplicant. I reverse engineered the wpa_cli's source code. Went through its implementation and used functions provided in wpa_ctrl.h/c. This implementation takes care of everything. You can use/modify whatever you want, build your executable and you're done!
Here's the official link to wpa_supplicant's ctrl_interface:
http://hostap.epitest.fi/wpa_supplicant/devel/ctrl_iface_page.html
I doubt this answer will still be relevant to the author of this question,
but for anybody who stumbles upon this like I did:
The situation is now better than all those years ago if you don't want to include GTK/QT in your project to access dbus.
There is dbus API in Embedded Linux Library by Intel (weird I remember it being open, maybe it is just for registered users now?)
and systemd sd-bus library now offers public API. You probably run systemd anyway unless you have a really constrained embedded system.
I have worked with GDbus, dbus-cpp and sd-bus and although I wanted a C++ library,
I found sd-bus to be the simplest and the least problematic experience.
I did not try its C++ bindings but they also look nice
#include <stdio.h>
#include <systemd/sd-bus.h>
#include <stdlib.h>
const char* wpa_service = "fi.w1.wpa_supplicant1";
const char* wpa_root_obj_path = "/fi/w1/wpa_supplicant1";
const char* wpa_root_iface = "fi.w1.wpa_supplicant1";
sd_bus_error error = SD_BUS_ERROR_NULL;
sd_bus* system_bus = NULL;
sd_event* loop = NULL;
sd_bus_message* reply = NULL;
void cleanup() {
sd_event_unref(loop);
sd_bus_unref(system_bus);
sd_bus_message_unref(reply);
sd_bus_error_free(&error);
}
void print_error(const char* msg, int code) {
fprintf(stderr, "%s %s\n", msg, strerror(-code));
exit(EXIT_FAILURE);
}
const char* get_interface(const char* iface) {
int res = sd_bus_call_method(system_bus,
wpa_service,
wpa_root_obj_path,
wpa_root_iface,
"GetInterface",
&error,
&reply,
"s",
"Ifname", "s", iface,
"Driver", "s", "nl80211");
if (res < 0) {
fprintf(stderr, "(get) error response: %s\n", error.message);
return NULL;
}
const char* iface_path;
/*
* an object path was returned in reply
* this works like an iterator, if a method returns (osu), you could call message_read_basic in succession
* with arguments SD_BUS_TYPE_OBJECT_PATH, SD_BUS_TYPE_STRING, SD_BUS_TYPE_UINT32 or you could
* call sd_bus_message_read() and provides the signature + arguments in one call
* */
res = sd_bus_message_read_basic(reply, SD_BUS_TYPE_OBJECT_PATH, &iface_path);
if (res < 0) {
print_error("getIface: ", res);
return NULL;
}
return iface_path;
}
const char* create_interface(const char* iface) {
int res = sd_bus_call_method(system_bus,
wpa_service,
wpa_root_obj_path,
wpa_root_iface,
"CreateInterface",
&error,
&reply,
"a{sv}", 2, //pass array of str:variant (dbus dictionary) with 2
//entries to CreateInterface
"Ifname", "s", iface, // "s" variant parameter contains string, then pass the value
"Driver", "s", "nl80211");
if (res < 0) {
fprintf(stderr, "(create) error response: %s\n", error.message);
return NULL;
}
const char* iface_path;
res = sd_bus_message_read_basic(reply, SD_BUS_TYPE_OBJECT_PATH, &iface_path);
if (res < 0) {
print_error("createIface: ", res);
}
return iface_path;
}
int main() {
int res;
const char* iface_path;
//open connection to system bus - default either opens or reuses existing connection as necessary
res = sd_bus_default_system(&system_bus);
if (res < 0) {
print_error("open: ", res);
}
//associate connection with event loop, again default either creates or reuses existing
res = sd_event_default(&loop);
if (res < 0) {
print_error("event: ", res);
}
// get obj. path to the wireless interface on dbus so you can call methods on it
// this is a wireless interface (e.g. your wifi dongle) NOT the dbus interface
// if you don't know the interface name in advance, you will have to read the Interfaces property of
// wpa_supplicants root interface — call Get method on org.freedesktop.DBus properties interface,
// while some libraries expose some kind of get_property convenience function sd-bus does not
const char* ifaceName = "wlp32s0f3u2";
if (!(iface_path = get_interface(ifaceName))) { //substitute your wireless iface here
// sometimes the HW is present and listed in "ip l" but dbus does not reflect that, this fixes it
if (!(iface_path = create_interface(ifaceName))) {
fprintf(stderr, "can't create iface: %s" , ifaceName);
cleanup();
return EXIT_FAILURE;
}
}
/*
call methods with obj. path iface_path and dbus interface of your choice
this will likely be "fi.w1.wpa_supplicant1.Interface", register for signals etc...
you will need the following to receive those signals
*/
int runForUsec = 1000000; //usec, not msec!
sd_event_run(loop, runForUsec); //or sd_event_loop(loop) if you want to loop forever
cleanup();
printf("Finished OK\n");
return 0;
}
I apologize if the example above does not work perfectly. It is an excerpt from an old project I rewrote to C from C++ (I think it's C(-ish), compiler does not protest and you asked for C) but I can't test it as all my dongles refuse to work with my desktop right now. It should give you a general idea though.
Note that you will likely encounter several magical or semi-magical issues.
To ensure smooth developing/testing do the following:
make sure other network management applications are disabled (networkmanager, connman...)
restart the wpa_supplicant service
make sure the wireless interface is UP in ip link
Also, because is not that well-documented right now:
You can access arrays and inner variant values by sd_bus_message_enter_container
and _exit counterpart. sd_bus_message_peek_type might come handy while doing that.
Or sd_bus_message_read_array for a homogenous array.
The below snippet works for me
if (argType == DBUS_TYPE_STRING)
{
printf("Got string argument, extracting ...\n");
char* strBuffer = NULL;
dbus_message_iter_get_basic(&args, &strBuffer);
printf("Received string: \n %s \n",strBuffer);
}
What happens to an open file handle on Linux if the pointed file meanwhile gets:
Moved away -> Does the file handle stay valid?
Deleted -> Does this lead to an EBADF, indicating an invalid file handle?
Replaced by a new file -> Does the file handle pointing to this new file?
Replaced by a hard link to a new file -> Does my file handle "follow" this link?
Replaced by a soft link to a new file -> Does my file handle hit this soft link file now?
Why I'm asking such questions: I'm using hot-plugged hardware (such as USB devices etc.). It can happen, that the device (and also its /dev/file) gets reattached by the user or another Gremlin.
What's the best practice dealing with this?
If the file is moved (in the same filesystem) or renamed, then the file handle remains open and can still be used to read and write the file.
If the file is deleted, the file handle remains open and can still be used (This is not what some people expect). The file will not really be deleted until the last handle is closed.
If the file is replaced by a new file, it depends exactly how. If the file's contents are overwritten, the file handle will still be valid and access the new content. If the existing file is unlinked and a new one created with the same name or, if a new file is moved onto the existing file using rename(), it's the same as deletion (see above) - that is, the file handle will continue to refer to the original version of the file.
In general, once the file is open, the file is open, and nobody changing the directory structure can change that - they can move, rename the file, or put something else in its place, it simply remains open.
In Unix there is no delete, only unlink(), which makes sense as it doesn't necessarily delete the file - just removes the link from the directory.
If on the other hand the underlying device disappears (e.g. USB unplug) then the file handle won't be valid any more and is likely to give IO/error on any operation. You still have to close it though. This is going to be true even if the device is plugged back in, as it's not sensible to keep a file open in this case.
File handles point to an inode not to a path, so most of your scenarios still work as you assume, since the handle still points to the file.
Specifically, with the delete scenario - the function is called "unlink" for a reason, it destroys a "link" between a filename (a dentry) and a file. When you open a file, then unlink it, the file actually still exists until its reference count goes to zero, which is when you close the handle.
Edit: In the case of hardware, you have opened a handle to a specific device node, if you unplug the device, the kernel will fail all accesses to it, even if the device comes back. You will have to close the device and reopen it.
I'm not sure about the other operations, but as for deletion: Deletion simply doesn't take place (physically, i.e. in the file system) until the last open handle to the file is closed. Thus it should not be possible to delete a file out from under your application.
A few apps (that don't come to mind) rely on this behavior, by creating, opening and immediately deleting files, which then live exactly as long as the application - allowing other applications to be aware of the first app's lifecycle without needing to look at process maps and such.
It's possible similar considerations apply to the other stuff.
if you want to check if the file handler(file descriptor) is okay, you can call this function.
/**
* version : 1.1
* date : 2015-02-05
* func : check if the fileDescriptor is fine.
*/
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdio.h>
/**
* On success, zero is returned. On error, -1 is returned, and errno is set
* appropriately.
*/
int check_fd_fine(int fd) {
struct stat _stat;
int ret = -1;
if(!fcntl(fd, F_GETFL)) {
if(!fstat(fd, &_stat)) {
if(_stat.st_nlink >= 1)
ret = 0;
else
printf("File was deleted!\n");
}
}
if(errno != 0)
perror("check_fd_fine");
return ret;
}
int main() {
int fd = -1;
fd = open("/dev/ttyUSB1", O_RDONLY);
if(fd < 0) {
perror("open file fail");
return -1;
}
// close or remove file(remove usb device)
// close(fd);
sleep(5);
if(!check_fd_fine(fd)) {
printf("fd okay!\n");
} else {
printf("fd bad!\n");
}
close(fd);
return 0;
}
The in-memory information of a deleted file (all the examples you give are instances of a deleted file) as well as the inodes on-disk remain in existence until the file is closed.
Hardware being hotplugged is a completely different issue, and you should not expect your program to stay alive long if the on-disk inodes or metadata have changed at all.
The following experiment shows that MarkR's answer is correct.
code.c:
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <strings.h>
#include <stdio.h>
void perror_and_exit() {
perror(NULL);
exit(1);
}
int main(int argc, char *argv[]) {
int fd;
if ((fd = open("data", O_RDONLY)) == -1) {
perror_and_exit();
}
char buf[5];
for (int i = 0; i < 5; i++) {
bzero(buf, 5);
if (read(fd, buf, 5) != 5) {
perror_and_exit();
}
printf("line: %s", buf);
sleep(20);
}
if (close(fd) != 0) {
perror_and_exit();
}
return 0;
}
data:
1234
1234
1234
1234
1234
Use gcc code.c to produce a.out. Run ./a.out. When you see the following output:
line: 1234
Use rm data to delete data. But ./a.out will continue to run without errors and produce the following whole output:
line: 1234
line: 1234
line: 1234
line: 1234
line: 1234
I have done the experiment on Ubuntu 16.04.3.
Under /proc/ directory you will find a list of every process currently active, just find your PID and all data regarding is there. An interresting info is the folder fd/, you will find all file handlers currently opened by the process.
Eventually you will find a symbolic link to your device (under /dev/ or even /proc/bus/usb/), if the device hangs the link will be dead and it will be impossible to refresh this handle, the process must close and open it again (even with reconnection)
This code can read your PID's link current status
#include <unistd.h>
#include <stdio.h>
#include <dirent.h>
int main() {
// the directory we are going to open
DIR *d;
// max length of strings
int maxpathlength=256;
// the buffer for the full path
char path[maxpathlength];
// /proc/PID/fs contains the list of the open file descriptors among the respective filenames
sprintf(path,"/proc/%i/fd/",getpid() );
printf("List of %s:\n",path);
struct dirent *dir;
d = opendir(path);
if (d) {
//loop for each file inside d
while ((dir = readdir(d)) != NULL) {
//let's check if it is a symbolic link
if (dir->d_type == DT_LNK) {
const int maxlength = 256;
//string returned by readlink()
char hardfile[maxlength];
//string length returned by readlink()
int len;
//tempath will contain the current filename among the fullpath
char tempath[maxlength];
sprintf(tempath,"%s%s",path,dir->d_name);
if ((len=readlink(tempath,hardfile,maxlength-1))!=-1) {
hardfile[len]='\0';
printf("%s -> %s\n", dir->d_name,hardfile);
} else
printf("error when executing readlink() on %s\n",tempath);
}
}
closedir(d);
}
return 0;
}
This final code is simple, you can play with linkat function.
int
open_dir(char * path)
{
int fd;
path = strdup(path);
*strrchr(path, '/') = '\0';
fd = open(path, O_RDONLY | O_DIRECTORY);
free(path);
return fd;
}
int
main(int argc, char * argv[])
{
int odir, ndir;
char * ofile, * nfile;
int status;
if (argc != 3)
return 1;
odir = open_dir(argv[1]);
ofile = strrchr(argv[1], '/') + 1;
ndir = open_dir(argv[2]);
nfile = strrchr(argv[2], '/') + 1;
status = linkat(odir, ofile, ndir, nfile, AT_SYMLINK_FOLLOW);
if (status) {
perror("linkat failed");
}
return 0;
}