Zookeeper客户端cli_st为何在crontab中运行不正常?

实践中,发现直接在命令行终端运行cli_st时,能够得到预期的结果,但一将它放到crontab中,则只收到:

bye

相关的一段clit_st源代码如下:

if (FD_ISSET(0, &rfds)) {
int rc;
int len = sizeof(buffer) - bufoff -1;
if (len <= 0) {
fprintf(stderr, "Can't handle lines that long!\n");
exit(2);
}
rc = read(0, buffer+bufoff, len);
if (rc <= 0) {
fprintf(stderr, "bye\n");
break;
}
bufoff += rc;
buffer[bufoff] = '\0';
while (strchr(buffer, '\n')) {
char *ptr = strchr(buffer, '\n');
*ptr = '\0';
processline(buffer);
ptr++;
memmove(buffer, ptr, strlen(ptr)+1);
bufoff = 0;
}
}
zookeeper_process(zh, events);

经推断和测试,以及借助strace工具调查,发现问题出在了“if (FD_ISSET(0, &rfds)) {”一处。正常它应当不成立的。

这导致cli_st主动断开了与zookeeper服务端的连接,从zookeeper的服务端日志文件可以看到这个动作:

caught end of stream exception
Unable to read additional data from client sessionid 0x2513c8566c1000b, likely client has closed socket

这段日志显示,cli_st关闭了连接。

问题的原因即是:

cron在fork子进程后,运行命令之前,会关闭stdin,这样导致clit_st中“if (FD_ISSET(0, &rfds)) {”成立,致使连接被关闭。

可以通过简单程序观察cron会关闭或重定向了stdint:

#include
#include
#include
int main()
{
char buf[1024] = {0};
int n = read(0, buf, sizeof(buf)-1);
printf("n=%d, errno=%d: %m\n", n, errno);
return 0;
}

stdin正常,上面代码的进程会挂住,直接读取到stdin或stdin被关闭。但实际结果是:

n=0, errno=0: Success

read的返回值为0,表示stdin已关闭或重定向了。

可借助dup2让stdin复活:

#include
#include
#include
#include
#include
#include
int main()
{
char buf[1024] = {0};
int n = read(0, buf, sizeof(buf)-1);
printf("n=%d, errno=%d: %m\n", n, errno);
int fd = open("/tmp/abcde", O_RDONLY);
printf("fd=%d\n", fd);
if (-1 == dup2(fd, 0))
printf("dup2 error: %m\n");
n = read(0, buf, sizeof(buf)-1);
printf("n=%d, errno=%d: %m\n", n, errno);
if (n>0)
{
buf[n]=0;
printf("%s\n", buf);
}
return 0;
}

上面这段代码运行结果:

n=0, errno=0: Success
fd=3
n=7, errno=0: Success
dsfsfd

要解决Zookeeper客户端cli_st在cron中运行的问题,最简单的办法是注释掉下段代码,然后重新编译,以跳过读标准输入:

bufoff=0; // 当注释下段代码时,需要加上它应付编译器
buffer[0]=0; // 当注释下段代码时,需要加上它应付编译器
#if 0
if (FD_ISSET(0, &rfds)) {
int rc;
int len = sizeof(buffer) - bufoff -1;
if (len <= 0) {
fprintf(stderr, "Can't handle lines that long!\n");
exit(2);
}
rc = read(0, buffer+bufoff, len);
if (rc <= 0) {
fprintf(stderr, "bye\n");
break;
}
bufoff += rc;
buffer[bufoff] = '\0';
while (strchr(buffer, '\n')) {
char *ptr = strchr(buffer, '\n');
*ptr = '\0';
processline(buffer);
ptr++;
memmove(buffer, ptr, strlen(ptr)+1);
bufoff = 0;
}
}
#endif

cron的实现大致如下,它会将标准输入、输出和出错重定向到/dev/null,这导致后面对stdin的read返回0。有关cron的实现,可以浏览cron.c(http://blog.chinaunix.net/uid-20682147-id-5521210.html):

#include
#include
#include
#include
#include
#include
#include
int main()
{
int n;
char buf[1024];
// 重定向stdin到/dev/null
int fd = open("/dev/null", O_RDWR, 0);
dup2(fd, 0); // 重定向0到fd,0即为stdin
pid_t pid = fork();
if (0 == pid)
{
n = read(0, buf, sizeof(buf)-1); // 返回0
printf("n=%d, errno=%d: %m\n", n, errno);
exit(0);
}
return 0;
}

相关文章:

http://blog.chinaunix.net/uid-20682147-id-4977039.html(Cron运行原理)

dup&dup2:

fid = dup(fildes);

等同于

fid = fcntl(fildes, F_DUPFD, 0);

fid和fildes都指向fildes。

fid = dup2(fildes, fildes2); // 重定向fildes2到fildes

等同于:

close(fildes2);

fid = fcntl(fildes, F_DUPFD, fildes2);

fid、fildes和fildes2指向fildes。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Python中文社区

Python网络编程中的套接字名和DNS解析

这一次要讲的是套接字名和DNS,并且还会涉及到网络数据的发送接受和网络错误的发生和处理。下面说套接字名,在创建和部署每个套接字对象时总共需要做5个主要的决定,主...

3867
来自专栏PhpZendo

深入浅出 Laravel 路由执行原理

可以说几乎所有的框架都会涉及到「路由」的处理,简单一点讲就将用户请求的 url 分配到对应的处理程序。

1663
来自专栏乐百川的学习频道

Python 日志输出

打印日志是很多程序的重要需求,良好的日志输出可以帮我们更方便的检测程序运行状态。Python标准库提供了logging模块,让我们也可以方便的在Python中打...

2539
来自专栏抠抠空间

logging模块

函数式简单配置 import logging logging.debug('debug message') logging.info('info mes...

28212
来自专栏小程序解决方案的专栏

Wafer2 Node.js QuickStart 架构分析

Wafer2 的 Node.js QuickStart 采用了 Koa.js 框架编写,Koa 将整个请求过程看做全异步的操作,使用 Node.js 7.6 开...

6.1K6
来自专栏用户2442861的专栏

tinyhttpd 剖析

http://blog.csdn.net/jcjc918/article/details/42129311

1152
来自专栏闵开慧

tomcat6.0下找不到jasper-runtime.jar

今天有点需求,需要用jasper-runtime.jar包。但是我在我的\apache-tomcat-6.0.16\lib目录下,怎么也找不到这个jar包。结果...

3165
来自专栏月牙寂

docker源码分析(5)---daemon

第一时间获取文章,可以关注本人公众号 月牙寂道长 yueyajidaozhang

3228
来自专栏技术问题

中小型局域网本地配置路由器的详细方法?

中小型企业配置路由器的方法和其他路由器配置很相似,这里我们主要讲解了中小型局域网本地配置路由器的详细方法,接着双击刚刚创建好的“PerformRouterDis...

1050
来自专栏landv

烽火2640路由器命令行手册-01-基础配置命令

使用copy命令可以从tftp服务器读取文件到路由器,也可以将路由器文件系统中的某个文件写到TFTP服务器。

2132

扫码关注云+社区