2. CVS vs. STARTEAM——服务器设置

2.1. CVS服务器设置

2.1.1. 运行 CVS

  • /etc/services

    ...
    cvspserver      2401/tcp
    cvspserver      2401/udp
    ...
  • 用xinetd运行: /etc/xinetd.d/cvs

    service cvspserver
    {
            socket_type     = stream        
            protocol        = tcp
            wait            = no
            user            = root
            server          = /usr/bin/cvs
            server_args     = -f --allow-root=/repos/root1 --allow-root=/repos/root2 pserver
            disable         = no
    }
  • 用inetd运行: /etc/inetd.conf

    ...
    cvspserver stream tcp nowait root /usr/bin/cvs cvs -f --allow-root=/repos/project --allow-root=/repos/user pserver
    ...

2.1.2. 添加帐号和设置权限

  • 创建系统帐号

    假设目录 /repos/project 作为多人共享项目的版本控制根目录,需要用组权限控制;/repos/user作为存放个人独占地版本控制根目录。管理员帐号为cvsroot,项目版本控制的公共帐号为 cvsproject,用户版本控制的公共帐号为 cvsuser,相应的用户组为 cvsroot, cvsproject, cvsuser。

    $ groupadd cvsproject
    $ groupadd cvsuser
    $ groupadd cvsroot
    $ useradd  -g cvsproject -s /sbin/nologin         cvsproject
    $ useradd  -g cvsuser    -s /sbin/nologin         cvsuser
    $ useradd  -g cvsroot    -G cvsproject,cvsuser    cvsroot               (1)
    $ useradd  -g cvsuser    -s /sbin/nologin         cvs_jiangxin          (2)
    $ useradd  -g cvsuser    -s /sbin/nologin         cvs_johnson           
    (1)
    设置 cvsroot 属于多个组,这样 cvsroot 用户除了进行系统维护外(如添加新的工程),还可以和其它组用户一样具有管理代码的权限。
    (2)
    用来和CVS用户帐号一一对应的系统帐号。系统帐号禁止登录,密码设置在相应的 CVS 对应帐号文件中设置。
  • 创建CVS根目录

    $ mkdir -p /repos/project
    $ mkdir -p /repos/user
    $ chown cvsroot:cvsroot /repos/project
    $ chown cvsroot:cvsroot /repos/user
    $ chmod  775 /repos
    $ chmod 2775 /repos/project
    $ chmod 2775 /repos/user
    $ su - cvsroot
    $ cvs -d /repos/project init
    $ cvs -d /repos/user init

    运行完毕 cvs init 之后,在CVS根目录下创建了配置目录CVSROOT,权限如下:

    $ ls -l /repos/project/CVSROOT/
    
    -r--r--r--    1 cvsroot  cvsroot       493 Jan 21 10:37 checkoutlist
    -r--r--r--    1 cvsroot  cvsroot       696 Jan 21 10:37 checkoutlist,v
    -r--r--r--    1 cvsroot  cvsroot       760 Jan 21 10:37 commitinfo
    -r--r--r--    1 cvsroot  cvsroot       963 Jan 21 10:37 commitinfo,v
    -r--r--r--    1 cvsroot  cvsroot       527 Jan 21 10:37 config
    -r--r--r--    1 cvsroot  cvsroot       730 Jan 21 10:37 config,v
    -r--r--r--    1 cvsroot  cvsroot       753 Jan 21 10:37 cvswrappers
    -r--r--r--    1 cvsroot  cvsroot       956 Jan 21 10:37 cvswrappers,v
    -r--r--r--    1 cvsroot  cvsroot      1025 Jan 21 10:37 editinfo
    -r--r--r--    1 cvsroot  cvsroot      1228 Jan 21 10:37 editinfo,v
    drwxrwxr-x    2 cvsroot  cvsroot      4096 Jan 21 10:37 Emptydir
    -rw-rw-rw-    1 cvsroot  cvsroot         0 Jan 21 10:37 history
    -r--r--r--    1 cvsroot  cvsroot      1141 Jan 21 10:37 loginfo
    -r--r--r--    1 cvsroot  cvsroot      1344 Jan 21 10:37 loginfo,v
    -r--r--r--    1 cvsroot  cvsroot      1151 Jan 21 10:37 modules
    -r--r--r--    1 cvsroot  cvsroot      1354 Jan 21 10:37 modules,v
    -r--r--r--    1 cvsroot  cvsroot       564 Jan 21 10:37 notify
    -r--r--r--    1 cvsroot  cvsroot       767 Jan 21 10:37 notify,v
    -r--r--r--    1 cvsroot  cvsroot       649 Jan 21 10:37 rcsinfo
    -r--r--r--    1 cvsroot  cvsroot       852 Jan 21 10:37 rcsinfo,v
    -r--r--r--    1 cvsroot  cvsroot       879 Jan 21 10:37 taginfo
    -r--r--r--    1 cvsroot  cvsroot      1082 Jan 21 10:37 taginfo,v
    -rw-rw-rw-    1 cvsroot  cvsroot         0 Jan 21 10:37 val-tags
    -r--r--r--    1 cvsroot  cvsroot      1026 Jan 21 10:37 verifymsg
    -r--r--r--    1 cvsroot  cvsroot      1229 Jan 21 10:37 verifymsg,v

    可以看出文件 history, val-tags 的权限是任何人可读写,其它文件的权限是任何帐号只读。

    文件 CVSROOT/val-tags 用来确定是否一个TAG是可用的;文件 CVSROOT/history 用来记录CVS的访问记录。

  • 创建 CVS 用户帐号

    使用系统帐号不安全,而CVS提供了独立于系统的用户帐号管理。

    使用配置文件 CVSROOT/passwd, CVSROOT/passwd, CVSROOT/passwd ,来管理帐号。

    $ cat /repos/project/CVSROOT/passwd
    jiangxinroot:_passwd_here_:cvsroot	(1)
    jiangxin:_passwd_here_:cvsproject
    johnson:_passwd_here_:cvsproject	(2)
    anonymous::cvsproject			(3)
    
    $ cat /repos/project/CVSROOT/readers
    anonymous				(4)
    (1)
    用户帐号 jiangxinroot,具有和系统帐号 cvsroot 同样权限。
    (2)
    用户帐号 jiangxin, johnson,都具有系统帐号 cvsproject 同样的权限,
    (3)
    匿名帐号 anonymous 的密码为空。
    (4)
    readers 文件中出现的用户帐号,只具有只读权限。

