【问题标题】:Can read but not write proc file in C可以在C中读取但不能写入proc文件
【发布时间】:2019-07-10 22:36:05
【问题描述】:

我正在编写一个 C 程序来更改屏幕亮度,因为 xbacklight 在我的情况下不起作用。解决方案应该是原生 C(没有 system() 函数),因为程序应该可以通过 setuid 作为普通用户执行。调用外部 shell 命令或脚本会导致内核忽略该位。

读取控制亮度的 proc 文件可以正常工作,但使用 C 写入它不会产生任何结果,即使我以 root 身份运行程序。 fprintf 调用返回 -130,指示错误。作为健全性检查,我包含了一个使用 system() 作为注释的工作解决方案。

[...]
const char* brightness = "/sys/class/backlight/intel_backlight/brightness";
f = fopen(brightness, (!strncmp(argv[1], "get", 3)) ? "r" : "rw");
[...]

int get_brightness() {
  int buff;
  fscanf(f, "%d", &buff);
  return buff;
}

int set(int i) {
  i = MAX(0, MIN(255, i));
  fprintf(f, "%d", i);
  printf("%d", i);
  //char *cmd = (char*) malloc(59 *sizeof(char));
  //snprintf(cmd, 59, "echo %d > %s", i, brightness);
  //system(cmd);
  //free(cmd);
}

【问题讨论】:

  • 你为什么使用 rw 模式?
  • 失败后的errno是什么(你的代码没有检查fprintf()失败...)?使用sterror()perror() 获得人类可读的错误。
  • fprintf(f, "%d\n", i); 工作得更好吗?
  • @MarkPlotnick 在/sys/proc 文件中写入的值不必由换行符终止。
  • 要写入/proc,您的进程必须以root 权限运行。你确认# echo 5 > /sys/class/backlight/intel_backlight/brightness实际上改变了亮度吗?一些发行版实现实际上并不接受对brightness 的更改。您应该阅读/sys/class/backlight/intel_backlight/max_brightness 来设置可接受值的上限。

标签: c linux screen-brightness


【解决方案1】:
f = fopen(brightness, (!strncmp(argv[1], "get", 3)) ? "r" : "rw");

"rw" 不是 fopen(3)mode 参数的有效参数。要使用fopen(3)以读/写模式打开文件,您应该使用"r+"

使用"rw" 是未定义的行为——在Linux/glibc 中,它将被视为单个"r",文件将以只读模式打开,printf -> write 将失败。

一般来说,对如此小的文件使用缓冲 i/o 并不是一个非常聪明的主意。如果您需要将格式化数据写入文件描述符,您应该查看dprintf(3)

另外,我只会在 setuid 程序中使用固定值列表,而不必验证参数,并注意验证代码本身不会成为责任,等等。

【讨论】:

  • 我相信这个答案没有抓住重点,请参阅Rules on how to access information in sysfs(并不是说您确定的问题是错误的......)
  • 真的,你错过了 "devpath 的所有元素都必须是真实的目录。指向 /sys/devices 的符号链接必须始终解析到它们的真实目标,并且目标路径必须用于访问设备。这样,设备的 devpath 与事件时使用的内核的 devpath 匹配。"?
  • 我愿意,并且在尝试写入 sysfs 上包含符号链接的路径后进行简单的 strace(或在 fclose 之后进行正确验证),该路径包含导致 EINVAL 的符号链接(无效访问) 包含符号链接的路径。如果您真的认为这只是一个建议,您可以进行测试,然后通过kernel.org 告诉人们他们的文档有误。 ?
  • 别误会,我不是在批评你。从普通文件读/写的角度来看,这是非常不明显的行为。通常,链接是所有实际用途的文件,在这种情况下接受。
  • strace 正在跟踪诸如 open(2) 或 write(2) 之类的系统调用,而不是诸如 fprintf(3) 或 fclose(3) 之类的库函数。而且你永远不会写入 path,而是打开(2)一个路径(如果是符号链接,它将由内核解析),然后写入由 open(2) 返回的文件描述符)。而EINVAL不是无效访问,而是“无效参数”。
最近更新 更多