Why is Linux's sendfile() incompatible with /dev/zero? - c++

why is sendfile incompatible with /dev/zero ? why does this code
#include <iostream>
#include <sys/sendfile.h>
#include <fcntl.h>
#include <string.h>
int main(){
int f1 = fileno(tmpfile());
int devzero = open("/dev/zero", O_RDONLY);
if(f1 < 0 || devzero < 0){
throw std::runtime_error("Failed to open files");
}
ssize_t sent = sendfile(f1, devzero, 0, 1);
std::cout << "sent " << sent << " bytes. errno " << errno << " strerror " << strerror(errno) << std::endl;
return 0;
}
consistently output
sent -1 bytes. errno 22 strerror Invalid argument
?

/dev/zero is a special file. It doesn’t represent an on disk file.
From sendfile documentation:
The in_fd argument must correspond to a file which supports mmap(2)-like operations (i.e., it cannot be a socket).
...
EINVAL Descriptor is not valid or locked, or an mmap(2)-like operation is not available for in_fd, or count is negative.
EINVAL out_fd has the O_APPEND flag set. This is not currently supported by sendfile().
These section of kernel source is reporting the error:
if (unlikely(out->f_flags & O_APPEND))
return -EINVAL;
ret = rw_verify_area(WRITE, out, opos, len);
I performed some testing. Reading from a tmpfile(), a regular file, and /dev/zero works. Writing to a regular file works, but writing to /dev/zero fails one of those checks (O_APPEND permissions or rw_verify_area() ).

Related

inotify inconsistent with devices

On Linux, I am trying to detect a bluetooth controller being connected and start reading from it. I know there's SDL to do that, but I just wanted to learn how to do it specifically on Linux. So I'm using the inotify api to wait for the file /dev/input/js0 to show up. But when I detect the file I cannot open it. I have the following c++ code:
#include <iostream>
#include <fstream>
#include <sys/inotify.h>
#include <unistd.h>
#include <linux/joystick.h>
#include <string.h>
constexpr int NAME_MAX = 16;
int main(int argc, char **argv) {
std::string path = std::string(argv[1]);
std::string directory = path.substr(0, path.find_last_of("/"));
std::string file = path.substr(path.find_last_of("/") + 1);
std::cout << "Directory is " << directory << ", file is " << file << std::endl;
int fd = inotify_init();
if (inotify_add_watch(fd, directory.c_str(), IN_CREATE) < 0) {
std::cout << "Could not watch: " << file << std::endl;
return -1;
}
else
std::cout << "Watching: " << file << std::endl;
char buffer[sizeof(struct inotify_event) + NAME_MAX + 1];
while (true) {
if (read(fd, buffer, sizeof(buffer)) < 0) {
std::cout << "Error reading event" << std::endl;
break;
}
struct inotify_event &event = (struct inotify_event &) buffer;
std::cout << event.name << std::endl;
if ((strcmp(event.name, file.c_str()) == 0) && (event.mask & IN_CREATE)) {
std::cout << "File has been created" << std::endl;
close(fd);
break;
}
}
std::fstream file_stream(file, std::fstream::in);
std::cout << file_stream.is_open() << std::endl;
}
If I run it to detect a regular file, it works, it waits for the file creation event, and when trying to open it with a std::fstream, is_open returns true. But if I run it to detect /dev/input/js0, even when the event comes and the file is detected, opening the fstream does not work, as is_open returns false. Is inotify appropriate to detect device files? If not, what would be the right way to do so?
According to inotify(7)
Inotify reports only events that a user-space program triggers
through the filesystem API. As a result, it does not catch
remote events that occur on network filesystems. (Applications
must fall back to polling the filesystem to catch such events.)
Furthermore, various pseudo-filesystems such as /proc, /sys, and
/dev/pts are not monitorable with inotify.
I would say that /dev/input/ also falls into this bucket.
I wonder if udev could be used: you should get info about the device using udevinfo -a -p /dev/input/js0, but also see what events connecting the peripheral generates using udevadm monitor --environment --udev.
Edit: if you successfuly get an inotify event but can't read the file:
Did you try reading the file with another simpler program when the BT device is already connected?
Is there a difference between fstream::open and open from <cstdio>?
Have you checked the permissions on the device? Also what does cat /dev/input/js0 produces?

