So, guys, now continuing from the same point where we left in the previous lecture video, now that
we have already implemented the sending side of our userspace program, now we will build the receiving
side of our userspace program.
So you can see as per the flow chart that we discussed earlier right after the bind system call, what
we need to do is to start a receiving thread, right?
So guys, you can see on the screen I have opened the same file userspace dot C and in the main function,
right after we have completed the bind of the netlink socket, we will going to write a code to launch
a new thread before we enter into the infinite loop.
Right.
So what I will going to do is to simply start a receiver thread here so you can see in these three lines
I have specified a new type of data structure called thread argument.
Right?
That this data structure is very, very simple.
It has only one member of type int and that is socket file descriptor, right?
So whatever Netlink socket file descriptor we created, we are assigning the same socket file descriptor
to the sock underscore member of this data structure.
And then after that we were going to start a receiver thread.
For that we will call the function start kernel data receiver thread.
From this function we will going to create a new thread and launch it.
So we were going to implement this function outside the main function that is on the top of the main,
and you can see that.
So first of all, you can see that we have created a thread type variable.
So now here I expect you that you have a little exposure to P threads and I am simply initializing the
attribute of a P thread with the default values.
And then after that I am simply creating a thread.
And that's all with the implementation of this function.
So I have finally launched a receiver thread.
The function which the receiver thread will execute is this function that is start kernel receiver thread.
Right?
And the argument that is passed to this function is the thread argument.
Remember this thread argument contains the socket file descriptor, so all the logic of receiving the
data from the kernel space will going to be implemented inside this function.
Remember, this function will execute as a thread.
So now let us discuss the implementation of this function next.
So, guys, as you can see that I'm about to start the implementation of this function.
So the very first thing that we will do in this function is to define some variables which we will going
to use in this function.
I will discuss the usage of these variables as we come across their need.
So the very first thing that I will do is to extract the socket file descriptor from the argument which
was passed to this function.
And we already know that the argument that was passed to this function was this data structure, right?
So now we have an access to the socket file descriptor because using this socket file descriptor, we
will be able to receive the message from the kernel space through netlink socket.
Now it is pretty much obvious that we need some chunk of memory into which we can copy the data which
is coming from the kernel space.
So for that we will going to create a buffer and you can see that the size of this buffer is the size
of the Netlink message header LAN plus the size of the max payload.
So this max payload is a constant value and you can simply hash define this value to some big enough
value which could be sufficient to receive messages from the kernel space.
So you can hash define it to like 2048 bytes.
So note that the buffer that we have allocated is the buffer which is pointed by this netlink header
receive pointer.
Right.
Why we have typecasted this buffer into netlink message header type because the message that we will
going to receive from the kernel space would be of the format that it have netlink message header appended
by the payload.
Right now, again, we will going to enter into the infinite loop, right?
Because our user space application should always be in a position to receive messages from the kernel
space at any point of time.
So now that we have entered into the do while loop, which is an infinite loop, so let us initialize
our chunk of memory into which we will copy the message received from the kernel space and we will going
to wrap up our memory into the vector data structure like the same way we did while sending the message
to the kernel space.
And then we will take the outer message header as usual and we will initialize this outer message header
to zero and then we will assign the pointer to this i o vector to this outer message header.
So now this thing is not new to you.
You must be familiar with this.
We always have to wrap our netlink message header inside the i o vector and then.
And then our vector pointer should be given to the outer message header.
Right now, the message name in this case will be null because we are not sending the message to the
kernel space but rather we are receiving the message.
Underscore name is actually the address of the destination.
But in this case there is no destination because our user space program itself is a receiver.
Right?
And you can specify i o villain as one and because the message name is null, so message name length
will be zero.
Now that everything is now set up, now we are in a position to actually wait for the arrival of the
message from the kernel space.
For that we need to invoke the receive message system call.
Right.
And you can see that the first argument is the socket file descriptor that we had passed.
And the second argument is the pointer to the outer message header.
Right.
So now receive message is a blocking system call.
And as I explained earlier, our userspace program will continue to block here until it receives the
message from the kernel space.
Also note that it is our separate receiver thread which gets blocked here.
Our main thread will continue to run.
Now, let us assume that our user space program has received a message from the kernel space, so we
will get unblocked.
Right.
And we will execute further.
So now whatever the mass is, the kernel space has sent to us.
We can retrieve the pointer to the netlink message header as follows.
You can see that the same pointer has been retrieved from the i o vector which was hidden inside the
outer message header.
So now we have an access to the Netlink message header and behind this netlink message header we have
a payload.
We can always access the payload which is present behind this Netlink message header using Netlink message
data macro which is provided by Netlink socket API.
And now simply we can print this message which has been received from the kernel space.
Right?
The return value of received message is the number of bytes which we have received.
Right?
So here we are printing the number of bytes followed by the actual message which we have received from
the kernel space.
And finally we will enclose this do while loop so you can see that it is an infinite loop.
After receiving and printing the message, we will again go back and again we will do all the initialization
and we will get block on the receive message system call that is waiting for the next message from the
kernel space.
Right?
So this completes the receiving part of our user space program.
Now let us compile and see that we should be able to send the message to the kernel space as well as
we should be able to receive a reply from the kernel space.