2012-07-26 14 views
1

我很猶豫要發佈這個問題,因爲我認爲某人已經在某個地方問過它了,但經過很多衝刷之後,我已經空了,所以在這裏。守護進程/背景通過另一個程序的腳本啓動的進程

背景:我正在運行一個本地代理(用C語言編寫,通過TCP監聽),它允許遠程執行少量腳本/命令。 (通過web界面,具體說明)。腳本本身是二進制文件,bash或perl腳本的混合體,只要它們在列表中被允許,代理程序本身並不關心。

(這是一個企業,內部網絡上,這是非常早期的階段,所以請不要在這個時候討論安全性的優點。)

的C代理代碼啓動流程是這樣的:

sprintf(mrun, "%s %s 2>&1", file, args); 
mexec = popen(mrun, "r"); 
[read some returned buffer] 
pclose(mexec); 

這種方法適用於既有外部bash和Perl腳本,提供的腳本只是執行命令(或者在前臺做的事情)。不過,我最近需要擴展一個腳本以包含重新啓動守護進程,在這種情況下,命名爲。

腳本本身(bash)的方法很簡單:

#!/bin/bash 
pkill -9 named 
/local/mnt/named/sbin/named -c /local/mnt/named/var/named.conf & 
echo "restarted" 

我遇到的問題是,該腳本無法完成(即重新啓動的是從來沒有回顯的)時通過C代理運行,所以控制永遠不會返回並且TCP套接字永遠不會被釋放。就代理而言,該流程仍在運行。如果我從終端運行腳本,它可以正常工作,控制權會返回給我。

我是否錯過了一些東西,它允許腳本在從C守護進程分叉時正常執行,而不僅僅是從bash終端調用?

我知道nohup,如果其他所有的都失敗了,我想可以使用它,但是我很好奇是否有其他解決方法。

+1

您的「C代理」是否讀取所有輸出? IOW,它是否在等待它的輸入被對方關閉?如果是這樣,可以預料它永遠不會結束:在後臺啓動的named會將句柄保持爲stdout並且正在運行。因此,即使重新啓動腳本完成,仍然有輸出等待(來自named)。嘗試使用自己的句柄來提供命名:'.../named .../dev/null&'並且看看它是否有幫助 – fork0 2012-07-26 20:42:21

+0

它只讀取一次緩衝區,所以它不會等待輸入。你建議的改變有所幫助。劇本本身似乎完成了,現在我得到了回聲。但是這引發了一個不同的問題:一旦腳本完成,代理程序就不能關閉套接字連接。它只是掛起,直到我殺死命名。所以,現在看起來我在生成進程的過程中遇到了另一個問題,即保持原始TCP連接處於打開狀態,即使它不應該也不會使用相同的端口。 – Randy 2012-07-26 22:03:50

+0

@Randy:在調用'close'之前,嘗試在'SHUT_RDWR'中傳遞'shutdown'來獲取套接字的方式。套接字'fd'可能被複制到子進程。 – jxh 2012-07-26 22:51:44

回答

0

基於從上面的意見反饋,我能拿到劇本繼續啓動守護進程之後的工作,多虧了一些額外的重定向:

/local/mnt/named/sbin/named -c /local/mnt/named/var/named.conf </dev/null &> /dev/null & 

所以,感謝fork0對於該位知識。

之後,我發現TCP套接字連接將無法正常關閉,即使腳本已完成工作。在經過一些更多的信息並做了大量的研究之後,事實證明,子進程將繼承(並保持打開)來自父進程(包括套接字)的文件描述符。

我仔細查看了一些方法來斷定子進程,但沒有真正發現任何對我有用的方法(或者不構成整個代理程序的重寫)。

最後,我偶然發現了這個問題,這是關係而不是在一個編程語言,我用:

os.execute without inheriting parent's fds

這基本上涉及到的子進程關閉代碼中任何打開的文件描述符,從而釋放他們被父母關閉。 (我認爲?)

我在bash腳本中添加了幾行代碼,以便在啓動named之前執行此操作,並且它可以工作。

for i in `nawk 'BEGIN{ for(i=1;i<=255;i++) print i}'` 
do 
eval exec `echo $i | sed -e 's/.*/&<\&-/'` 
done 

(我想起來用NAWK而不是以次因爲我需要它在Solaris和Linux上運行。)

一些基本的測試表明,這已經解決了不能夠到插座的主要問題關閉,但我需要做更多的研究,以確定這是否會產生我不知道的其他後果。也許還有更好,更安全的方式來實現這一目標,但至少我正處在正確的軌道上。