2008/02/29

[Programming][Kernel] 核心同步導論

共享記憶體的應用程式環境裡面,資源必須加以保護以免發生同時存取的情況。即便是核心的本身也不能例外。保護共享資源的目的,在於防止不同的「執行緒 (threads of execution)」同時存取或修改同一個資源,彼此間互相覆蓋修改資料或存友到還沒完成改變的狀態。因為並行競爭的關係,而導致系統變得不穩定,又非常難以追蹤,所以在程式寫作的一開始注意一些細節是很重要的習慣。

保護的重要性


保護記憶體的重要性 -> 同交易 Transaction 的重要性(略)

同步鎖定


同步鎖定可以提供所需要的機制:作用方式等同家裡的門鎖。關鍵區域可以比喻做門後的房間。這個房間一次只能容許一個人(執行緒)進入,當人進入房間,它會將身後的房門鎖上,對共享資源完成更動才會打開門鎖,離開房間。上鎖期間,如果有另外一個執行緒前來敲門,也只能在門外等候,如果裡面的人還沒有完事,就沒有辦法進入。這就是同步鎖的重要性:它可以避免並行存取 (concurrency) 並且保護佇列內的資料完整。

並行存取發生的原因


在用戶層,同步的需求來自於核心排程器可以主動決定應用程式的執行權。因此在任何時刻,程序都可能會非自願地在關鍵區域中失去執行權,換另一個程序使用處理器。如果這個新來的程序稍進入同一個關鍵區域(假設兩個程序都是一般的執行緒,存取一樣的共享資源),就會引發競爭的狀況 (race condition)。當多個單執行緒的程序分享檔案,或單一程序碰到系統傳來的「信號 signals 」時也可能產生類似的問題;因為系統信號會在不定時刻以「非同步」方式發生,這種並行處理並不是真的同時發生,而是因為切割執行時間導致的狀況,我們在這裡稱之為「虛擬並行 - (pseudo-concurrency)」。

在能進行對稱多重處理 (symetrical multiprocessing) 的機器上面,兩組處理器可能同時執行同一份關鍵程式碼。這種狀況稱為「真。並行處理 (true concurrency)」。雖然這兩種並行處理的語意和成因都不相同,但兩者都會導致相同的競爭情況,需要加以設想保護的機制。

核心也有類似機制可能造成並行處理的情況,分別是:

  • 中斷 (interrupts)

  • softirqs 與 tasklets

  • 核心先佔 (kernel preemption)

  • 休眠與用戶層同步

  • 對稱式多重處理


可以避免中斷程式並行干擾的程式碼被稱作「 interrupt-safe (對中斷而言是安全的)」。可以避免在對稱多重處理的機器上面出現並行干擾的程式碼被稱作為 「SMP-safe (對 SMP 而言安全)」。可以避免在核心搶占執行權後出現並行干擾的程式碼則叫「 preempt-safe 」。

哪些資料是需要鎖定保護的呢?大多數全域性的核心資料結構會需要。有一個簡單明顯的判斷法則是:如果其它執行緒可以存取同一組的資料,就需要用到同步鎖。如果其它人也可以看見這筆資料,就鎖上它。要記得是鎖定資料而不是程式碼。

每當撰寫核心的程式碼的時候,你後

  • 資料是否有全域的屬性?另外一個執行緒是否也可以存取它?

  • 程序環境與中斷環境是否共享這些資料?是否在兩個不同的中斷處理程序中共用?

  • 如果程序存取這份資料時發生了執行權的轉移,取得執行權的新程序是否會存取相同的資料?

  • 目前的程序是否會在某些情況下休眠 (暫停)?如果會,共享資源會變成什流狀態?

  • 有沒有辦法避免其它程序錯誤地刪除 (釋放) 使用中的資源 ?

  • 如果別的處理器同時使用這個函式,會發生什麼事?

  • 你打算在程式裡頭做什麼?

[Programming][Kernel] 中斷與中斷處理程式(1)

中斷簡述


