Golang:gosync 简单文件同步 Simple File Sync

作者:Ajian 发布时间:April 23, 2012 分类:Golang

前言:最近学习Go语言 尝试写了一个简单的文件同步,基于tcp的,功能比较简单。基于Go version 1 编译。

该程序的功能:

  1. 只能传输单一文件,文件名不能有特殊符号和空格
  2. 有serve端和client命令 使用同一个程序
  3. 不能同步目录结构
  4. 不能改名字
  5. 无法查看进度
该程序主要就是将指定的文件同步到新的服务器和指定的目录下面,并且该文件的时间和属性跟原来一样,可以使用rsync进行检测。
该程序名字 gosync
gosync.go
package main

import (
    "os"
    "fmt"
    "net"
    "flag"
    "time"
    "crypto/md5"
    "io"
    "strings"
    "strconv"
)

type sysFileInfo struct{
    fName       string
    fSize       int64
    fMtime      time.Time
    fPerm       os.FileMode
    fMd5        string
    fType       bool
}

var (
    listenPort = flag.String( "port","7722","server listen port" )
    syncFile = flag.String( "file","","transfer file" )
    syncHost = flag.String( "host","","server host" )
    syncSer = flag.Bool( "d",false,"server mode")
    syncFold = flag.String( "dir","/tmp/gosync/","recive sync fold ")
)

func main(){
    flag.Parse()
    if *syncSer {  
        servPort:=fmt.Sprintf( ":%s",*listenPort )
        l,err := net.Listen( "tcp",servPort )
        if err != nil{
           fmt.Println( "net failed",err ) 
        }
        err = os.MkdirAll( *syncFold , 0755)
        if err != nil{
           fmt.Println( err ) 
        }
        fmt.Println( "Start Service" )
        Serve( l )
     }else{  
        destination:=fmt.Sprintf( "%s:%s",*syncHost,*listenPort )
        clientSend( *syncFile,destination)
     }
}

func clientSend(files string,destination string){
    fInfo:=getFileInfo( files)
    newName :=fmt.Sprintf( "%s",fInfo.fName)
    cmdLine:=  fmt.Sprintf( "upload %s %d %d %d %s " ,newName,fInfo.fMtime.Unix(),fInfo.fPerm,fInfo.fSize,fInfo.fMd5) 
    cn,err:=net.Dial( "tcp", destination)
    if err !=nil {
        fmt.Println( "connect error",err )
        return
    }
    defer cn.Close()
    cn.Write( []byte( cmdLine ) )
    cn.Write( []byte( "\r\n" ) )
    fileHandle,err := os.Open( files )
    if err != nil {
        fmt.Println("open ERROR",err) 
        return
    }
    io.Copy( cn,fileHandle)
    for{
        buffer :=make( []byte,1024)
        num,err := cn.Read(buffer)
        if err == nil && num > 0{
            fmt.Println(  string(buffer[ :num ]) )
            break
        }
    }
}

func getFileInfo( filename string) *sysFileInfo{
    fi,err:= os.Lstat( filename ) 
    if err != nil {
        fmt.Println("info ERROR",err) 
        return nil
    }
    fileHandle,err := os.Open( filename )
    if err != nil {
        fmt.Println("open ERROR",err) 
        return nil
    }

    h := md5.New()
    _,err = io.Copy( h,fileHandle )
    fileInfo := & sysFileInfo {
        fName : fi.Name(),
        fSize : fi.Size(),
        fPerm : fi.Mode().Perm(),
        fMtime: fi.ModTime(),
        fType : fi.IsDir(),
        fMd5  : fmt.Sprintf( "%x", h.Sum( nil )),
    }
        return fileInfo
}

func Serve( l net.Listener) {
    for{
        conn,err := l.Accept()
        if err != nil{
            if ne,ok := err.( net.Error );ok && ne.Temporary(){
                continue
            }
            fmt.Println( "network error",err )
        }
        go Handler(conn)
    }
}

