1. 程式人生 > >linux C : 子程序監聽父程序使用的socket埠問題

linux C : 子程序監聽父程序使用的socket埠問題

前言

在cm中,有個服務程式,收到socket命令後,啟動一個子程序。
即使子程序沒有任何socket操作,用lsof -i :port 來檢視,也會看到子程序在監聽父程序開的socket埠。
如果父程序由於某種原因退出了(假設是崩潰,除錯或收到了web系統的命令退出),再重啟父程序,埠被子程序監聽,導致bind失敗。
因為沒想到問題原因,看程式碼也看不出來,就擱了一段時間。
前幾天,同事發現,如果手工啟動子程序,是不會佔用父程序埠的。就是說,如果不是父程序啟動的子程序,子程序就不會監聽父程序的socket埠。
發現這個現象後,我們幾個搞C的同事,立馬都想得到了,哦,原來是子程序繼承了父程序的socket控制代碼。
再去查資料,原來走在前面的同學將這個問題都解決了。當時,隨便挑了一個解決方法搞定。那個方法是父程序建立socket控制代碼後,在父程序裡,用控制API,關掉了應該繼承給子程序的socket控制代碼(並不影響父程序自己使用剛建立的socket控制代碼), 這種方法感覺好暴力。還有方法是建立socket時,引數2或上SOCK_CLOEXEC標誌,子程序就不繼承父程序的socket控制代碼了,這種方法正規。

今天將這個實驗做了一下。
加上Makefile的改進,將service, client, sub_proc,都放在一個工程中,根據Makefile傳入引數的不同,可以分別build多個工程(不同的工程編譯選項,不同的輸出檔名)。又寫了一個控制指令碼,將編譯,執行,測試都一次執行完,很方便。
測試流程為:
* 啟動service, 等待client來聊天.
* 執行client, client 發命令,要求啟動sub_proc,收到應答後,退出.
* service收到client命令後,啟動sub_proc.
* sub_proc啟動後,睡x秒退出
* 在sub_proc未退出前,用lsof命令觀察sub_proc是否佔用service開的socket埠。

經過實驗,可得出結論。當建立一個socket後,要固定做2件事:
* 將建立的socket埠不讓子程序繼承
* 將建立的socket埠設定成埠地址可複用。

工程下載點

實驗

工程實現的預覽,就按Source Insight中的順序來,不整理了.

#!/bin/bash
# ==============================================================================
# @file build_all_project.sh
# ==============================================================================
killall my_service killall my_client killall my_sub_proc make BUILD_TYPE="service" rebuild make BUILD_TYPE="client" rebuild make BUILD_TYPE="sub_proc" rebuild ./my_service ./my_client sleep 1 ps aux | grep my_ lsof -i :55555 sleep 1 killall my_service sleep 2 ./my_service sleep 1 ps aux | grep my_ lsof -i :55555 sleep 1 ps aux | grep my_ lsof -i :55555 sleep 1 ps aux | grep my_ lsof -i :55555
// @file client.cpp
// @brief 

#ifdef MACRO_ON_MAKEFILE_BUILD_AS_CLIENT

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#include <errno.h>

#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>

#include "my_syslog.h"

#include "client.h"

void show_socket_err_log();

int client()
{
    MYLOG_D(">> client");

    int i_rc = -1;
    int sk = SOCKET_INVALID;
    struct sockaddr_in sa;
    char sz_buf[1024] = {'\0'};

    do {
        // when create socket, use flag SOCK_CLOEXEC
        // set parent's socket port don't listen by sub proc
        sk = socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, 0);
        if (sk < 0) {
            show_socket_err_log();
            break;
        }

        // socket port re use
        int i_reuse = 1;
        i_rc = setsockopt(sk, SOL_SOCKET, SO_REUSEADDR, (char *)&i_reuse, sizeof(i_reuse));
        if (i_rc != 0) {
            show_socket_err_log();
            break;
        }

        MYLOG_D("socket create ok");

        memset(&sa, 0, sizeof(sa));
        sa.sin_family = AF_INET;
        sa.sin_addr.s_addr = inet_addr(SERVICE_IP);
        sa.sin_port = htons(SERVICE_PORT);

        i_rc = connect(sk, (struct sockaddr*)&sa,sizeof(sa));
        if (i_rc != SOCKET_OPT_OK) {
            show_socket_err_log();
            break;
        }

        MYLOG_D("socket connect ok");

        memset(sz_buf, 0, sizeof(sz_buf));
        strcpy(sz_buf, "start");
        i_rc = send(sk, sz_buf, sizeof(sz_buf), 0);
        if (i_rc < 0) {
            show_socket_err_log();
            break;
        }

        MYLOG_D("socket send ok");

        memset(sz_buf, 0, sizeof(sz_buf));
        i_rc = recv(sk, sz_buf, sizeof(sz_buf), 0);
        if (i_rc < 0) {
            show_socket_err_log();
            break;
        }

        MYLOG_D("socket recv ok");

        MYLOG_D("server answer : %s", sz_buf);
    } while (0);

    if (SOCKET_INVALID != sk) {
        close(sk);
        sk = SOCKET_INVALID;
    }

    MYLOG_D("<< client");
    return 0;
}

void show_socket_err_log()
{
    int i_rc = errno;
    MYLOG_D("socket_error : %s", strerror(i_rc));
}

#endif // #ifdef MACRO_ON_MAKEFILE_BUILD_AS_CLIENT

// @file client.h
// @brief 

#ifdef MACRO_ON_MAKEFILE_BUILD_AS_CLIENT

#include "my_syslog.h"