2.1.3. 创建工程

只能以 cvsroot 用户创建工程。因为 cvs 根目录的权限设置为 cvsroot 帐户可写。

  • 在客户端创建工程

    client$ cvs -d :pserver:jiangxinroot@10.0.0.7:/repos/project login
    client$ cvs -d :pserver:jiangxinroot@10.0.0.7:/repos/project import -m "add module test, vendor jiangxin, init_tag start." test jiangxin start
    
    $ ls -l /repos/project
    drwxrwxr-x    3 cvsroot  cvsroot      4096 Jan 21 10:54 CVSROOT
    drwxrwsr-x    2 cvsroot  cvsroot      4096 Jan 21 11:00 test	 (1)
    
    $ chown -R cvsproject:cvsproject test
    $ chmod -R 770 test		
    $ chmod -R g+s test		(2)
    
    client$ cvs -d :pserver:jiangxin@10.0.0.7:/repos/project co test		(3)
    client$ cvs -d :pserver:johnson@10.0.0.7:/repos/project  co -d test2 test
    (1)
    设置工程/模块 test 的目录权限,使只有 cvsproject组用户才有读写权限,其他帐号拒绝访问。
    (3)
    用户帐号 jiangxin, johnson 映射为系统帐号 cvsproject,因此可以访问工程 test。
    (2)
    之所以为模块 test 设置 g+s,是为了当设置一个用户属于多个时,操作不同工程时,能够保障文件的组id维持不变。否则可能影响其它用户的权限。可以用更简单的命令执行 chmod -R 2770 test 进行设置。

    于是创建了多用户共享的工程 test。

  • 另一种创建工程的方法:在服务器端创建工程

    可以直接在服务器端创建目录,设置权限,即完成工程的创建。当然这样创建的工程只是一个空的工程,需要在客户端为空的工程逐个添加文件和目录。

    $ cat /repos/project/CVSROOT/passwd
    jiangxroot:_passwd_here_:cvsroot
    anonymous:_passwd_here_:cvsuser
    jiangxin:_passwd_here_:cvs_jiangxin
    johnson:_passwd_here_:cvs_johnson
    
    $ cd /repos/project
    $ mkdir jiangxin; chown -R cvs_jiangxin:cvsuser jiangxin; chmod -R 2700 jiangxin
    $ mkdir johnson;  chown -R cvs_johnson:cvsuser  johnson;  chmod -R 2700 johnson

    于是创建了两个用户独占的工程。

2.1.4. 用CVS管理文件进行功能扩充

确省安装的CVS的权限仅仅作用于目录,而不能精细到文件级别。而且即使用户只需要拥有文件的只读权限,也要对相应的目录具有写权限,因为需要在目录下创建锁定文件。有一个办法可以避免此问题,即:通过配置文件 CVSROOT/config 的 LockDir 来设置单独的锁定目录,为该单独的锁定目录设置更宽泛的权限控制。

CVS 提供了功能的扩充接口:CVSROOT目录下的管理文件。这些文件提供了相应功能扩充的接口,不但可以完成精细的权限控制,还能完成更加个性化的功能。关于CVSROOT下的脚本,我们有一个非常好的参照:

Setting up a CVS repository - the FreeBSD way。我们可以参照这个指南,定制我们自己的CVSROOT脚本。

另外一个重要的参照:《Cederqvist's CVS Manual》的附录 C: “Reference manual for Administrative files”。