func Handler( conn net.Conn) {
    defer conn.Close()
    state := 0
    var cmd *sysFileInfo
    var fSize int64
    var tempFileName string
    var n int64
    for {
        buffer :=make( []byte,2048)
        num,err := conn.Read(buffer)
        numLen:=int64( num )
        if err != nil && err != io.EOF {
            fmt.Println( "cannot read",err )
        }
        n=0
        if state  == 0 {
            n,cmd = cmdParse( buffer[:num] )
            tempFileName = fmt.Sprintf( "%s.newsync",cmd.fName)
            fSize = cmd.fSize
            state = 1
        } 
        if state == 1 {
            last := numLen
            if fSize <= numLen-n {
                last = fSize + n
                state = 2
            }
            err = writeToFile( buffer[int( n ):int( last )],tempFileName,cmd.fPerm )
            if err != nil{
                fmt.Println( "read num error : ",err )
            }
            fSize -=last-n 
            if state == 2{
                os.Remove( cmd.fName)
                err = os.Rename( tempFileName,cmd.fName)
                if err != nil{
                    fmt.Println( "rename ",tempFileName," to ",cmd.fName," failed" ) 
                }
                err = os.Chtimes( cmd.fName,time.Now(),cmd.fMtime )
                if err != nil{
                    fmt.Println( "change the mtime error ",err ) 
                }
                fileHandle,err := os.Open( cmd.fName)
                if err != nil {
                    fmt.Println("open ERROR",err) 
                }
                h := md5.New()
                io.Copy( h,fileHandle )
                newfMd5 := fmt.Sprintf( "%x", h.Sum( nil ))
                if newfMd5 == cmd.fMd5{
                    sendInfo:=fmt.Sprintf("%s sync success",cmd.fName)
                    conn.Write([]byte(sendInfo))
                }else{
                    sendInfo:=fmt.Sprintf("%s sync failed",cmd.fName)
                    conn.Write([]byte(sendInfo))
                }
            }
        }
    }
}

func cmdParse( infor []byte) ( int64 , *sysFileInfo) {
    var i int64
    for i=0;i<int64(len(infor));i++ {
       if infor[i] == '\n' && infor[i-1]  == '\r' {
           cmdLine:=strings.Split( string( infor[:i-1] ) ," ") 
           fileName := fmt.Sprintf( "%s/%s",*syncFold,cmdLine[ 1 ] )
           filePerm, _ := strconv.Atoi( cmdLine[ 3 ])
           fileMtime,_:= strconv.ParseInt( cmdLine[ 2 ],10,64 )
           fileSize,_:= strconv.ParseInt( cmdLine[ 4 ],10,64)
           fileInfo := & sysFileInfo {
                fName : fileName, 
                fMtime: time.Unix( fileMtime,0 ),
                fPerm : os.FileMode(filePerm),
                fSize : fileSize,
                fMd5  : string(cmdLine[ 5 ]),
           }
           return i+1,fileInfo
       }
    }
       return 0,nil
}

func writeToFile( data []byte ,fileName string,perm os.FileMode)  error{
    writeFile,err := os.OpenFile( fileName,os.O_RDWR | os.O_APPEND | os.O_CREATE ,perm)
    if err != nil{
        fmt.Println( "write file error:",err )
        return err
    }
    defer writeFile.Close()
    _,err = writeFile.Write( data )
    if err != nil{
       fmt.Println( "write file error",err ) 
       return err 
    }
    return nil
}
使用:
usage:
Usage of ./gosync:
  -d=false: server mode
  -dir="/tmp/gosync/": recive sync fold 
  -file="": transfer file
  -host="": server host
  -port="7722": server listen port
服务端开启
#./gosync -d -dir /tmp/xxxx
client传输文件:
# ./gosync --file=/root/Videos/DSCF2394.avi --host=127.0.0.1 --port=7722
/tmp/xxxx/DSCF2394.avi sync success
手动检查文件正确性:
# md5sum /tmp/xxxx/DSCF2394.avi 
eb50332d3b3b6f36b773046aca16e908  /tmp/xxxx/DSCF2394.avi
# md5sum /root/Videos/DSCF2394.avi
eb50332d3b3b6f36b773046aca16e908  /root/Videos/DSCF2394.avi

Golang:xml解析和类型重组

作者:Ajian 发布时间:April 18, 2012 分类:Golang

Go对xml的支持很好,好像其它的配置文件格式还没有太多支持的,像ini yaml 都还没有看到。我之前很少解析xml配置文件,对于很少使用这种强类型语言的自己(之前接触的语言都是弱类型语言像php python shell),还是遇到不少问题。


xml配置文件 sample.xml

