【问题标题】:write() to sysfs entry /sys/bus/pci/devices/.../driver/remove_id failswrite() 到 sysfs 条目 /sys/bus/pci/devices/.../driver/remove_id 失败
【发布时间】:2020-12-01 07:43:18
【问题描述】:

看到 /sys/bus/pci/devices/.../driver/remove_id 文件上的 write() 函数失败,返回 -1 和 errno 等于 19 (ENODEV)。

但是,通过命令行也可以正常工作。我已经检查了文件权限,似乎可以让用户对该文件执行写入操作(--w------)。

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);

看到 bytes_written 等于 -1 并且 errno 显示为 19。

如果您发现代码 sn-p 有问题,请告诉我?

【问题讨论】:

  • 你需要一个完整的例子,即in_buf的内容应该在你的例子中,#include-s,代码应该在main里面。您还声称通过命令行可以正常工作 - 您必须准确地编写您发出的有效命令行。将其转换为 C 应该是微不足道的,但是由于您没有显示您在做什么,因此无法分辨。 一旦您提供了所有需要的信息,这个问题就很容易解决
  • 好的,谢谢您的建议。根据观察,目标文件需要 sudo 权限,因为它由 root 拥有并且正在尝试使用另一个具有 sudo 权限的用户。通过使用 system() 函数解决了这个问题,并传入了需要执行的命令。

标签: c linux pci sysfs


【解决方案1】:

您没有提供足够的信息来查明问题。

但是,这里有一个示例程序 example.c,它表明是您的实现存在错误:

#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;
}

使用例如编译它gcc -Wall -Wextra -O2 example.c -o example,并使用例如运行./example /sys/bus/pci/devices/.../driver/remove_id "vendor_id device_id"。 不带参数运行它,或者将-h--help 作为唯一参数运行,它会将使用信息打印到标准错误中。

该程序基本上做了echo -n "vendor_id device_id" &gt; /sys/bus/pci/devices/.../drivers/remove_id 所做的事情。

如果成功则不会输出任何东西,只是返回成功(退出状态0)。如果有任何类型的错误,它将报告为标准错误。

如果您知道目标路径始终是设备或伪文件(如 /sys 或 /proc 中的那些),请改用 fd = open(path, O_WRONLY | O_NOCTTY | O_CLOEXEC);O_CLOEXEC 意味着如果进程在任何时候分叉,这个特定的文件描述符不会复制到子进程。 O_NOCTTY表示如果路径是tty设备,并且当前进程没有控制终端,内核不会将打开的设备作为控制终端。

echo -n使用O_CREAT | O_TRUNC,这样如果目标路径存在且是普通文件,则截断,不存在则创建。它不影响打开现有的字符设备和伪文件。无论何时使用O_CREAT,都必须有第三个参数,它会影响创建文件的访问方式。这种模式通常是0666,允许读写访问,由当前的 umask 调节。可以使用mode_t mask = umask(0); umask(mask); 获取当前的umask。 umask 中设置的访问模式位在最终访问模式下始终为零,umask 中清除的访问模式位取自文件创建时 open() 命令的第三个参数。

【讨论】:

  • 感谢您的详细解释。事实上,你分享的这段代码揭示了问题的真正根源。
  • 在打开目标文件时看到 EACCES。使用 sudo,shell 命令通过。 echo "XXXX YYYY" > sudo /sys/bus/pci/devices/.../driver/remove_id pass
  • 所以,为了解决这个问题,使用 system() 函数来运行它。但是,如果可以解释如何处理您发出 sudo 并在 open() 本身运行此命令的文件,那将是一件好事。
  • @Vijay:这是权限的问题。选项各不相同,从使二进制 setuid root (在这种情况下,二进制文件将始终以超级用户/root 权限运行,无论是谁或什么运行它),到将此部分移动到单独的辅助二进制文件(以便只有该部分是 setuid root ),将此部分移动到一个帮助脚本,该脚本使用sudo another-helper-script,它验证参数然后执行回显;和 sudo 配置,允许后一个脚本以 root 身份运行(例如,由管理员用户)。还有其他选择。
  • @Vijay:授予必要权限的“最佳”或推荐方式实际上取决于您的程序做什么,除了从 PCI 设备分离驱动程序。实际上,我建议您提出一个新问题,也许在这里或在 unix.stackexchange.com 上,以获取有关在您的特定整体场景中如何最好地处理特权(授予特权)的建议。请记住,为了能够从 PCI 设备上分离驱动程序,您确实需要 root 权限;但是是否隔离权限,或者如果您的程序的其他部分也需要 root 权限,则决定 如何 您应该这样做。
【解决方案2】:

两个可能的问题:

  1. write()系统调用中使用sizeof(in_buf)会写入字符串“vendorId deviceId”,如果in_buf[ ] 大于 10 个字符。
  2. 也许 in_buf 不是表而是指针,所以 sizeof(in_buf) 将返回 4 或 8(指针的大小分别为 32 位或 64 位系统),但不是它指向的字符串的长度。

所以,在这两种情况下(in_buf 定义为表或指针),strlen(in_buf) 代替 sizeof(in_buf)如果字符串以 '\0' 结尾,则对于要写入的数据长度而言,这是最安全的解决方案。

【讨论】:

  • 感谢您的快速评论。
  • 其实在使用 sizeof(in_buf) 操作符之前,已经尝试过 strlen(in_buf) 得到的“vendor_id device_id”为 9。但是使用 sizeof(in_buf) 时显示 120,即列表大小 120 bytes per宣言。作为检查的一部分,检查了文件权限,这似乎也可以。想知道 errno 显示为 ENODEV,但 sysfs 条目似乎是有效的。
  • 所以,答案是否定的。即使使用 strlen(in_buf) 也没有解决问题。谢谢。
  • 你试过在字符串末尾添加'\n'吗?因为当您执行 "echo "XXXX YYYY" > /sys/bus/pci/devices/.../driver/remove_id" 时会引入一个 '\n'。
  • 感谢您的建议。它也没有用。根据观察,目标文件需要 sudo 权限,因为它由 root 拥有并且正在尝试使用另一个具有 sudo 权限的用户。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-10-25
  • 1970-01-01
  • 2022-11-16
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多