#define SERVICE_IP "127.0.0.1"
#define SERVICE_PORT 55555
#define SOCKET_OPT_OK 0
#define SOCKET_INVALID -1

int client();
#endif // #ifdef MACRO_ON_MAKEFILE_BUILD_AS_CLIENT

// @file main.cpp

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h> // daemon
#include <signal.h>

#include "my_syslog.h"

#ifdef MACRO_ON_MAKEFILE_BUILD_AS_SERVICE
#include "service.h"
#endif // #ifdef MACRO_ON_MAKEFILE_BUILD_AS_SERVICE

#ifdef MACRO_ON_MAKEFILE_BUILD_AS_CLIENT
#include "client.h"
#endif // #ifdef MACRO_ON_MAKEFILE_BUILD_AS_CLIENT

#ifdef MACRO_ON_MAKEFILE_BUILD_AS_SUB_PROC
#include "prog_not_use_socket.h"
#endif // #ifdef MACRO_ON_MAKEFILE_BUILD_AS_SUB_PROC

#ifndef SAFE_DELETE
#define SAFE_DELETE(p) \
    if (NULL != (p)) { \
        delete (p); \
        (p) = NULL; \
    }
#endif // #ifndef SAFE_DELETE

void init(const char* psz_log_owner_name);
void uninit();
void proc_sig_term(int num);
int fn_test();

int main(int argc, char** argv)
{
#ifdef MACRO_ON_MAKEFILE_BUILD_AS_SERVICE
    init("my_service");
#endif // #ifdef MACRO_ON_MAKEFILE_BUILD_AS_SERVICE

#ifdef MACRO_ON_MAKEFILE_BUILD_AS_CLIENT
    init("my_client");
#endif // #ifdef MACRO_ON_MAKEFILE_BUILD_AS_CLIENT

#ifdef MACRO_ON_MAKEFILE_BUILD_AS_SUB_PROC
    init("my_sub_proc");
#endif // #ifdef MACRO_ON_MAKEFILE_BUILD_AS_SUB_PROC

    fn_test();
    uninit();

    MYLOG_D("THE END");
    return EXIT_SUCCESS;
}

void init(const char* psz_log_owner_name)
{
    int i = 0;

    // only service, sub_proc prog run on background    
#ifdef MACRO_ON_MAKEFILE_BUILD_AS_SERVICE
    daemon(0, 0);
#else MACRO_ON_MAKEFILE_BUILD_AS_SUB_PROC
    daemon(0, 0);
#endif // #ifdef MACRO_ON_MAKEFILE_BUILD_AS_SERVICE

    ns_syslog::open_syslog((NULL != psz_log_owner_name) ? psz_log_owner_name : "ns_syslog");

    // 設定控制變數中的日誌條件, 實際應用中, 是從配置檔案讀取的控制開關
    ns_syslog::g_log_condition.b_EMERG = false;
    ns_syslog::g_log_condition.b_CRIT = true;
    ns_syslog::g_log_condition.b_ALERT = true;
    ns_syslog::g_log_condition.b_ERR = true;
    ns_syslog::g_log_condition.b_WARNING = true;
    ns_syslog::g_log_condition.b_NOTICE = true;
    ns_syslog::g_log_condition.b_INFO = true;
    ns_syslog::g_log_condition.b_DEBUG = true;

    // 根據控制變數, 設定日誌的mask
    // 在實際應用中, 這裡可以是動態設定, e.g. 配置檔案檢測執行緒發現配置變了, 需要變更某些級別的日誌記錄結果
    ns_syslog::set_log_level(
        ns_syslog::g_log_condition.b_EMERG, 
        ns_syslog::g_log_condition.b_ALERT,
        ns_syslog::g_log_condition.b_CRIT,
        ns_syslog::g_log_condition.b_ERR,
        ns_syslog::g_log_condition.b_WARNING,
        ns_syslog::g_log_condition.b_NOTICE,
        ns_syslog::g_log_condition.b_INFO,
        ns_syslog::g_log_condition.b_DEBUG);

    // clear screen (print 25 empty line)
    for (i = 0; i < 25; i++) {
        MYLOG_D("");
    }

    signal(SIGTERM, proc_sig_term);
}

void uninit()
{
    ns_syslog::close_syslog();
}

void proc_sig_term(int num)
{
    MYLOG_D("SIGTERM = %d, num = %d", SIGTERM, num);
    MYLOG_D("maybe can do some clean task after quit");
    exit(1);    
}

int fn_test()
{
    MYLOG_D(">> fn_test()");

#ifdef MACRO_ON_MAKEFILE_BUILD_AS_SERVICE
    service();
#endif // #ifdef MACRO_ON_MAKEFILE_BUILD_AS_SERVICE

#ifdef MACRO_ON_MAKEFILE_BUILD_AS_CLIENT
    client();
#endif // #ifdef MACRO_ON_MAKEFILE_BUILD_AS_CLIENT

#ifdef MACRO_ON_MAKEFILE_BUILD_AS_SUB_PROC
    sub_proc();
#endif // #ifdef MACRO_ON_MAKEFILE_BUILD_AS_SUB_PROC


    MYLOG_D("<< fn_test()");
    return 0;
}

# ==============================================================================
# @file makefile
# ==============================================================================
# @note 
#   test case - see value from Makefile commandline or in the Makefile
#       make BUILD_TYPE="service" rebuild
#       make BUILD_TYPE="client" rebuild
#       make BUILD_TYPE="sub_proc" rebuild

MY_MAKE_FILE_PATH_NAME = $(MAKEFILE_LIST)

BIN_NAME_SERVICE = my_service
BIN_NAME_CLIENT = my_client
BIN_NAME_SUB_PROC = my_sub_proc