<?xml version="1.0" encoding="utf-8"?>
<Autosync Version="1.01">
    <Filter>
        <Exclude>(.*)\.svn$</Exclude>
        <Exclude>(.*)\.gz</Exclude>
        <Exclude>^info/.*</Exclude>
    </Filter>
    <ListenPort>8786</ListenPort>
    <Timeout>60</Timeout>
    <Servthread>20</Servthread>

    <Debug>/tmp/sync_debug.log</Debug>
    <Warning>/tmp/sync_warning.log</Warning>
    <Fatal>/tmp/sync_fatal.log</Fatal>

    <Service Name="local">
        <Ip>192.168.1.20</Ip>
        <Project Module="TEST" User="www" Group="www" Path="/tmp/test">
            <Remote Ip="192.168.1.22" Port="8080" Module="TEST"/>
            <Remote Ip="192.168.1.21" Port="2277" Module="TEMP"/>
        </Project>
        <Project Module="kkkk" User="nobody" Group="nobody" Path="/tmp/kkkk">
            <Remote Ip="192.168.1.23" Module="TEST"/>
        </Project>
    </Service>

    <Service Name="21serv">
        <Filter>
            <Exclude>(.*)\.svn$</Exclude>
            <Exclude>(.*)\.gz</Exclude>
            <Exclude>^info/.*</Exclude>
        </Filter>
        <ListenPort>8788</ListenPort>
        <Timeout>30</Timeout>
        <Servthread>10</Servthread>
        <Ip>192.168.1.21</Ip>
        <Project Module="TEMP" User="nobody" Group="nobody" Path="/tmp/temp">
        </Project>
    </Service>
</Autosync>

该配置的作用就不具体说 但是主要实现的是 Service 中的Filter ListenPort Timeout Servthread 优先级比Global的变量高,解析例子里面将这个xml配置文件进行了结构重组。

service[name]{Filter ,ListenPort,Timeout,Servthread,Project[module]}

Project[module]{user,grop,path,remote[module]}

remote[module]{ip,port}

PS:go解析的xml文件都要首字母大写 不然在解析的时候会比较的麻烦。那就规范全部用大写开头吧。

xml.go

package main
import (
    "encoding/xml"
    "fmt"
    "flag"
    "os"
)
var (
    gServ= flag.String("service","local","service name")
    gFile= flag.String("config","sample.xml","config name")
)

type XmlConfig struct {
    Version     string      `xml:"Version,attr"`
    Filter      []string    `xml:"Filter>Exclude"`
    ListenPort  int         
    Timeout     int
    Servthread  int
    Debug       string
    Warning     string
    Fatal       string
    Security    XmlSecurity
    Service[]   XmlService
}

type XmlService struct {
    Name        string  `xml:"Name,attr"`
    Filter      []string    `xml:"Filter>Exclude"`
    Servthread  int
    ListenPort  int
    Timeout     int
    Ip          string
    Security    XmlSecurity
    Project[]   XmlProject
}

type XmlProject struct {
    Module      string  `xml:"Module,attr"`
    User        string  `xml:"User,attr"`
    Group       string  `xml:"Group,attr"`
    Path        string  `xml:"Path,attr"`
    Security    XmlSecurity
    Remote[]     XmlRemote
}

type XmlRemote struct {
    Module      string  `xml:"Module,attr"`
    Port        int     `xml:"Port,attr"`
    Ip          string  `xml:"Ip,attr"`
}

type XmlSecurity struct {
    Ip[]    string
}

//////////////////////////
/////////////////////////
type sysInoInfo struct{
    Name        string     
    Filter      []string    
    ListenPort  int
    Timeout     int
    Servthread  int
    Project     map[string]*InoProject
}

type InoProject struct{
    Module      string 
    User        string  
    Group       string 
    Path        string
    Remote      map[string]*InoRemote
}

type InoRemote struct{
    Ip          string
    Port        int
    Module      string
}

var gConfig XmlConfig

func XmlPrase(fileName string) bool{
    file,err := os.Open( fileName )
    if err != nil {
        fmt.Println( "open xml file Fail",err )
    }
    defer file.Close()
    fi,_ := file.Stat()
    buffer := make( []byte,fi.Size() )
    _,err = file.Read( buffer )
    if err != nil{
        fmt.Println( "read xml file fail",err )
        return false
    }
    err = xml.Unmarshal( buffer,&gConfig )
    if err != nil{
        fmt.Println( "get config file fail",err )
        return false
    }
    return true
}