核心的一個工作任務是管理週邊的硬體設備,不過由於核心處理器的科技進步發展,現在的處理器通常速度都比週邊快上好幾個量級,對於一些很慢的週邊,核心發出請求之後不應該空等它的回應,等它真的完成工作之後再回來處理相關的事宜。為了達成這個目的,其中一個做法是「輪詢 (polling)」的方式,來回一個週期的檢查週邊的狀態並加以反應。但是這樣做其實很花力氣,無論週邊有沒有完成任務,核心都還要花力氣去檢查。如果可以,讓週邊主動通知核心介入的時機,核心在其它的時間完全不用花力氣去理會,顯然比上述的做法要好上很多,我們稱之為「中斷(interrupts)」。

舉例來說,當你打字的時候,鍵盤控制器會發出中斷提醒作業系統要注意:有按鍵被按下了。這些電氣訊號就是中斷訊號。收到迅號後,處理器會通知作業系統處理新進的資料,週邊相對於處理器會以非同步的方式產生中斷,意即中斷可以在任何時間點發生。因此如要在中斷發生時能立即加以處理,核心也需要能隨時被打斷才可以。

每個週邊會連結到特定的中斷,而每個中斷會對應到一個特定的數值。作業系統得以藉此判斷訊號是由哪個硬體裝置所發出,進而採用不同的應用程式來加以處理。這些數值,如同電話號碼一般,代表的是不同的線路,所以又被稱作「中斷要求線路的週邊裝置(interrupt request lines),或簡稱為 IRQ lines。像 PC 上的計時器使用的中斷就是 IRQ 0 ,而 IRQ 1 通常會是由鍵盤所使用。

中斷處理程式


核心裡面用來處理特定中斷的函式叫做「中斷處理程式 (interrupt handler)」或「中斷服務例行常式」 (interrupt service routine 簡稱做 ISR )。每個產生中斷的週邊都會跟一個中斷處理程式相連。舉例來說,核心中可能會有甲函式專責處理系統計時器的中斷,而乙函式則負責處理鍵盤所產生的訊號。其實,對於週邊來說,中斷處理程式就是「驅動程式」的一部份,放在核心當中,負責管理該項硬體。

中斷處理程式一般都是 C 函式來處理,為了讓核心可以用一致的方式來傳遞資訊,這類函式的介面必須符合特定的原型(prototype),除此之外,寫法跟一般的函式沒有什麼兩樣。與其它的函式不同的地方在於中斷發生的時候,核心會用中斷處理程式回應週邊的請求,同時,他們的執行環境很特別,叫做中斷執行環境(interrupt context),簡稱做中斷環境,這一點晚點再來做詳細的介紹。

因為中斷隨時都會發生,所以中斷處理程式會隨時執行,為了能盡快的讓被打斷的程式回工作,迅速地完成處理是一件很重要的事情。所以雖然週邊硬體會希望它的中斷要求能盡快得到滿足,但是對系統其它的部份來說,被打斷的時間要越短越好。在極簡單的情況下,中斷處理程式只需要回應硬體,我得到你的通知了,這樣就可以讓硬體回去工作。但大多的情況下並不是這樣,中斷程式往往還有很多事情要做。舉網路裝置的中斷處理程式為例:除了回應硬體之外,處理程式還得把封包由網路硬體複製到系統主記憶體上面,稍加處理之後,再把資料傳給合適的協層 (protocol stack) 或應用程式。顯然,這是很麻煩的工作。(尤其在網卡資料量愈來愈多的現代高速網卡)

2008/02/27

[軟體] Firefox 的 temp dir

蠻討厭的一個情況, firefox 在 download 時會先 create 一個 temp file 出來,這個 temp file 不見得跟 download target 同一個目錄,所以如果 download dir 有空間, temp dir 沒有空間就出了問題。
所以把 firefox 需要的 tempdir 都先找過了:

autodownload 模式用這個
pref("browser.download.dir", "/root/");
pref("browser.download.downloadDir", "/root/");
pref("browser.download.useDownloadDir", true);


extension 底下應該是用這個 (未確認)
pref("mozex.general.tmpdir","/root/");

不過呢,上述都不是我前面想用的,真正的解是在 /usr/lib/firefox/firefox 這個啟動的 script 底下 export TMPDIR=/root/,另外 download dir 如果不存在,會走到 pwd 去,所以也可以順便在 export PWD 一下。

2008/02/26

[Programming][Kernel] 系統呼叫