如下,是我的定制过程:

  • 下载CVSROOT

    $ cvs -d :pserver:anoncvs@anoncvs.freebsd.org:2401/home/ncvs login   # 输入密码 anoncvs
    $ cvs -d :pserver:anoncvs@anoncvs.freebsd.org:2401/home/ncvs co  CVSROOT-src
  • 升级CVS

    确认安装的CVS服务器版本,要高于 1.11.2 。因为我们要用到在版本 1.11.2 才出现的功能:能够在检查commit log 后重新读入 commit log,以实现对 commit log 的格式化。

  • CVSROOT/config

    #SystemAuth=no                 (1)
    LockDir=/var/lock/cvs/root1    (2)
    #TopLevelAdmin=no
    LogHistory=all
    RereadLogAfterVerify=always    (3)
    (1)
    如果设置 SystemAuth=no,则只通过 CVS 提供的身份验证。
    (2)
    指定单独的锁定目录, 便可以更灵活的设置数据仓库的目录权限。
    (3)
    启用只有在 1.11.2 版本才具有的 commit log 重写功能。
  • CVSROOT/cvsignore

    设置版本控制过程中,忽略的文件。更新文件时,这些文件将被隐藏,不在显示为 "?" 类型文件。例如:

    *.db
    *.info
    *.[Sp]o
    *.core
    *.aps
    *.clw
    *.exe
    *.ncb
    *.obj
    *.opt
    *.plg
    Debug
    Release

    亦可由每个目录下的文件 .cvsignore 来控制。

  • CVSROOT/cvswrappers

    匹配文件名,并作相应处理。如: -kb 即以二进制方式处理文件。

    *.gif -k 'b'
    *.GIF -k 'b'
    *.jpg -k 'b'
    ...
  • CVSROOT/modules

    设置数据仓库中的模块名,可以通过命令:“cvs co -c”察看当前数据仓库(repository)中包含的模块/工程名称。也可以在调整服务器端目录结构时,设置 modules 来保持和以前设置的兼容性。

    CVSROOT	CVSROOT
    module1	module2 &module3
  • CVSROOT/checkoutlist

    列在 checkoutlist 中的文件,在 checkin 后,能够自动在服务器 CVSROOT 目录中重建。

    #access             (1)
    avail
    cfg.pm
    cfg_local.pm
    commit_prep.pl
    commitcheck
    cvs_acls.pl
    exclude
    log_accum.pl
    logcheck
    options
    rcstemplate
    tagcheck
    unwrap
    wrap
    (1)
    access 文件供 FreeBSD 的cvs wrapper程序调用,进行权限控制,如不需要该功能注释掉,忽略该文件。
  • CVSROOT/notify

    可以忽略。

  • CVSROOT/commitinfo

    Commit 事件要触发三个脚本文件,依次是 commitinfo, verifymsg, loginfo。其中先遍历整个目录树对所有需要 commit 的文件执行 commitinfo文件。再分别针对每一个目录执行 verifymsg, loginfo 脚本。

    commitinfo 完成的功能:通过用户主机名、用户名来检查权限;确认CVS服务器的版本号不低于某个版本;将遍历目录树的结果(最后一个目录名)记录下来,以便接下来运行 verifymsg, loginfo的脚本能够确认运行结束等。

    相关文件:CVSROOT/commitcheckCVSROOT/cvs_acls.plCVSROOT/availCVSROOT/commit_prep.plCVSROOT/excludeCVSROOT/cfg.pmCVSROOT/cfg_local.pm

    文件 CVSROOT/avail,用以精细控制权限。例如:

    group|meisters|peter,jdp,markm,joe
    # Pick up the list of bad users from ncvs/CVSROOT/badcommitters  See that
    # file for details
    group|penaltybox|!badcommitters
    
    unavail
    avail||CVSROOT
    avail||distrib
    avail||doc
    avail||ports
    avail||src
    unavail||src/contrib/binutils,src/contrib/file
    avail|obrien|src/contrib/binutils,src/contrib/file
    unavail||src/contrib/tcpdump
    avail|fenner,nectar|src/contrib/tcpdump
    avail||www
    avail|:meisters
    unavail|:penaltybox

    文件 CVSROOT/cfg.pm,是 perl脚本的核心包。可以根据需要加入所需的代码。

    ... ...
    
    # The username of the committer.
    $COMMITTER = $ENV{"CVS_USER"} || $ENV{"LOGNAME"} || $ENV{'USER'} || getlogin   (1)
    		|| (getpwuid($<))[0] || sprintf("uid#%d",$<);
    ... ...
    
    # Send mail when directories are created in the repository.
    # 0 = off, 1 = on.
    $MAIL_ON_DIR_CREATION = 1;   (2)
    
    ... ...
    (1)
    增加检查环境变量 CVS_USER。以能够正确反映使用 CVSROOT/password 文件进行身份验证的用户名。
    (2)
    创建目录的事件,也发送邮件。参见脚本:CVSROOT/log_accum.pl
  • CVSROOT/verifymsg

    相关文件:CVSROOT/logcheck

    用于检查和格式化 commit log。禁止在版本控制提交时,使用空的 commit log。对于 wincvs 在用户不提交 commit log 时,会自动使用“no message”作为commit log。为了禁止该情况发生,需要定制该脚本:

    $ diff -c -r1.1 logcheck
    *** logcheck    14 Feb 2003 05:33:39 -0000      1.1
    --- logcheck    19 Feb 2003 08:56:18 -0000
    ***************
    *** 47,54 ****
    --- 47,56 ----
    
      # Remove leading and trailing blank lines.  (There will be at most
      # one because of the duplicate removal above).
    + local $^W = 0;                        (1)
      shift @log_in if $log_in[0] eq "";
      pop @log_in if $log_in[-1] eq "";
    + local $^W = 1;
    
      # Scan through the commit message looking for templated headers
      # as defined in the configuration file, and rcstemplate.
    ***************
    *** 104,109 ****
    --- 106,114 ----
      # completely empty.  This is a bug in cvs.
      my $log = "@log_in";
      die "Log message contains no content!\n" if $log =~ /^\s*$/;
    +
    + # commit without commit log using WINCVS , will automatically provide commit log as "no message".
    + die "Log message contains no content using WINCVS!\n" if $log =~ /^no message$/ or $log =~ /^\.+$/; (2)
    (1)
    隐藏语法警告
    (2)
    禁止其它的无意义的commit log,其中一个是 WINCVS 风格,一个是本人的风格。
  • CVSROOT/loginfo

    相关文件:CVSROOT/log_accum.plCVSROOT/cfg.pm CVSROOT/cfg_local.pm

    将 commit log 分门别类存储在目录 CVSROOT/commitlogs 下,并同时通过邮件外发。为了防止一次事件触发多次的邮件外发,该脚本利用到 commitinfo 的运行结果,只有确认到了目录树的最后,才发送邮件。 模块和存储日志文件以及用户邮件列表在文件CVSROOT/cfg_local.pm中定义。

    定制 CVSROOT/cfg_local.pm

    
