顯示具有 scripts 標籤的文章。 顯示所有文章
顯示具有 scripts 標籤的文章。 顯示所有文章

2011年11月17日 星期四

系統備份 script

紀錄一下半年前的筆記(快沒時間了...Orz)。

之前備份策略是每個禮拜將 /home , /etc , /var 備份到另一顆硬碟上,過兩個禮拜再存到外接硬碟,但有時候會忘記整理,就會碰到備份檔把整顆硬碟空間吃光的情形;現在改成先檢查硬碟空間再備份,如果空間不夠會在 terminal 上顯示訊息,等待十分鐘後再次檢查硬碟空間,如果要在 cron job 啟動 GUI 程式,只要在 cmd 前面加上 export DISPLAY=:0 就行了。

原本想說用 `wall` 或 `talk` 來送訊息,卻發現 rxvt-unicode 收不到廣播訊息,不信邪想翻翻 `walk` 的原始碼看看到底是怎麼寫的,結果發現原始碼內的輸出訊息不大一樣,才發現 Arch Linux 是用 sysinitv 裡面的 `wall` 而不是 util-linux 內的 `wall`,也因此發現有 pkgtools 這個好用的 package,只要下 pkgfile -s CMD 就可以知道是哪個 package 提供的程式。

看了 sysinitv 內的 dowall.c 發現是用 getutent() 來抓目前登入的使用者,但是抓不到 rxvt-unicode 登入的紀錄,還有 xterm +ut 也是抓不到,後來乾脆就直接去掃 /dev/pts/ ,用檔案擁有者來判斷登入者,也很方便, getutent() 測試原始碼如下,編譯完成後只要執行就會列出有抓到登入的使用者。

#include <sys/types.h>
#include <stdio.h>
#include <utmp.h>
#include <unistd.h>

int main(){

int type ,pid;
char *tty,*id, *user, *host;
struct utmp *utmp;
while ((utmp = getutent()) != NULL) {
type = utmp->ut_type;
pid = utmp->ut_pid;
tty = utmp->ut_line;
id = utmp->ut_id;
user = utmp->ut_user;
host = utmp->ut_host;

printf("%d - %d - %s - %s - %s - %s\n",type , pid, tty, id, user, host);
}
}


因為 dcron 有類似 anacron 的功能,所以只要把 script 丟進 /etc/cron.weekly 就會自動執行,這次還用到 logging 這個 module ,非常方便,有了這個 module 就不必自己辛苦刻 log 了 :)


Reference:

2011年5月26日 星期四

sha1 hash encoded in base64

到了報稅的季節,幫家人安裝報稅軟體的時候發現有提供 SHA1 驗證檔,點進去一看,應該是以 base64 編碼,在 Windows 下有安裝 Python 2.7 ,就直接解碼看看( 沒想到 Python 內建的 IDLE 那麼難用,已經習慣 shell 裡按上下鍵叫出之前的指令了... ),解開一看發現真的是 hash (註一),沒有轉成一般可見的文字,跟以前看過的驗證碼(註二)不一樣 ,還以為是我下錯指令或編碼不是 base64 ,翻了說明文件才發現是我見識淺薄,用 hashlib 的 hash.digest() 就可以得到相同結果,使用 hash.hexdigest() 才會得到一般的驗證碼,但是這串文字又不能直接 decode("hex"),在 Python 裡面 decode("hex") 或 encode("hex") 都是已經轉碼好了(註三),身為一個懶得思考的傢伙,就直接跑去看人家 hashlib 原始碼是怎麼寫的,結果人家是用 C 寫的,後來又發現 binascii 這個 module 可以直接用 hexlify 這個 function 轉換字串,又跑去看原始碼,看到註解還真有點囧
/* make hex version of string, taken from shamodule.c */

for(i=j=0; i<sizeof(digest); i++) {
char c;
c = (digest[i] >> 4) & 0xf;
c = (c>9) ? c+'a'-10 : c + '0';
hex_digest[j++] = c;
c = (digest[i] & 0xf);
c = (c>9) ? c+'a'-10 : c + '0';
hex_digest[j++] = c;
}