系統呼叫是核心程式提供的一組介面,用來與用戶層 (user-space) 中的程序互動,應用程式透過系統呼叫可以存取硬體及作業系統所提供的資源。這面扮演著郵差般的角色,讓應用程式可以對系統提出各種要求,而核心程式收到要求後會給予回應,意即滿足或拒絕這些要求。這組界面的存在讓應用程式受到約束,不能為所欲為,這也是系統之所以可以穩定的重要因素。

系統呼叫如同中間層般的存在,讓硬體及核心與用戶層得以分隔開來,在 Linux 系統底下應用程式除了 exception (例外處理) 與 trap (一種由處理器所發出,可加以截獲處理的錯誤處理方式) 只能藉由系統呼叫與核心溝通。其它型態的介面像裝置檔案 (device nodes) 或 proc 檔案系統,最終也是透過系統呼叫與核心做溝通。

用戶層的應用程式沒有辦法直接使用核心層的程式碼這是系統呼叫裡頭一個重要的原則。所以應用程式必須利用某種方式通知通知核心,同時切換到核心去執行該功能。在這邊所指的機制,就是產生軟體中斷 (software interrupt),這會讓硬體隨後產生一個異常的狀況 (exception) ,進而啟用異常處理的中介程式 (expeption handler) 來處理。

以 x86 平台為例,軟體中斷可以使用指令 $0x80 觸發,觸發後就會啟動系統到核心層,執行第 128 號程式,這個程式就是前述系統呼叫中介程式中的一員,它有一個很直覺的名字,叫 system_call() 。由於處理方式和硬體架構相關,所以採用的是組合語言的形式,放在 entry.S6 檔案裡頭。 近年來, x86 處理器加入了一個名為 sysenter 的功能。與使用 int 中斷指令相比,這個功能提供了更快速專屬的方式,可以藉由這個方式進入核心層,執行系統呼叫。這個功能很快就成為主流的一部份。然而不管系統如何呼叫中介程式,最重要的還是在用戶層用例外觸發或 trap 的方式進入核心執行的方式。

2008/02/25

[Programming][Kernel] 程序的排程

一般排程演算法都是「根據程序優先序(priority-based)」來決定排程的方式。作法是:根據程序的價值和所需的處理器時間,將程序分成不同的等級。具有較高優先序的程序會比低優先序的程序先執行,而具有相同優先序的程序則會以 round-robin (一個接一個輪流,反覆這種過程) 的方式進行排程。在某些系統中(linux 也包含在內),高優先序的程序也會取得較長的執行時段。擁有可執行時段及最高優先的可執行程序總是會優先執行。使用者與系統都可以藉改變程序的優先序來影響系統的排程行為。 (賦予 nice 值)

基於這樣的想法, Linux 提出了「以動態優先序為基礎(dynamic priority-based)」的排程方案。它的概念是說,在一開始的時候賦予程序基本的優先序,然後讓排程器藉動態改變優先序的方式,來滿足想達成的排程目標。例如,如果某程序等待 I/O 的時間遠超過實際的執行時間,則這個程序明顯屬於 I/O 密集的類型。這時,按照 Linux 的做法,跟著的動作就是增加這個程序的優先序。反之,如果某程序總是一口氣就用完它被賦予的 timeslice ,則明顯是處於處理器密集的類型,跟著,它的優先序就會機動的減少。

# (優先序 nice 值,是愈低則分配到的 timeslice 會愈多,所以上文的優先序調高用的是「增加優先序(值)」而不是「調高優先序」,主要是因為中文裡頭的「高優先」,明顯是要先做,在文中容易引起歧義,故避免之。)

[軟體] Amsn (中文問題﹑ tls 問題修正)

這篇 作者應該是叫小菜吧?... 好可愛的名字。
這篇主要是寫來記錄 amsn 在中文問題上面的處理,其實可以直接下 fixamsn.sh 來處理,就好。

