-
Notifications
You must be signed in to change notification settings - Fork 202
Description
Under certain conditions (e.g. sudo with the log_input or log_output flag) the stdin of Mitogen's first stage can be set non-blocking (O_NONBLOCK) this results in fp.read() returning None if the buffer is empty.
The error seen by the user looks like
MITO000
Traceback … TypeError: a bytes-like object is required, not 'NoneType'
This may be related to #712, #1185.
Credit to @Forgetyk for the original error and diagnosis, their words follow.
Environment
Component Version OS Linux 5247089-it57222 5.15.0-142-generic #152-Ubuntu SMP Mon May 19 10:54:31 UTC 2025 x86_64 x86_64 x86_64 x86_64 GNU/Linux Python 3.10.12 sudo 1.9.9 OpenSSH OpenSSH_8.9p1 Ubuntu-3ubuntu0.10, ansible-core 2.12.10 All tests were run with
strategy = mitogen_linearandpipelining = true.Reproducer
Target host – enable sudo I/O logging:
# /etc/sudoers Defaults log_output Defaults!/usr/bin/sudoreplay !log_output Defaults!/usr/local/bin/sudoreplay !log_output Defaults!/sbin/reboot !log_output Defaults loglinelen=0 Defaults logfile=/var/log/sudo.log #Defaults:ansible !log_output, !log_input root ALL=(ALL:ALL) ALL %admin ALL=(ALL) ALL %sudo ALL=(ALL:ALL) ALL @includedir /etc/sudoers.dTarget host – ssh settings:
# /etc/ssh/sshd_config Protocol 2 SyslogFacility AUTH LogLevel INFO LoginGraceTime 60 PermitRootLogin no PermitEmptyPasswords no IgnoreRhosts yes HostbasedAuthentication no X11Forwarding no ClientAliveInterval 900 ClientAliveCountMax 0 AllowTcpForwarding no ChallengeResponseAuthentication no UsePAM yes Subsystem sftp /usr/lib/openssh/sftp-server PrintMotd no ListenAddress 0.0.0.Controller – standard Mitogen config (only the interesting bits):
# ansible.cfg [defaults] strategy_plugins = /…/ansible_mitogen/plugins/strategy strategy = mitogen_linear [ssh_connection] pipelining = trueInventory
[target] iac-test-2 ansible_user=ansible ansible_python_interpreter=/usr/bin/python3Playbook
become: yes gather_facts: no hosts: - iac-test-2 roles: - some_role vars: some_var: "{{ var }}"Result
BlockingIOError: [Errno 35] Resource temporarily unavailable mitogen.core.py", line 2247, in writeThe crash appears within a few hundred ms, long before any module code is executed.
What is happening
sudo log_output/log_inputspawns a pseudo‑terminal in front of the command it runs and copies all I/O through that PTY.
The PTY’s kernel buffer is only ~64 KiB.- Mitogen opens both ends of its pipes O_NONBLOCK and uses an event loop.
- During the bootstrap it pushes ~35 KiB of compressed preamble in one go; a couple of other writes follow immediately.
When the PTY buffer fills up,write()returnsEAGAIN→BlockingIOError, which is not retried and kills the stream.Plain Ansible (no Mitogen) performs the same copy through sudo, but its
os.write()is blocking, so it simply waits and survives.Work‑around that mask the issue
Defaults:ansible !log_output, !log_inputmight be a solution for other similar problems, but it is forbidden within our production regulations.Because disabling I/O logging is off‑limits for compliance, we need Mitogen to gracefully retry
EAGAIN(or temporarily switch to blocking writes).-- Originally posted by @Forgetyk in #1299 (comment)