BUILD_TYPE_AS_SERVICE = service
BUILD_TYPE_AS_CLIENT = client
BUILD_TYPE_AS_SUB_PROC = sub_proc

IS_BUILD_TYPE_VALID = 0

IS_BUILD_TYPE_SERVICE = 0
IS_BUILD_TYPE_CLIENT = 0
IS_BUILD_TYPE_SUB_PROC = 0

MACRO_ON_MAKEFILE_BUILD_AS = MACRO_ON_MAKEFILE_BUILD_AS_UNKNOW

BIN = invalid_bin

ifdef BUILD_TYPE
    ifeq ($(BUILD_TYPE), $(BUILD_TYPE_AS_SERVICE))
        IS_BUILD_TYPE_VALID = 1
        IS_BUILD_TYPE_SERVICE = 1
        BIN = $(BIN_NAME_SERVICE)
        MACRO_ON_MAKEFILE_BUILD_AS = MACRO_ON_MAKEFILE_BUILD_AS_SERVICE
    else ifeq ($(BUILD_TYPE), $(BUILD_TYPE_AS_CLIENT))
        IS_BUILD_TYPE_VALID = 1
        IS_BUILD_TYPE_CLIENT = 1
        BIN = $(BIN_NAME_CLIENT)
        MACRO_ON_MAKEFILE_BUILD_AS = MACRO_ON_MAKEFILE_BUILD_AS_CLIENT
    else ifeq ($(BUILD_TYPE), $(BUILD_TYPE_AS_SUB_PROC))
        IS_BUILD_TYPE_VALID = 1
        IS_BUILD_TYPE_SUB_PROC = 1
        BIN = $(BIN_NAME_SUB_PROC)
        MACRO_ON_MAKEFILE_BUILD_AS = MACRO_ON_MAKEFILE_BUILD_AS_SUB_PROC
    else
        IS_BUILD_TYPE_VALID = 0
    endif
else
    IS_BUILD_TYPE_VALID = 0
endif

LINE80 = --------------------------------------------------------------------------------
CC = g++ -std=c++98
CFLAGS = -Wall -g

INC = -I.
LIBPATH = -L/usr/lib/ -L/usr/local/lib/

ifeq (1, $(IS_BUILD_TYPE_SERVICE))
    LIBS = 
else ifeq (1, $(IS_BUILD_TYPE_CLIENT))
    LIBS = 
else ifeq (1, $(IS_BUILD_TYPE_SUB_PROC))
    LIBS = -lcurses
else
    LIBS = 
endif

DEPEND_CODE_DIR = ./empty_dir \

DEPEND_CODE_SRC = $(shell find $(DEPEND_CODE_DIR) -name '*.cpp')
DEPEND_CODE_OBJ = $(DEPEND_CODE_SRC:.cpp=.o)

ROOT_CODE_SRC = $(shell find ./ -name '*.cpp')
ROOT_CODE_OBJ = $(ROOT_CODE_SRC:.cpp=.o)

SUB_CODE_DIR = ./socket_easy
SUB_CODE_SRC = $(shell find $(SUB_CODE_DIR) -name '*.cpp')
SUB_CODE_OBJ = $(SUB_CODE_SRC:.cpp=.o)

.PHONY: help
help:
    clear
    @echo "usage:"
    @echo
    @echo "build project as service"
    @echo "make BUILD_TYPE=\"${BUILD_TYPE_AS_SERVICE}\" rebuild"
    @echo
    @echo "build project as client"
    @echo "make BUILD_TYPE=\"${BUILD_TYPE_AS_CLIENT}\" rebuild"
    @echo
    @echo "build project as client"
    @echo "make BUILD_TYPE=\"${BUILD_TYPE_AS_SUB_PROC}\" rebuild"

.PHONY: clean
clean:
    clear

    @echo
    @echo
    @echo
    @echo
    @echo
    @echo
    @echo
    @echo
    @echo
    @echo

    @echo
    @echo
    @echo
    @echo
    @echo
    @echo
    @echo
    @echo
    @echo
    @echo

    @echo
    @echo
    @echo
    @echo
    @echo

    @echo make clean
    @echo $(LINE80)

    @echo "@file $(MY_MAKE_FILE_PATH_NAME)"

    @echo "IS_BUILD_TYPE_VALID = $(IS_BUILD_TYPE_VALID)"
    @echo "IS_BUILD_TYPE_SERVICE = $(IS_BUILD_TYPE_SERVICE)"
    @echo "IS_BUILD_TYPE_CLIENT = $(IS_BUILD_TYPE_CLIENT)"
    @echo "IS_BUILD_TYPE_SUB_PROC = $(IS_BUILD_TYPE_SUB_PROC)"

    @echo $(LINE80)

    rm -f $(BIN) $(ROOT_CODE_OBJ) $(DEPEND_CODE_OBJ) $(SUB_CODE_OBJ)
    rm -rf /usr/lib/$(BIN)
    rm -rf ./$(BIN)

.PHONY: all
all:$(BIN)
    @echo $(LINE80)
    @echo make all
    chmod 777 $(BIN)
    find . -name $(BIN)

$(BIN) : $(ROOT_CODE_OBJ) $(DEPEND_CODE_OBJ) $(SUB_CODE_OBJ)
    $(CC) $(CFLAGS) -o [email protected] $^ $(SHLIBS) $(INC) $(LIBPATH) $(LIBS)

.cpp.o:
    $(CC) -c $(CFLAGS) -D$(MACRO_ON_MAKEFILE_BUILD_AS) $^ -o [email protected] $(INC) $(LIBPATH) $(LIBS)