不過一來 fixamsn.sh 裡面的 amsn 不是最新版,所以一安裝完就開始哀說要更新新版,二來,在我的 gusty 上面裝好了之後, tls 不會自動安裝好,手動也還是有問題。所以索性開 fixamsn.sh 出來看,在 amsn 的版本上面是用 amsn-0.97RC1.tar.bz2 ,這行如果直接換掉,後面 tls 也不會有問題,版本也不用重更新了。
diff -u fixamsn.sh fixamsn.orig.sh
--- fixamsn.sh 2008-02-25 11:15:18.000000000 +0800
+++ fixamsn.orig.sh 2008-02-25 11:12:47.000000000 +0800
@@ -160,10 +160,10 @@
wget -c $SOURCEFORGE/tcl/tk8.5a6-src.tar.gz || error_exit "Could not download Tk source code."
tar -xsf tk8.5a6-src.tar.gz
mv tk8.5a6 tk
- #amsn 0.96
- wget -c $SOURCEFORGE/amsn/amsn-0.97RC1.tar.bz2 || error_exit "Could not download aMSN source code."
- tar -xsf amsn-0.97RC1.tar.bz2
- mv amsn-0.97RC1 amsn
+ #amsn svn snap
+ wget -c http://www.amsn-project.net/amsn_dev.tar.gz || error_exit "Could not download aMSN source code."
+ tar zxvf amsn_dev.tar.gz
+ mv msn amsn

fi

#compile tcl (into /usr/local, to leave tcl 8.4 untouched)


另外值得一提的是,這個 fixamsn.sh 的作者是 Vuen ,原文發表應該是在英文版的 ubuntu 論壇上面。

這個需要註冊才能下載的 script 我上傳到我的 googlepage 底下:
fixamsn.sh

ㄟ... 一樣有 amsn 版本的問題,所以我順手上傳了我的版本比較乾脆,省的 patch 。

2008/02/22

[Programming] PHP-GTK


> PHP Warning: PHP Startup: Unable to load dynamic library
> '/usr/lib/php/modules/php_gtk2.so' - /usr/lib/php/modules/php_gtk2.so:
> undefined symbol: phpi_get_le_gd in Unknown on line 0

這邊是回說:
要嘛把 GD 從 php enable 起來(所以要重編 php),不然就是 disable 掉 php-gtk 的 GD 支援。
不過其實我的解法是:
yum install php-gd
然後重新 configure php-gtk && make && make install 就好了。

留個記錄,問題沒那麼複雜哦~

[Linux][資訊安全] 安全漏洞

除了之前 vmsplice 的漏洞之外,今天又在 milw0rm 上面看到一個 xorg-xfs的漏洞 。

這應該是在說明,個人用的 Linux Desktop ,還是要盡量縮小帳號的開放,不小心就又不知不覺給人家 root 權了。

2008/02/15

[Linux] PGP 流程及說明

先來說明一下在網路上面傳遞訊息最怕的問題是什麼:

  1. 明文傳遞的訊息內文很容易藉由各種方式取得,像是 sniffer 之類的的。

  2. 網路上的訊息難以驗證它的真實性

  3. 既使上面兩點都忽略,也難以保證這個訊息沒有被竄改過。



因為上述三點原因,所以我們通常不會在網路上面用 msn 討論重要的事情, msn 的內文傳遞沒有加密,很容易用 sniffer 就截聽下來。然而,既使是用 ssl 方式傳遞訊息,如果全程被監聽,且加密程序不夠複雜,也還是會在時間內被破解訊息。更不用說,在使用無線網路的情況下,要監聽溢漏的封包或假造封包也是很簡單的事情。

那麼我們只需做到確保不會有任何除收信人以外的第三者收到訊息訊息不會被竄改 兩點就可以確保訊息的安全傳遞,本篇 PGP 的流程及說明 就在說明問題點的解法及重要觀念:

  1. PGP 的訊息傳遞是安全的傳遞方法。

  2. 數位簽章社交認證 ﹑認證中心 是身分辨別的重要手續



PGP 的傳遞方式


前提: A(發) 跟 B(收) 都知道對方的公錀 看過對方的簽名。

A 發信時使用 B 的(數位簽章)專用加密器加密信件內文 -- 發信 --> B 用自已的私錀 自已的解密器打開信件閱讀。

這樣簡單的方法,就可以確保只有 B 能閱讀內文了嗎?顯然沒有那麼簡單,問題:
1. B 專用的 加密器(數位簽章)|解密器 怎麼來的?
2. B 如何知道這封信是 A 發的?