func main(){
    flag.Parse()
    XmlPrase( *gFile)
    allService := make( map[string]*sysInoInfo)
    for _, xServer := range gConfig.Service{
        serv, ok := allService[xServer.Name]
        if !ok { serv = new( sysInoInfo  )  }
        allService[xServer.Name] = serv
        serv.Filter = xServer.Filter
        serv.ListenPort = xServer.ListenPort
        serv.Timeout= xServer.Timeout
        serv.Servthread= xServer.Servthread
        if  len(xServer.Filter) == 0 {
            serv.Filter = gConfig.Filter
        }
        if   xServer.ListenPort == 0{
            serv.ListenPort = gConfig.ListenPort
        }
        if   xServer.Timeout== 0{
            serv.Timeout= gConfig.Timeout
        }
        if   xServer.Servthread == 0{
            serv.Servthread= gConfig.Servthread
        }

        serv.Project = make( map[string]*InoProject)
        for _,xProject := range xServer.Project{
            pro,ok := serv.Project[xProject.Module]
            if !ok { pro = new( InoProject)  }
            serv.Project[xProject.Module] = pro
            pro.User=xProject.User
            pro.Group=xProject.Group
            pro.Path=xProject.Path

            pro.Remote = make( map[ string ]*InoRemote )
            for _,xRemote := range xProject.Remote{
                remo,ok := pro.Remote[xRemote.Module]
                if !ok { remo = new( InoRemote)  }
                pro.Remote[ xRemote.Module ] = remo
                remo.Ip = xRemote.Ip
                remo.Port = 7788
                if xRemote.Port == 0{
                    remo.Port = xRemote.Port
                }
            }
        }
    }
    fmt.Println( allService[ *gServ])
//    allService[ *gServ ]
    for key , value:= range allService[ *gServ ].Project {
        fmt.Println( "Project:",key)
        fmt.Println( value)
        for key,v := range value.Remote{
            fmt.Println( "Remote:",key)
            fmt.Println( v)
        }
    }
}

直接运行 看下结果:

# ./run xml.go
# ./xml
&{ [(.*)\.svn$ (.*)\.gz ^info/.*] 8786 60 20 map[kkkk:0xf8400a6320 TEST:0xf8400a62d0]}
Project: kkkk
&{ nobody nobody /tmp/kkkk map[TEST:0xf84009fab0]}
Remote: TEST
&{192.168.1.23 0 }
Project: TEST
&{ www www /tmp/test map[TEMP:0xf84009fa80 TEST:0xf84009fa50]}
Remote: TEST
&{192.168.1.22 7788 }
Remote: TEMP
&{192.168.1.21 7788 }
# ./xml --service=21serv --config=sample.xml
&{ [(.*)\.svn$ (.*)\.gz ^info/.*] 8788 30 10 map[TEMP:0xf8400b8550]}
Project: TEMP
&{ nobody nobody /tmp/temp map[]}

Golang:So easy 命令行参数传递Flag 对比Shell python

作者:Ajian 发布时间:April 13, 2012 分类:Shell,Golang

    感叹Go language写参数传递太简单了,so easy .如果要写脚本或者写服务肯定很多时候都要进行命令行参数的传递。

如:

./flag  --config=main.xml --port=7788

对比shell和python就感觉go真是很简单方便。下面对比下几个语言吧:

Shell

Shell使用了两个getopts和getopt getopts可以接受短命令 getopt就可以复杂一些 包括长命令 而且两个命令是分开的 感觉很原始

getopts 短命令

#!/bin/bash
if [ $# -eq 0 ];then
    echo "erminating..." >&2 ; exit 1 ;
else
    while getopts ":c:p:" opt; do
        case $opt in
            c)
                config=$OPTARG 
                echo "Option c, argument $config" 
                ;;
            p)
                port=$OPTARG 
                echo "Option p, argument $port"
                ;;
            *)
                echo "nternal error!" ; exit 1 ;;
        esac
    done
fi

Output:

# bash shortflag.sh -c config.xml -p 8080
Option c, argument config.xml
Option p, argument 8080

getopt长命令

#!/bin/bash
TEMP=`getopt -o p:c: --long port:,config: \
      -- "$@"`