沒想到我連這麼簡單的字元轉換都記不起來,還用
bin( ord("a") >> 4 & 0xf )
配合 ascii 表查了一下才瞭解這段程式
算 hash 的時候還忘記要用 binary mode 讀檔...Orz

後來寫了一小段 script 來比對驗證碼是否符合,如果明年幫忙安裝報稅軟體的時候可以拿來用 ( 感覺機會不大 XD ),最後要抱怨一下,這 "個人綜合所得稅電子結算申報程式" 真是他媽的難用,難用到我只是幫家人操作讀取檔案和列印單據就想摔滑鼠的程度。


註一: \xe6@\x0e\x86\xdeD\xdeW\x87\xf8\t\x9c=o\xee\xd3\xdcw\xdc
註二:
60e6400e86de44de5787f8099c3d6feed3dc77dc
註三:"abc".encode("hex") == "616263"

2011年4月8日 星期五

log cron jobs output

之前在看 /var/log/crond.log 的時候常會看到以下類似的訊息
Mar 20 18:37:01 `hostname` crond[1234]: FILE /var/spool/cron/root USER root PID 9999 job sys-daily
Mar 20 18:37:16 `hostname` crond[5678]: mailing cron output for user root job sys-daily
Mar 20 18:37:16 `hostname` crond[5678]: unable to exec /usr/sbin/sendmail: cron output for user root job sys-daily to /dev/null
Arch Linux 預設不會安裝 sendmail,所以 cron jobs 所產生的訊息沒辦法傳給使用者,但是又懶得去安裝與設定 postfix ,乾脆就寫個 script 把這些訊息存到 /var/log/cron_output/ 這個目錄下面,沒想到這一寫也是花了不少時間,我果然是個遜泡...

因為 crond 執行 cron jobs 時會把權限切換到該使用者一樣,寫入 log 的時候會有權限問題,如果 root 沒有幫忙在 /var/log/cron_output/ 下面建立 ${USER}.cron_output.log,會改成存到使用者的家目錄。

開放讓使用者能寫入 /var/ 下的檔案雖然有點危險,如果帳號被盜可能會把硬碟空間吃光導致其他 log 寫不進去,雖然可以用 quota 或另外準備一個 cron job 來偵測硬碟剩餘空間判斷要不要砍掉這個檔案或拿掉寫入權限,但身為一個不喜歡分開看 log 檔的懶惰傢伙,就讓他隨風而去吧~
把這支 script 丟到 /var/log/cron_output/ 後

sudo mkdir -v /var/log/cron_output/
sudo touch /var/log/${USER}.cron_output.log
sudo chmod 600 /var/log/${USER}.cron_output.log
sudo chattr +a /var/log/${USER}.cron_output.log

再修改 /etc/conf.d/crond,將
CROND_ARGS="-S -l info"
改成
CROND_ARGS="-S -l info -M /var/log/cron_output/cron_output_to_log.sh"

順便利用 logrotate 來管理這些 log

結果隔天馬上就發現有兩個問題要改,還是去年的問題,一堆 pacnew 檔案到現在都還沒 merge XDD~

Reference:
dcron man package
鳥哥的 Linux 私房菜:登錄檔的輪替 (logrotate)

2011年2月21日 星期一

use `tree` to list current directory contents

想要看某個目錄下面的目錄和檔案結構,但是用 `ls` 一層層列出來實在是太麻煩了,而且也很不直觀,用 `tree` 可以過濾不想要的 pattern,也可以設定 level,寫了支小 script 來處理各項變數;將查詢的 script 放在同一個目錄下面,方便管理,將每次的查詢結果加上日期儲存起來。

放在 bitbucket 上的 repository 裡,以後如果寫些 script 也會丟進裡面( 年輕人就是要玩 HG ! )


#!/bin/bash
# using `tree` to list current directory
# extract current directory name, record time and append to output file
# original: tree -L 3 -I "porn*|tree*" --noreport ./ >> ./tree.txt