1. 最好的方式當然是 B 直接用手拿一隻 usb 給 A 說,裡面有我的數位簽章(加密器),你要發信的時候用這個東西加密過,就只有我能解開來看,別人拿去也沒有辦法解讀。不過還有一個方式就是透過社交方式,由其它人認識 B 的人轉交 B 的數位簽章,因為大家都認識都看過 B 的簽名,所以可以相互認證,得知這個數位簽章是不是 B 的,也就不用擔心有人假造簽章,讓你的信件沒辦法正確傳達給 B 。(當然也還有認證中心這一條線可以得到 B 的數位簽章)

2. 如何知道是 A 發的呢? 重點在於(數位簽章)加密器和(私錀)解密器的特性上面。這兩個資料分別是兩個很大的質數 p, q ,當 p 拿來加密時,只有 q 能拿來解密。當 q 拿來加密時,只有 p 能解密。也因此,在判別是不是 A 所發的信件時,我們可以把加密器跟解密器互換,用 私錀 來對信件的時間內文等特徵加密,再傳送給 B 之後,讓 B 使用 A 的數位簽章解密,只要信件內文﹑時間等特徵相符合,就可以證明是 A 所發的。

以上,傳遞安全﹑雙方驗證安全,剩下的就只有保護好你所持有的私錀跟讓大家都認識你的數位簽名了。

--
參考文件: PGP 簡介(簡中)

2008/02/14

[Linux] env 的用法

Q: #!/usr/bin/env bash 跟 #!/usr/bin/bash 有什麼不一樣?
呃... 其實我也不知道,所以我也跑去 man env 看了一下:
NAME
env - run a program in a modified environment

SYNOPSIS
env [OPTION]... [-] [NAME=VALUE]... [COMMAND [ARG]...]


呃…這有看沒有懂說。

結果今天在使用的時候才明白:
bash 有的時候會放在不是 /usr/bin 底下,所以下 env bash 可以給出如果 bash 不在 /usr/bin/ 底下的情況,還能使用 bash 做 interpreter。
同樣的情況也會發生在 php/pike 的身上。

像是有的時候 php cli 不是自已裝的,script 就會寫在
#!/usr/bin/php ,可是這個 script 拿到別台機器 php cli 是放在 /usr/local/bin/ 底下的就出問題啦。
所以寫做 #!/usr/bin/env php 就可以帶來一種安全的便利。

[Programming][Kernel] 程序的建立

程序的建立


Unix 在程序的產生上採取相當獨特的作法。絕大多數系統會設計一種叫「 spawn 」的機制,為新的程序先建立起新的定址空間,之後便讀進執行檔,並開始執行它。 Unix 系統上會將這些步驟分離成兩個函式,分別為 fork() 跟 exec() 。首先說明的是: fork() 會產生一個子程序,這是當前 task 的複製品;跟父程序不同的地方只有 PID﹑ PPID﹑特定的資源(如懸滯信號)與統計資料等不會作繼承動作的東西。第二個函式則是 exec() ,這個函式會將新的執行檔載入到定址空間裡面,同時開始執行它。將 fork() 和 exec() 依序結合後,就跟大部份的作業系統上所使用的單一函式相同。

※ 「大部份的作業系統上所使用的單一函式」這是在說 像 win32 上的 system() 嗎?還是 exec() ?

Copy-on-Write


以往的作法在呼叫 fork() 時,所有的父程序所擁有的資源都會複製一份給子程序。我們在複製時可以很容易發現到,不少資源可以藉由共享的動作來讓父子程序共用;所以每一樣資源都複製的動作不旦原始也缺乏效率。更糟的是,如果緊接著的動作就是執行新的程式,所先前所作的複製就完全是畫蛇添足了。在 Linux 系統底下, fork() 會藉由「 copy-on-write 」分頁(就是寫入分頁前才進行複製的動作)這個機制來防止前面提到的這種浪費行為。 Copy-on-Write 或簡稱 (COW) 是一種延遲或完全防止資料複製的技術。父程序和子程序並非各自擁有完整的空間,而是共享單一個程序定址空間。共享的資料會預先打上特殊的標記,以確保在寫入動作發生的時候再來進行對應的資料複製;到發生寫入動作完成後,程序才可以說是「擁有不同的資料」,而在這之前,資料都是以唯讀的方式進行共享的動作。這個技術可以延遲定址空間內的資料複製,進而讓記憶體分頁寫入的動作減少,可以說是相當重要的最佳化機制,讓程序得以快速的執行,而不用在意分頁上的資源與時間浪費。

