install and config smarttools on Debian 7
#install
apt-get install smartmontools
#display smart information
smartctl -i /dev/sda
#fast selftest
smartctl -t short /dev/sda
#full test
smartctl -t long /dev/sda
#view the error list of selftest
smartctl -l error /dev/sda
#config smarttools
/etc/default/smartmontools
enable_smart="/dev/sda" #要檢查的硬碟
start_smartd=yes #開機後自動啟用
smartd_opts="--interval=1800" #檢查的間隔時間
/etc/smartd.conf
#只在有錯誤狀態時發 mail 通知
/dev/hdc -H -C 0 -U 0 -m admin@example.com
2014/10/24
2014/10/23
install monitorix on debian 7 (wheezy)
install minotorix on Debian 7
apt-get install rrdtool perl libwww-perl libmailtools-perl libmime-lite-perl librrds-perl libdbi-perl libxml-simple-perl libhttp-server-simple-perl libconfig-general-perl
版本可能會變
wget http://www.monitorix.org/monitorix_3.7.0-izzy1_all.deb
dpkg -i monitorix_3.7.0-izzy1_all.deb
安裝完成後就已經啟動服務
設定檔 /etc/monitoric/monitorix.conf
可以把要監控的項目設定成 y,不想監控的設定成 n
<httpd_builtin> 底下是內建 httpd 的設定,安全起見不建議跟 apache 榜一起
<auth> 可以設定要經過認證
enabled = y
htpasswd = /var/lib/monitorix/htpasswd 預設的密碼檔
產生密碼檔
cd /var/lib/monitorix
htpasswd -b -c -d htpasswd admin admin
產生第2~ 個人
htpasswd -b -d htpasswd aimwang iloveu
重新啟動服務,設定變更或者新增使用者都要重啟才生效
/etc/init.d/monitorix restart
apt-get install rrdtool perl libwww-perl libmailtools-perl libmime-lite-perl librrds-perl libdbi-perl libxml-simple-perl libhttp-server-simple-perl libconfig-general-perl
版本可能會變
wget http://www.monitorix.org/monitorix_3.7.0-izzy1_all.deb
dpkg -i monitorix_3.7.0-izzy1_all.deb
安裝完成後就已經啟動服務
設定檔 /etc/monitoric/monitorix.conf
可以把要監控的項目設定成 y,不想監控的設定成 n
<httpd_builtin> 底下是內建 httpd 的設定,安全起見不建議跟 apache 榜一起
<auth> 可以設定要經過認證
enabled = y
htpasswd = /var/lib/monitorix/htpasswd 預設的密碼檔
產生密碼檔
cd /var/lib/monitorix
htpasswd -b -c -d htpasswd admin admin
產生第2~ 個人
htpasswd -b -d htpasswd aimwang iloveu
重新啟動服務,設定變更或者新增使用者都要重啟才生效
/etc/init.d/monitorix restart
2014/10/21
Sourceforge 的 igmpproxy 程式解析
官方位址
因為 igmp proxy 是利用 interface 的 ip/netmask 來判斷封包是否過濾,一般的 IPoE 使用上都沒什麼問題,但是遇到 PPP 連線時,對方傳過來的 mcast 封包 source 會是 P-t-P dstaddr,而不是我們這邊的 IP。這個情形會導致 igmpproxy 把 mcast 都丟掉,所以要在 line 28 那邊作一些小改變。
Line 29-36 是根據 IfDescEp->Flags == 0x10d1 時,addr 改取 dstaddr 而不是 srcaddr,然後重算 subnet,再交給後面加入 allowednets。這樣就可以讓 PPPoE 也能享受 igmp proxy,提供用戶極致的影音服務。
原始程式檔列表
lib.c | 公用函式,都是用在網路位址處理 |
mcgroup.c | 處理 join/leace Multicast group |
ifvc.c | 建立及提供搜尋 interface vector 功能 |
config.c | 讀取 config 設定檔 |
udpsock.c | 建立 udp sock |
request.c | 處理 igmp request |
igmp.c | 收/送 igmp 封包 |
confread.c | config 子集,讀檔相關功能 |
igmpproxy.c | igmpproxy 主流程 |
syslog.c | 處理 log 訊息 |
mroute-api.c | mroute api 相關程式 |
kern.c | 不確定,看起來是打包用的 |
rttable.c | igmpproxy 內部使用的 routing table 以及跟 kernel 增減 routing 的程式 |
callout.c | 佇列處理 |
igmpproxy.h | |
os-linux.h | |
os-openbsd.h | |
os-freebsd.h | |
os-dragonfly.h | |
os-netbsd.h |
igmpproxy 運作概說
multicast 的訂閱是由 igmp 來進行,但是 multi 的特性類似於 broadcast,所以並不能透過 route 或 nat 轉發,所以需要在 router/gateway 上提供 igmp proxy 服務,在 downstream 收到 request 的時候,從 upstream 轉發出去,同時要去通知 kernel 收到這個 group 的 multicast 要轉發給 downstream。因為 downstream 有可能不只一個 host 要訂閱某個 group,igmpproxy 不可以在確定某 host leave 的時候就直接切斷這個 group 的 multicast routing,所以需要維護一套自己要看的 routing table,在確定 downstream 都沒有人有訂閱這個 group 的時候才能切斷。
後面的程式解析只看 igmpproxy 的主要流程,也就是收到 igmp 封包之後開始解析,看看該程式是如何做出 igmpproxy 所需的功能。
acceptIgmp()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | // protocol == 0; kernel 發現新 source 的 group,所以要把他加入 routing 表 if (ip->ip_p == 0) { checkVIF = getIfByIx( upStreamVif ); // 根據 src 位址找出 upstream vif if(checkVIF == 0) { // 不是 upstream vif,跳開 else if(src == checkVIF->InAdr.s_addr) { // 根本就是自己發的,跳開 else if(!isAdressValidForIf(checkVIF, src)) { // 不在 upstream 的網段,跳開 } activateRoute(dst, src); // 啟用 routing,然後跳開 } // 後面會根據收到的 igmp 封包決定後續動作 switch (igmp->igmp_type) { case IGMP_V1_MEMBERSHIP_REPORT: case IGMP_V2_MEMBERSHIP_REPORT: // 收到 igmp membership report acceptGroupReport(src, group, igmp->igmp_type); return; case IGMP_V2_LEAVE_GROUP: // 收到 igmp leave acceptLeaveMessage(src, group); return; case IGMP_MEMBERSHIP_QUERY: // 收到 igmp query,總覺得應該要回應這個 query 比較好,可是實際操作沒問題,所以就當作不需要就好 return; default: // 其他當作不認識 my_log(LOG_INFO, 0, "ignoring unknown IGMP message type %x from %s to %s", igmp->igmp_type, inetFmt(src, s1), inetFmt(dst, s2)); return; } } |
acceptGroupReport()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | if(!IN_MULTICAST( ntohl(group) )) {} // 如果 group 不在 multicast 位址範圍內,跳開 sourceVif = getIfByAddress( src ); // 根據 src 位址找出 vif if(sourceVif == NULL) {} // 找不到 vif,跳開 if(sourceVif->InAdr.s_addr == src) {} // src 就是自己,跳開 if(sourceVif->state == IF_STATE_DOWNSTREAM) { // 是 downstream 的 vif 才處理 if(sourceVif->allowedgroups == NULL) { // 沒有設定白名單 insertRoute(group, sourceVif->index); // 加入路由後跳開 } for(sn = sourceVif->allowedgroups; sn != NULL; sn = sn->next) { if((group & sn->subnet_mask) == sn->subnet_addr) { // 在白名單裡面 insertRoute(group, sourceVif->index); // 加入路由 } } } |
1 2 3 4 5 6 7 8 | if(!IN_MULTICAST( ntohl(group) )) {} // 如果 group 不在 multicast 位址範圍內,跳開 sourceVif = getIfByAddress( src ); // 根據 src 位址找出 vif if(sourceVif == NULL) {} // 找不到 vif,跳開 // 這次不檢查是不是自己發的!? if(sourceVif->state == IF_STATE_DOWNSTREAM) { // 是 downstream 的 vif 才處理 setRouteLastMemberMode(group); // 把 group 設定成沒有成員模式 sendGroupSpecificMemberQuery(gvDesc); // 送出 query,驗證是不是還有人要接收 group } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | croute = findRoute(group); // 根據 group 位址找 route 紀錄 if(croute==NULL) { // route 不存在 newroute = (struct RouteTable*)malloc(sizeof(struct RouteTable)); // 建立新的 route 紀錄 newroute->group = group; newroute->upstrState = ROUTESTATE_NOTJOINED; // 新的 route 所以設定為 notjoined BIT_ZERO(newroute->vifBits); // 清空要接收的 vif 表 croute = newroute; // croute 後面還會用到 if(ifx >= 0) { BIT_SET(newroute->vifBits, ifx); // 設定接收的 vif } if(routing_table == NULL) { // routing_table 還不存在,這個 newroute 就當頭 } else { // routing_table 已經存在,找適當位置插入這一筆新的 } } else { // route 已經存在 BIT_SET(croute->vifBits, ifx); // 更新要接收的 vif 表 if(croute->originAddr != 0) { // 有來源在播送給這個 group 的話 internUpdateKernelRoute(croute, 1) // 告訴 kernel 要把這個 group 轉送 } } if(croute->upstrState != ROUTESTATE_JOINED) { // 還沒對 upstream 送出 join sendJoinLeaveUpstream(croute, 1); // 送出 join report } |
1 2 3 4 5 6 7 | croute = findRoute(group); // 根據 group 尋找 route if(croute!=NULL) { // 有找到 if(croute->upstrState == ROUTESTATE_JOINED && conf->fastUpstreamLeave) { //已經送過 join 而且有設定 fast leave sendJoinLeaveUpstream(croute, 0); // 送出 leave report } croute->upstrState = ROUTESTATE_CHECK_LAST_MEMBER; // 更改 upstream 狀態 } |
sendGroupSpecificMemberQuery
1 2 3 4 5 6 7 8 9 | GroupVifDesc *gvDesc = (GroupVifDesc*) argument; // 把參數設定成 GroupVifDesc 型態 if(gvDesc->started) { // 已經啟用 if(!lastMemberGroupAge(gvDesc->group)) { // 最新一次的 member 檢查沒有得到 member 的回應 return; } else { gvDesc->started = 1; // 設定成啟動 } sendIgmp(...); // 送出 membership query,詢問還有誰在接收這個 group timer_setTimer(...); // 重設下次檢查時間 |
主程式流程
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | int main( int ArgCn, char *ArgVc[] ) { for (int c; (c = getopt(ArgCn, ArgVc, "vdh")) != -1;) { // 解析輸入參數 // d 將紀錄輸出到 stderr // v 紀錄 info 等級 // vv 紀錄 debug 等級 // h 顯示語法說明 } do { // 這個是主程式迴圈,還不是主迴圈,很有趣的寫法 :D,有什麼好處我不知道,請知道的人幫忙釋疑 if( ! loadConfig( configFilePath ) ) {} //讀取設定檔 if ( !igmpProxyInit() ) {} // 系統初始化 igmpProxyRun(); // 真正的主迴圈,開始收 igmp 封包跑 igmpproxy 流程 igmpProxyCleanUp(); // 清除 } while ( false ); } |
設定檔
1 2 3 4 5 6 7 8 9 | int loadConfig(char *configFile) { while ( token != NULL ) { if(strcmp("phyint", token)==0) { tmpPtr = parsePhyintToken(); // 解析 phy } else if(strcmp("quickleave", token)==0) {} // quickleave 標籤 ... ... } } |
PPP 連線的問題
先來看原本的程式 (ifvc.c)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | // Get the interface adress... IfDescEp->InAdr = ((struct sockaddr_in *)&IfPt->ifr_addr)->sin_addr; addr = IfDescEp->InAdr.s_addr; memcpy( IfReq.ifr_name, IfDescEp->Name, sizeof( IfReq.ifr_name ) ); IfReq.ifr_addr.sa_family = AF_INET; ((struct sockaddr_in *)&IfReq.ifr_addr)->sin_addr.s_addr = addr; // Get the subnet mask... if (ioctl(Sock, SIOCGIFNETMASK, &IfReq ) < 0) my_log(LOG_ERR, errno, "ioctl SIOCGIFNETMASK for %s", IfReq.ifr_name); mask = ((struct sockaddr_in *)&IfReq.ifr_addr)->sin_addr.s_addr; subnet = addr & mask; /* get if flags ** ** typical flags: ** lo 0x0049 -> Running, Loopback, Up ** ethx 0x1043 -> Multicast, Running, Broadcast, Up ** ipppx 0x0091 -> NoArp, PointToPoint, Up ** grex 0x00C1 -> NoArp, Running, Up ** ipipx 0x00C1 -> NoArp, Running, Up */ if ( ioctl( Sock, SIOCGIFFLAGS, &IfReq ) < 0 ) my_log( LOG_ERR, errno, "ioctl SIOCGIFFLAGS" ); IfDescEp->Flags = IfReq.ifr_flags; // Insert the verified subnet as an allowed net... IfDescEp->allowednets = (struct SubnetList *)malloc(sizeof(struct SubnetList)); if(IfDescEp->allowednets == NULL) my_log(LOG_ERR, 0, "Out of memory !"); // Create the network address for the IF.. IfDescEp->allowednets->next = NULL; IfDescEp->allowednets->subnet_mask = mask; IfDescEp->allowednets->subnet_addr = subnet; |
因為 igmp proxy 是利用 interface 的 ip/netmask 來判斷封包是否過濾,一般的 IPoE 使用上都沒什麼問題,但是遇到 PPP 連線時,對方傳過來的 mcast 封包 source 會是 P-t-P dstaddr,而不是我們這邊的 IP。這個情形會導致 igmpproxy 把 mcast 都丟掉,所以要在 line 28 那邊作一些小改變。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 | // Get the interface adress... IfDescEp->InAdr = ((struct sockaddr_in *)&IfPt->ifr_addr)->sin_addr; addr = IfDescEp->InAdr.s_addr; memcpy( IfReq.ifr_name, IfDescEp->Name, sizeof( IfReq.ifr_name ) ); IfReq.ifr_addr.sa_family = AF_INET; ((struct sockaddr_in *)&IfReq.ifr_addr)->sin_addr.s_addr = addr; // Get the subnet mask... if (ioctl(Sock, SIOCGIFNETMASK, &IfReq ) < 0) my_log(LOG_ERR, errno, "ioctl SIOCGIFNETMASK for %s", IfReq.ifr_name); mask = ((struct sockaddr_in *)&IfReq.ifr_addr)->sin_addr.s_addr; subnet = addr & mask; /* get if flags ** ** typical flags: ** lo 0x0049 -> Running, Loopback, Up ** ethx 0x1043 -> Multicast, Running, Broadcast, Up ** ipppx 0x0091 -> NoArp, PointToPoint, Up ** grex 0x00C1 -> NoArp, Running, Up ** ipipx 0x00C1 -> NoArp, Running, Up */ if ( ioctl( Sock, SIOCGIFFLAGS, &IfReq ) < 0 ) my_log( LOG_ERR, errno, "ioctl SIOCGIFFLAGS" ); IfDescEp->Flags = IfReq.ifr_flags; // aimwang: when pppx get dstaddr for use if (0x10d1 == IfDescEp->Flags) { if ( ioctl( Sock, SIOCGIFDSTADDR, &IfReq ) < 0 ) my_log(LOG_ERR, errno, "ioctl SIOCGIFDSTADDR for %s", IfReq.ifr_name); addr = ((struct sockaddr_in *)&IfReq.ifr_dstaddr)->sin_addr.s_addr; subnet = addr & mask; } // Insert the verified subnet as an allowed net... IfDescEp->allowednets = (struct SubnetList *)malloc(sizeof(struct SubnetList)); if(IfDescEp->allowednets == NULL) my_log(LOG_ERR, 0, "Out of memory !"); // Create the network address for the IF.. IfDescEp->allowednets->next = NULL; IfDescEp->allowednets->subnet_mask = mask; IfDescEp->allowednets->subnet_addr = subnet; |
Line 29-36 是根據 IfDescEp->Flags == 0x10d1 時,addr 改取 dstaddr 而不是 srcaddr,然後重算 subnet,再交給後面加入 allowednets。這樣就可以讓 PPPoE 也能享受 igmp proxy,提供用戶極致的影音服務。
2014/10/17
Install Redmine 2.5.2 on Debian 7.6 with gitolite 0.7.7
Debian 7.6 + Redmine 2.5.2 + 0.7.7
GW: 172.20.254.254
DNS: 8.8.4.4 8.8.8.8
root:111111
octtel:111111
需要更改 IP 時:
nano /etc/network/interfaces
New password for the MySQL "root" user: 回答 "111111"
Repeat password for the MySQL "root" user: 回答 "111111"
adduser --system --shell /bin/bash --gecos 'Redmine Administrator' --group --disabled-password --home /opt/redmine redmine
ssh-keygen -t rsa -N '' -f ~/.ssh/redmine_gitolite_admin_id_rsa
(會產生 /opt/redmine/.ssh/redmine_gitolite_admin_id_rsa 跟 /opt/redmine/.ssh/redmine_gitolite_admin_id_rsa.pub)
ln -s /opt/redmine/.ssh/redmine_gitolite_admin_id_rsa /opt/redmine/.ssh/id_rsa
ln -s /opt/redmine/.ssh/redmine_gitolite_admin_id_rsa.pub /opt/redmine/.ssh/id_rsa.pub
exit
System username for gitolite: 回答 "git"
Repository path: 回答 "/opt/gitolite"
Administrator's SSH key: 回答 "/opt/redmine/.ssh/redmine_gitolite_admin_id_rsa.pub"
cd ~
wget http://www.redmine.org/releases/redmine-2.5.2.tar.gz
tar zxf redmine-2.5.2.tar.gz
mv redmine-2.5.2/* .
rm -Rf redmine-2.5.2
CREATE USER 'redmine'@'localhost' IDENTIFIED BY 'redmine';
GRANT ALL PRIVILEGES ON redmine.* TO 'redmine'@'localhost';"
Enter password: 輸入 "111111"
cp database.yml.example database.yml
cp configuration.yml.example configuration.yml
nano database.yml
內容改成如下 (其他項目用 # 標示)
production:
adapter: mysql2
database: redmine
host: localhost
username: root
password: "111111"
encoding: utf8
mkdir public/plugin_assets
bundle install --without development test postgresql sqlite
rake generate_secret_token
RAILS_ENV=production rake db:migrate
RAILS_ENV=production rake redmine:load_default_data
email_delivery:
delivery_method: :smtp
smtp_settings:
enable_starttls_auto: true
address: "smtp.gmail.com"
port: 587
domain: "smtp.gmail.com"
authentication: :plain
user_name: "your_email@gmail.com"
password: "your_password"
git clone https://github.com/jbox-web/redmine_bootstrap_kit.git
git clone https://github.com/jbox-web/redmine_git_hosting.git
cd redmine_git_hosting
git checkout v0.7.7
ln -s /opt/redmine/.ssh/redmine_gitolite_admin_id_rsa /opt/redmine/plugins/redmine_git_hosting/ssh_keys/redmine_gitolite_admin_id_rsa
ln -s /opt/redmine/.ssh/redmine_gitolite_admin_id_rsa.pub /opt/redmine/plugins/redmine_git_hosting/ssh_keys/redmine_gitolite_admin_id_rsa.pub
sed -i 's/$GL_GITCONFIG_KEYS = ""/$GL_GITCONFIG_KEYS = ".*"/g' /opt/gitolite/.gitolite.rc
exit
git clone git@localhost:gitolite-admin.git
cd gitolite-admin/
echo "repo @all
RW+ = admin" >> conf/gitolite.conf
git commit -m 'Automatic Repository Initialization' conf/gitolite.conf
git push
cd ~
rm -rf gitolite-admin
bundle install --without development
RAILS_ENV=production rake redmine:plugins:migrate
增加
redmine ALL=(git) NOPASSWD:ALL
git ALL=(redmine) NOPASSWD:ALL
cd /var/www
ln -s /opt/redmine/public redmine
echo "RailsBaseURI /redmine
PassengerUserSwitching on
PassengerUser redmine
PassengerGroup redmine" > /etc/apache2/sites-available/redmine
a2ensite redmine
service apache2 reload
a2enmod ssl
service apache2 restart
git config --global http.sslverify false
安裝 Debian 7.6 基本系統(只選標準系統工具跟 ssh)
IP: 172.20.3.12/16GW: 172.20.254.254
DNS: 8.8.4.4 8.8.8.8
root:111111
octtel:111111
需要更改 IP 時:
nano /etc/network/interfaces
安裝相依套件
aptitude update && aptitude -y install sudo ssh bzip2 zip unzip apache2 libapache2-mod-passenger mysql-server libmysqlclient-dev ruby ruby-dev git git-core gitolite libmagickcore-dev libmagickwand-dev libicu-dev imagemagickNew password for the MySQL "root" user: 回答 "111111"
Repeat password for the MySQL "root" user: 回答 "111111"
建立 redmine, gitolite 的使用者
adduser --system --shell /bin/bash --gecos 'Git Administrator' --group --disabled-password --home /opt/gitolite gitadduser --system --shell /bin/bash --gecos 'Redmine Administrator' --group --disabled-password --home /opt/redmine redmine
產生 redmine 的憑證
su redminessh-keygen -t rsa -N '' -f ~/.ssh/redmine_gitolite_admin_id_rsa
(會產生 /opt/redmine/.ssh/redmine_gitolite_admin_id_rsa 跟 /opt/redmine/.ssh/redmine_gitolite_admin_id_rsa.pub)
ln -s /opt/redmine/.ssh/redmine_gitolite_admin_id_rsa /opt/redmine/.ssh/id_rsa
ln -s /opt/redmine/.ssh/redmine_gitolite_admin_id_rsa.pub /opt/redmine/.ssh/id_rsa.pub
exit
設定 gitolite
dpkg-reconfigure gitoliteSystem username for gitolite: 回答 "git"
Repository path: 回答 "/opt/gitolite"
Administrator's SSH key: 回答 "/opt/redmine/.ssh/redmine_gitolite_admin_id_rsa.pub"
安裝 redmine
sudo su - redminecd ~
wget http://www.redmine.org/releases/redmine-2.5.2.tar.gz
tar zxf redmine-2.5.2.tar.gz
mv redmine-2.5.2/* .
rm -Rf redmine-2.5.2
建立 database 和 user
mysql -u root -p -v -e "CREATE DATABASE redmine CHARACTER SET utf8;CREATE USER 'redmine'@'localhost' IDENTIFIED BY 'redmine';
GRANT ALL PRIVILEGES ON redmine.* TO 'redmine'@'localhost';"
Enter password: 輸入 "111111"
組態資料跟mail
cd /opt/redmine/configcp database.yml.example database.yml
cp configuration.yml.example configuration.yml
nano database.yml
內容改成如下 (其他項目用 # 標示)
production:
adapter: mysql2
database: redmine
host: localhost
username: root
password: "111111"
encoding: utf8
建立 plugin 資料夾
cd ~mkdir public/plugin_assets
修改 sudoer (為了安全後面會限縮權限,但是這邊安裝需要開放全部)
nano /etc/sudoers
增加
redmine ALL=(ALL) NOPASSWD:ALL
git ALL=(ALL) NOPASSWD:ALL
完成 redmine
sudo gem install bundlerbundle install --without development test postgresql sqlite
rake generate_secret_token
RAILS_ENV=production rake db:migrate
RAILS_ENV=production rake redmine:load_default_data
設定 sendmail (以 gmail 為例)
修改 /opt/redmine/configuration.ymlemail_delivery:
delivery_method: :smtp
smtp_settings:
enable_starttls_auto: true
address: "smtp.gmail.com"
port: 587
domain: "smtp.gmail.com"
authentication: :plain
user_name: "your_email@gmail.com"
password: "your_password"
安裝 gitolite
cd ~/pluginsgit clone https://github.com/jbox-web/redmine_bootstrap_kit.git
git clone https://github.com/jbox-web/redmine_git_hosting.git
cd redmine_git_hosting
git checkout v0.7.7
ln -s /opt/redmine/.ssh/redmine_gitolite_admin_id_rsa /opt/redmine/plugins/redmine_git_hosting/ssh_keys/redmine_gitolite_admin_id_rsa
ln -s /opt/redmine/.ssh/redmine_gitolite_admin_id_rsa.pub /opt/redmine/plugins/redmine_git_hosting/ssh_keys/redmine_gitolite_admin_id_rsa.pub
設定 Key
sudo su - gitsed -i 's/$GL_GITCONFIG_KEYS = ""/$GL_GITCONFIG_KEYS = ".*"/g' /opt/gitolite/.gitolite.rc
exit
設定自動初始化 repository
cd ~git clone git@localhost:gitolite-admin.git
cd gitolite-admin/
echo "repo @all
RW+ = admin" >> conf/gitolite.conf
git commit -m 'Automatic Repository Initialization' conf/gitolite.conf
git push
cd ~
rm -rf gitolite-admin
安裝 plugin 到 redmine
cd ~bundle install --without development
RAILS_ENV=production rake redmine:plugins:migrate
修改 sudoer
nano /etc/sudoers增加
redmine ALL=(git) NOPASSWD:ALL
git ALL=(redmine) NOPASSWD:ALL
修改 Apache mod_passenger 設定
su rootcd /var/www
ln -s /opt/redmine/public redmine
echo "RailsBaseURI /redmine
PassengerUserSwitching on
PassengerUser redmine
PassengerGroup redmine" > /etc/apache2/sites-available/redmine
a2ensite redmine
service apache2 reload
啟用 https
a2ensite default-ssla2enmod ssl
service apache2 restart
其他
git 客戶端無法 clone 未被公證單位認證的憑證提供的 .git,可以執行下列指令不檢查憑證git config --global http.sslverify false
2014/08/15
簡單 Struct 清單的使用
這是一個簡單 Struct 的使用,資料餵入的方法只用單純的插入,暫時也還沒有遇到需求,所以就不寫搜尋跟刪除的功能了。
/**********
* Define *
* ********/
#define SLIST_ENTRY(s_type) struct s_type *next;
#define SLIST_IS_EMPTY(head) (head == NULL)
#define SLIST_NOT_EMPTY(head) (head != NULL)
#define SLIST_FOREACH(var, c_type, head) \
for (var=head; var!=NULL; var=var->next)
#define SLIST_INSERT(var, c_type, head) \
{
c_type *tail = head; \
var->next = NULL; \
if (NULL == tail) \
head = var; \
else \
{ \
while (tail && tail->next) \
tail = tail->next; \
if (tail) \
tail->next = var; \
} \
}
#define SLIST_FREE(c_type, head) \
if (head) \
{ \
c_type *this=head, *next; \
while (NULL != this) \
{ \
next = this->next; \
free(this); \
this = next; \
}; \
head = NULL; \
}
/***********
* Example *
* *********/
// Define Struct
typedef struct S_MyType
{
char FirstName[30];
char LastName[30];
SLIST_ENTRY(S_MyType);
} C_MyType;
// Define variable
C_MyType *Users;
// Insert Element
C_MyType *ptr = (C_MyType *)malloc(sizeof(C_MyType));
if (ptr)
{
ENSURE_ZERO(ptr, sizeof(C_MyType));
sprintf(ptr->FirstName, "Aim");
sprintf(ptr->LastName, "Wang");
SLIST_INSERT(ptr, C_MyType, Users);
}
// Read all data
C_MyType *ptr;
int i = 0;
SLIST_FOREACH(ptr, C_MyType, Users)
{
printf("User No.%2d: %s %s", i++, ptr->FirstName, ptr->LastName);
}
// release memory
SLIST_FREE(C_MyType, Users);
/**********
* Define *
* ********/
#define SLIST_ENTRY(s_type) struct s_type *next;
#define SLIST_IS_EMPTY(head) (head == NULL)
#define SLIST_NOT_EMPTY(head) (head != NULL)
#define SLIST_FOREACH(var, c_type, head) \
for (var=head; var!=NULL; var=var->next)
#define SLIST_INSERT(var, c_type, head) \
{
c_type *tail = head; \
var->next = NULL; \
if (NULL == tail) \
head = var; \
else \
{ \
while (tail && tail->next) \
tail = tail->next; \
if (tail) \
tail->next = var; \
} \
}
#define SLIST_FREE(c_type, head) \
if (head) \
{ \
c_type *this=head, *next; \
while (NULL != this) \
{ \
next = this->next; \
free(this); \
this = next; \
}; \
head = NULL; \
}
/***********
* Example *
* *********/
// Define Struct
typedef struct S_MyType
{
char FirstName[30];
char LastName[30];
SLIST_ENTRY(S_MyType);
} C_MyType;
// Define variable
C_MyType *Users;
// Insert Element
C_MyType *ptr = (C_MyType *)malloc(sizeof(C_MyType));
if (ptr)
{
ENSURE_ZERO(ptr, sizeof(C_MyType));
sprintf(ptr->FirstName, "Aim");
sprintf(ptr->LastName, "Wang");
SLIST_INSERT(ptr, C_MyType, Users);
}
// Read all data
C_MyType *ptr;
int i = 0;
SLIST_FOREACH(ptr, C_MyType, Users)
{
printf("User No.%2d: %s %s", i++, ptr->FirstName, ptr->LastName);
}
// release memory
SLIST_FREE(C_MyType, Users);
2014/08/12
find_pid_by_cmdline
要寫一個送 kill 到指定程式的 code,所以先要弄出這個根據 command line 找到 pid 的 function
#include <dirent.h>
static int find_pid_by_cmdline(const char *cmdline)
{
DIR *dir;
struct dirent *entry;
char *name, *p;
char status[32];
char buf[1024];
FILE *fp;
int pid, i;
dir = opendir("/proc");
if (!dir)
return 0;
while (entry = readdir(dir)) {
name = entry->d_name;
if (!(*name >= '0' && *name <= '9'))
continue;
pid = atoi(name);
sprintf(status, "/proc/%d/cmdline", pid);
if ((fp = fopen(status, "r")) == NULL)
continue;
name = fgets(buf, sizeof(buf), fp);
fclose(fp);
if (name == NULL)
continue;
for (i=0; i<1024; i++) {
if (buf[i] == 0) {
if (buf[i+1] == 0) {
i = 1024;
} else {
buf[i] = ' ';
}
}
}
if (!strcmp(buf, cmdline)) {
closedir(dir);
return pid;
}
}
closedir(dir);
return 0;
}
2014/07/17
Debian 7 安裝 Asterisk
說來慚愧,在自稱台灣第一大的 VoIP Gateway 製造商待了兩年多的時間,剛開始一年多整天玩網路,最近幾個月被螃蟹夾到手,光是搞定他家的 VLAN 就頭昏腦脹了,一直都沒有機會去真正瞭解 VoIP 這東西。今天抽個時間先把 Asterisk 裝起來,再來慢慢玩好了。
以下是安裝過程的筆記,只安裝主系統跟弄出 WEB 介面而已,其他東西以後有空再說
1. 安裝 Debian 7 amd64 (依照習慣不安裝視窗介面,節省記憶體跟硬碟空間)
套件只勾選 shell system 跟 ssh server
2. 更新系統
#apt-get update
#apt-get upgrade
3. 安裝 Asterisk
#apt-get install asterisk
(會有一堆相依套件被安裝,能拒絕嗎?當然不)
ITU-T telephone code: 886 (參考 http://en.wikipedia.org/wiki/List_of_country_calling_codes)
4. 安裝 Asterisk GUI (因為 Debian 套件沒有內建 GUI,所以要下載原始碼自己 Make,原始碼採用 svn 方式下載)
#apt-get install subversion
#cd /usr/src
#svn co http://svn.digium.com/svn/asterisk-gui/trunk asterisk-gui
#cd asterisk-gui
#./configure
#make
#make install
*** GUI 會被安裝在 /var/lib/asterisk/static-http,而 asterisk 實際會用 /usr/share/asterisk/static-http
#rm -rf /usr/share/asterisk/static-http/
#ln -s /var/lib/asterisk/static-http/ /usr/share/asterisk/
#cd /var/lib/asterisk
#chown -R asterisk.asterisk static-http
#chown -R asterisk.asterisk scripts
#chown -R asterisk.asterisk gui_backups
修改 /etc/asterisk/http.conf
[general]
enabled=yes
bindaddr=your_ip
bindport=8088
prefix=asterisk
enablestatic=yes
redirect = / /asterisk/static/config/index.html
修改 /etc/asterisk/manager.conf
[general]
webenabled = yes
[admin]
secret = your_password
read = system,call,log,verbose,command,agent,config
write = system,call,log,verbose,command,agent,config
#/etc/init.d/asterisk restart
接著就可以用瀏覽器瀏覽 http://your_ip:8088/ 進入管理介面
以下是安裝過程的筆記,只安裝主系統跟弄出 WEB 介面而已,其他東西以後有空再說
1. 安裝 Debian 7 amd64 (依照習慣不安裝視窗介面,節省記憶體跟硬碟空間)
套件只勾選 shell system 跟 ssh server
2. 更新系統
#apt-get update
#apt-get upgrade
3. 安裝 Asterisk
#apt-get install asterisk
(會有一堆相依套件被安裝,能拒絕嗎?當然不)
ITU-T telephone code: 886 (參考 http://en.wikipedia.org/wiki/List_of_country_calling_codes)
4. 安裝 Asterisk GUI (因為 Debian 套件沒有內建 GUI,所以要下載原始碼自己 Make,原始碼採用 svn 方式下載)
#apt-get install subversion
#cd /usr/src
#svn co http://svn.digium.com/svn/asterisk-gui/trunk asterisk-gui
#cd asterisk-gui
#./configure
#make
#make install
*** GUI 會被安裝在 /var/lib/asterisk/static-http,而 asterisk 實際會用 /usr/share/asterisk/static-http
#rm -rf /usr/share/asterisk/static-http/
#ln -s /var/lib/asterisk/static-http/ /usr/share/asterisk/
#cd /var/lib/asterisk
#chown -R asterisk.asterisk static-http
#chown -R asterisk.asterisk scripts
#chown -R asterisk.asterisk gui_backups
修改 /etc/asterisk/http.conf
[general]
enabled=yes
bindaddr=your_ip
bindport=8088
prefix=asterisk
enablestatic=yes
redirect = / /asterisk/static/config/index.html
修改 /etc/asterisk/manager.conf
[general]
webenabled = yes
[admin]
secret = your_password
read = system,call,log,verbose,command,agent,config
write = system,call,log,verbose,command,agent,config
#/etc/init.d/asterisk restart
接著就可以用瀏覽器瀏覽 http://your_ip:8088/ 進入管理介面
2014/04/03
[linux shell script] findgrep
[ "2" != "$#" ] && {
echo "syntax:"
echo " $0 "'"filename" "greptext"'
echo ' filename: like *.c or *.cpp'
echo ' greptext: text you want to search.'
echo "example:"
echo " $0 "'"*.c" "TODO:"'
} || {
echo '[exec] find . -name "'$1'" | xargs grep "'$2'"'
find . -name "$1" | xargs grep "$2"
}
echo "syntax:"
echo " $0 "'"filename" "greptext"'
echo ' filename: like *.c or *.cpp'
echo ' greptext: text you want to search.'
echo "example:"
echo " $0 "'"*.c" "TODO:"'
} || {
echo '[exec] find . -name "'$1'" | xargs grep "'$2'"'
find . -name "$1" | xargs grep "$2"
}
2014/03/13
Freeradius 設定作 eap 認證
eap 認證
eap 認證有兩點要注意
- freeradius 必須有伺服器憑證
- 被認證端的根憑證必須在 trusted ca
測試
安裝 eapol_test 需用 root 權限
wget http://hostap.epitest.fi/releases/wpa_supplicant-0.5.10.tar.gz tar xvf wpa_supplicant-0.5.10.tar.gz cd wpa_supplicant-0.5.10/ cp defconfig .config make eapol_test cp eaplo_test /usr/bin
測試用 config 檔 /tmp/eapol_test.conf.peap
network={ eap=PEAP eapol_flags=0 key_mgmt=IEEE8021X identity="myid" password="mypw" ca_cert="/etc/raddb/certs/ca.pem" phase2="auth=MSCHAPV2" anonymous_identity="anonymous" }
執行測試
eapol_test -c /tmp/eapol_test.conf.peap -a127.0.0.1 -p1812 -stesting123 -r1
傳回結果,看到 SUCCESS 表示認證連結成功
... ... ... ... 省略 ... ... ... ... EAP: deinitialize previously used EAP method (25, PEAP) at EAP deinit ENGINE: engine deinit MPPE keys OK: 1 mismatch: 0 SUCCESS
標籤: (Edit tags)
2014/02/06
修正 sourceforge 裡面 igmpproxy 在 pppx 連線下 (例如 pppoe) 無法運作的問題
igmpproxy 透過 config 檔設定 interface 是 upstream 或是 downstream,然後由 ifvc.c 裡面的 buildIfVc() 來建立各 interface 的資訊。buildIfVc() 建立資訊的方式是去蒐集各 interface 的 ip 資訊,然而 p-t-p 的 ip 資訊會拿到近端 ip/32,導致無法成功運行 igmpproxy 該做的事情。所以需要更改程式,讓他取得遠端的 ip 而不是近端的 ip 才能正常運作。
需增加的程式如下(紅色部份):
if ( ioctl( Sock, SIOCGIFFLAGS, &IfReq ) < 0 )
my_log( LOG_ERR, errno, "ioctl SIOCGIFFLAGS" );
IfDescEp->Flags = IfReq.ifr_flags;
需增加的程式如下(紅色部份):
if ( ioctl( Sock, SIOCGIFFLAGS, &IfReq ) < 0 )
my_log( LOG_ERR, errno, "ioctl SIOCGIFFLAGS" );
IfDescEp->Flags = IfReq.ifr_flags;
// aimwang: when pppx get dstaddr for use
if (0x10d1 == IfDescEp->Flags) // when pppx
{
if ( ioctl( Sock, SIOCGIFDSTADDR, &IfReq ) < 0 ) // get dstaddr
my_log(LOG_ERR, errno, "ioctl SIOCGIFDSTADDR for %s", IfReq.ifr_name);
addr = ((struct sockaddr_in *)&IfReq.ifr_dstaddr)->sin_addr.s_addr; // rewrite addr
subnet = addr & mask; // recalc subnet
}
// Insert the verified subnet as an allowed net...
IfDescEp->allowednets = (struct SubnetList *)malloc(sizeof(struct SubnetList));
if(IfDescEp->allowednets == NULL) my_log(LOG_ERR, 0, "Out of memory !");
這樣就能取得遠端 ip 來進行 igmpproxy 該有的行為
訂閱:
文章 (Atom)