C++:从 pthread 调用 sendmail 导致 Broken Pipe
C++: Calling sendmail from pthread results in Broken Pipe
我正在尝试在单独的 pthread
中发送一封包含 sendmail
的电子邮件。此代码在 99.9% 的时间内有效。
void* emailClientThreadFct(void* emailClientPtr)
{
EmailClient* emailClient = static_cast<EmailClient*>(emailClientPtr);
try
{
emailClient->Send();
}
catch (const exception& excep)
{
SYSLOG_ERROR("E-mail client exception: %s", excep.what());
}
delete emailClient;
return NULL;
}
// Send email for current output in a separate thread
pthread_t emailThread;
pthread_attr_t attr;
/* Initialize and set thread detached attribute */
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
pthread_create(&emailThread, &attr, emailClientThreadFct, emailClientObj);
0.1% 的时间,我在执行以下调用时收到错误 fwrite error Broken Pipe
。根据我的阅读,Broken Pipe (EPIPE 32) 通常是接收器问题,但 sendmail 是本地进程......可能是我向 fwrite 发送了太多数据?或者我在我的 pthread 实例化中做错了什么?还是 sendmail 崩溃了?
void EmailClient::Send() const
{
// Flush all open output streams, as recommended by popen man page
fflush(NULL);
string popen_command = "sendmail -t -oi >/dev/null 2>&1");
// Open pipe to Mail Transport Agent (MTA)
errno = 0;
FILE* stream = popen(popen_command.c_str(), "w");
if (stream == NULL)
{
throw exception("Cannot send email popen");
}
errno = 0;
if (fwrite(message.data(), message.size(), 1, stream) < 1)
{
pclose(stream);
throw exception("fwrite error ", strerror(errno));
}
// Close MTA
errno = 0;
if (pclose(stream) == -1)
printf("\"Error closing the MTA pipe (%s)\"", strerror(errno))
}
EPIPE
表示另一端(你正在写入的进程)已经死了。如果存在 fork 失败(popen
调用 shell,因此涉及另一个子进程),则可能会发生这种情况,因为系统中的进程暂时过多。更直接的原因是 sendmail
在读取所有标准输入之前失败并过早退出,例如由于格式错误的电子邮件 headers.
不幸的是,popen
不是一个非常可靠的接口。您最好使用 fork
/execve
或 posix_spawn
,使用临时文件作为输入或 I/O 多路复用 poll
,只是为了能够捕获 sendmail
可能产生的任何错误。或者,您可以尝试使用 -oee
调用 sendmail
,这应该会通过电子邮件报告任何错误,但如果 sendmail
本身的创建失败,这将无济于事。
我正在尝试在单独的 pthread
中发送一封包含 sendmail
的电子邮件。此代码在 99.9% 的时间内有效。
void* emailClientThreadFct(void* emailClientPtr)
{
EmailClient* emailClient = static_cast<EmailClient*>(emailClientPtr);
try
{
emailClient->Send();
}
catch (const exception& excep)
{
SYSLOG_ERROR("E-mail client exception: %s", excep.what());
}
delete emailClient;
return NULL;
}
// Send email for current output in a separate thread
pthread_t emailThread;
pthread_attr_t attr;
/* Initialize and set thread detached attribute */
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
pthread_create(&emailThread, &attr, emailClientThreadFct, emailClientObj);
0.1% 的时间,我在执行以下调用时收到错误 fwrite error Broken Pipe
。根据我的阅读,Broken Pipe (EPIPE 32) 通常是接收器问题,但 sendmail 是本地进程......可能是我向 fwrite 发送了太多数据?或者我在我的 pthread 实例化中做错了什么?还是 sendmail 崩溃了?
void EmailClient::Send() const
{
// Flush all open output streams, as recommended by popen man page
fflush(NULL);
string popen_command = "sendmail -t -oi >/dev/null 2>&1");
// Open pipe to Mail Transport Agent (MTA)
errno = 0;
FILE* stream = popen(popen_command.c_str(), "w");
if (stream == NULL)
{
throw exception("Cannot send email popen");
}
errno = 0;
if (fwrite(message.data(), message.size(), 1, stream) < 1)
{
pclose(stream);
throw exception("fwrite error ", strerror(errno));
}
// Close MTA
errno = 0;
if (pclose(stream) == -1)
printf("\"Error closing the MTA pipe (%s)\"", strerror(errno))
}
EPIPE
表示另一端(你正在写入的进程)已经死了。如果存在 fork 失败(popen
调用 shell,因此涉及另一个子进程),则可能会发生这种情况,因为系统中的进程暂时过多。更直接的原因是 sendmail
在读取所有标准输入之前失败并过早退出,例如由于格式错误的电子邮件 headers.
popen
不是一个非常可靠的接口。您最好使用 fork
/execve
或 posix_spawn
,使用临时文件作为输入或 I/O 多路复用 poll
,只是为了能够捕获 sendmail
可能产生的任何错误。或者,您可以尝试使用 -oee
调用 sendmail
,这应该会通过电子邮件报告任何错误,但如果 sendmail
本身的创建失败,这将无济于事。