2008/02/13

[Firefox] 修改 alert 提示對話框

想把 alert 拿掉,得先把這些都處理掉。不然就要去搞 embed 底下的東西了吧?
var promptSvc = Components.classes["@mozilla.org/embedcomp/prompt-service;1"].getService(Components.interfaces.nsIPromptService);
真麻煩。

[Programming] PHP-GTK 的中文問題

這篇一樣在做備份記錄:

PHPGTK 的中文問題除了字型找不到之外,就是 codepage 的設定而已。所以在 php.ini 底下設 php-gtk.codepage = UTF8 可以解決這個問題。
當然也可以直接下 ini_set("php-gtk.codepage","utf8");直接解決。

在 phpgtk 的 dl 問題上面,因為 extension dir 已經在 php script cli 載入前就設下去了,所以沒辦法使用 ini_set 來處理。(不過如果 extension_dir 設為空值或使用 ../../ 的話,應該是可以下絕對路徑來處理。)

更新 2008-03-07 :
phpgtk 的 api reference 看起來少了很多東西,不知道是不是因為還在 1.0 的文件的關係,現在要找 reference 都得先去
php-gtk-2.0.0alpha/ext/gtk+
底下再 grep function 出來,然後再去 GTK+ Reference 找參考。

[Programming] pttget.pl

備份~ 抓 ptt 上面文章的 perl script (從 kenwu 那邊修改的,好像有點小問題,我也忘了~)

#!/usr/bin/perl
#
# PTT BBS Board Crawler BETA 3, Powered By LiloHuang
# http://blog.roodo.com/kenwu/
# 使用 GPL V2 釋出擷取文章功能部份程式
# Last Updated: 2007/09/02
#
# Fix: 修正 guest無法登入問題
#

$|=1;
use strict;
use Net::Telnet;
use Time::HiRes qw(usleep);

system("title KUSO PTT BBS Board Crawler BETA 3");
print "########################################\n";
print "# KUSO PTT BBS Board Crawler BETA 3 #\n";
print "# Powered By LiloHuang #\n";
print "# http://blog.roodo.com/kenwu/ #\n";
print "########################################\n\n";
print "說明: 這是一個可以將 PTT BBS看板進行抓取的程式...\n";
print " 目前仍在測試階段, 還有許\多Bug與不穩定...\n";
print " 僅供測試使用, 切莫用於非法用途侵權他人文章!!\n\n";

my $host;
print "站台: \n";
print " 1. ptt.cc\n";
print " 2. ptt2.cc\n";
print "請選擇: ";
my $site = 1; #;
chomp $site;
$host = ($site eq '1'?"ptt.cc":"ptt2.cc");
mkdir("./$host") unless(-d "./$host/");
print "\nBBS 帳號: ";
my $id = "我的帳號"; #;
chomp $id;
&except("沒有輸入帳號, 程式即將關閉.") if($id eq '');
print "BBS 密碼: ";
my $pass = "我的密碼"; #;
chomp $pass;
print "看板名稱: ";
my $board = "Sodagreen"; #;
chomp $board;
&except("沒有輸入看板名稱, 程式即將關閉.") if($board eq '');
print "起始文章編號: ";
my $start = 8435; #;
chomp $start;
&except("沒有輸入起始編號, 程式即將關閉.") if($start eq '');
print "結束文章編號: ";
my $end = 8436; #;
chomp $end;
&except("沒有輸入結束編號, 程式即將關閉.") if($end eq '');
&except("結束編號 < 起始編號, 邏輯錯誤, 程式即將關閉.") if($end<$start);

my $num = $end - $start + 1;
my $esc = chr(27);
my @stack;
my $buf;
my $cnt;
my $bot;
my $rec = 0;
my $bar = '─'x39;