$CHECK_HEADERS = 0;       (1)
    $IDHEADER = 'FreeBSD';
    $UNEXPAND_RCSID = 1;
    
    %TEMPLATE_HEADERS = (
    	"Reviewed by"		=> '.*',
    	"Submitted by"		=> '.*',
    	"Obtained from"		=> '.*',
    	"Approved by"		=> '.*',
    	"PR"			=> '.*',
    	"MFC after"		=> '\d+(\s+(days?|weeks?|months?))?'
    );
    
    $MAILCMD = "/usr/local/bin/mailsend -H";
    $MAIL_BRANCH_HDR  = "X-FreeBSD-CVS-Branch";
    $ADD_TO_LINE = 0;
    
    # Sanity check to make sure we've been run through the wrapper and are
    # now primary group 'ncvs'.
    #
    #$COMMITCHECK_EXTRA = sub {      (2)
    #	my $GRP=`/usr/bin/id -gn`;
    #	chomp $GRP;
    #	unless ( $GRP =~ /^ncvs$/ ) {
    #		print "You do not have group ncvs (commitcheck)!\n";
    #		exit 1;	# We could return false here.  But there's
    #			# nothing to stop us taking action here instead.
    #	}
    #	return 1;
    #};
    
    # Wrap this in a hostname check to prevent mail to the FreeBSD
    # list if someone borrows this file and forgets to change it.
    my $hostname = `/bin/hostname`;
    die "Can't determine hostname!\n" if $? >> 8;
    chomp $hostname;
    if ($hostname =~ /^repoman\.freebsd\.org$/i) {
    	@COMMIT_HOSTS = qw(repoman.freebsd.org);
    }
    
    
    @LOG_FILE_MAP = (                          (3)
    	'CVSROOT'	=> '^CVSROOT/',
    	'distrib'	=> '^distrib/',
    	'doc'		=> '^doc/',
    	'ports'		=> '^ports/',
    	'www'		=> '^www/',
    
    
    	'other'		=> '.*'
    );
    # CVSROOT is still shared between src, ports, doc at the moment. projects has
    # its own CVSROOT.
    @MAIL_MAP = (                              (4)
    	'all-committers@FreeBSD.org cvs-src@FreeBSD.org cvs-ports@FreeBSD.org cvs-doc@FreeBSD.org cvs-all@FreeBSD.org' => '^CVSROOT/',
    	'src-committers@FreeBSD.org cvs-src@FreeBSD.org cvs-all@FreeBSD.org' => '^src/',
    	'src-committers@FreeBSD.org cvs-src@FreeBSD.org cvs-all@FreeBSD.org' => '^distrib/',
    	'ports-committers@FreeBSD.org cvs-ports@FreeBSD.org cvs-all@FreeBSD.org' => '^ports/',
    	'doc-committers@FreeBSD.org cvs-doc@FreeBSD.org cvs-all@FreeBSD.org' => '^doc/',
    	'doc-committers@FreeBSD.org cvs-doc@FreeBSD.org cvs-all@FreeBSD.org' => '^www/',
    	'projects-committers@FreeBSD.org cvs-projects@FreeBSD.org cvs-all@FreeBSD.org' => '^projects/',
    
    	'all-committers@FreeBSD.org cvs-all@FreeBSD.org' => '.*'
    ); 
    
    # Email addresses of recipients of commit mail.
    $MAILADDRS = 'jiangxin';                     (5)
    
    
    1; # Perl requires all modules to return true.  Don't delete!!!!
    #end
    (1)
    设置为0,不强制文件头包含特定的CVS关键字。
    (2)
    注释该函数,不检查用户组。
    (3)
    定制该数组,将 CVS 模块的 commit log 存储在对应的文件中。
    (4)
    定制该数组,对应模块的 commit,邮件通知相应用户。
    (5)
    确省的邮件地址。对于没有在 MAIL_MAP 数组找到匹配的邮件地址,即使用该地址。确省为 'nobody'。
  • CVSROOT/taginfo

    在执行 tag/rtag 命令前执行该脚本,如果脚本返回非零值,tag/rtag 动作取消。 相关脚本:CVSROOT/tagcheck。负责对添加/删除 TAG 事件进行控制——允许/禁止/发送邮件。

    由于 tag/rtag 事件不象 commit 事件,不是通过多个脚本的配合完成,而是只通过一个脚本 taginfo 完成。这就出现一个问题:如果为一个目录树打上TAG,则可能多次执行脚本,可能要多次触发邮件发送。我的解决办法是,根据TAG进程的 PID 确定在整个过程唯一的文件名,将日志记录到该文件中,taginfo 脚本本身无法知道是否结束,而是系统通过 crontab 定期执行脚本 CVSROOT/checkmailspool.sh来检查是否有完成的 tag 邮件需要外发。

    
