From 70d139e6071c946875ebe686b2aebf5b23df919a Mon Sep 17 00:00:00 2001 From: sunteya Date: Thu, 5 Dec 2013 11:18:55 +0800 Subject: [PATCH 1/8] force set $HOME to target user --- doc/init.d/cow | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/init.d/cow b/doc/init.d/cow index dde597bf..3bdfd1fe 100755 --- a/doc/init.d/cow +++ b/doc/init.d/cow @@ -65,7 +65,7 @@ do_start() { fi echo "starting cow" # sudo will set the group to the primary group of $USER - sudo -u $USER $BIN & + sudo -u $USER -H $BIN & PID=$! echo $PID > $PID_FILE sleep 0.3 From f84429ec5e725aca4e8460bfbcbb8bc6a63c0639 Mon Sep 17 00:00:00 2001 From: sunteya Date: Mon, 9 Dec 2013 16:05:29 +0800 Subject: [PATCH 2/8] install shell use release version tag --- install-cow.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install-cow.sh b/install-cow.sh index 3a962de2..be8433fc 100755 --- a/install-cow.sh +++ b/install-cow.sh @@ -86,7 +86,7 @@ chmod +x $tmpbin || exit_on_fail "Can't chmod for $tmpbin" # Download sample config file if no configuration directory present -doc_base="https://github.com/cyfdecyf/cow/raw/master/doc" +doc_base="https://github.com/cyfdecyf/cow/raw/$version/doc" config_dir="$HOME/.cow" is_update=true if [ ! -e $config_dir ]; then From 8ecc3e9d08ebcd150adba5bb5a84f850578e1a3d Mon Sep 17 00:00:00 2001 From: Chen Yufei Date: Mon, 9 Dec 2013 16:51:13 +0800 Subject: [PATCH 3/8] Update github raw URL to subdomain for install script. --- install-cow.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install-cow.sh b/install-cow.sh index be8433fc..4082b0a2 100755 --- a/install-cow.sh +++ b/install-cow.sh @@ -86,7 +86,7 @@ chmod +x $tmpbin || exit_on_fail "Can't chmod for $tmpbin" # Download sample config file if no configuration directory present -doc_base="https://github.com/cyfdecyf/cow/raw/$version/doc" +doc_base="https://raw.github.com/cyfdecyf/cow/$version/doc" config_dir="$HOME/.cow" is_update=true if [ ! -e $config_dir ]; then From 50264c5c71a6709a99802cbd9b5937d54860f2d8 Mon Sep 17 00:00:00 2001 From: Chen Yufei Date: Mon, 9 Dec 2013 16:52:30 +0800 Subject: [PATCH 4/8] Include stderr output in log for startup script. --- doc/init.d/cow | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/doc/init.d/cow b/doc/init.d/cow index 3bdfd1fe..0847a8b5 100755 --- a/doc/init.d/cow +++ b/doc/init.d/cow @@ -19,6 +19,7 @@ USER=usr GROUP=grp PID_DIR=/var/run PID_FILE=$PID_DIR/cow.pid +LOG_FILE=/var/log/cow RET_VAL=0 @@ -65,7 +66,7 @@ do_start() { fi echo "starting cow" # sudo will set the group to the primary group of $USER - sudo -u $USER -H $BIN & + sudo -u $USER -H -- $BIN >$LOG_FILE 2>&1 & PID=$! echo $PID > $PID_FILE sleep 0.3 From 0ef33fc5afc5db341b9cbabb8a4968e88b895956 Mon Sep 17 00:00:00 2001 From: Chen Yufei Date: Fri, 20 Dec 2013 11:25:24 +0800 Subject: [PATCH 5/8] Fix possible cross FS rename for stat. --- main.go | 3 ++- sitestat.go | 41 ++++++++++++++++++++++++++++------------- 2 files changed, 30 insertions(+), 14 deletions(-) diff --git a/main.go b/main.go index 5eb0dac1..d8abd607 100644 --- a/main.go +++ b/main.go @@ -20,8 +20,9 @@ func sigHandler() { syscall.SIGHUP) for sig := range sigChan { + // May handle other signals in the future. info.Printf("%v caught, exit\n", sig) - storeSiteStat() + storeSiteStat(siteStatExit) break } /* diff --git a/sitestat.go b/sitestat.go index f11de023..e8e721e6 100644 --- a/sitestat.go +++ b/sitestat.go @@ -251,7 +251,7 @@ func (ss *SiteStat) GetVisitCnt(url *URL) (vcnt *VisitCnt) { return ss.create(url.Host) } -func (ss *SiteStat) store(file string) (err error) { +func (ss *SiteStat) store(statPath string) (err error) { if err = mkConfigDir(); err != nil { return } @@ -289,10 +289,10 @@ func (ss *SiteStat) store(file string) (err error) { } // Store stat into temp file first and then rename. - // This avoids problem if SIGINT causes program to exit but stat writing - // is half done. + // Ensures atomic update to stat file to avoid file damage. - f, err := ioutil.TempFile("", "stat") + // Create tmp file inside config firectory to avoid cross FS rename. + f, err := ioutil.TempFile(dsFile.dir, "stat") if err != nil { errl.Println("create tmp file to store stat", err) return @@ -305,9 +305,9 @@ func (ss *SiteStat) store(file string) (err error) { f.Close() // Windows don't allow rename to existing file. - os.Remove(file + ".bak") - os.Rename(file, file+".bak") // original stat may not exist - if err = os.Rename(f.Name(), file); err != nil { + os.Remove(statPath + ".bak") + os.Rename(statPath, statPath+".bak") + if err = os.Rename(f.Name(), statPath); err != nil { errl.Println("can't rename newly created stat file", err) return } @@ -428,22 +428,37 @@ func initSiteStat() { if err := siteStat.load(dsFile.stat); err != nil { os.Exit(1) } - // dump site stat once every hour, so we don't always need to close cow to - // get updated stat + // Dump site stat while running, so we don't always need to close cow to + // get updated stat. go func() { for { - time.Sleep(time.Hour) - storeSiteStat() + time.Sleep(5 * time.Minute) + storeSiteStat(siteStatCont) } }() } +const ( + siteStatExit = iota + siteStatCont +) + +// Lock ensures only one goroutine calling store. +// siteStatFini ensures no more calls after going to exit. var storeLock sync.Mutex +var siteStatFini bool -func storeSiteStat() { +func storeSiteStat(cont byte) { storeLock.Lock() + defer storeLock.Unlock() + + if siteStatFini { + return + } siteStat.store(dsFile.stat) - storeLock.Unlock() + if cont == siteStatExit { + siteStatFini = true + } } func loadSiteList(fpath string) (lst []string, err error) { From 602bbcfac7df2aec5d9256bc03b99fd7a11f8008 Mon Sep 17 00:00:00 2001 From: Chen Yufei Date: Fri, 20 Dec 2013 11:34:35 +0800 Subject: [PATCH 6/8] Better parse header error msg. --- http.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/http.go b/http.go index d5a255dd..603a9885 100644 --- a/http.go +++ b/http.go @@ -552,7 +552,7 @@ func (h *Header) parseHeader(reader *bufio.Reader, raw *bytes.Buffer, url *URL) return } if name, val, err = splitHeader(line); err != nil { - errl.Printf("%v raw header:\n%s\n", err, raw.Bytes()) + errl.Printf("split header %v\nline: %s\nraw header:\n%s\n", err, line, raw.Bytes()) return } // Wait Go to solve/provide the string<->[]byte optimization @@ -562,6 +562,7 @@ func (h *Header) parseHeader(reader *bufio.Reader, raw *bytes.Buffer, url *URL) continue } if err = parseFunc(h, val); err != nil { + errl.Printf("parse header %v\nline: %s\nraw header:\n%s\n", err, line, raw.Bytes()) return } } @@ -701,7 +702,7 @@ func parseResponse(sv *serverConn, r *Request, rp *Response) (err error) { } if err = rp.parseHeader(reader, rp.raw, r.URL); err != nil { - errl.Printf("parse response header: %v %s\n%s", err, rp, rp.Verbose()) + errl.Printf("parse response header: %v %s\n%s", err, r, rp.Verbose()) return err } From 64fb387c84e3923bf0d80abd098220f69e8c25ef Mon Sep 17 00:00:00 2001 From: Chen Yufei Date: Fri, 20 Dec 2013 11:36:04 +0800 Subject: [PATCH 7/8] Rename dsFile to configPath. --- config.go | 18 +++++++++--------- config_unix.go | 2 +- config_windows.go | 2 +- sitestat.go | 10 +++++----- 4 files changed, 16 insertions(+), 16 deletions(-) diff --git a/config.go b/config.go index 2f7b7c58..9c661a0a 100644 --- a/config.go +++ b/config.go @@ -70,7 +70,7 @@ type Config struct { var config Config var configNeedUpgrade bool // whether should upgrade config file -var dsFile struct { +var configPath struct { dir string // directory containing config file and blocked site list alwaysBlocked string // blocked sites specified by user alwaysDirect string // direct sites specified by user @@ -85,9 +85,9 @@ func init() { initConfigDir() // fmt.Println("home dir:", homeDir) - dsFile.alwaysBlocked = path.Join(dsFile.dir, alwaysBlockedFname) - dsFile.alwaysDirect = path.Join(dsFile.dir, alwaysDirectFname) - dsFile.stat = path.Join(dsFile.dir, statFname) + configPath.alwaysBlocked = path.Join(configPath.dir, alwaysBlockedFname) + configPath.alwaysDirect = path.Join(configPath.dir, alwaysDirectFname) + configPath.stat = path.Join(configPath.dir, statFname) config.DetectSSLErr = false config.AlwaysProxy = false @@ -109,7 +109,7 @@ func parseCmdLineConfig() *Config { var c Config var listenAddr string - flag.StringVar(&c.RcFile, "rc", path.Join(dsFile.dir, rcFname), "configuration file") + flag.StringVar(&c.RcFile, "rc", path.Join(configPath.dir, rcFname), "configuration file") // Specifying listen default value to StringVar would override config file options flag.StringVar(&listenAddr, "listen", "", "listen address, disables listen in config") flag.IntVar(&c.Core, "core", 2, "number of cores to use") @@ -722,10 +722,10 @@ func checkConfig() { } func mkConfigDir() (err error) { - if dsFile.dir == "" { + if configPath.dir == "" { return os.ErrNotExist } - exists, err := isDirExists(dsFile.dir) + exists, err := isDirExists(configPath.dir) if err != nil { errl.Printf("Error checking config directory: %v\n", err) return @@ -733,8 +733,8 @@ func mkConfigDir() (err error) { if exists { return } - if err = os.Mkdir(dsFile.dir, 0755); err != nil { - errl.Printf("Error create config directory %s: %v\n", dsFile.dir, err) + if err = os.Mkdir(configPath.dir, 0755); err != nil { + errl.Printf("Error create config directory %s: %v\n", configPath.dir, err) } return } diff --git a/config_unix.go b/config_unix.go index 36c9993c..6ff78cde 100644 --- a/config_unix.go +++ b/config_unix.go @@ -17,5 +17,5 @@ const ( func initConfigDir() { home := getUserHomeDir() - dsFile.dir = path.Join(home, ".cow") + configPath.dir = path.Join(home, ".cow") } diff --git a/config_windows.go b/config_windows.go index 3fee928e..98f8d16a 100644 --- a/config_windows.go +++ b/config_windows.go @@ -17,5 +17,5 @@ const ( func initConfigDir() { // On windows, put the configuration file in the same directory of cow executable // This is not a reliable way to detect binary directory, but it works for double click and run - dsFile.dir = path.Dir(os.Args[0]) + configPath.dir = path.Dir(os.Args[0]) } diff --git a/sitestat.go b/sitestat.go index e8e721e6..97b72754 100644 --- a/sitestat.go +++ b/sitestat.go @@ -292,7 +292,7 @@ func (ss *SiteStat) store(statPath string) (err error) { // Ensures atomic update to stat file to avoid file damage. // Create tmp file inside config firectory to avoid cross FS rename. - f, err := ioutil.TempFile(dsFile.dir, "stat") + f, err := ioutil.TempFile(configPath.dir, "stat") if err != nil { errl.Println("create tmp file to store stat", err) return @@ -326,10 +326,10 @@ func (ss *SiteStat) loadBuiltinList() { } func (ss *SiteStat) loadUserList() { - if directList, err := loadSiteList(dsFile.alwaysDirect); err == nil { + if directList, err := loadSiteList(configPath.alwaysDirect); err == nil { ss.loadList(directList, userCnt, 0) } - if blockedList, err := loadSiteList(dsFile.alwaysBlocked); err == nil { + if blockedList, err := loadSiteList(configPath.alwaysBlocked); err == nil { ss.loadList(blockedList, 0, userCnt) } } @@ -425,7 +425,7 @@ func (ss *SiteStat) GetDirectList() []string { var siteStat = newSiteStat() func initSiteStat() { - if err := siteStat.load(dsFile.stat); err != nil { + if err := siteStat.load(configPath.stat); err != nil { os.Exit(1) } // Dump site stat while running, so we don't always need to close cow to @@ -455,7 +455,7 @@ func storeSiteStat(cont byte) { if siteStatFini { return } - siteStat.store(dsFile.stat) + siteStat.store(configPath.stat) if cont == siteStatExit { siteStatFini = true } From f9fa1785c92a5ab84657712b89532b49e5ec3eb3 Mon Sep 17 00:00:00 2001 From: Chen Yufei Date: Fri, 20 Dec 2013 20:12:46 +0800 Subject: [PATCH 8/8] Bump version to 0.9.1 --- CHANGELOG | 4 ++++ README.md | 3 ++- config.go | 2 +- install-cow.sh | 2 +- 4 files changed, 8 insertions(+), 3 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 9645e85a..fdafc445 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,7 @@ +0.9.1 (2013-12-20) + * Fix can't save site stat bug + * Improve install and startup script + 0.9 (2013-12-02) * New feature: two COW servers can be connected using encrypted connection, thus we have an encrypted HTTP proxy chain that can diff --git a/README.md b/README.md index e44865c2..6225b53d 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ COW 是一个简化穿墙的 HTTP 代理服务器。它能自动检测被墙网站,仅对这些网站使用二级代理。 -当前版本:0.9 [CHANGELOG](CHANGELOG) +当前版本:0.9.1 [CHANGELOG](CHANGELOG) [![Build Status](https://travis-ci.org/cyfdecyf/cow.png?branch=develop)](https://travis-ci.org/cyfdecyf/cow) **欢迎在 develop branch 进行开发并发送 pull request :)** @@ -125,6 +125,7 @@ COW 默认配置下检测到被墙后,过两分钟再次尝试直连也是为 - @tevino: http parent proxy basic authentication - @xupefei: 提供 cow-hide.exe 以在 windows 上在后台执行 cow.exe +- @sunteya: 改进启动和安装脚本 Bug reporter: diff --git a/config.go b/config.go index 9c661a0a..886fe738 100644 --- a/config.go +++ b/config.go @@ -15,7 +15,7 @@ import ( ) const ( - version = "0.9" + version = "0.9.1" defaultListenAddr = "127.0.0.1:7777" ) diff --git a/install-cow.sh b/install-cow.sh index 4082b0a2..9e5e8e68 100755 --- a/install-cow.sh +++ b/install-cow.sh @@ -1,6 +1,6 @@ #!/bin/bash -version=0.9 +version=0.9.1 arch=`uname -m` case $arch in