LEVEL=3
EXCLUDE_PATTERN="porn*|tree*"
DIRNAME=`basename $PWD`
OUTPUT_FILE="tree.txt"

echo "Using \`tree\` to list contents of \"" $DIRNAME "\" to" $OUTPUT_FILE

echo >> $OUTPUT_FILE
echo "********" >> $OUTPUT_FILE
echo `date` >> $OUTPUT_FILE
echo "********" >> $OUTPUT_FILE
echo >> $OUTPUT_FILE

cd ..
tree -L $LEVEL -I "$EXCLUDE_PATTERN" --noreport $DIRNAME >> $DIRNAME/$OUTPUT_FILE
script download

Reference:
tree man page
Four ways to extract the current directory name

2010年9月30日 星期四

利用 shell script 一次同步多個目錄

買了顆日立 1T 硬碟搭配 CyberSLIM S1 來備份資料,雖然只能使用 USB 傳輸,但總比再弄張 sata 轉接卡接硬碟來的方便。

平常電源都是關閉,只有要傳資料時才會開啟,但每次都要 sudo mount ,接著在不同目錄內找新資料傳過去,實在很麻煩,這時想到可以利用 rsync 來同步資料夾,不用自己比對,想說這麼簡單的東西不必再用 Python ,練習寫個 shell script 就行了,沒想到也是卡了快一天...Orz

作法是在 /etc/fstab 內先將硬碟設定為一般使用者可以 mount ( pass 要設為 0 ,不然一開機就會檢查,然後卡住... )
# get UUID
blkid

# In /etc/fstab, set user, noauto
/dev/disk/by-uuid/a123-456a-789d-abcd /mnt/HDD ext4 rw,user,noauto,suid,dev,exec,async 0 0

sync.sh:

#!/bin/bash
# Description:
# sync multiple folders to external Hard disk

MOUNT_POINT=/mnt/HDD

### array in shell script
SOURCE_DIR=(
/mnt/SOURCE_1/
/mnt/SOURCE_2/
/mnt/SOURCE_3/
)

DEST_DIR=(
/mnt/DEST_1
/mnt/DEST_2
/mnt/DEST_3
)

# check if Hard disk mounted or not
if [[ -z `mount | grep "$MOUNT_POINT"` ]]
then
mount $MOUNT_POINT
echo "mount $MOUNT_POINT "
else
echo "$MOUNT_POINT already mounted !!"
fi

echo

# if unable to mount external Hard disk , stop the script and leave
if [[ -z `mount | grep "$MOUNT_POINT"`]]
then
echo "***** Unable to mount $MOUNT_POINT *****"
exit 1
fi

### seq : like range() in python
### ${#SOURCE_DIR[*]} : total element number in $SOURCE_DIR

# use for loop to sync multiple directories, counter start from 0
for counter in `seq 0 $(( ${#SOURCE_DIR[*]} - 1 ))`
do
echo "*********************************************"
echo "Source:" ${SOURCE_DIR[$counter]}
echo "Dest: " ${DEST_DIR[$counter]}
echo "*********************************************"
echo
rsync -av --exclude="*.sh" ${SOURCE_DIR[$counter]} \
${DEST_DIR[$counter]}
echo
done

exit 0;
裡面大部份程式碼都是從網路上的範例 copy 過來,應該還有更好的寫法,但可以正常運作就足夠了。

令我驚訝的是 shell script 內也有 for ... in ... ( 不好意思,我什麼都不知道 ),命令串接也很好用;但變數存取、運算和陣列相關的操作讓我很想用 python ;一些簡單的東西用 shell script 寫起來應該會很方便,只是目前還不熟...

2010年4月18日 星期日

[Script] Split audio files by cue