.PHONY: rebuild
rebuild:
    make -f $(MY_MAKE_FILE_PATH_NAME) clean

ifeq (1, $(IS_BUILD_TYPE_VALID))
    @echo $(LINE80)
    make -f $(MY_MAKE_FILE_PATH_NAME) all
    chmod 775 ./$(BIN)
    ldd ./$(BIN)
else
    @echo $(LINE80)
    @echo "error : make file command line input error, please see help" 
    @echo "please run => make help" 
    @echo $(LINE80)
endif


// @file my_syslog.cpp
// @brief syslog日誌巨集的實現

#include "my_syslog.h"

namespace ns_syslog {

TAG_LOG_CONDITION g_log_condition;

void open_syslog(const char* pszLogOwner)
{
    openlog(((NULL != pszLogOwner) ? pszLogOwner : "my_syslog"), LOG_NOWAIT | LOG_PID, LOG_LOCAL1);
}

void set_log_level(
    bool b_EMERG,
    bool b_CRIT,
    bool b_ALERT,
    bool b_ERR,
    bool b_WARNING,
    bool b_NOTICE,
    bool b_INFO,
    bool b_DEBUG)
{
    int i_mask = 0;

    if (b_EMERG) {
        // LOG_EMERG 日誌會阻塞控制檯程式, 必須要使這個條件為false, 不能執行這裡
        // LOG_EMERG 不僅是記錄到日誌, 還列印到正在執行的程式上, 阻塞了程式的執行. 不能用這種日誌
        i_mask |= LOG_MASK(LOG_EMERG);    
    }

    if (b_ALERT) {
        i_mask |= LOG_MASK(LOG_ALERT);
    }

    if (b_CRIT) {
        i_mask |= LOG_MASK(LOG_CRIT);
    }

    if (b_ERR) {
        i_mask |= LOG_MASK(LOG_ERR);
    }

    if (b_WARNING) {
        i_mask |= LOG_MASK(LOG_WARNING);
    }

    if (b_NOTICE) {
        i_mask |= LOG_MASK(LOG_NOTICE);
    }

    if (b_INFO) {
        i_mask |= LOG_MASK(LOG_INFO);
    }

    if (b_DEBUG) {
        i_mask |= LOG_MASK(LOG_DEBUG);
    }

    setlogmask(i_mask);
}

void close_syslog()
{
    closelog();
}

} // namespace ns_syslog {

// @file my_syslog.h
// @brief syslog日誌巨集的定義

#ifndef __MY_SYSLOG_H__
#define __MY_SYSLOG_H__

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#include <syslog.h>
// #include <unistd.h>
// #include <sys/types.h>