if [ $? != 0 ] ; then echo "Terminating..." >&2 ; exit 1 ; fi

eval set -- "$TEMP"

while true ; do
        case "$1" in
                -p|--port) echo "Option p, argument \`$2'" ; shift 2 ;;
                -c|--config)echo "Option c, argument \`$2'" ; shift 2 ;;
                --) shift ; break ;;
                *) echo "Internal error!" ; exit 1 ;;
        esac
done
echo "Remaining arguments:"
for arg do
   echo '--> '"\`$arg'" ;
done

Output:

# bash longflag.sh -c config.xml -p 8080
Option c, argument `config.xml'
Option p, argument `8080'
Remaining arguments:
# bash longflag.sh --config=config.xml --port=8080
Option c, argument `config.xml'
Option p, argument `8080'
Remaining arguments:

Python

同样有一个getopt函数不过 该函数已经包括了长命令格式 做为可选的一个参数 进化了。

#!/usr/bin/python
import getopt, sys
def main():
    try:
        opts, args = getopt.getopt(sys.argv[1:], "p:c:", ["port=", "config="])
    except getopt.GetoptError:
        print "Terminating..." 
        sys.exit(2)
    config = None
    port = 8001
    for o, a in opts:
        if o in ("-p", "--port"):
            port= a
            print "Option p, argument %s"%port
        if o in ("-c", "--config"):
            config = a
            print "Option c, argument %s"%config

if __name__ == "__main__":
    main()

Output:

# python flag.py  --port=9000 --config=file.xml
Option p, argument 9000
Option c, argument file.xml
# python flag.py  -p 9000 -c file.xml
Option p, argument 9000
Option c, argument file.xml

看到上面这些如果不认真看 还真容易晕吧。到Go了 想像不到的简单。

Go

使用flag库,一切都简化了

package main

import (
    "flag"
    "fmt"
)

var (
     openPort= flag.String("port",":8080","http listen port")
     configFile= flag.String( "config","","config file name" )
)

func main(){
    flag.Parse()
    fmt.Println("Configure Filename: ",*configFile)
    fmt.Println("Open Port: ",*openPort)
}

Output:

#./run flag.go
# ./flag -port 8001 -config mirror.xml
Configure Filename:  mirror.xml
Open Port:  8001
# ./flag --port 8001 --config mirror.xml
Configure Filename:  mirror.xml
Open Port:  8001
# ./flag --port=8001 --config=mirror.xml
Configure Filename:  mirror.xml
Open Port:  8001
# ./flag -port=8001 -config=mirror.xml
Configure Filename:  mirror.xml
Open Port:  8001

看到结果了吧 只要一定义 引用可以任意的 不用纠结他是怎么工作的 要的就是快捷。

里面有个run是我的小脚本,可以方便的帮助我编译做测试的脚本

#!/bin/bash
filename=$1
outfile=`echo ${filename%.*}`
go build -o $outfile $filename

更多flag函数的使用去官网查看吧: http://golang.org/pkg/flag

openSUSE 12.1 配置工作环境[3]

作者:Ajian 发布时间:March 31, 2012 分类:openSUSE

  今天是基本环境配置的最后一篇,主要是细节的环境,更多的跟运维工作需求相关,当然使用者和开发者也可以得到一样的方便。

History 条数设置
因为是自己的工作环境 所以history的条数设置多点也没有问题。方便自己查看。
vim /etc/profile
if test -z "$PROFILEREAD" ; then
HISTSIZE=5000
export HISTSIZE
fi
另外还可以设置history的显示时间 不过默认openSUSE就是这样的。服务器上倒是要设置下。

剪切板条数设置
configure klipper
剪切板是一个很好的复制的工具,会存储所有复制过的内容 如果想粘贴或者重复利用之前复制过的内容就可以直接在剪切板里面找 就可以了 而且还可以进行搜索,默认的条数只有10条吧 太少了。根本满足不了我的需求。最大值设置到2040 好像就不能再大了。

klipper

konsole 快捷方式 样式调整
Konsole 是方便的终端软件,开新的tab 默认是ctrl+alt+t 但 firefox和chrome都了是ctrl+T 所以要修改下konsole的快捷方式 修改方式类似之前说的修改kopete查看消息的方式