自從去年那篇 Split(Rename) audio files by cue files 後,覺得還要一個一個打指令實在太麻煩了,就自己寫個 script 來完成全部步驟,所需 package 相同, 只是要多加一個 ffmpeg 來幫 ape 轉檔,shnsplit 內建是 mac ,但是原作者網站都掛了,AUR 上又連到 gentoo 的軟體包去抓,實在很怪,就用 ffmpeg 來轉,如果 cue 是 UTF-8 的話會移除 BOM 再餵給 cuebreakpoint 才不會出錯,分割完後使用 cuetag.sh 設定 tag 資訊,但只支援 id3 和 vorbis,最後再改檔名。

下載

內容:

#! /usr/bin/env python
# -*- coding: utf-8 -*-
# require: ffmpeg, cuebreakpoint, cueprint, cuetag.sh , shnsplit
# use ffmpeg to convert musics

import sys
import os
import glob
import getopt
import subprocess as sp

###### usage ######
usage = """ \
Usage :
-c cuefile: the cue files to be used
-i file : the music file to be split
-o format : specify the format to be converted
"""

###### main ######

def parse_opt():
c_file = m_file = format = ""
try:
opts, args = getopt.getopt( sys.argv[1:],"c:i:o:" )
for o in opts:
if o[0] == "-c":
c_file = o[1]
elif o[0] == "-i":
m_file = o[1]
elif o[0] == "-o":
format = o[1]

# raise Error if not correct args
for f in [ c_file, m_file, format]:
if f == "": raise ValueError

return c_file ,m_file ,format

except ( getopt.GetoptError, ValueError ):
print usage
sys.exit()

def try_cmd( cmd_arg ):
# error strings
error = ["error", "Error", "ERROR"]
try:
ret = sp.Popen([ cmd_arg ],stdout=sp.PIPE,stderr=sp.PIPE,\
shell=True).communicate()
# if error strings passed to stdout or stderror
# raise error and stop the script
for es in error:
if ( es in ret[0] ) or ( es in ret[1]):
raise OSError( "\n\nExecution failed !!!\n" +\
"error messages :\n\n" +\
ret[0] + ret [1] )
return ret[0]
except OSError, e:
raise

def track_rename( c_file ):
track_names = try_cmd("cueprint -t '%02n. %t\n' " + c_file)
track_names = track_names.split('\n')
tracks = glob.glob("./split_cue_tmp_*")
tracks = sorted( tracks )
file_type = tracks[0].rpartition('.')[2]
zf = zip(tracks, track_names)
for t_name in zf:
print t_name[0], t_name[1]
os.rename( t_name[0], t_name[1] + '.' + file_type)

def remove_BOM( utf8_file ):
with open(utf8_file,'r') as f:
content = f.readlines()

check_c = map( ord, [ c for c in content[0][0:3] ] )

if check_c == [239, 187, 191] :
print "There are BOM inside!"
content[0] = content[0][3:]
with open(utf8_file + ".tmp.cue" , 'w') as f:
f.writelines(content)
return utf8_file + ".tmp.cue"
else:
print "File with no BOM"
return utf8_file

def check_format( m_file, format ):
if format == "":
return False
f_string = [format.lower(), format.upper(), format.capitalize()]
f_info = try_cmd("file " + m_file)
print f_info
for s in f_string:
if s in f_info:
print "Orginal file format is the same as specified !!"
return True
# different file format, need conversion
return False

def split_tracks(c_file, m_file, format):
# mac not installed , use ffmpeg to convert ape
if check_format( m_file, "ape"):
new_m_file = m_file.rpartition('.')[0] + '.' + format.lower()
try_cmd("ffmpeg -i " + m_file + " " + new_m_file)
m_file = new_m_file

# cuebreakpoints |shnsplit
s_args = " -a split_cue_tmp_ -o " + format + " "
try_cmd("cuebreakpoints " + c_file + "|shnsplit " + s_args + m_file)
# use cuetag.sh to add tags (only for vorbis and id3)
try_cmd("cuetag.sh " + c_file + " split_cue_tmp_*")

def main():
c_file, m_file, format = parse_opt()
c_file = remove_BOM( c_file )
split_tracks(c_file, m_file, format)
track_rename(c_file)


