Reputation: 45
Seeing the write() function failing on /sys/bus/pci/devices/.../driver/remove_id file returning -1 with errno equal to 19 (ENODEV).
But, same works fine via command line. I have checked the file permission which seems to be fine (--w-------) for user to perform write on this file.
int fp = 0;
int buffer_length = 0;
int bytes_written = 0;
fp = open(cmd_buf, O_WRONLY); // where cmd_buf will hold this string
// "/sys/bus/pci/devices/.../driver/remove_id"
if (fp == -1)
{
return -1;
}
// where inbuf will be a char * pointing to pci vendor device id like
// this, "XXXX YYYY"
bytes_written = write(fp, in_buf, sizeof(in_buf));
printf(" bytes_written : %d \n ", bytes_written);
Seeing bytes_written equal to -1 and errno shows 19.
Please let me know if you find something wrong with the code snippet?
Upvotes: 0
Views: 582
Reputation: 91
You do not provide enough information to pinpoint the problem.
However, here is an example program, example.c, that shows that it is your implementation that has the bug:
#define _POSIX_C_SOURCE 200809L
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <stdio.h>
/* Write string 'data' to existing file or device at 'path'.
Returns 0 if success, errno error code otherwise.
*/
int write_file(const char *path, const char *data)
{
const char *const ends = (data) ? data + strlen(data) : data;
ssize_t n;
int fd;
/* NULL or empty path is invalid. */
if (!path || !*path)
return errno = EINVAL;
fd = open(path, O_WRONLY | O_CREAT | O_TRUNC | O_NOCTTY | O_CLOEXEC, 0666);
if (fd == -1)
return errno; /* errno already set by open(). */
/* Write the contents of data. */
while (data < ends) {
n = write(fd, data, (size_t)(ends - data));
if (n > 0) {
/* Wrote n bytes. */
data += n;
} else
if (n != -1) {
/* C Library bug: Should never occur. */
close(fd);
return errno = EIO;
} else {
/* Error in errno. */
const int saved_errno = errno;
close(fd);
return errno = saved_errno;
}
}
if (close(fd) == -1) {
/* It is possible for close() to report a delayed I/O error. */
return errno;
}
/* Success. */
return 0;
}
static void usage(const char *argv0)
{
fprintf(stderr, "\n");
fprintf(stderr, "Usage: %s [ -h | --help ]\n", argv0);
fprintf(stderr, " %s FILE CONTENTS\n", argv0);
fprintf(stderr, "\n");
fprintf(stderr, "This does the same thing as 'echo -n \"CONTENTS\" > FILE'.\n");
fprintf(stderr, "\n");
}
int main(int argc, char *argv[])
{
if (argc < 2) {
usage((argv && argv[0] && argv[0][0]) ? argv[0] : "(this)");
return EXIT_SUCCESS;
} else
if (argc > 3) {
usage((argv && argv[0] && argv[0][0]) ? argv[0] : "(this)");
return EXIT_FAILURE;
} else
if (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) {
usage((argv && argv[0] && argv[0][0]) ? argv[0] : "(this)");
return EXIT_SUCCESS;
}
if (write_file(argv[1], argv[2])) {
fprintf(stderr, "%s: %s.\n", argv[1], strerror(errno));
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
Compile it using e.g. gcc -Wall -Wextra -O2 example.c -o example
, and run using e.g. ./example /sys/bus/pci/devices/.../driver/remove_id "vendor_id device_id"
.
Run it without arguments, or with -h
or --help
as the only argument, and it will print usage information to standard error.
The program essentially does what echo -n "vendor_id device_id" > /sys/bus/pci/devices/.../drivers/remove_id
does.
If it is successful, it will not output anything, just return success (exit status 0). If there is any kind of an error, it will report it to standard error.
If you know the target path is always a device or a pseudo-file (like those in /sys or /proc), use fd = open(path, O_WRONLY | O_NOCTTY | O_CLOEXEC);
instead. O_CLOEXEC
means that if the process forks at any point, this particular file descriptor is not copied to the child process. O_NOCTTY
means that if the path is a tty device, and the current process does not have a controlling terminal, the kernel is NOT to make the opened device the controlling terminal.
echo -n
uses O_CREAT | O_TRUNC
, so that if the target path exists and is a normal file, it is truncated, but if it does not exist, it is created. It does not affect opening existing character devices and pseudofiles. Whenever O_CREAT
is used, there must be a third parameter, which affects the access mode of the file created. This mode is usually 0666
, allowing read and write access as moderated by the current umask. One can obtain the current umask using mode_t mask = umask(0); umask(mask);
. Access mode bits set in umask are always zero in the final access mode, and access mode bits clear in umask are taken from the third parameter of the open() command when the file is created.
Upvotes: 1
Reputation: 5211
Two possible problems:
So, in both cases (in_buf defined as a table or a pointer), strlen(in_buf) instead of sizeof(in_buf) is the most secured solution for the length of the data to write provided that the string is terminated by '\0'.
Upvotes: 1