System 配置
Link 链接dropbox同步的配置文件
linux-htd5:~ # ln -s Dropbox/System/bashrc .bashrc
linux-htd5:~ # ln -s Dropbox/System/bash_profile .bash_profile
linux-htd5:~ # ln -s Dropbox/System/vimrc .vimrc

~/.bashrc

 

#各种自己的服务器环境变量和自定义命令缩写
. /home/server/private/towiki/.hostlist_variable.tmp
#go语言的path路径
PATH=$PATH:/root/go/bin
export PATH

~/.bash_profile

#!/bin/bash
#we'd like it to cache
#keychain的配置
/usr/bin/keychain ${HOME}/.ssh/id_rsa
source ${HOME}/.keychain/${HOSTNAME}-sh > /dev/null

#go的环境变量
export GOROOT=$HOME/go
export GOARCH=amd64
export GOOS=linux

 

运维管理工具
keychain

相当方便的工具,可以让两个client之间进行通讯只要他们有相同的pub key  

安装 zypper in keychain


cssh  

全称cluster ssh 是可以多窗口同时操作的工具
http://sourceforge.net/projects/clusterssh/
wget http://cdnetworks-kr-1.dl.sourceforge.net/project/clusterssh/1.%20ClusterSSH%20Series%203/3.28/clusterssh-3.28.tar.gz
tar zxvf clusterssh-3.28.tar.gz
cd clusterssh-3.28/
cat README
可以查看安装方法 依赖perl module有
Tk
X11::Protocol
zypper in perl-Tk perl-X11-Protocol -y

运行 cssh 后面接多个IP 或者user@IP 最好是建立key 不然得单独分别输入密码

之前cssh的截图文章 Linux超强的批处理管理工具--CSSH

pssh 

全称 Parallel SSH Tools
http://code.google.com/p/parallel-ssh/%E3%80%80
wget http://parallel-ssh.googlecode.com/files/pssh-2.3.1.tar.gz
tar zxvf pssh-2.3.1.tar.gz
cd pssh-2.3.1
python setup.py install

使用详细查看 之前写的文章Linux超强的批处理工具--pssh(parallel-ssh) 

还有一个类似的软件叫onall 而且也有他自己的特点 有兴趣可以找找。

最后推荐一个网站 有完整的中文翻译和使用相关的内容 虽然是对openSUSE 11.4的新的一样试用:

http://libitum.org/opensuse-guide

openSUSE 12.1 配置工作环境[2]

作者:Ajian 发布时间:March 30, 2012 分类:openSUSE

 

   继续上次的,都是临时写的,平时也只是记录一些命令和过程,可能会有笔误,上一篇也稍微做了些修改。希望有人可以一起讨论和分享。

PS:默认安装的primary语言为English Secondary 为Chinese 这样比较好是因为英文的报错 更容易查找,并且英语的文档比较多些,同样不会影响中文的使用,尤其在系统安装的时候就应该安装上这样就会有跟中文设置相关的一些安装。

KDE升级4.8
先升级下KDE桌面,默认是4.7,升级到KDE4.8
添加KDE48的源 并且让/etc/zypper/repos.d 里面只留下 该源还有update non-oss oss 这四个源 

[KDE48]
name=KDE48
enabled=1
autorefresh=0
baseurl=http://download.opensuse.org/repositories/KDE:/Release:/48/openSUSE_12.1/
type=rpm-md
keeppackages=0

执行命令 zypper dup
如果有软件冲突 取舍下安装完就基本可以了。

安装zimwiki
zimwiki 是一个我比较喜欢的桌面wiki,可以很方便的随手记录相关的文档, 而且基本符合wiki的格式,最主要的是他的存储方式是文本的,所以可以在终端下直接写,甚至对于dropbox的同步也是相关方便。

http://zim-wiki.org/
wget http://zim-wiki.org/downloads/zim-0.55.tar.gz
依赖的包
gtk+ >= 2.6
python >= 2.5
python-gtk
python-gobject
python-simplejson (for python < 2.6)
python-xdg (optional, but recommended)
xdg-utils (optional, but recommended)
确认这些安装了之后 设置变量
XDG_DATA_HOME=/usr/local/share
export XDG_DATA_HOME
解压进入安装
./setup.py install
zim 启动 选择目录dropbox内的note目录 就可以看到更新的笔记了
系统重启后可以在菜单里面找到zim到时再拉到任务栏中 如图一样放置就可以了。


Selection_010.png

