Clamav殺毒軟體源碼分析筆記[五]
刺蝟@http://blog.csdn.net/littlehedgehog
[Socket通訊]
套間字socket常用於服務/客戶模型的應用程式之間的通訊和資料連線,需要重點關注的是這個所謂的服務端/用戶端完全可以是一台電腦的兩個應用程式。前面我們已經提到了,Clamd就是這個服務端,也就是說它是整個程式的頂樑柱,所有的關鍵邏輯處理都是它一人為之,不可不謂之強悍。所有的這些功勞都應該歸功於Clamd中的線程處理,不過我們先還不是來關注線程的,先看看socket的建立。
- int localserver(const struct optstruct *opt, const struct cfgstruct *copt, struct cl_node *root)
- {
- struct sockaddr_un server;
- int sockfd, backlog;
- struct cfgstruct *cpt;
- struct stat foo;
- char *estr;
- /* 下面是初始化套間字 順便貼上了設定檔中有關localsocket的資訊
- # Path to a local socket file the daemon will listen on.
- # Default: disabled
- LocalSocket /tmp/clamd
- */
- memset((char *) &server, 0, sizeof(server));
- server.sun_family = AF_UNIX;
- strncpy(server.sun_path, cfgopt(copt, "LocalSocket")->strarg, sizeof(server.sun_path));
- if ((sockfd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1)
- {
- estr = strerror(errno);
- /*
- fprintf(stderr, "ERROR: socket() error: %s/n", estr);
- */
- logg("!Socket allocation error: %s/n", estr);
- exit(1);
- }
- if (bind(sockfd, (struct sockaddr *) &server, sizeof(struct sockaddr_un)) == -1)
- {
- if (errno == EADDRINUSE) //連接埠號碼或者socket被其他進程使用
- {
- if (connect(sockfd, (struct sockaddr *) &server, sizeof(struct sockaddr_un)) >= 0) //這裡串連成功表明socket確實是被別的進程使用了
- {
- close(sockfd);
- logg("!Socket file %s is in use by another process./n", server.sun_path);
- exit(1);
- }
- if (cfgopt(copt, "FixStaleSocket")) //該socket不是被別的進程佔據了,下面我們要做修複工作,設定檔中有FixStaleSocket 意思是要修理失效的socket
- {
- logg("^Socket file %s exists. Unclean shutdown? Removing.../n", server.sun_path);
- if (unlink(server.sun_path) == -1) //從檔案系統中刪除一個名稱。如果名稱是檔案的最後一個串連,並且沒有其它進程將檔案開啟,名稱對應的檔案會實際被刪除。
- {
- estr = strerror(errno);
- logg("!Socket file %s could not be removed: %s/n", server.sun_path, estr);
- exit(1);
- }
- if (bind(sockfd, (struct sockaddr *) &server, sizeof(struct sockaddr_un)) == -1) //再次bind如果失敗
- {
- estr = strerror(errno);
- logg("!Socket file %s could not be bound: %s (unlink tried)/n", server.sun_path, estr);
- exit(1);
- }
- }
- else if (stat(server.sun_path, &foo) != -1)
- {
- logg("!Socket file %s exists. Either remove it, or configure a different one./n", server.sun_path);
- exit(1);
- }
- }
- else //這裡出現了其他錯誤
- {
- estr = strerror(errno);
- logg("!Socket file %s could not be bound: %s/n", server.sun_path, estr);
- exit(1);
- }
- }
- logg("Unix socket file %s/n", server.sun_path);
- if ((cpt = cfgopt(copt, "MaxConnectionQueueLength"))) //Maximum length the queue of pending connections may grow to. backlog參數為提出串連請求後,在伺服器接收該串連請求時的等待隊列中的串連數
- backlog = cpt->numarg;
- else
- backlog = CL_DEFAULT_BACKLOG;
- logg("Setting connection queue length to %d/n", backlog);
- if (listen(sockfd, backlog) == -1)
- {
- estr = strerror(errno);
- /*
- fprintf(stderr, "ERROR: listen() error: %s/n", estr);
- */
- logg("!listen() error: %s/n", estr);
- exit(1);
- }
- acceptloop_th(sockfd, root, copt);
- return 0;
- }
注意這裡建立的是本地socket的,區別比較大,但是在我這裡唯一的區別就是加粗了,形成了很黑很粗壯的效果...
這裡我們主要就是建立個socket的,其實也沒啥好說的,主要是整個函數的調用,將改變整個曆史進程方向──acceptloop_th(sockfd, root, copt); 因為我們要被一直束縛在裡面,貌似永世不得超生了。
acceptloop_th 很長很強大,所以我也就不把源碼貼出來了,否則有湊字數的嫌疑,重點關注下下面一塊:
- /* set up signal handling */
- sigfillset(&sigset);
- sigdelset(&sigset, SIGINT);
- sigdelset(&sigset, SIGTERM);
- sigdelset(&sigset, SIGSEGV);
- sigdelset(&sigset, SIGHUP);
- sigdelset(&sigset, SIGPIPE);
- sigdelset(&sigset, SIGUSR2);
- sigprocmask(SIG_SETMASK, &sigset, NULL); //通過這條語句 進程有了新的訊號屏蔽集 這裡是屏蔽一些我們不關心的訊號
- /* SIGINT, SIGTERM, SIGSEGV */
- sigact.sa_handler = sighandler_th;
- sigemptyset(&sigact.sa_mask);
- sigaddset(&sigact.sa_mask, SIGINT);
- sigaddset(&sigact.sa_mask, SIGTERM);
- sigaddset(&sigact.sa_mask, SIGHUP);
- sigaddset(&sigact.sa_mask, SIGPIPE);
- sigaddset(&sigact.sa_mask, SIGUSR2);
- sigaction(SIGINT, &sigact, NULL); //下面都是安裝新的訊號處理常式
- sigaction(SIGTERM, &sigact, NULL);
- sigaction(SIGHUP, &sigact, NULL);
- sigaction(SIGPIPE, &sigact, NULL);
- sigaction(SIGUSR2, &sigact, NULL);
為什麼這裡需要重新設定這麼多的訊號處理呢?原因就是我們要屏蔽或者攔截其中有些訊號,做出相應的反應,這貌似是廢話...???? 比如SIGINT,這訊號會導致進程終止,我們需要在終止之前把記錄寫入log檔案,ok,必須攔截訊號函數.