首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >码头集装箱中的os.getlogin抛出FileNotFoundError

码头集装箱中的os.getlogin抛出FileNotFoundError
EN

Stack Overflow用户
提问于 2022-10-24 23:56:10
回答 1查看 87关注 0票数 0

如果在Docker容器中执行,os.getlogin()将抛出一个FileNotFoundError: [Errno 2] No such file or directory

我知道Python文档建议使用不同的方法,但在某些代码中,我不能简单地更改它。

我使用的是ubuntu22.04和python 3.10.6 (都在Docker容器中)。

我是托管从Windows 10与码头桌面和WSL2。

这是一辆MWE:

代码语言:javascript
运行
复制
FROM ubuntu:22.04

ENV DEBIAN_FRONTEND=noninteractive

RUN apt-get update && \
    apt-get install -y \
        python3

构建并运行它时:

代码语言:javascript
运行
复制
docker build -t mwe .
docker run -it mwe

然后在Docker容器中执行以下操作:

代码语言:javascript
运行
复制
python3 -c "import os; print(os.getlogin())"

它会抛出错误:

代码语言:javascript
运行
复制
Traceback (most recent call last):
  File "<string>", line 1, in <module>
FileNotFoundError: [Errno 2] No such file or directory

有什么好办法来避免这个错误吗?

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2022-10-25 01:38:03

这在一定程度上是Python os.getlogin问题的重复,但答案并没有真正找到问题的根源。

这是而不是的问题。我们可以用最小的C程序来再现相同的行为:

代码语言:javascript
运行
复制
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>

int main() {
    char *login = getlogin();
    if (login == NULL) {
        perror("getlogin");
        exit(1);
    }

    printf("login: %s\n", login);
    exit(0);
}

如果在容器中运行此操作,您将得到输出:

代码语言:javascript
运行
复制
getlogin: No such device or address

通过glibc源代码来跟踪这个错误,错误来自getlogin代码,但实际上是由__getlogin_r_loginuid中的代码引起的。

代码语言:javascript
运行
复制
  if (uid == (uid_t) -1)
      {
            __set_errno (ENXIO);
                  return ENXIO;
      }

我们点击这个代码是因为/proc/self/loginuid的值是:

代码语言:javascript
运行
复制
$ cat /proc/self/loginuid
4294967295

其中4294967295只是-1,在本例中这意味着“未初始化此值”,这是因为我们没有通过通常为我们设置的任何类型的登录管理器输入这个shell。

对于一个简单的解决方法,我们只需将当前的UID写入该文件:

代码语言:javascript
运行
复制
root@d4da9e11f42e:~# python3 -c 'import os; print(os.getlogin())'
Traceback (most recent call last):
  File "<string>", line 1, in <module>
OSError: [Errno 6] No such device or address
root@d4da9e11f42e:~# echo $UID > /proc/self/loginuid
root@d4da9e11f42e:~# python3 -c 'import os; print(os.getlogin())'
root

这将是相对容易烘焙进入您的容器启动。

另一种选择是对os模块进行猴子标记,以便用其他东西(如getpass.getuser())替换os.getlogin

听起来好像WSL没有提供loginuid特性。

这个答案仍然准确地指出了问题所在:getlogin()无法读取loginuid文件,并且您得到了相同的错误(您可以在__getlogin_r_loginuid实现的前面看到代码路径)。

如果您的内核没有提供这个特性,那么您唯一的真正选择就是修复代码--要么修改对os.getlogin()的调用,要么在遗留代码调用os.getlogin()之前安排对os.getlogin()模块进行猴子加密。

一个稍微不那么正统的选择是使用函数插入来覆盖getlogin库调用。

C中的最小fake_getlogin.c代码开始

代码语言:javascript
运行
复制
char *getlogin(void) {
    return "root";
}

将其编译到共享库:

代码语言:javascript
运行
复制
gcc -fPIC -c fake_getlogin.c
ld -o fake_getlogin.so -shared fake_getlogin.o

将其嵌入到Dockerfile中,并安排通过/etc/ld.so.preload预加载它

代码语言:javascript
运行
复制
FROM ubuntu:22.04

RUN DEBIAN_FRONTEND=noninteractive \
    apt-get update && \
    apt-get install -y \
        python3

COPY fake_getlogin.so /lib/fake_getlogin.so
RUN echo /lib/fake_getlogin.so > /etc/ld.so.preload

现在,重新尝试您的原始复制器,您会发现它运行时没有错误:

代码语言:javascript
运行
复制
root@4c86cde47db1:/# python3 -c 'import os; print(os.getlogin())'
root

这假设没有更多特定于WSL的怪癖需要克服。

如果您决定在实践中使用函数插入解决方案,您可能应该安排将构建共享库作为映像构建过程的一部分;例如:

代码语言:javascript
运行
复制
FROM ubuntu:22.04 AS builder

RUN DEBIAN_FRONTEND=noninteractive \
    apt-get update && \
    apt-get install -y \
        build-essential

WORKDIR /src
COPY fake_getlogin.c ./
RUN gcc -fPIC -c fake_getlogin.c && \
    ld -o fake_getlogin.so -shared fake_getlogin.o

FROM ubuntu:22.04

COPY --from=builder /src/fake_getlogin.so /lib/fake_getlogin.so

RUN DEBIAN_FRONTEND=noninteractive \
    apt-get update && \
    apt-get install -y \
        python3

RUN echo /lib/fake_getlogin.so > /etc/ld.so.preload
票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/74187896

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档