gmail 插件
Gmail是桌面插件gmail的邮件提醒工具,相关的方便 。Add Widgets → Get New Widgets -> 搜索gmail 安装就可以了
安装完后 有点小细节的配置 ,需要注意 Label 留空 Username 填写账号就可以了 不需要 @gmail.com 还可以进行misc的配置 如调整检查时间为1min检查一次


Selection_003.png

还可以发现更多的Widgets。

Radiotray安装
配置的时候来点音乐不错 ,网络收音机,安装方法是 参考swyear 的 http://swyear.blogspot.com(正常是看不到的 这个你懂的 这个blog有更多的openSUSE的内容)
# zypper in radiotray radiotray-lang
台湾相关的网络电台
http://dl.dropbox.com/u/6331820/radiotray-bookmarks.xml
下载下来改名为 bookmark.xml 放在 ~/.local/share/radiotray/

正常播放需要gstreamer相关的插件
搜索如下的软件进行安装
gstreamer-0_10-ffmpeg
gstreamer-0_10-fluendo-mp3
gstreamer-0_10-plugins-bad
gstreamer-0_10-plugins-ugly
(以上必修,以下可选)
还可以
gstreamer-0_10-fluendo-mpegdemux
gstreamer-0_10-fluendo-mpegmux
gstreamer-0_10-plugins-bad-orig-addon
gstreamer-0_10-plugins-good-extra
gstreamer-0_10-plugins-ugly-orig-addon

我经常听kiss radio ,推荐给你。

同步kopete历史消息
kopete 经常使用gmail MSN 很有原来聊天的一些历史记录,这些历史记录尤其在工作上的可能在回忆的时候可以帮助很大 所以我一般都是将聊天记录在dropbox进行自动同步的。
先创建好账号后
cd /root/.kde4/share/apps/kopete
ln -s /root/Dropbox/System/notebook_kopete_logs logs
/root/Dropbox/System/notebook_kopete_logs 是我在dropbox同步保持的日志目录
对于gmail除了这样保存历史,默认在gmail邮箱里面也是可以保存的。但对于有多账号的如MSN保存在kopete的历史记录里面很好 而且查询相当的方便。

Kopete配置

默认的kopete的配置直的非常不喜欢 在配置里面必需设置

显示用户头像: 在Contact List  勾选 Use contact photos when available

同一组的消息显示在一个窗口: Behavior 的Chat 选项卡 选择 Group Messages From Contacts in Same Group in Same Chat Window

当然你想改变字体窗口样式都可以。

还有查看消息的快捷键:在Setting --> Configure Shortcuts Setting -->search : read 修改custom:为Alt+w 这个是我的习惯  KDE下所有的软件都有本地快捷方式和全局快捷方式 可以方便的进行配置。其它软件同样的方法进行修改。

snapshot6.png

System Tray 修改
可能是因为升级了4.8不知道为什么kopete会默认的隐藏起来,有时候为马上看到消息就会不知道了。
所以需要将kopete设置总是可见
操作:右键 System Tray 就是声音 剪切版的位置 System Tray Setting ,将kopete改为Always Visible 其它的了可以 想自动隐藏的 想总是可见的都可以设置


snapshot5.png

Rss阅读同步
因为我订阅了很多RSS 如果每次换个电脑又要重新来会很麻烦,当然有人要说Google在线的阅读 我还是比较习惯本地的阅读 所以没办法了。
启动Personal Informastion Magnager 需要启动akonadi服务 默认不让root启动该服务。如果不启动这个很多依赖的软件无法正常工作
如何用ROOT用户启动akonadi?
I encounter the same errors too. Actually, I am using Gentoo. As fduraibi mentioned, it is just because mysqld can't start as root. I add "user=root" in the Akonadi local mysql configuration file (on my computer, it is /root/.local/share/akonadi/mysql.conf), and Akonadi server can be started successfully.
修改/root/.local/share/akonadi/mysql.conf 在最后添加user=root即可。
再进行链接
cd /root/.kde4/share/apps/akregator/data
ln -s /root/Dropbox/System/feeds.opml .
重新启动就可以看到一样的RSS了。

chromium(chrome) for root
右键开始菜单Edit Applications 找到chromium(chrome) 在 command一行中添加像如下
chromium %u --user-data-dir=/opt/googlecache

Selection_004.png

(待续...)