namespace ns_syslog {

typedef struct _tag_log_condition {
    bool b_EMERG;
    bool b_ALERT;
    bool b_CRIT;
    bool b_ERR;

    bool b_WARNING;
    bool b_NOTICE;
    bool b_INFO;
    bool b_DEBUG;

    _tag_log_condition() {
        b_EMERG = false;
        b_ALERT = false;
        b_CRIT = false;
        b_ERR = false;

        b_WARNING = false;
        b_NOTICE = false;
        b_INFO = false;
        b_DEBUG = false;
    }
} TAG_LOG_CONDITION;

extern TAG_LOG_CONDITION g_log_condition;

// ----------------------------------------------------------------------------
// syslog macro
// ----------------------------------------------------------------------------

#define MYLOG_EMERG(fmt, ...) \
if (ns_syslog::g_log_condition.b_EMERG) { \
syslog(LOG_EMERG, "[%s : %s.%d : %s()] : " fmt, "EMERG", __FILE__, __LINE__, __FUNCTION__, ##__VA_ARGS__); \
}

#define MYLOG_EM(fmt, ...) \
if (ns_syslog::g_log_condition.b_EMERG) { \
syslog(LOG_INFO, "[%s : %s.%d : %s()] : " fmt, "EMERG", __FILE__, __LINE__, __FUNCTION__, ##__VA_ARGS__); \
}

// ----------------------------------------------------------------------------

#define MYLOG_ALERT(fmt, ...) \
if (ns_syslog::g_log_condition.b_ALERT) { \
syslog(LOG_ALERT, "[%s : %s.%d : %s()] : " fmt, "ALERT", __FILE__, __LINE__, __FUNCTION__, ##__VA_ARGS__); \
}

#define MYLOG_A(fmt, ...) \
if (ns_syslog::g_log_condition.b_ALERT) { \
syslog(LOG_INFO, "[%s : %s.%d : %s()] : " fmt, "ALERT", __FILE__, __LINE__, __FUNCTION__, ##__VA_ARGS__); \
}

// ----------------------------------------------------------------------------

#define MYLOG_CRIT(fmt, ...) \
if (ns_syslog::g_log_condition.b_CRIT) { \
syslog(LOG_CRIT, "[%s : %s.%d : %s()] : " fmt, "CRIT", __FILE__, __LINE__, __FUNCTION__, ##__VA_ARGS__); \
}

#define MYLOG_C(fmt, ...) \
if (ns_syslog::g_log_condition.b_CRIT) { \
syslog(LOG_INFO, "[%s : %s.%d : %s()] : " fmt, "CRIT", __FILE__, __LINE__, __FUNCTION__, ##__VA_ARGS__); \
}

// ----------------------------------------------------------------------------

#define MYLOG_ERR(fmt, ...) \
if (ns_syslog::g_log_condition.b_ERR) { \
syslog(LOG_ERR, "[%s : %s.%d : %s()] : " fmt, "ERR", __FILE__, __LINE__, __FUNCTION__, ##__VA_ARGS__); \
}

#define MYLOG_E(fmt, ...) \
if (ns_syslog::g_log_condition.b_ERR) { \
syslog(LOG_INFO, "[%s : %s.%d : %s()] : " fmt, "ERR", __FILE__, __LINE__, __FUNCTION__, ##__VA_ARGS__); \
}

// ----------------------------------------------------------------------------

#define MYLOG_WARNING(fmt, ...) \
if (ns_syslog::g_log_condition.b_WARNING) { \
syslog(LOG_WARNING, "[%s : %s.%d : %s()] : " fmt, "WARNING", __FILE__, __LINE__, __FUNCTION__, ##__VA_ARGS__); \
}

#define MYLOG_W(fmt, ...) \
if (ns_syslog::g_log_condition.b_WARNING) { \
syslog(LOG_INFO, "[%s : %s.%d : %s()] : " fmt, "WARNING", __FILE__, __LINE__, __FUNCTION__, ##__VA_ARGS__); \
}

// ----------------------------------------------------------------------------

#define MYLOG_NOTICE(fmt, ...) \
if (ns_syslog::g_log_condition.b_NOTICE) { \
syslog(LOG_NOTICE, "[%s : %s.%d : %s()] : " fmt, "NOTICE", __FILE__, __LINE__, __FUNCTION__, ##__VA_ARGS__); \
}

#define MYLOG_N(fmt, ...) \
if (ns_syslog::g_log_condition.b_NOTICE) { \
syslog(LOG_INFO, "[%s : %s.%d : %s()] : " fmt, "NOTICE", __FILE__, __LINE__, __FUNCTION__, ##__VA_ARGS__); \
}

// ----------------------------------------------------------------------------

#define MYLOG_INFO(fmt, ...) \
if (ns_syslog::g_log_condition.b_INFO) { \
syslog(LOG_INFO, "[%s : %s.%d : %s()] : " fmt, "INFO", __FILE__, __LINE__, __FUNCTION__, ##__VA_ARGS__); \
}

#define MYLOG_I(fmt, ...) \
if (ns_syslog::g_log_condition.b_INFO) { \
syslog(LOG_INFO, "[%s : %s.%d : %s()] : " fmt, "INFO", __FILE__, __LINE__, __FUNCTION__, ##__VA_ARGS__); \
}

// ----------------------------------------------------------------------------

#define MYLOG_DEBUG(fmt, ...) \
if (ns_syslog::g_log_condition.b_DEBUG) { \
syslog(LOG_DEBUG, "[%s : %s.%d : %s()] : " fmt, "DEBUG", __FILE__, __LINE__, __FUNCTION__, ##__VA_ARGS__); \
}

#define MYLOG_D(fmt, ...) \
if (ns_syslog::g_log_condition.b_DEBUG) { \
syslog(LOG_INFO, "[%s : %s.%d : %s()] : " fmt, "DEBUG", __FILE__, __LINE__, __FUNCTION__, ##__VA_ARGS__); \
}

// ----------------------------------------------------------------------------

void open_syslog(const char* pszLogOwner);
void set_log_level(
    bool b_EMERG = false,
    bool b_CRIT = false,
    bool b_ALERT = false,
    bool b_ERR = false,
    bool b_WARNING = false,
    bool b_NOTICE = false,
    bool b_INFO = false,
    bool b_DEBUG = false);
void close_syslog();

} // namespace ns_syslog {

#endif // #ifndef __MY_SYSLOG_H__
// @file prog_not_use_socket.cpp
// @brief 

#ifdef MACRO_ON_MAKEFILE_BUILD_AS_SUB_PROC

#include <stdio.h>
#include <unistd.h>
#include <curses.h> // aptitude install ncurses-dev
#include "prog_not_use_socket.h"

int sub_proc()
{
    char c_tmp = '\0';
    MYLOG_D(">> sub_proc");
    /*
    printf("if parent process don't process Inheritance issues, \n"
        "the sub_proc will be use parent process's socket port,\n"
        "even without any socket operation\n");

    // flush stdin, and press 'q' + enter key to quit
    printf("press 'q' key to quit\n");
    fflush(stdin);
    c_tmp = getchar();
    while (('q' != c_tmp) && (EOF != c_tmp)) {
        c_tmp = getchar();
    }
    */

    sleep(60);

    MYLOG_D("<< sub_proc");
    return 0;
}

#endif // #ifdef MACRO_ON_MAKEFILE_BUILD_AS_SUB_PROC

// @file prog_not_use_socket.h
// @brief 

#ifdef MACRO_ON_MAKEFILE_BUILD_AS_SUB_PROC

#include "my_syslog.h"

int sub_proc();
#endif // #ifdef MACRO_ON_MAKEFILE_BUILD_AS_SUB_PROC

// @file service.cpp
// @brief 

#ifdef MACRO_ON_MAKEFILE_BUILD_AS_SERVICE

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <time.h>

#include <errno.h>

#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>

#include "my_syslog.h"

#include "service.h"

int service()
{
    int sk_service = SOCKET_INVALID;
    int i_rc = 0;
    struct sockaddr_in sa;

    MYLOG_D(">> service");
    do {
        // when create socket, use flag SOCK_CLOEXEC
        // set parent's socket port don't listen by sub proc
        sk_service = socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, 0);
        if (sk_service < 0) {
            show_socket_err_log();
            break;
        }

        MYLOG_D("socket create ok");

        // socket port re use
        int i_reuse = 1;
        i_rc = setsockopt(sk_service, SOL_SOCKET, SO_REUSEADDR, (char *)&i_reuse, sizeof(i_reuse));
        if (i_rc != 0) {
            show_socket_err_log();
            break;
        }

        memset(&sa, 0, sizeof(sa));
        sa.sin_family = AF_INET;
        sa.sin_addr.s_addr = htonl(INADDR_ANY);
        sa.sin_port = htons(SERVICE_PORT);

        i_rc = bind(sk_service, (struct sockaddr*)&sa, sizeof(sa));
        if (i_rc != SOCKET_OPT_OK) {
            show_socket_err_log();
            break;
        }

        MYLOG_D("socket bind ok");

        i_rc = listen(sk_service, BACKLOG);
        if (i_rc != SOCKET_OPT_OK) {
            show_socket_err_log();
            break;
        }

        MYLOG_D("socket listen ok");

        fn_socket_service_proc(sk_service);

    } while (0);

    if (sk_service >= 0) {
        close(sk_service);
        sk_service = -1;
    }

    MYLOG_D("<< service");
    return 0;
}

void fn_socket_service_proc(int sk_service)
{   
    int sk_client = SOCKET_INVALID;
    struct sockaddr_in sa;
    socklen_t len = sizeof(sa);

    time_t tt_now;
    char sz_recv[1024];
    int i_cb_recv = 0;

    while(1) {
        MYLOG_D("wait client ...");
        sk_client = accept(sk_service, (struct sockaddr*)&sa, &len);
        if (sk_client < 0) {
            show_socket_err_log();
            break;
        }

        memset(sz_recv, 0, sizeof(sz_recv));
        i_cb_recv = recv(sk_client, sz_recv, sizeof(sz_recv), 0);
        if (i_cb_recv < 0) {
            show_socket_err_log();
            continue;
        }

        if (0 == strcmp(sz_recv, "start")) {
            MYLOG_D("recv cmd : start");
            system("/home/dev/my_sub_proc");
        }

        fn_log_packet(sz_recv, i_cb_recv);

        memset(sz_recv, 0, sizeof(sz_recv));
        tt_now = time(NULL);
        sprintf(sz_recv, "%24s\r\n", ctime(&tt_now));
        send(sk_client, sz_recv, strlen(sz_recv), 0);

        close(sk_client);   
        sk_client = SOCKET_INVALID;
    }
}

void fn_log_packet(const char* p_data, int i_len)
{
    if ((NULL != p_data) && (i_len > 0)) {
        MYLOG_D("packet len = %d", i_len);

        // only print front 4 bytes
        if (i_len >= 5) {
            MYLOG_D("data : %2.2x %2.2x %2.2x %2.2x %2.2x : %c%c%c%c%c", 
                p_data[0],
                p_data[1],
                p_data[2],
                p_data[3],
                p_data[4],

                p_data[0],
                p_data[1],
                p_data[2],
                p_data[3],
                p_data[4]);
        }
    } else {
        MYLOG_D("packet invalid, can't parse");
    }
}

void show_socket_err_log()
{
    int i_rc = errno;
    MYLOG_D("socket_error : %s", strerror(i_rc));
}

#endif // #ifdef MACRO_ON_MAKEFILE_BUILD_AS_SERVICE

// @file service.h
// @brief 

#ifdef MACRO_ON_MAKEFILE_BUILD_AS_SERVICE

#define SERVICE_PORT 55555
#define SOCKET_OPT_OK 0
#define SOCKET_INVALID -1

#define BACKLOG 5

void show_socket_err_log();
void fn_socket_service_proc(int sk_service);
void fn_log_packet(const char* p_data, int i_len);
int service();

#endif // #ifdef MACRO_ON_MAKEFILE_BUILD_AS_SERVICE

// @file readme.txt

// gen log (build and run)
// ./build_all_project.sh >& /home/build_and_run.log

my_client: no process found
my_sub_proc: no process found
find: 鈥?./empty_dir鈥?: 娌℃湁閭d釜鏂囦歡鎴栫洰褰?
find: 鈥?./socket_easy鈥?: 娌℃湁閭d釜鏂囦歡鎴栫洰褰?
make -f  Makefile clean
find: 鈥?./empty_dir鈥?: 娌℃湁閭d釜鏂囦歡鎴栫洰褰?
find: 鈥?./socket_easy鈥?: 娌℃湁閭d釜鏂囦歡鎴栫洰褰?
find: 鈥?./empty_dir鈥?: 娌℃湁閭d釜鏂囦歡鎴栫洰褰?
find: 鈥?./socket_easy鈥?: 娌℃湁閭d釜鏂囦歡鎴栫洰褰?
make[1]: Entering directory `/home/dev'
clear




make clean
--------------------------------------------------------------------------------
@file  Makefile
IS_BUILD_TYPE_VALID = 1
IS_BUILD_TYPE_SERVICE = 1
IS_BUILD_TYPE_CLIENT = 0
IS_BUILD_TYPE_SUB_PROC = 0
--------------------------------------------------------------------------------
rm -f my_service ./service.o ./my_syslog.o ./main.o ./prog_not_use_socket.o ./client.o  
rm -rf /usr/lib/my_service
rm -rf ./my_service
make[1]: Leaving directory `/home/dev'
--------------------------------------------------------------------------------
make -f  Makefile all
find: 鈥?./empty_dir鈥?: 娌℃湁閭d釜鏂囦歡鎴栫洰褰?
find: 鈥?./socket_easy鈥?: 娌℃湁閭d釜鏂囦歡鎴栫洰褰?
make[1]: Entering directory `/home/dev'
g++ -std=c++98 -c -Wall -g -DMACRO_ON_MAKEFILE_BUILD_AS_SERVICE service.cpp -o service.o -I. -L/usr/lib/ -L/usr/local/lib/ 
g++ -std=c++98 -c -Wall -g -DMACRO_ON_MAKEFILE_BUILD_AS_SERVICE my_syslog.cpp -o my_syslog.o -I. -L/usr/lib/ -L/usr/local/lib/ 
g++ -std=c++98 -c -Wall -g -DMACRO_ON_MAKEFILE_BUILD_AS_SERVICE main.cpp -o main.o -I. -L/usr/lib/ -L/usr/local/lib/ 
main.cpp:64:7: warning: extra tokens at end of #else directive [enabled by default]
make[1]: Warning: File `prog_not_use_socket.cpp' has modification time 17 s in the future
g++ -std=c++98 -c -Wall -g -DMACRO_ON_MAKEFILE_BUILD_AS_SERVICE prog_not_use_socket.cpp -o prog_not_use_socket.o -I. -L/usr/lib/ -L/usr/local/lib/ 
g++ -std=c++98 -c -Wall -g -DMACRO_ON_MAKEFILE_BUILD_AS_SERVICE client.cpp -o client.o -I. -L/usr/lib/ -L/usr/local/lib/ 
g++ -std=c++98 -Wall -g -o my_service service.o my_syslog.o main.o prog_not_use_socket.o client.o  -I. -L/usr/lib/ -L/usr/local/lib/ 
--------------------------------------------------------------------------------
make all
chmod 777 my_service
find . -name my_service
./my_service
make[1]: 璀﹀憡錛氭嫻嬪埌鏃墮挓閿欒銆傛偍鐨勫壋寤哄彲鑳芥槸涓嶅畬鏁寸殑銆?
make[1]: Leaving directory `/home/dev'
chmod 775 ./my_service
ldd ./my_service
    linux-vdso.so.1 =>  (0x00007fff7c553000)
    libstdc++.so.6 => /usr/lib/x86_64-linux-gnu/libstdc++.so.6 (0x00002b68c28c0000)
    libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00002b68c2bc7000)
    libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00002b68c2e4a000)
    libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00002b68c3060000)
    /lib64/ld-linux-x86-64.so.2 (0x00002b68c269e000)
find: 鈥?./empty_dir鈥?: 娌℃湁閭d釜鏂囦歡鎴栫洰褰?
find: 鈥?./socket_easy鈥?: 娌℃湁閭d釜鏂囦歡鎴栫洰褰?
make -f  Makefile clean
find: 鈥?./empty_dir鈥?: 娌℃湁閭d釜鏂囦歡鎴栫洰褰?
find: 鈥?./socket_easy鈥?: 娌℃湁閭d釜鏂囦歡鎴栫洰褰?
find: 鈥?./empty_dir鈥?: 娌℃湁閭d釜鏂囦歡鎴栫洰褰?
find: 鈥?./socket_easy鈥?: 娌℃湁閭d釜鏂囦歡鎴栫洰褰?
make[1]: Entering directory `/home/dev'
clear




make clean
--------------------------------------------------------------------------------
@file  Makefile
IS_BUILD_TYPE_VALID = 1
IS_BUILD_TYPE_SERVICE = 0
IS_BUILD_TYPE_CLIENT = 1
IS_BUILD_TYPE_SUB_PROC = 0
--------------------------------------------------------------------------------
rm -f my_client ./service.o ./my_syslog.o ./main.o ./prog_not_use_socket.o ./client.o  
rm -rf /usr/lib/my_client
rm -rf ./my_client
make[1]: Leaving directory `/home/dev'
--------------------------------------------------------------------------------
make -f  Makefile all
find: 鈥?./empty_dir鈥?: 娌℃湁閭d釜鏂囦歡鎴栫洰褰?
find: 鈥?./socket_easy鈥?: 娌℃湁閭d釜鏂囦歡鎴栫洰褰?
make[1]: Entering directory `/home/dev'
g++ -std=c++98 -c -Wall -g -DMACRO_ON_MAKEFILE_BUILD_AS_CLIENT service.cpp -o service.o -I. -L/usr/lib/ -L/usr/local/lib/ 
g++ -std=c++98 -c -Wall -g -DMACRO_ON_MAKEFILE_BUILD_AS_CLIENT my_syslog.cpp -o my_syslog.o -I. -L/usr/lib/ -L/usr/local/lib/ 
g++ -std=c++98 -c -Wall -g -DMACRO_ON_MAKEFILE_BUILD_AS_CLIENT main.cpp -o main.o -I. -L/usr/lib/ -L/usr/local/lib/ 
main.cpp:64:7: warning: extra tokens at end of #else directive [enabled by default]
make[1]: Warning: File `prog_not_use_socket.cpp' has modification time 16 s in the future
g++ -std=c++98 -c -Wall -g -DMACRO_ON_MAKEFILE_BUILD_AS_CLIENT prog_not_use_socket.cpp -o prog_not_use_socket.o -I. -L/usr/lib/ -L/usr/local/lib/ 
g++ -std=c++98 -c -Wall -g -DMACRO_ON_MAKEFILE_BUILD_AS_CLIENT client.cpp -o client.o -I. -L/usr/lib/ -L/usr/local/lib/ 
g++ -std=c++98 -Wall -g -o my_client service.o my_syslog.o main.o prog_not_use_socket.o client.o  -I. -L/usr/lib/ -L/usr/local/lib/ 
--------------------------------------------------------------------------------
make all
chmod 777 my_client
find . -name my_client
./my_client
make[1]: 璀﹀憡錛氭嫻嬪埌鏃墮挓閿欒銆傛偍鐨勫壋寤哄彲鑳芥槸涓嶅畬鏁寸殑銆?
make[1]: Leaving directory `/home/dev'
chmod 775 ./my_client
ldd ./my_client
    linux-vdso.so.1 =>  (0x00007fff6aac3000)
    libstdc++.so.6 => /usr/lib/x86_64-linux-gnu/libstdc++.so.6 (0x00002b6f766c1000)
    libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00002b6f769c8000)
    libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00002b6f76c4b000)
    libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00002b6f76e61000)
    /lib64/ld-linux-x86-64.so.2 (0x00002b6f7649f000)
find: 鈥?./empty_dir鈥?: 娌℃湁閭d釜鏂囦歡鎴栫洰褰?
find: 鈥?./socket_easy鈥?: 娌℃湁閭d釜鏂囦歡鎴栫洰褰?
make -f  Makefile clean
find: 鈥?./empty_dir鈥?: 娌℃湁閭d釜鏂囦歡鎴栫洰褰?
find: 鈥?./socket_easy鈥?: 娌℃湁閭d釜鏂囦歡鎴栫洰褰?
find: 鈥?./empty_dir鈥?: 娌℃湁閭d釜鏂囦歡鎴栫洰褰?
find: 鈥?./socket_easy鈥?: 娌℃湁閭d釜鏂囦歡鎴栫洰褰?
make[1]: Entering directory `/home/dev'
clear




make clean
--------------------------------------------------------------------------------
@file  Makefile
IS_BUILD_TYPE_VALID = 1
IS_BUILD_TYPE_SERVICE = 0
IS_BUILD_TYPE_CLIENT = 0
IS_BUILD_TYPE_SUB_PROC = 1
--------------------------------------------------------------------------------
rm -f my_sub_proc ./service.o ./my_syslog.o ./main.o ./prog_not_use_socket.o ./client.o  
rm -rf /usr/lib/my_sub_proc
rm -rf ./my_sub_proc
make[1]: Leaving directory `/home/dev'
--------------------------------------------------------------------------------
make -f  Makefile all
find: 鈥?./empty_dir鈥?: 娌℃湁閭d釜鏂囦歡鎴栫洰褰?
find: 鈥?./socket_easy鈥?: 娌℃湁閭d釜鏂囦歡鎴栫洰褰?
make[1]: Entering directory `/home/dev'
g++ -std=c++98 -c -Wall -g -DMACRO_ON_MAKEFILE_BUILD_AS_SUB_PROC service.cpp -o service.o -I. -L/usr/lib/ -L/usr/local/lib/ -lcurses
g++ -std=c++98 -c -Wall -g -DMACRO_ON_MAKEFILE_BUILD_AS_SUB_PROC my_syslog.cpp -o my_syslog.o -I. -L/usr/lib/ -L/usr/local/lib/ -lcurses
g++ -std=c++98 -c -Wall -g -DMACRO_ON_MAKEFILE_BUILD_AS_SUB_PROC main.cpp -o main.o -I. -L/usr/lib/ -L/usr/local/lib/ -lcurses
main.cpp:64:7: warning: extra tokens at end of #else directive [enabled by default]
make[1]: Warning: File `prog_not_use_socket.cpp' has modification time 16 s in the future
g++ -std=c++98 -c -Wall -g -DMACRO_ON_MAKEFILE_BUILD_AS_SUB_PROC prog_not_use_socket.cpp -o prog_not_use_socket.o -I. -L/usr/lib/ -L/usr/local/lib/ -lcurses
prog_not_use_socket.cpp: In function 鈥榠nt sub_proc()鈥?:
prog_not_use_socket.cpp:13:7: warning: unused variable 鈥榗_tmp鈥? [-Wunused-variable]
g++ -std=c++98 -c -Wall -g -DMACRO_ON_MAKEFILE_BUILD_AS_SUB_PROC client.cpp -o client.o -I. -L/usr/lib/ -L/usr/local/lib/ -lcurses
g++ -std=c++98 -Wall -g -o my_sub_proc service.o my_syslog.o main.o prog_not_use_socket.o client.o  -I. -L/usr/lib/ -L/usr/local/lib/ -lcurses
--------------------------------------------------------------------------------
make all
chmod 777 my_sub_proc
find . -name my_sub_proc
./my_sub_proc
make[1]: 璀﹀憡錛氭嫻嬪埌鏃墮挓閿欒銆傛偍鐨勫壋寤哄彲鑳芥槸涓嶅畬鏁寸殑銆?
make[1]: Leaving directory `/home/dev'
chmod 775 ./my_sub_proc
ldd ./my_sub_proc
    linux-vdso.so.1 =>  (0x00007ffff91ff000)
    libncurses.so.5 => /lib/x86_64-linux-gnu/libncurses.so.5 (0x00002b8a41537000)
    libtinfo.so.5 => /lib/x86_64-linux-gnu/libtinfo.so.5 (0x00002b8a41759000)
    libstdc++.so.6 => /usr/lib/x86_64-linux-gnu/libstdc++.so.6 (0x00002b8a41983000)
    libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00002b8a41c8a000)
    libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00002b8a41f0c000)
    libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00002b8a42123000)
    libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00002b8a424ae000)
    /lib64/ld-linux-x86-64.so.2 (0x00002b8a41315000)
root      18274  0.0  0.1  11888   512 ?        Ss   00:26   0:00 ./my_service
root      18280  0.0  0.1  18348   564 ?        Ss   00:26   0:00 /home/dev/my_sub_proc
root      18282  0.0  0.1   8060   868 pts/0    S+   00:26   0:00 grep my_
COMMAND     PID USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
my_servic 18274 root    4u  IPv4 118835      0t0  TCP *:55555 (LISTEN)
my_sub_pr 18280 root    5u  IPv4 118836      0t0  TCP localhost:55555->localhost:58706 (CLOSE_