my %login = (
"請輸入代號" => "$id\n",
"請輸入您的密碼" => "$pass\n",
"您想刪除其他重複的" => "N\n",
"錯誤嘗試" => "\n",
"任意鍵" => "\n",
"酸甜苦辣板" => "q",
"鴻雁往返" => "q"
);

my %board = (
"任意鍵" => "\n",
);

print "\n正在登入 $host...";
&build;
while( $buf = &get ) {
if($buf =~/由於人數過多,請您稍後再來。/) {
print "線上人數過多, 自動重新連線第 ".sprintf("%3d",++$rec)." 次\r";
$bot->close;
&build;
}
&except("目前已有太多 guest 在站上") if($buf =~/目前已有太多 guest 在站上/);
&except("這裡沒有這個人啦!") if($buf =~/這裡沒有這個人啦/);
&except("密碼不對喔") if($buf =~/密碼不對喔/);
last if($buf =~/.+目前坊裡有.+?$/);
foreach (keys %login) {
&put($login{$_}) if($buf=~/$_/);
}
}
print "Done!\n";

print "進入 $board 看板...";
&put("s");
usleep(1000);
&put("$board\n");
while( $buf = &get ) {
last if($buf =~/文章選讀/);
foreach (keys %login) {
&put($login{$_}) if($buf=~/$_/);
}
}
print "Done!\n\n";

$cnt = 0;
&put("$start\n\n");

mkdir("./$host/$board") unless(-d "./$host/$board/");

while( $buf = &get ) {
last if($buf =~/文章選讀/);
push(@stack, $buf);
print "正在擷取第 ".sprintf("%3d",($cnt+1))." 篇文章...\r";
unless( $buf !~ /\Q (100%)\E/)
{
&callback($start+$cnt);
undef @stack;
last if ++$cnt==$num;
}
&put(".");
}
print "\n\n總共 $cnt 文章擷取完畢\n\n";
$bot->close;
system("PAUSE");