if __name__ == "__main__":
main()


2009年12月17日 星期四

利用 cron 與 rsync 備份資料

Reference:
鳥哥的私房菜 - 例行性工作排程、rsync manual、A turial on using rsync、rsync example、tar - exclude files

上次電腦當機後,決心要幫電腦資料做定期備份,才不會一次打破整籃雞蛋,我的構想是每天晚上利用 rsync 備份更動最頻繁的 home 到另一顆硬碟上,每周五利用 tar 將不見時會搥心肝的重要資料打包成 gz 存到另一顆硬碟,再燒到光碟上,並存入隨身碟,總共存放在三處,如此一來,資料消失在世界上的風險性相信可以大大降低。

不過存放備份的硬碟是 fat 格式,造成 rsync 時沒辦法複製權限和一堆有的沒的東東而錯誤,卡在那邊花了不少時間,後來就隨它去了,不能設權限也沒差,反正資料在就好,而且用 tar 打包時就可以保留檔案權限了,而 tar 時原本想用 bzip2 或 lzma 壓縮,但是所花的時間多到讓我受不了,乾脆用簡單的 gzip ,有壓總比沒壓好,下一步要把 anacron 搞懂,才不會因為有時電腦沒開而少了一次備份。

備份的 script 都是用 python 寫的,rsync 的 exclude 跟 tar 有點不太一樣,為了研究這個還花了不少時間, 不過有了範本後,下次要寫其他 script 應該可以更快上手。
rsync script:

#! /usr/bin/env python
# -*- coding: utf-8 -*-
# use 'rsync' to backup files to a fat partition
# write logs into log_file

import time
import sys
import subprocess as sp

# not configure ACL, xattrs currently(can use -AX to sync)
# the same as -va --compress -stats --delete
# args = " --verbose --archive --compress --stats --delete "

# because sync file from ext3 to vfat lead to errors (vfat don't have
# some file attributes), need to remove some args
args = " --verbose --recursive --times --compress --delete "

# need to sync some windows program, so use exclude
# instead of --cvs-exclude
ex_PATTERN = [ "*~", "*.pyc",\
"**cache**","**Cache**","**CACHE**", "**.macromedia/",\
"**.opera/icons/", "**.thumbnails/"]

exclude = ''.join( map( lambda p: " --exclude=" + p, ex_PATTERN))

SRC = " /home/myHome"
DEST = " /mnt/HardDisk/back/sync"
# there is also --log-file=FILE option
log_file = "/home/mylog.log"

def write_log( messages ):
with open(log_file, 'a') as f:
f.writelines("\n***************************************\n")
f.writelines( time.ctime() )
f.writelines("\n***************************************\n")
f.writelines( messages )

try:
ret = sp.Popen(["rsync" + args + exclude + SRC + DEST],\
stdout=sp.PIPE,shell=True).communicate()[0]
write_log(ret)
except OSError, e:
write_log("Execution failed:" + e )





tar script:

#! /usr/bin/env python
# -*- coding: utf-8 -*-
# use 'tar' to backup files, save them as my_tar_Year_Month_Day.tar.gz
# write logs into log_file

import time
import sys
import subprocess as sp

# use -f in front of DEST to prevent args mix up
args = " -czv --exclude-caches-all"

SRC = " /mnt/HardDisk/my_precious \
/mnt/HardDisk/rotl "

TIME_STRING = time.strftime( "%Y_%m_%d", time.localtime() )

DEST = " /mnt/HardDisk/bak/mytar_" + TIME_STRING + ".tar.gz"

log_file = "/home/mytargz.log"

ex_PATTERN = [ "*~", "*.pyc",\
"**cache**", "**Cache**","**CACHE**",\
"**.macromedia/", "**.opera/icons/", "**.thumbnails/"]

exclude = ''.join( map( lambda p: " --exclude=" + p, ex_PATTERN))


def write_log( messages ):
with open(log_file, 'a') as f:
f.writelines("\n***************************************\n")
f.writelines( time.ctime() )
f.writelines("\n***************************************\n")
f.writelines( messages )