#!/usr/bin/perl -w
    #
    # Author: Jiang Xin
    # Reference: http://www.worldhello.net/
    #
    
    use strict;
    use lib $ENV{CVSROOT};
    use CVSROOT::cfg;
    
    
    #############################################################
    #
    # Main Body
    #
    # TAG  add/mov/del  repo  files...
    # $1   $2           $3    $4   ...
    #
    ############################################################
    
    my $tag = shift;
    my $action = shift;
    my $repos = shift;
    my $fileitem = "";
    my $filerev= "";
    my $filelist = "";
    
    my $uid = $cfg::COMMITTER;
    my $userlist = "";
    my $pattern = "";
    my $permission = 1;
    my $to = "";
    
    my $tmpstr = &cfg::get_mail_name($repos);
    $tmpstr =~ s/\@/\./g ;
    $tmpstr="nobody" unless $tmpstr;
    my $MAILFILE = "/var/spool/cvsmail/cvs.tag.$tmpstr.$cfg::PID"; (1)
    
    die "Usage: tagcheck tag action repos files...\n" unless $repos;
    
    for my $i (0 .. ($#cfg::TAG_MAP - 1) / 2) { (2)
    	$userlist = $cfg::TAG_MAP[$i * 2];
    	$pattern = $cfg::TAG_MAP[$i * 2 + 1];
    	
    	if ($tag =~ /$pattern/i)
    	{
    		if ($userlist =~ /\b$uid\b/i)
    		{
    			$permission=1;
    			last;
    		}
    		else
    		{
    			$permission=0;
    			last;
    		}
    	}
    }
    
    if ($permission == 0)
    {
    	# normal users can not do this.
    	print STDERR "User \"$cfg::COMMITTER\" canot perform this operation!\n";
    	print STDERR "Only users: $userlist, can handle tag patterm: \"$pattern\"!\n";
    }
    
    while ($fileitem = shift)
    {
            $filerev = shift;
            $filelist = sprintf("%s\t%-24s:\t%s\n", $filelist, $fileitem, $filerev);
    }
    
    print "save message in spool `dirname $MAILFILE`...\n";
    
    my @email = ();
    
    if (! -e $MAILFILE )
    {
    	$to = &cfg::get_mail_name($repos); (3)
    	push @email, "From: $uid<$uid>";
    	push @email, "To: $to";
    	$tmpstr = sprintf("Date: %s", `date -R`);
    	chomp $tmpstr;
    	push @email, $tmpstr;
    
    	if ($permission == 0)
    	{
    		push @email, "Subject: cvs tag FAILED! ($action $tag on $repos)";
    	}
    	else
    	{
    		push @email, "Subject: cvs tag success: $action $tag on $repos";
    	}
    
    	push @email, "";
    
    	delete $ENV{'TZ'};
    	$tmpstr = sprintf("%-11s:    %-8s", "Author", $cfg::COMMITTER);
    	push @email, $tmpstr;
    	$tmpstr = sprintf("%-11s:    %-8s", "Date", `/bin/date +"%Y/%m/%d %H:%M:%S %Z"`);
    	chomp $tmpstr;
    	push @email, $tmpstr;
    	$tmpstr = sprintf("%-11s:    %-8s", "Tag", $tag);
    	push @email, $tmpstr;
    	$tmpstr = sprintf("%-11s:    %-8s", "Operation", $action);
    	push @email, $tmpstr;
    
    	push @email, "";
    	push @email, "  $cfg::MAILBANNER", "" if $cfg::MAILBANNER;
    }
    
    if ($permission == 0)
    {
    	push @email, "Permission denied: $action $tag on $repos !";
    	push @email, "--------------------------------------------------";
    }
    else
    {
    	$tmpstr = sprintf("%-11s:    %-8s", "Repository", $repos);
    	push @email, $tmpstr;
    	push @email, $filelist if $filelist;
    }
    
    #save mail to spool
    open MAIL, ">> $MAILFILE "
    	or die "Cannot open file $MAILFILE for append.";
    print MAIL map { "$_\n" } @email;
    close MAIL;
    
    if ($permission == 0)
    {
    	exit 1
    }
    else
    {
    	exit 0
    }
    (1)
    确定进程唯一的文件名称;
    (2)
    @cfg::TAG_MAP 数组定义了需要权限控制的 TAG 名称,以及授权人列表。受限的TAG名称对应于软件开发中的里程碑,要严格的权限控制。和该模式匹配的 tag,只能被授权人操作,其它名称的 TAG,所有用户都可以操作。
    (3)
    邮件地址亦从 MAIL_MAP 数组中获取;

2.2. Starteam服务器权限设置

每一个服务器配置一个独立的用户数据库设置。用户的权限设置灵活,和本机帐号设置无关(不用设置本地帐号),且和本地路径无关。

2.2.1. 用户管理

  1. 如果使能了Lockout功能,则需要建立两个系统管理员帐号,以防止一个被锁定(如多次错误登录被无限期死锁),能用另外一个解锁。

  2. 建组原则(假设组名为 team1)

    • 建立用户组和子组

      假设组名为 team1,首先建立一个大组 ga_team1 组;再在 team1 组下再建立三个child group: g_team1_admin 组,g_team1_users 组和 g_team1_rdonly 组(即只读组)。

    • 建组的考虑

      将权限分配到组,是管理权限的基本原则。因为 starteam 在赋予权限时,是将用户名和组名混在一起,因此为组名加上前缀 “g_” 或者 “ga_”。区分 g_ 和 ga_ 是因为要防止误将权限设置到更大的 ga_ 组中(如后所述)。

      组管理员属于 g_team1_admin 组,拥有管理 LABEL 权限,创建新视图,以及管理代码权限分配权限;starteam管理员部分授权,目的是减少starteam管理员的管理负担。提高效率。

      小组的其它用户属于 g_team1_users 组,除了不具有 g_team1_admin 组的管理权限外,其它权限同 g_team1_admin。

      对于其它有研读代码或者掌握进度需求的用户,如 QA、部门领导等属于 g_team1_rdonly 组,除了具有只读方式看代码权限外,其它同 all users。

      建立项目组 ga_project_manager,是独立于项目组(也可以考虑为每个项目组单独设置),进行项目管理。为管理需求变更,在其下建立 g_pm_req;为管理TODO LIST,建立 g_pm_todo组。

      警告

      分配权限时,要注意不要把权限赋予大组(如 ga_team1 组)。因为 team1 组包括了 g_team1_rdonly 组(只读用户组)!

    • 关于组本身的权限

      除 Administrators 组外,任何组不要设置组的权限,以免组的权限设置超越了项目的安全设置,使对项目的安全设置形同虚设。

  3. 关于发布机使用独立用户

    在UNIX命令行上进行代码的check in/check out,需要建立独立用户,如:team1_release。以免由于使用同一个用户名在不同工作站check代码,造成文件状态UNKNOWS的情况。参见Starteam Howto中关于文件状态unknown的FAQ。

2.2.2. 服务器配置的安全设置

  1. Starteam VTS 的启动方式设置为NT服务方式

    保证服务器重启后,Starteam自动启动。

  2. Starteam 服务器端配置

    • General

      设置登录超时60秒,未动作超时300分钟。

    • Audits

      设置只保留10天安全日志。

    • Vault

      设置不清除文件状态表;

      设置最大CACHE为400MB。

      设置Cache的文件夹在Valut文件夹之外,以使在备份时,对cache区别对待 (不备份Cache)。

    • Notifications

      使能Email通知。

    • Protocal

      只保留一种协议支持:TCP/IP(SOCKETS),因为别的协议支持有问题;

      TCP/IP加密级别:不作设置,但是管理员要通知用户,如果客户端支持传输加密,请至少选择RSA R4 stream加密。windows客户端都支持,但是unix命令行不支持。

  3. 访问权限(Access Rights)

    管理员完全权限,不赋予其它用户任何权限。

  4. 系统策略(System Policy)

    • Access Rights

      使能 Ignore object ownership, 即忽略OwnerShip,一切均按照权限设置。

      不选择 Ignore group privileges, 即在保证只有 Administrators 组设置了组权限情况下,允许Administrators组用户超越项目的权限限制实施管理。

    • Security Events

      设置只保留30天的安全日值。

    • Passwords

      设置密钥最小长度6个字符。

    • 在用户管理(User manager)界面,设置所有用户第一次登录必须修改口令,并且不允许设置空口令。

    • Logon Failures

      设置5次失败登录锁定帐号,帐号锁定5分钟。

2.2.3. 工程的权限设置

先打开要配置的工程,Project->Access Rights,来设置工程权限。

  1. Project

    • All Users

      设置所有用户(All Users)具有如下权限:

      See Project and Properties

    • g_xxx_admin

      设置组管理员(如:g_team1_admin 组)具有如下权限:

      赋予除了 Delete project 外所有权限。以便于管理员修改自身权限,给自己加上删除label权限等,但要注意缺省没有赋予管理员的权限,当管理员完成相应管理功能后,恢复原状。

      警告

      不能赋给任何人删除工程权限!因为工程删除不可逆。

  2. Views

    • All Users

      设置所有用户(All Users)具有如下权限:

      See View and its Properties

    • g_xxx_admin

      设置组管理员(如:g_team1_admin 组)具有如下权限:

      赋予除了 Delete View, Delete Labels 外所有权限。

    警告

    Delete View, Delete Labels 不可逆,因此不赋予任何人此权限。但为了减轻管理员负担,赋予了 g_team1_admin 组 change view security setting 权限,g_team1_admin可以修改自己权限,使具有删除 label, 甚至是删除 view 权限,但要注意完成维护功能后,立即去掉该两项权限!

  3. Child Folders

    • g_team1_rdonly 组

      设置只读组(如:g_team1_rdonly 组)具有如下权限:

      具有:See folder and its properties, See folder history, See folder links 权限。

    • g_team1_users 组

      设置普通用户组(g_team1_users 组)具有如下权限:

      除 Modify folder properties, Delete folder from parent folder, Change folder security set 外所有权限。

    • g_team1_admin组

      设置组管理员(如:g_team1_admin 组)具有如下权限:

      除了 Delete folder from parent folder 外所有权限。

  4. Files

    • g_team1_rdonly 组

      设置只读组(如:g_team1_rdonly 组)具有如下权限:

      具有:See file and its properties, See file history, See file links, Check out files 权限。

    • g_team1_users 组

      设置普通用户组(g_team1_users 组)具有如下权限:

      除 Change file security set 外所有权限。

    • g_team1_admin 组

      设置组管理员(如:g_team1_admin 组)具有如下权限:

      所有权限。

      警告

      Move folder out of a view or project 权限也是危险操作,文件移出视图,也从历史视图中去掉,可能会破坏完整性。但将文件或目录移动到另外一个视图,是从服务器端删除文件的唯一办法。因此慎用。

  5. Change Request

    • All Users

      添加所有用户(All Users),将权限设置为空。即默认禁止一切的策略。

    注

    工程的权限设置中的ChildFolders, Files, Change request 等的权限设置,用来确定工程的默认设置。即为没有对各项进行单独权限设置时的默认安全设置。

    将 Change Request 定义为默认禁止一切,目的是只在指定目录添加 Change Request.

  6. Topics

    • All Users

      设置所有用户(All Users)具有如下权限:

      设置除了 “Change topic security set”,“Delete Topic from folder”外所有权限。提供用户通过 TOPIC 进行讨论以及保存备忘。

    • 设置组管理员(如:g_team1_admin 组)具有所有权限。

2.2.4. 视图的权限设置

先打开要配置的视图,View->Access Rights,来设置视图权限。

  1. 根视图

    可以不设置权限,继承整个工程中的Project AccessRight的 Views 的各项权限设置。

  2. 分支视图

    按需设置。可以使老版本的维护由其他人完成,或者进行 xp 模式开发,为开发者(非管理员)设置更大的权限。

2.2.5. 文件夹的权限设置

选择视图下相应文件夹,鼠标右键单击,选择 Advanced->Access Rights,设置权限。

需要先设置“根目录”权限。再单独设置各一级子目录(defect, src, doc等)的权限。如果需要还可以对二级目录等(模块)设置权限,但不建议设置二级目录的权限。

  1. 根目录

    根目录和子目录在权限设置上的异同

    1. 根目录的权限不传递,即在根目录上设置的权限对派生的子视图没有影响;而非根目录会传递权限,所以尽量不要在子目录(非根目录)上设置权限,以免连带影响其它视图中该目录的权限,造成混乱。

    2. 根目录继承视图或者工程的 Child Folder 权限;子目录先继承根目录权限, 再继承视图,工程权限。

    3. 因此,不要设置根目录的权限页的 Child Folder 页,这样没有设置权限的子目录便可以继承视图的 Child Folder页的权限。但是可能需要设置根目录权限页的 This Folder 页,因为 This Folder的权限默认取自视图或者工程的 Child Folder 设置。如果有子目录的权限大于视图或者工程的 Child Folder的设置,就需要设置This Folder 设置。但是副作用是,子目录的权限并没有完全继承视图或者工程的更严格的设置,而是采用了根目录的 This Folder 设置和 视图的Subfolder设置的组合(因为根目录没有设置Subfolder权限)。这样有可能使所有能够访问根目录的用户访问整个目录树!!!好像没有更好的解决方案。

    根目录上的权限设置:

    • This Folder

      All Users:所有用户(All Users)具有 See folder and its properties, See folder history, See folder links 权限。

      g_team1_admin: 组管理员(g_team1_admin)具有:除了Delete folder from parent folder 外所有权限。

    • Child Folders,Files, Change Requests, Tasks, Topics

      不作设置,以继承视图或者工程的权限设置。

  2. Defect目录

    • This Folder

      All Users : 所有用户(All Users)具有—— See folder and its properties, See folder history, See folder links 权限。

      g_qa_admin:QA组管理员(g_qa_admin)具有——除了Delete folder from parent folder 外所有权限。

      g_team1_admin:team1组管理员(g_team1_admin)具有——除了Delete folder from parent folder 外所有权限。

      注

      设置服务QA管理员和team1目录管理权限,允许QA管理员建立子目录,用于对CR分类。

    • Child Folders

      All Users:所有用户(All Users)具有——See folder and its properties, See folder history, See folder links 权限。

      g_qa_admin:QA组管理员(g_qa_admin)具有——除了Delete folder from parent folder 外所有权限。

      g_team1_admin:team1组管理员(g_team1_admin)具有——除了Delete folder from parent folder 外所有权限。

    • Change Requests

      All Users:所有用户(All Users)具有——除了Change CR Security set , Delete CR from folder外所有权限。

      g_qa_admin:QA组管理员(g_qa_admin)具有——除了Delete CR from folder 外所有权限。

      警告

      为了减轻管理员负担,赋予了 g_qa_admin 组 change CR security setting 权限,g_qa_admin可以修改change request的权限,使之可以被删除,再删除该 cr。

    • Files, Tasks, Topics

      不另外设置权限,继承视图或者工程的权限。

  3. src 目录

    src 目录是用来存放源代码的目录,应着重注意本目录权限设置。

    可以不作任何单独设置,因为继承了视图或者工程的权限设置。如果工程或者视图关于文件的权限设置足够安全的话,可以不必设置本目录权限。

  4. ProjectManagement 目录

    ProjectManagement 目录,用于以文件和 TOPIC形式存储 TODO_LIST, 编程规范,需求变更。

    1. ProjectManagement 目录

      • This Folder

        All Users :所有用户具有—— See folder and its properties, See folder history, See folder links 权限。

        ga_project_manager 组 :具有——除 Modify folder properties, Delete folder from parent folder, Change folder security set 外所有权限。

        g_team1_admin组 :具有——除 Delete folder from parent folder 外所有权限。

      • Child Folders

        All Users :所有用户具有—— See folder and its properties, See folder history, See folder links 权限。

        ga_project_manager 组 :具有——除 Modify folder properties, Delete folder from parent folder, Change folder security set 外所有权限。

        g_team1_admin组 :具有——除 Delete folder from parent folder 外所有权限。

      • Files

        All Users:具有——See file and its properties, See file history, See file links, Check out files 权限。

        ga_project_manager:普通用户组具有——除了 Change file security set, Delete file from folder 外的所有权限。

        g_team1_admin:组管理员具有——除了 Delete file from folder 外的所有权限。

      • Topic

        All Users:具有——除了 Change top security set, Delete topic from forder 外所有权限。

        g_team1_admin:具有——除了 Delete topic from forder 外所有权限。

      • Change

        暂不设置,采用工程缺省禁用配置。

    2. TODO LIST子目录

      所有用户具有只读文件,读写TOPIC权限;g_pm_todo 组 和 g_team1_admin组具有读写文件,读写topic,和整理目录权限。

    3. 编程规范

      所有用户都有整理目录,读写topic权限;g_team1_admin 多了一项管理文件的权限。

    4. 需求变更

      所有用户具有只读文件,只读TOPIC权限;g_pm_req 组 和 g_team1_admin 组具有读写文件,读写topic,和整理目录权限。

2.2.6. Starteam服务器优化

2.2.6.1. 提高数据库性能

  • 数据库可以有多种选择,但是ACCESS基本可以满足需求,虽然有1.2G文件大小限制,但一般应用不会超过。数据库的选择不是性能瓶颈。

  • 安装和使用 Copy Indexes Tools in "StarTeam VirtualTeam Server 4.0\Tuning Scripts\MS Access\Package"。

  • 压缩数据库;(ODBCàsystem dsn)

  • 增加数据库的Max Buffer size从2048至8192,Threads 从3至 24。

  • 关于其他数据库,参见 http://devforum.starbase.com/starbase/Main.asp

2.2.6.2. 提高Starteam服务器性能

  • 增加Cache目录文件大小至400M;

  • 将文件状态表导出时间从180天减少到30天;

  • 将审计导出时间从不导出更改为30天导出;

  • 在客户端设置Allow check out of deltas "Tools | Workstation | Files "Optimize for slow connection.""

  • 参见http://devforum.starbase.com/starbase/Articles/Default.asp?id=206

2.2.6.3. 备份

备份涉及到:

  • The Configuration and cipher files

    Configuration\server.scg

    Configuration\server.cph

  • Database files

    Database\StarTeamDB.mdb (Assuming Access database)

  • Vault:

    Archive dir: *.*

    Attachments dir: *.*

  • Optional

    Cache dir: *.*

参见http://devforum.starbase.com/starbase/Articles/Default.asp?id=133