Setting ctrl with V4L2 on ov5640

I would like to control various ov5640 camera parameters by using ioctl and VIDIOC_S_CTRL from V4L2 in the following manner:
#include <string>
#include <iostream>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include <malloc.h>
#include <cstring>
#include <sys/mman.h>
#include <sys/ioctl.h>
#include <linux/videodev2.h>
#define IOCTL_TRIES 3
#define CLEAR(x) memset (&(x), 0, sizeof (x))
static int xioctl(int fd, int request, void *arg)
{
int r;
int tries = IOCTL_TRIES;
do {
r = ioctl(fd, request, arg);
} while (--tries > 0 && r == -1 && EINTR == errno);
return r;
}
bool v4l2_ctrl_set(int fd, uint32_t id, int val)
{
struct v4l2_control ctrl;
CLEAR(ctrl);
ctrl.id = id;
ctrl.value = val;
if (xioctl(fd, VIDIOC_S_CTRL, &ctrl) == -1) {
std::cout << "Failed to set ctrl with id "
<< id << " to value " << val
<< "\nerror (" << errno << "): " << strerror(errno) << std::endl;
return false;
}
return true;
}
int main()
{
int fd = open("/dev/video0", O_RDWR | O_NONBLOCK);
if (fd == -1) {
std::cout << "Failed to open the camera" << std::endl;
return -1;
}
v4l2_ctrl_set(fd, V4L2_CID_SATURATION, 100);
return 0;
}
Unfortunately ioctl fails and I get error (25): Inappropriate ioctl for device. I'm using Intrinsyc Open-Q 820 µSOM with linaro 4.14. I've managed to add some debugs prints to ov5640 driver file in ov5640_s_ctrl function before if (sensor->power_count == 0) { (in case there were problems with power save mode) and recompile the kernel. I ran the code, but looking through dmesg my printk message doesn't get printed, so that means that ov5640_s_ctrl doesn't get called even though the callback is set:
static const struct v4l2_ctrl_ops ov5640_ctrl_ops = {
.g_volatile_ctrl = ov5640_g_volatile_ctrl,
.s_ctrl = ov5640_s_ctrl,
};
Am I using V4L2 wrong? Should I enable something before setting the controls? It's even more confusing since I manage to get an image from the camera with v4l2, but I can't set/get any controls.
In the the kernel source code of ov5640.c that you supplied, the driver is assigned the flag V4L2_SUBDEV_FL_HAS_DEVNODE which means it might supply a subdev node /dev/v4l-subdevX. According to the kernel docs:
Device nodes named v4l-subdevX can be created in /dev to access sub-devices directly. If a sub-device supports direct userspace configuration it must set the V4L2_SUBDEV_FL_HAS_DEVNODE flag before being registered.`
So you can try to set the control directly from the v4l-subdevX node if it exists.

Why is a newly created socket readble and writable, which is monitored by select()?

I wrote the following code. I create a socket , and use select() to monitor this newly created socket. After select() calling, this socket is detected readable and writable.
How to explain this phenomenon? Does it have anything to do with socket recvbuf/sndbuf and recvlowat/sndlowat?
#include <iostream>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <sys/select.h>
#include <sys/time.h>
int main()
{
int nClientFd = socket(AF_INET, SOCK_STREAM, 0);
if (nClientFd == -1)
{
std::cout << "create socket failed, errno: " << errno << std::endl;
return -1;
}
fd_set readFdSet;
FD_ZERO(&readFdSet);
FD_SET(nClientFd, &readFdSet);
fd_set writeFdSet;
FD_ZERO(&writeFdSet);
FD_SET(nClientFd, &writeFdSet);
struct timeval stTimeout;
stTimeout.tv_sec = 1;
stTimeout.tv_usec = 0;
if (select(nClientFd + 1, &readFdSet, &writeFdSet, nullptr, &stTimeout) == -1)
{
std::cout << "select failed" << std::endl;
close(nClientFd);
return -1;
}
if (FD_ISSET(nClientFd, &readFdSet))
{
std::cout << "socket is readable" << std::endl;
}
if (FD_ISSET(nClientFd, &writeFdSet))
{
std::cout << "socket is writable" << std::endl;
}
close(nClientFd);
return 0;
}
Output:
socket is readable
socket is writable
To begin with, Linux / glibc's select() is documented to sometimes spuriously report file descriptors to be readable, but we don't need to rely on that to explain the behavior.
The main issue appears to be that you misinterpret the meaning of select() setting a file descriptor in one of the provided read or write fd sets. That does not necessarily mean that data can successfully be transferred via the fd. Rather it means that the fd entered or was already in a state where an attempt to do so would not block, which includes the case that such an attempt would fail without blocking. The Linux and especially the POSIX documentation is clear on this point.
With that being the case, the behavior you describe is exactly what I would expect. You've created a stream-oriented socket but not connected it to a peer, so I would expect read() and write() attempts on it to fail immediately. select() is therefore right to return its file descriptor in its read and write fd sets.

mmap() after deleting the file

I was told, that mmap() might be in trouble, if someone deletes the original file. I was wondering if that really happens. So i created some little test-program. I am using linux.
#include <iostream>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
int main(int, char**)
{
char const * const fileName = "/tmp/demo-file.dat";
size_t size;
{
struct stat st;
stat(fileName, &st);
size = st.st_size;
}
int fd = open(fileName, O_RDWR);
if (fd == -1)
{
std::cout << "open() failed, errno = " << errno << ":" << strerror(errno) << std::endl;
return (-1);
}
else
{
std::cout << "open() done (ok)" << std::endl;
}
for (int i = 20; i > 0; --i)
{
std::cout << "file open()'ed, wait #" << i << " seconds before mmap()" << std::endl;
sleep(1);
}
void *data = mmap((void*)0L, size, PROT_READ, MAP_SHARED, fd, (off_t)0);
if (data == (void*)-1)
{
std::cout << "mmap() failed, errno = " << errno << ":" << strerror(errno) << std::endl;
}
else
{
std::cout << "mmap() done (ok)" << std::endl;
}
for (int i = 20; i > 0; --i)
{
std::cout << "going to close() socket in #" << i << " seconds" << std::endl;
sleep (1);
}
close(fd);
for (int i = 30; i > 0; --i)
{
std::cout << "going to umap() files in #" << i << " seconds (still accessing the data)" << std::endl;
for (unsigned int x = 0; x < size; ++x)
{
char cp = *(char*) (data + x);
(void) cp;
}
sleep(1);
}
munmap(data, size);
for (int i = 30; i > 0; --i)
{
std::cout << "going to terminate #" << i << " seconds" << std::endl;
sleep(1);
}
return 0;
}
Whenever i delete the file - after the open() operation - it doesn't have negative impact to then mmap(). I can still acess the data in the test program.
When i delete the file right after close(), but before mmap(), it works. I can also still see the old file in the /proc/[pid]/fd/ area:
lrwx------ 1 frank frank 64 Mär 22 20:28 3 -> /tmp/demo-file.dat (deleted)
The rest of the program works.
Even when i delete the file after the close() it still succeeds to access the mmap() data.
However in both cases, after the close() i cannot see the
lrwx------ 1 frank frank 64 Mär 22 20:28 3 -> /tmp/demo-file.dat (deleted)
anymore. (btw: where is then noted, that this file "still exists somehow"?)
So is it the opposite, that it is guaranteed, that mmap() will still be able to operate on the data, even if the file was manually deleted (in a shell or by some other process)?
Here's what's happening.
The first thing to check is
$ls -i /tmp/demo-file.dat
65 /tmp/demo-file.dat
Note the inode number of the file is 65.
On starting the program, here's what it has in its lsof output (apart from other entries not relevant to the current discourse)
a.out 29271 zoso 3u REG 0,21 5 65 /tmp/demo-file.dat
This is a result of the open() that's been done. Note that the inode number is the same as the other file. The open() has increased the ref count on the same inode. Also, note that REG indicates a regular file.
Now if the file is deleted (using rm etc.), here's what the lsof looks like
a.out 29271 zoso 3u REG 0,21 5 65 /tmp/demo-file.dat (deleted)
This is expected since the file that was opened has been deleted but the handle that to its inode still is open in the process.
Moving on to the mmap, and here's the lsof output
a.out 29271 zoso DEL REG 0,21 65 /tmp/demo-file.dat
a.out 29271 zoso 3u REG 0,21 5 65 /tmp/demo-file.dat (deleted)
Now there's another new entry but note that this is of type DEL which indicates (lifting from lsof man page):
''DEL'' for a Linux map file that has been deleted;
Since lsof can't stat the original file anymore, it puts this mapping as DEL with no size of course, yet note that the inode number still remains the same i.e 65.
Now after close() has been called on the fd here's what lsof shows
a.out 29271 zoso DEL REG 0,21 65 /tmp/demo-file.dat
Note that the (deleted) entry is gone since that fd to a REG file has been closed and now only the mmap'd memory remains.
After munmap() this entry too is gone (no more references to /tmp/demo-file.dat and finally the program ends.

errno value is not updated (c++)

I'm new to coding (currently learning c++ & I know a little of C)...
was reading about functions in math.h and read about errno...
According to the site I referred :-
Domain error (input argument is outside the range in which the operation is mathematically defined, e.g. std::sqrt(-1), std::log(-1), or std::acos(2)). If MATH_ERRNO bit is set, EDOM is assigned to errno. If MATH_ERREXCEPT bit is set, FE_INVALID is raised.
So I tried writing a small program with that knowledge...
#include <iostream>
#include <cerrno>
#include <cmath>
using namespace std;
int main (void)
{
errno = 0;
cout<<"\nWhat I get :-\n";
cout << "log(-3) = " << log(-3) << "\n";
//shouldn't it do (errno = EDOM) in the above step?
cout << "errno = " << errno << "\n";
cout << strerror(errno) << "\n";
errno = EDOM;
cout<<"\nWhat I want :-\n";
cout << "log(-3) = " << log(-3) << "\n";
cout << "errno = " << errno << "\n";
cout << strerror(errno) << "\n\n";
return(0);
}
And in the output I see that the errno is not getting updated to EDOM in my first block even though -3 is not in the domain of log()...
Output:-
What I get :-
log(-3) = nan
errno = 0
Undefined error: 0
What I want :-
log(-3) = nan
errno = 33
Numerical argument out of domain
I don't understand what I'm missing here...
Plz help....
I'm compiling my code on Apple LLVM version 7.3.0 (clang-703.0.31) in Mac.
#define MATH_ERRNO 1 is illegal. You should not redefine standard library symbols. MATH_ERRNO is already defined as 1 by standard.
You cannot set how implementation handles error (aside from compiler-specific switches. Read documentation for your compiler). You can only check it:
if (math_errhandling & MATH_ERRNO)
std::cout << "Error reporting uses errno\n";
else
std::cout << "Error reporting does not use errno\n";
if (math_errhandling & MATH_ERREXCEPT)
std::cout << "Error reporting uses floating-point exceptions\n";
else
std::cout << "Error reporting does not use floating-point exceptions\n";
For clang, relevant flags are -fmath-errno/-fmath-no-errno to use/not use errno.
From discussion on reported bug it seems, that Mac implementation of standard library doesn't use errno at all. So you are out of luck if you want use it for error reporting.
You can find complete example (in C) of math err handling at: http://www.cplusplus.com/reference/cmath/math_errhandling/
For completness example from that site:
#include <stdio.h> /* printf */
#include <math.h> /* math_errhandling */
#include <errno.h> /* errno, EDOM */
#include <fenv.h> /* feclearexcept, fetestexcept, FE_ALL_EXCEPT, FE_INVALID */
#pragma STDC FENV_ACCESS on
int main () {
errno = 0;
if (math_errhandling & MATH_ERREXCEPT) feclearexcept(FE_ALL_EXCEPT);
printf ("Error handling: %d",math_errhandling);
sqrt (-1);
if (math_errhandling & MATH_ERRNO) {
if (errno==EDOM) printf("errno set to EDOM\n");
}
if (math_errhandling &MATH_ERREXCEPT) {
if (fetestexcept(FE_INVALID)) printf("FE_INVALID raised\n");
}
return 0;
}