• lighttpd cgi不能重启


    1. 背景

    cgi出现coredump后,lighttpd不能拉动cgi重启。

    2. 重现问题

    2.1. cgi实现

    1. /*! cgi简单实现 */
    2. #include
    3. #include
    4. #include
    5. #include
    6. #include
    7. #include
    8. #include
    9. int main(int argc, const char *argv)
    10. {
    11. while (1) {
    12. FCGX_Request request = {0};
    13. FCGX_InitRequest(&request, 0, 0);
    14. FCGX_Accept_r(&request);
    15. system("sleep 100000000");
    16. FCGX_Finish_r(&request);
    17. }
    18. return 0;
    19. }

    2.2. 场景重现

    1. # 运行lighttpd,此处将worker线程配置为4个
    2. /smbdir/third_party/sbin/lighttpd -f /smbdir/third_party/lighttpd/etc/lighttpd.conf -D
    3. # 手动kill掉cgi会出现cgi不能正常启动的问题
    4. killall -11 cgi

    3. 原因

    lighttpd利用mod_facgi模块实现与cgi的交互,而cgi利用标准输入与lighttpd之间的信息互通,当在cgi中执行shell命令时,子进程会继承父进程的一些属性(标准输入也会被继承)。

    3.1. lighttpd判断cgi是否正在运行

    1. gw_fd = fdevent_socket_cloexec(proc->saddr->sa_family, SOCK_STREAM, 0);
    2. if (-1 == gw_fd) {
    3. log_perror(errh, __FILE__, __LINE__, "socket()");
    4. return -1;
    5. }
    6. do {
    7. status = connect(gw_fd, proc->saddr, proc->saddrlen);
    8. log_perror(errh, __FILE__, __LINE__, "status[%d], reason[%s], errno[%d]", status, strerror(errno), errno);
    9. } while (-1 == status && errno == EINTR);
    10. if (-1 == status && errno != ENOENT && proc->unixsocket) {
    11. log_perror(errh, __FILE__, __LINE__,
    12. "connect %s", proc->unixsocket->ptr);
    13. unlink(proc->unixsocket->ptr);
    14. }
    15. close(gw_fd);

    如果此时cgi进程出现coredump,而由其产生的子进程由于继承了cgi的标准输入,会使上述connect系统调用正常返回0,从而导致lighttpd重启cgi进程失败

    3.2. lighttpd重启cgi原理

    1. int status;
    2. /*! lighttpd管理进程会在此处监听子进程状态,如果有子进程终止运行,则fdevent_waitpid_intr会返回 */
    3. if (-1 != (pid = fdevent_waitpid_intr(-1, &status))) {
    4. log_monotonic_secs = server_monotonic_secs();
    5. log_epoch_secs = server_epoch_secs(srv);
    6. /*! 子进程终止运行后,该函数会最终调用gw_spawn_connection重启cgi进程 */
    7. if (plugins_call_handle_waitpid(srv, pid, status) != HANDLER_GO_ON) {
    8. if (!timer) alarm((timer = 5));
    9. continue;
    10. }
    11. switch (fdlog_pipes_waitpid_cb(pid)) {
    12. default: break;
    13. case -1: if (!timer) alarm((timer = 5));
    14. __attribute_fallthrough__
    15. case 1: continue;
    16. }
    17. /**
    18. * check if one of our workers went away
    19. */
    20. for (int n = 0; n < npids; ++n) {
    21. if (pid == pids[n]) {
    22. pids[n] = -1;
    23. num_childs++;
    24. break;
    25. }
    26. }
    27. }

    4. 解决方案

    自行封装形如popen或system类的接口,内部使用execl系列函数执行shell命令。但在执行命令前优先关闭标准输入(close(0);)即可解决该问题。

  • 相关阅读:
    TCP/IP网络编程:P1->理解网络编程和套接字
    SpringBoot SpringBoot 开发实用篇 4 数据层解决方案 4.9 MongoDB 下载与安装
    SystemUI控制状态栏通知面板自动展开和收起
    【TS】笔记-TypeScript环境搭建
    安防视频平台EasyCVR视频调阅全屏播放显示异常是什么原因?
    AI搜索,围攻百度
    自动驾驶数据标注有哪些?
    Explore EP9164S HDMI2.0 1分4分配器
    用DIV+CSS技术设计我的家乡网站(web前端网页制作课作业)南宁绿城之都
    LeetCode557. 反转字符串中的单词 III
  • 原文地址:https://blog.csdn.net/xie__peng/article/details/139831493