try:
# use -f in front of DEST to prevent args mix up
ret = sp.Popen(["tar" + args + exclude + " -f " + DEST + SRC ],\
stdout=sp.PIPE,shell=True).communicate()[0]
write_log(ret)
except OSError, e:
write_log("Execution failed:" + e )

2009年10月27日 星期二

my iptables rule

網路狀態: 電腦直接連中華電信小烏龜 ADSL 上網,沒有開任何服務,只有裝 openntpd 利用 time.stdtime.gov.tw校時,針對 port scan 作防範,對於 icmp 封包沒有設防,INPUT 預設 log後丟棄

Ref: packet-filtering-HOWTO、酷學園翻譯、鳥哥私房菜- Linux 防火牆與 NAT 主機
、酷學園小州學長範例、ArchWiki、石牌國小防火牆設定、ip-sysctl.txt

Updated 10/30:網路設定 pppoe-setup 時若有選擇防火牆 (Standalone 或 Masq) 也會影響 iptables 設定,記得重跑一次,選擇 None


#! /bin/sh
#
# Create iptables'rules, remember to move it to
# /etc/iptables , then modify /etc/conf.d/iptables
#
# Author : lefthaha (at) gmail {dot} com
# Last Modify : 2009 /10 / 26

# clean current rules
iptables -F
iptables -X
iptables -Z

# set Policy
iptables -P INPUT DROP
iptables -P FORWARD DROP
iptables -P OUTPUT ACCEPT

# accept traffic from loopback iterface
iptables -A INPUT -i lo -j ACCEPT
# accept ICMP messages
iptables -A INPUT -p ICMP -j ACCEPT
# accept establised packet
iptables -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT

# create new chain
iptables -N block
iptables -N open
iptables -A INPUT -p TCP -j block
iptables -A INPUT -j open

# log then reject packet for imitate default behaviour
iptables -A INPUT -j LOG
iptables -A INPUT -p TCP -j REJECT --reject-with tcp-reset
iptables -A INPUT -p UDP -j REJECT --reject-with icmp-port-unreachable

#################### block chain rules ###################
# from http://www.study-area.org/tips/iptables_def.htm and
# http://wiki.archlinux.org/index.php/Simple_stateful_firewall_HOWTO
# http://www.spps.tp.edu.tw/documents/memo/iptables/iptables.htm

# Force SYN packets check
iptables -A block -i ppp0 -p TCP ! --syn -m state --state NEW -j DROP
# NMAP FIN/URG/PSH
iptables -A block -i ppp0 -p TCP --tcp-flags ALL FIN,URG,PSH -j DROP
# Xmas Tree
iptables -A block -i ppp0 -p TCP --tcp-flags ALL ALL -j DROP
# another Xmas Tree
iptables -A block -i ppp0 -p TCP --tcp-flags ALL SYN,RST,ACK,FIN,URG \
-j DROP

# nmap-Xmas
iptables -A block -i ppp0 -p TCP --tcp-flags URG,PSH,FIN URG,PSH,FIN \
-j DROP

# Null Scan
iptables -A block -i ppp0 -p TCP --tcp-flags ALL NONE -j DROP
# SYN/RST
iptables -A block -i ppp0 -p TCP --tcp-flags SYN,RST SYN,RST -j DROP
# SYN/FIN
iptables -A block -i ppp0 -p TCP --tcp-flags SYN,FIN SYN,FIN -j DROP
# FIN
iptables -A block -i ppp0 -p TCP --tcp-flags ALL FIN -j DROP

#################### open chain rules ####################
# for opened services

# for the connection close FIN/ACK packet
iptables -A open -p TCP --tcp-flags ALL ACK,FIN --sport 80 \
--dport 1024: -j ACCEPT

iptables-save > my_firewall.rules

echo If No error message, use iptables -L -nv to check,
echo Next move ./my_firewall.rules to /etc/iptables
echo then modify /etc/conf.d/iptables to load the rules at boot