我三天的工作結果,和大家分享
建立和配置操作都是在Redhat Linux8.0上做的。系統帶的CVS可執行程序在/usr/bin下面,版本信息為:Concurrent Versions System (CVS) 1.11.2 (client/server)。系統帶的這個cvs可執行程序既可以當客戶端使用,也可以用來建立cvs源代碼庫服務器。
建立一個源代碼庫主要有以下幾步:
(1)初始化cvs服務器環境。
#cvs -d/usr/local/source init
之后進入/usr/local/source,可以看到有一個目錄CVSROOT, 下面是初始化后的CVS服務器配置文件。暫且保持不動。
(2)把cvs服務放到xinetd系統服務中。
首先在/etc/xinetd.d目錄下生成任務配置文件cvspserver,文件名稱可以隨便用。
其中內容大致如下:
service cvspserver
{
flags = REUSE
socket_type = stream
wait = no
user = root
protocol = tcp
server = /usr/bin/cvs
server_args = -f --allow-root=/usr/local/source pserver
disable = no
}
其中server_args一個參數指定了源代碼庫路徑,一個指定了服務器使用密碼認證方式。
第二,要確認/etc/services文件中,有cvspserver關鍵詞,并分配了端口,如:cvspserver 2401/tcp。
第三,重新啟動xinetd服務,cvs服務就可以用了。
(3)測試。假定cvs服務器在192.168.0.205上,系統上有一個用戶cvs。登陸另一臺linxu機器,執行下列命令可以完成測試:
$export CVSROOT=:pserver:cvs@192.168.0.205:2401/usr/local/source
$cvs login
輸入密碼,沒有出錯提示表示登陸成功。
如果想在一個linux系統上建多個源代碼庫,分別提供cvs服務。重復上面步驟就可以了。
第一步時候要注意使用一個不同路徑。
第二步放到xinetd系統服務中稍微麻煩點。/etc/xinetd.d目錄下要生成一個新的任務配置文件,例如cvspserver1,文件中service名稱一定要區分第一個,例如service cvspserver1,server_args做相應變動。還要在/etc/services文件中,加入新的服務端口號,例如:cvspserver1 2402/tcp。重新啟動xinetd服務.
第三步測試時候,可以這樣設定:
$export CVSROOT=:pserver:cvs@192.168.0.205:2402/usr/local/source1
cvs作為并行版本控制系統,多用戶讀寫是需要的。但有時候希望有些用戶只能check out,不能提交新代碼。有時希望cvs上的某些用戶不能check out一些代碼,例如和另一個開發組共用一個CVS,希望另一個組的成員不能check out自己組代碼。
cvs服務器有一套自己的用戶認證機制,我使用時候沒采用,而是使用了系統上用戶直接作為cvs服務器用戶,采用linux組、用戶權限實現了這樣的功能。
舉例說明,在linxu系統上模擬實際兩個開發組a、b,分別有用戶a-user1,a-user2,b-user1,b-user2。
模塊a-source,希望a組成員都可以讀寫,b組成員沒有任何權限?梢允褂靡韵孪盗忻睿
#chown -R a-user1 a-source
#chgrp -R a a-source
#chmod -R 770 a-source
如果希望模塊a-source,只有a-user1可以寫,同組其它用戶可以讀,b組成員沒有任何權限,把chmod命令改成-R 750就行了。
必須說明的,cvs中默認一個用戶checkout代碼時候,會在當前模塊下生成一個鎖文件,如果這個用戶對當前模塊沒有寫權限,讀是不可能的。配合上面的權限設置,必須改一下cvs服務器配置。改成不在當前模塊目錄下生成鎖文件,把鎖文件集中到一個所有用戶都有讀寫權限的目錄。修改配置文件CVSROOT/config:
# Put CVS lock files in this directory rather than directly in the repository. #LockDir=/var/lock/cvs
把LockDir設上就可以了。
bjchenxu 回復于:2004-07-08 13:17:21 |
最后一段講LockDir講得好 |
殺無赦 回復于:2004-07-08 16:54:26 |
太好了,一直不知如何做,謝謝分享 |
smilebbsxp 回復于:2004-07-08 22:01:37 |
想在一個linux系統上建多個源代碼庫的話,只要執行 cvs -d/usr/local/otherpath init 然后在 server_args = -f --allow-root=/usr/local/source pserver 改為 server_args = --allow-root=/usr/local/source --allow-root=/usr/local/otherpath -f pserver 同時到otherpath的CVSROOT里面加個passwd來驗證 |
jhsea3do 回復于:2004-07-08 22:31:20 |
我比較關心那套CVS比較穩定,可靠 經常聽說CVS被攻陷 |
fei 回復于:2004-07-10 01:14:07 |
subversion |
platinum 回復于:2004-07-10 01:26:53 |
CVS 簡單教程 v2.1 日期:2003-04-19 作者:何偉平 版權聲明:本文檔遵循 FDL 版權發布,有關 FDL 的詳細信息,請參考: http://www.fsf.org/copyleft/fdl.html 何偉平 CVS 是 Concurrent Version System(并行版本系統)的縮寫,用于版本管理.如果大家曾經參與過多人協作開發的項目,大家肯定有這樣的痛苦經歷:由于多個人同時修改同一個文件,自己辛辛苦苦修改的程序被別人徹底刪除了.另外,如果你的軟件/程序已經發布了三個版本,而這時候用戶需要你修改第二個版本的東西,也許你會因為只保留了最新版本而痛哭流涕.還有就是你對程序做了一些修改,但是修改很少,你只想給遠方的同事發一個兩個版本之間的差別文件,這樣可以免于郵箱不夠大,網速太慢之類的問題.為了解決類似這樣的問題,以及諸如生成補丁文件,歷史版本修改等,一幫黑客(褒義)在原先 Unix 體系里很成熟的 SCCS 和 RCS 的基礎上,開發了 CVS.(SCCS:Source Code Control System,RCS:Revision Control System) CVS 的基本工作思路是這樣的:在一臺服務器上建立一個倉庫,倉庫里可以存放許多不同項目的源程序.由倉庫管理員統一管理這些源程序.這樣,就好象只有一個人在修改文件一樣.避免了沖突.每個用戶在使用倉庫之前,首先要把倉庫里的項目文件下載到本地.用戶做的任何修改首先都是在本地進行,然后用 cvs 命令進行提交,由 cvs 倉庫管理員統一修改.這樣就可以做到跟蹤文件變化,沖突控制等等. 由于 CVS 是典型的 C/S 結構的軟件,因此它也分成服務器端和客戶端兩部分.不過大多數 CVS 軟件都把它們合二為一了. 結合文檔和一些網上資源,我寫一點非常簡單的“速成”的教材.希望對大家有用. 下面是我的步驟和做法: 前提要求: root 權限; CVS軟件,請找到相關的rpm,tgz,deb 等包裝上,或者到 http://www.cvshome.org/CVS/Dev/code 下載原程序編譯安裝,這里我不準備介紹它的安裝,請參考CVS自身的文檔安裝.我使用Slackware的tgz包,安裝的命令是 #installpkg cvs*.tgz 其他包請參考對應包管理工具的命令. 一定的系統資源,要有一定內存(32M就能工作得很好),要一定的磁盤空間,看你的項目的大小和多少而定. 架設CVS服務器: 建立 CVSROOT 目錄,因為這里涉及到用戶對CVSROOT里的文件讀寫的權限問題,所以比較簡單的方法是建立一個組,然后再建立一個屬于該組的帳戶,而且以后有讀寫權限的用戶都要屬于該組.假設我們建一個組叫cvs,用戶名是cvsroot.建組和用戶的命令如下 #groupadd cvs #adduser cvsroot 生成的用戶家目錄在/home/cvsroot(根據自己的系統調整) 用 cvsroot 用戶登陸,修改 /home/cvsroot (CVSROOT)的權限,賦與同組人有讀寫的權限: $chmod 771 . (或者770應該也可以) 注意:這一部分工作是按照文檔說明做的,是否一定需要這樣沒有試驗,我會在做試驗后在以后版本的教程說得仔細一點.如果您有這方面的經驗請提供給我,謝謝. 建立CVS倉庫,(仍然是 cvsroot 用戶),用下面命令: $cvs -d /home/cvsroot init 以root身份登陸,修改 /etc/inetd.conf(使用 xinetd 的系統沒有此文件)和 /etc/services, 如果用的是 inetd 的系統,在 /etc/inetd.conf 里加入: cvsserver stream tcp nowait root /usr/bin/cvs cvs -f --allow-root=/home/cvsroot pserver 說明:上面的行是單獨一整行,/usr/bin/cvs 應該是你的cvs版本的命令路徑,請根據自己的系統調整./home/cvsroot 是你建立的CVSROOT的路徑,也請根據上面建立目錄的部分的內容做調整. 如果是使用 xinetd 的系統,需要在 /etc/xinetd.d/ 目錄下創建文件 cvspserver(此名字可以自己定義),內容如下: # default: on # description: The cvs server sessions; service cvsserver { socket_type = stream wait = no user = root server = /usr/bin/cvs server_args = -f --allow-root=/cvsroot pserver log_on_failure += USERID only_from = 192.168.0.0/24 } 其中only_from是用來限制訪問的,可以根據實際情況不要或者修改。修改該文件權限: # chmod 644 cvspserver 在/etc/services里加入: cvsserver 2401/tcp 說明:cvsserver 是任意的名稱,但是不能和已有的服務重名,也要和上面修改 /etc/inetd.conf 那行的第一項一致.這里我用的是 CVS 的口令認證方式,CVS 還有其他認證方式,我沒有做試驗,如果您有經驗,請補充,謝謝. 添加可以使用 CVS 服務的用戶到 cvs 組: 以 root 身份修改 /etc/group,把需要使用 CVS 的用戶名加到 cvs 組里,比如我想讓用戶 laser 和gumpwu 能夠使用 CVS 服務,那么修改以后的 /etc/group 應該有下面這樣一行: cvs:x:105:laser,gumpwu 在你的系統上GID可能不是105,沒有關系.主要是要把laser和gumpwu用逗號分隔開寫在最后一個冒號后面.當然,象RedHat等分發版有類似linuxconf這樣的工具的話,用工具做這件事會更簡單些. 重起inetd使修改生效: #killall -HUP inetd 如果使用的是 xinetd 的系統: # /etc/rc.d/init.d/xined restart 這樣服務器就設置完成了.我們接著搞客戶端. 設置客戶端 如果是 Linux(或者其他 *nix),客戶端和服務器端的軟件是一樣的,如果是Win,MAC等平臺,請到 http://www.loria.fr/cgi-bin/molli/wilma.cgi/rel 找相應的客戶端軟件,這里我先說一下在 Linux(*nix)里怎么做: 設置環境變量CVSROOT: $export CVSROOT=:pserver:laser@the_server_name:/home/cvsroot 注意:這里的pserver是訪問方式,我在上面設置的是口令認證,所以這里是pserver,如果你的CVS服務器設置成別的訪問模式,那么需要相應修改.laser是可以使用 CVS服務器的用戶名,這里可以根據你的設置修改,我在這個版本設置的是直接使用系統用戶的口令文件,也就是說laser必須是CVS服務器上的合法用戶,這里當然有安全問題,CVS可以設置成為擁有自己的用戶,我將在以后的版本里面增加這些內容,或者您也可以提供一些補充,或者直接讀CVS的文檔.the_server_name是CVS服務器的名稱或者IP地址,根據你的情況填寫,/home/cvsroot是你的CVS服務器的CVSROOT目錄,根據你的CVS服務器設置做修改或者詢問管理員.你可以把這行放到你的shell的profile里(.bash_profile,.profile等)這樣就不用每次敲一長串命令了. 登陸CVS服務器: $ cvs login,這時候 cvs 會問你口令,請把你在 CVS 服務器上的口令敲進去,這里是 laser 在 CVS服務器上的系統用戶的口令: Passwd:xxxxxxxx 成功登陸后將在你的家目錄建立一個 .cvspass 文件,以后就不用輸入口令了. 好,客戶端設置完成,簡單吧. 管理 cvs 服務器 服務器可以用了,現在大家最關心的就是如何管理服務器,比如,我想讓一些人有讀和/或寫 CVS 倉庫的權限,但是不想給它系統權限怎么辦呢? 不難,在 cvs 管理員用戶(在我這里是 cvsroot 用戶)的家目錄里有一個 CVSROOT 目錄,這個目錄里有三個配置文件,passwd, readers, writers,我們可以通過設置這三個文件來配置 CVS 服務器,下面分別介紹這幾個文件的作用: passwd:cvs 用戶的用戶列表文件,它的格式很象 shadow 文件: {cvs 用戶名}:[加密的口令]:[等效系統用戶名] 如果你希望一個用戶只是 cvs 用戶,而不是系統用戶,那么你就要設置這個文件,剛剛安裝完之后這個文件可能不存在,你需要以 cvs 管理員用戶手工創建,當然要按照上面格式,第二個字段是該用戶的加密口令,就是用 crypt (3) 加密的,你可以自己寫一個程序來做加密,也可以用我介紹的偷懶的方法:先創建一個系統用戶,名字和 cvs 用戶一樣,口令就是準備給它的 cvs 用戶口令,創建完之后從 /etc/shadow 把該用戶第二個字段拷貝過來,然后再把這個用戶刪除.這個方法對付數量少的用戶比較方便,人一多就不合適了,而且還有沖突條件(race condition)的安全隱患,還要 root 權限,實在不怎么樣.不過權益之計而已.寫一個小程序并不難,可以到 linuxforum 的編程版搜索一下,有個朋友已經寫了一個貼在上面了. 第三個字段就是等效系統用戶名,實際上就是賦與一個 cvs 用戶一個等效的系統用戶的權限,看下面的例子你就明白它的功能了. readers:有 cvs 讀權限的用戶列表文件.就是一個一維列表.在這個文件中的用戶對 cvs只有讀權限. writers:有 cvs 寫權限的用戶的列表文件.和 readers 一樣,是一個一維列表.在這個文件中的用戶對 cvs 有寫權限. 上面三個文件在缺省安裝的時候可能都不存在,需要我們自己創建,好吧,現在還是讓我們用一個例子來教學吧.假設我們有下面幾個用戶需要使用 cvs: laser, gumpwu, henry, betty, anonymous. 其中 laser 和 gumpwu 是系統用戶,而 henry, betty, anonymous 我們都不想給系統用戶權限,并且 betty 和 anonymous 都是只讀用戶,而且 anonymous 更是連口令都沒有.那么好,我們先做一些準備工作,先創建一個 cvspub 用戶,這個用戶的責任是代表所有非系統用戶的 cvs 用戶讀寫 cvs 倉庫. #adduser ... 然后編輯 /etc/group,令 cvspub 用戶在 cvs 組里,同時把其它有系統用戶權限的用戶加到 cvs 組里.(見上文) 然后編輯 cvs 管理員家目錄里 CVSROOT/passwd 文件,加入下面幾行: laser:$xxefajfka;faffa33:cvspub gumpwu:$ajfaal;323r0ofeeanv:cvspub henry:$fajkdpaieje:cvspub betty:fjkal;ffjieinfn/:cvspub anonymous::cvspub 注意:上面的第二個字段(分隔符為 :)是密文口令,你要用程序或者用我的土辦法生成. 編輯 readers 文件,加入下面幾行: anonymous betty 編輯 writer 文件,加入下面幾行: laser gumpwu henry 這樣就 ok 了,你再用幾個用戶分別登陸測試,就會發現一切都 ok 了.這里面的原理和說明我想就不多說了,其實很簡單,和系統管理用戶的概念是一樣的. 現在服務器和客戶端都設置好了,那么怎么用呢,我在這里寫一個最簡單的(估計也是最常用的)命令介紹: 首先,建立一個新的CVS項目,一般我們都已經有一些項目文件了,這樣我們可以用下面步驟生成一個新的CVS項目: 進入到你的已有項目的目錄,比如叫 cvstest: $cd cvstest 運行命令: $cvs import -m "this is a cvstest project" cvstest v_0_0_1 start 說明:import 是cvs的命令之一,表示向cvs倉庫輸入項目文件. -m參數后面的字串是描述文本,隨便寫些有意義的東西,如果不加 -m 參 數,那么cvs會自動運行一個編輯器(一般是vi,但是可以通過修改環境變量 EDITOR來改成你喜歡用的編輯器.)讓你輸入信息, cvstest 是項目名稱(實際上是倉庫名,在CVS服務器上會存儲在以這個名字 命名的倉庫里.) v_0_0_1是這個分支的總標記.沒啥用(或曰不常用.) start 是每次 import 標識文件的輸入層次的標記,沒啥用. 這樣我們就建立了一個CVS倉庫了,然后,我們可以把這個測試項目的文件刪除.試驗一下如何從倉庫獲取文件.這里我假設上面的所有客戶端工作你都已經做過了. 運行下面的命令: $cvs checkout cvstest 從倉庫中檢索出cvstest項目的源文件. 如果你已經做過一次checkout了,那么不需要重新checkout,只需要進入cvstest項目的目錄,更新一把就行了: $cd cvstest $cvs update 一下即可.又或者你不想直接更新,只是想看看有沒有更新的東西,那么: $cvs status 這時后會打印出一長串狀態報告(你可能需要用類似less這樣的命令分頁顯示,或者定向到一個輸出文件里慢慢看.),對項目中的每個文件有一份狀態報告,類似這樣: =================================================================== File: foo.c Status: Up-to-date Working revision: 1.1.1.1 'Some Date' Repository revision: 1.2 /home/cvsroot/cvstest/foo.c,v Sticky Tag: (none) Sticky Date: (none) Sticky Options: (none) 這里最重要的就是 Status 欄,這里總共可能有四種狀態: Up-to-date:表明你要到的文件是最新的. Locally Modified:表明你曾經修改過該文件,但還沒有提交,你的版本比倉庫里的新. Needing Patch:表明有個哥們已經修改過該文件并且已經提交了!你的版本比倉庫里的舊. Needs Merge:表明你曾經修改過該文件,但是偏偏有個不識相的也修改了這個文件,而且還提交給倉庫了! 如果你只是想保持軟件的同步的話(象我),那么上面的東西就足夠用了.可是如果多人協作開發項目的話,可就不是了這么簡單了.當你參加項目,維護文件時,就需要更多命令,比如說你我都是某 nasdaq 項目的開發人員: 1,你對某個文件做了修改,比如說改了ceo.c,加了一行程序:printf("where can I find VC to cheat!"); 改完之后你要把修改提交給倉庫,用命令: $cvs commit -m "add a complain" ceo.c 或者就是: $cvs commit -m "worry about money" 讓cvs幫你檢查哪個文件需要提交. 2,當我開始干活的時候,可能我先: $cvs status 一把,這時候我會看到: ================================================================== File: ceo.c Status: Needing Patch Working revision: 1.1.1.1 'Some Date' Repository revision: 1.2 /home/cvsroot/nastaq/ceo.c,v Sticky Tag: (none) Sticky Date: (none) Sticky Options: (none) 于是我知道有人改了ceo.c,于是我就: $cvs update ceo.c 或者干脆: $cvs update 把ceo.c這個文件更新為最新版本,然后再干活.然后提交. 如果這天你修改了coo.c,加了一行 puts("how about another kind of bragging?"); 并且提交了,但是這時候我已經 $cvs status 過了,就是說我不知道你的修改. 而我加了一行printf("You must shamelessly and seems knowingness to act as a coo"); 并且傻乎乎地提交: $cvs commit coo.c 這時候,CVS會告訴我 cvs commit: Examing . cvs server: Up-to-date check failed for 'coo.c' cvs [server aborted]: correct above error first! 于是我知道有個狗屎在我修改文件的當口做了提交,于是我 $cvs update 這時cvs會報告: RCS file: /home/cvsroot/nasdaq/coo.c,v retrieving revision 1.1.1.1 retrieving revision 1.2 Merging differences between 1.1.1.1 and 1.2 into coo.c rcsmerge: warning: conflicts during merge cvs update: conflicts found in coo.c C coo.c 告訴你coo.c有版本沖突,于是我編輯coo.c,這時一般文件里看起來象這樣: ... printf("You must shamelessly and seems knowingness to act as a coo"); <<<<<<< foo.c ======= ... puts("how about another kind of bragging?"); >>>>>>> 1.2 ... 于是我把上面改成: printf("You must shamelessly and seems knowingness to act as a coo"); puts("how about another kind of bragging?"); 然后 $cvs commit -m "merged" coo.c 于是下回你再更新的時候就有新的補釘要打...如此往復,直到完成所有修改. 不過這里有一些要注意的地方就是刪除程序,如果你刪掉一行對你可能沒有用的程序 puts("to be honest"); 而我不想刪除(因為我有用),而我不知情地直接: $cvs update 了,那么我的這行程序也完蛋了,所以這里我們要注意所有開發人員的協調,千萬不要亂刪東西,大不了用 #if 0 #endif 宏定義對括起來.實在要刪東西,那最好先標記一個版本: $cvs tag v_0_0_1 然后你可以發布并刪除你自己的工作目錄里這個版本的文件(注意:不是刪除倉庫里的.): $cvs release -d nasdaq 然后你再生成一個新分支: $cvs rtag -b -r v_0_0_1 v_0_0_1_1 nasdaq 然后再建立v_0_0_1_1的分支 $cvs checkout -r v_0_0_1_1 nasdaq 編輯并修改這個分支的文件,這樣的做法比較好. 不過要注意的是,新標記和新分支的建立最好由項目的管理人員負責,否則每個人都做一個分支,那么倉庫就太亂了.因此,比較的開發人員之間的直接溝通是不能忽略的.一般來說,在互聯網上的標準模式是有一個管理員(可能自己并不寫程序),有一個郵遞列表,大家都在郵遞列表上交流看法和做各種決議.當形成決議之后,管理員做一個新版本的標記.以此循環. 還有一些命令,比如要增加一個文件 garbage_china_concept_stocks_list: $cvs add garbage_china_concept_stocks_list 然后還要: $cvs commit garbage_china_concert_stocks_list 看起來有點象數據庫里的事務?的確是這樣.CVS維護著一個本地的參考文件(在CVS/Entries里),這樣提交的時候就可以一次地把所有改變放到服務器端,這樣也更安全.同樣,如果想刪除文件 bankrupted_web_site: $rm bankrupted_web_site $cvs remove bankrupted_web_site $cvs commit bankrupted_web_site 3,一些小技巧: $Header$ 標記:把這個標記放在文件的任何地方都會被 cvs 替換成最后修改的 cvs 用戶名,該文件當前版本號,最后修改時間,該文件的 cvs 倉庫路徑,看起來象下面這個樣子: // $Header: /home/cvsroot/simhost/simhost.cpp,v 1.2 2001/04/20 08:26:10 jqliu Exp $ 一般我們把它放在開頭,這樣對程序員修改文件非常便利,很多時候你只要看一眼開頭就知道文件是否最新. $Id$標記:把這個標記放在文件的任何地方都會被 cvs 替換成最后修改的 cvs 用戶名,該文件當前版本號,最后修改時間,該文件的 cvs 倉庫路徑,看起來象下面這個樣子: $Id: simhost.cpp,v 1.3 2001/04/24 02:27:36 simhost Exp $ 好了,上面所有的東西,估計就是我們用cvs時80%情況下用的命令和內容,包括文件的更新,提交,沖突的解決,分支的派生,增刪文件等.實際上cvs的功能之強大,遠遠超出我在這里描述的內容,我這個"速成"也就管不了太多了,希望隨著時間的推移,我們能夠更加有效地使用CVS.也希望大家能夠不斷補充這篇文章,最后能夠成為手冊,而不僅僅是速成.當然,還要更多地參考別的文檔. 參考資料: http://www.loria.fr/cgi-bin/molli/wilma.cgi/doc.865331095.html http://www.loria.fr/~molli/cvs/doc/cvs_toc.html |
onku 回復于:2004-07-13 15:03:13 |
老大,我在添加cvsroot用戶時,提示“useradd : unkuown gid 100”,然后按照步驟執行,最后2401端口沒有啟動,怎么回事啊。 |
hait 回復于:2004-07-14 16:18:49 |
useradd錯誤好像是添加的用戶所屬的組有什么問題,不知道說得對不對。 在/etc/xinetd.d目錄下添加任務配置文件cvspserver。重新啟動xinetd或者系統,2401端口應該是監聽了。檢查一下這個文件。還有/etc/services 看是否指定了2401服務端口?催@個端口是否打開,可以這樣試一下telnet 192.168.0.206 2401,如果不報連接拒絕,應該是打開了。 |
lovelotus123 回復于:2004-08-11 11:19:59 |
我一直在用CVS來管理配置項,但CVS的系統時間老是不正確,有時在文件屬性里面竟看出提交的時間是22:30,多可笑,我改了好多次,包括LINUX的時區,系統的時間,都改成正確的了,可提交配置項時時間就是不正確,到底是哪里的原因呢,請幫幫忙啦 |
文章來源于領測軟件測試網 http://www.kjueaiud.com/