sub callback {
open(FH,">./$host/$board/$_[0].ans");
my @out_buffer;
my $pos = 0;
for(my $i=0;$i<=$#stack;$i++) {
my @tmp = split(/\n/, $stack[$i]);
$tmp[0]=~s/^$esc\[H$esc\[J/$i==0?"":"\n"/e;
my $linetag = pop @tmp;
if($i > 0 && $i==$#stack) {
if($i < 2) {
if($linetag=~/目前顯示: 第 (\d+)~(\d+) 行/) {
my $rexp_s = $1;
$rexp_s = $rexp_s - 1 if($stack[$i]=~/$esc\[36m$bar$esc\[m/);
foreach(@tmp) {
s/$esc\[\d+;\d+H/\n/;
$out_buffer[$rexp_s++] = $_;
}
}
}else{
shift(@tmp) if($i>0);
foreach(@tmp) {
s/$esc\[\d+;\d+H/\n/;
$out_buffer[$pos++] = $_;
}
}
}else{
shift(@tmp) if($i>0);
foreach(@tmp) {
$out_buffer[$pos++] = $_;
}
}
}

foreach(@out_buffer) {
s/$esc\[K//g;
s/\r/\n/g;
print FH $_;
}
close(FH);
}

sub put {
$bot->put($_[0]);
usleep(200000);
}

sub get {
usleep(200000);
return $bot->get( Timeout => 10000 );
}

sub error {
print "網路連線逾時或程式發生異常, 程式即將關閉!\n\n";
system("PAUSE");
$bot->close;
exit;
}

sub except {
print "$_[0]\n\n";
system("PAUSE");
exit;
}

sub build {
$bot = new Net::Telnet (
Port => 23,
Timeout => 30,
Errmode => \&error
);
$bot->open($host);
}

[Programming] 一個程設的題目

做一下備份

void
ReverseWord(char *begin,char *end)
{
if ((begin==NULL) || (end==NULL)) return;
if (end < begin) return;
char *q = begin;
char *p = end;
char c;
while(q < p)
{
c = *q;
*q = *p;
*p = c;
q++;
p--;
}
}

int
main()
{
char str[] = "This is a test";
ReverseWord(str, str+(strlen(str)-1));
char *p,*q;
p = q = str;
while(*p!=0)
{
if (*p==' ')
{
ReverseWord(q,p - 1);
while((*p==' ')&&(*p!=0))
p ++;
if (*p!=0)
{
q = p;
p++;
}
} else {
p++;
}
}
ReverseWord(q,p - 1);
printf("%s\n",str);
return 0;
}

2008/02/11

[Linux] XMLRPC

我的機器被人家掃了幾次,主要都是針對 xmlrpc 這個名字的 script 去做掃描,所以興緻來了去查一下,才從這裡知道:
WordPress的xmlrpc.php腳本中沒有正確驗證對wp.suggestCategories方式的輸入,允許攻擊者通過注入任意SQL代碼控制SQL查詢,導致檢索用戶名和口令哈希。 ...


exploits 在這裡
不過這個 bug 是 2007 年 10 月 27 日就已經發現的了,現在還在掃啊?

2008/02/08

[Linux] Ubuntu 的開機檢查

查 tune2fs 的時候,查到 這篇文章 上的說明:
一次性的檢查

在 man 中沒這項設定,在網路上找到的資料。我們可在根目錄上下(/),設一個 fastboot 或是 forcefsck 檔案,這樣在啟動 ubuntu 時,就會忽略或是強制檢查。

# sudo touch /fastboot(or forcefsck)

倒回去查:這個的相依在 /etc/init.d/checkfs.sh ,換句話說第一個條件在 init.d 底下的 checkfs.sh 不使用的話,這個設定就沒有用了。

它的相反設定是: /forcefsck , 意即 sudo touch /forcefsck 就可以在下次開機的時候強迫開啟檢查的功能。而,如果想要保持它的設定,不要只是「一次性」用完就丟的話,把 /etc/init.d/checkfs.sh 的 #109 這行
rm -f /fastboot /forcefsck 拿掉,或直接 tune2fs -c -1 /dev/sda1 #device node (其實 0 跟 -1 ,在 tune2fs 裡面都是設到 -1 去)

2008/02/01

[軟體] Subversion 回復上一動

GSLin月初有提到 svn undo 的部份,不過因為 svn 版本的關係,或其它的因素,所以他的操作我沒有辦法很順利的操作。
像是 COMMITED:PREV 這些關鍵字的不存在(?)
svn merge -r COMMITED:PREV .
svn: Syntax error in revision argument 'COMMITED:PREV'


失敗了,所以回頭了解一下: svn merge 是什麼動作?

下面的指令可以查到說明:
svn help merge
像我是裝 ubuntu + 繁中的語言支援就可以看到:
svn merge: 將兩個來源之間的差異套用至工作複本路徑.
1. 第一種形式中, 來源 URL 各被指定到修訂版 N 與 M. 這兩個就是作為
比較的來源. 如果沒有指定修訂版的話, 預設為 HEAD.

2. 在第二種形式中, 對應到兩個來源的工作複本路徑的 URL 定義出用以
進行比較的來源. 這裡必須指定修訂版.

3. 第三種形式中, SOURCE 可為 URL 或工作複本項目, 後者會使用對應的
URL. 在修訂版 REV 的 URL 會以它存在於修訂版 N 與 M 的路徑來作
比較. 如果未指定 REV, 就會假設為 HEAD. '-c M' 選項等同於
'-r N:M', 其中 N = M-1. 使用 '-c -M' 則為相反: '-r M:N',
其中 N = M-1.


意即:
svn merge -c 和 -r 在某些條件下是一樣的動作。
假設現在是 rev1523
svn merge -r 1523:1521 . 就是把 1523(目前|新) 跟 1521(舊)的差異套用到現在的目錄底下。
同樣的動作也可以寫作:
svn merge -c -1521 . 把(目前)的版本跟 1521 版之間的差異套用到現在的目錄底下。

了解了這個動作的目的,就可以明白,在 svn 底下作 undo 的操作前, svn up|revert 的動作是一定要做的;至少讓目前的 rev 是在 svn repo 存在的,這樣才不會發生 Conflict 的情況。

[Windows] git-bash 底下的工具

因為工作轉到 Windows 平台上的關係,所以很多工具改到 Windows 上面運作,跟著在 TortoiseGit 底下使用 git-bash 來維護自己的專案原始碼。結果就是裝了前面提過的 auto-hotkey 使用熱鍵來提昇自己的平台操作速度; 但除了 hotkey...