NAME - CVS policy enforcement



SYNOPSIS -h|--help --man [-v | --verbose] mode [--sql] [-r | --cvsroot path] [-u | --user | --cvsuser name] [--module module] [--svv ${sVv}]


cvscop is a CVS policy program, launched by the CVS administrative files (commitinfo, loginfo, taginfo, and verifymsg). It can deny CVS actions or launch external scripts based on its configured rule set.

The output of cvscop is displayed by the CVS command line program or through the various logging facilities in a GUI CVS client (like WinCVS).


The mode argument contains the name of the CVS administrative script that launched cvscop. Some modes have special arguments that are automatically appended by CVS, and cvscop expects to see them when invoked in those modes.

Pre-commit phase, appropriate for user/filename/path policies.

Pre-commit phase for message content policies.

Post-commit phase, useful for logging and external program launching.

Only happens on cvs tag operations.

Not an actual phase, this mode displays the data given to cvscop. Using the --verbose option has a similar effect, except echo doesn't actually run any checks


-h, --help
Displays brief usage information.

Attempts to extract and display this pod documentation as a man page. Not the recommended method of viewing this text, but it's much more convenient than having to type pod2man | nroff -man | less, in case perldoc isn't working.

-v, --verbose
Causes cvscop to emit a large amount of information regarding its input, activites, regular expression tests, etc.. This option is useful for debugging a policy entry in the configuration file, but displays what may be too much information to a regular CVS user.

Enables SQL logging of CVS activites. Applies to loginfo mode only. --sql can be overridden by a <log> tag in the configuration file.

-r, --cvsroot path
Specifies the local path to the CVS repository. The path option is normally filled in with ${CVSROOT}.

-u, --user, --cvsuser name
Identifies the CVS user name.

--module module
To cvscop, a module is actually a top-level directory in CVS. cvscop will figure out what module is being used by the paths or filenames if they are present, so the module argument is usually not necessary.

--svv ${sVv} (loginfo only)
--svv provides the filenames and version numbers for each file being processed. It must be the last argument in the administrative file, and must be entered as --svv ${sVv}


The cvscop configuration file (usually cvscop-config.xml) is XML containing fail states or script triggers nested within pattern-match definitions.

The program reads the file as a tree, starting with the outermost tag (the root) and working its way in to the innermost tags (the leaves). If it gets to a pettern match that fails, the tags down that branch will not be followed.

See EXAMPLES below for configuration examples.


The entire file must be contained within a <cvscop> tag.


Defines the configuration section of the file. Configuration items are simple string parameters, defined with <param> tags.

<param name=name value=value />

The <param> tag has two parameters: name and value. All of the current configurable options revolve around SQL logging:

sql (on|off)
enables or disables SQL logging from loginfo mode.

SQL server

MySQL port number

database name

database login

dbpasswd database password


The <ruleset> tag encloses <match>, <deny>, <warn>, and <run> tags. A <ruleset> tag can only be enclosed within the root <cvscop> tag.


<match> tags have two parameters: field and boolean. They contain one or more regular expressions and more <match>, <deny>, <warn>, or <run> tags. The tags contained in a <match> tag will be skipped unless one or all (as configured by the boolean parameter) of its conditions are set.


Boolean match mode. Can be ``AND'' or ``OR''. ``AND'' states that all match conditions must be met to continue, while ``OR'' requires only one condition to be met. ``OR'' is the default, and does not need to be specified.


The field parameter specifies which of the following are to be matched:

action (taginfo mode only)
Which tag operation: move, delete, add

mode (all modes)
Mode: commitinfo, loginfo, taginfo, verifymsg, echo

cvsroot (all modes, requires --cvsroot option)
Local path to repository. This is just a directory path, with no :pserver or :ext, etc.. It is usually fed the argument ${CVSROOT}, which is translated by CVS into the path.

cvsuser (all modes, requires --cvsuser option)
Login being used. Usually take the argument ${USER}, which CVS translates into the correct user name.

path (commitinfo, taginfo)
Path from the module to the file(s), exclusive (does not include module or filename)

module (commitinfo, taginfo)
Top-level directory in the respository-- not a module as defined in the modules file.

message (verifymsg, loginfo)
This string contains the full text of the log message.

tag (taginfo only)
The name of the tag.

tempfile (verifymsg only)
The path and filename of the temporary file containing the log message. This one isn't terribly useful.

Regular expressions and the <match> tag

Regular expressions define a text pattern (Regular expressions are beyond the scope of this document. See perlre for help). In cvscop, simplified Perl regular expressions are used. The differences between cvscop and Perl regular expressions are:

Using m/pattern/ is not allowed. Use /pattern/ instead.

Prepending an exclamation mark applies a logical NOT to the match. For example, !/myname/ will match only if 'myname' is not present.

Only the i, m, s, or x modifiers can be appended to the pattern.

Any whitespace will be collapsed down into single spaces.

Escaping of some unusual characters will cause strange results. This is a known issue, and it's being worked on. For now, running a test installation for some of these expressions may help save some headaches.

Some sequences like $1 and (?{CODE}) are disabled.

Multiple expressions are defined in the same <match> by space-delimiting them.

If the parser determines that there are improperly-formatted expressions, it will attempt (after complaining to STDERR) to match the text content as a literal string (whitespace notwithstanding). It is not recommended to use non-regular-expression text in a <match> tag.


The <deny> tag contains a failure message. When its parent <match> tags have passed, the text contained in the <deny> tag is displayed and cvscop exits with a failure status, which causes the CVS operation to fail. If no message is provided, a generic ``Permission Denied'' message will be shown.


<warn> is similar to <deny>, but emits a harmless warning to the CVS output. No error code is generated (yet) and cvscop continues checking its ruleset.

<run>[command] [arguments]</run>

When a <run> tag is reached, it causes an external program or script (specified as the text inside the <run> tag) to be launched. Optionally, cvscop can check the program or script's exit status and exit with a fail status if appropriate.

If this attribute is set to any of the fields for <match>, it will fill '$_' (see below) in with the appropriate field. If it is set to ary_files, ary_version, or ary_oldver, the program or script will be run once each for the file names, CVS revisions, or old revision numbers, respectively. If '$_' appears in the command arguments, it will be filled in accordingly.

If this attribute is present at all, the error code will be taken into account after execution, and cvscop will exit upon failure.

<run> command line
The text content of a <run> tag contains the command and arguments to be run.

Variable replacement in <run> statements
The text of any <match> field can be inserted into a <run> tag's command arguments by prepending the name of the field with a dollar-sign (e.g. $cvsuser, $cvsroot). The default variable ($_) is filled in by the 'each' argument (above).

<!-- [comment] -->

Standard XML comments can be placed anywhere in the configuration file.


Cvscop's exit status is used by CVS to determine whether an action will be allowed. Exit status will be 0 if no fail condition has been met, or 1 if a <deny> tag has been reached. Cvscop can also be made to inherit the exit status from an external script or program launched by a <run> tag.


  1. Check out a copy of the CVSROOT directory
  2. Place in the CVSROOT working copy
    The working copy of the file must have the proper execute bit set to cause the real file to be executable.

  3. Create cvscop-config.xml
    See CONFIGURATION FILE above and EXAMPLES below.

  4. Edit commitinfo, loginfo, taginfo, and/or verifymsg
    See EXAMPLES below. Putting cvscop control over the CVSROOT directory is a bad idea, as a mistyped configuration file could cause you to be locked out of your repository. For this reason you should specifically have CVSROOT launch /bin/true, and use DEFAULT instead of ALL for a global cvscop configuration.

  5. Edit checkoutlist
    Any new files created must be added to the checkoutlist.

  6. Commit
    Remember to add the new files with cvs add. Upon commit all of the files listed in commitinfo will be refreshed.


Simple examples of commitinfo, loginfo, taginfo, and verifymsg with the recommended options

  # commitinfo
  CVSROOT /bin/true
  DEFAULT /path/to/CVSROOT/ commitinfo --cvsroot ${CVSROOT} --cvsuser ${USER}
  # loginfo
  CVSROOT /bin/true
  DEFAULT /path/to/CVSROOT/ loginfo --cvsroot ${CVSROOT} --cvsuser ${USER} --svv %{sVv}
  # taginfo
  CVSROOT /bin/true
  DEFAULT /path/to/CVSROOT/ taginfo --cvsroot ${CVSROOT} --cvsuser ${USER}
  # verifymsg
  CVSROOT /bin/true
  DEFAULT /path/to/CVSROOT/ verifymsg --cvsroot=${CVSROOT} --cvsuser=${USER}

A simple cvscop-config.xml that fails everything (not recommended)

          <deny />

The same, with SQL logging enabled

          <param name="sql" value="on" />
          <param name="db"       value="my_db_name" />
          <param name="dbuser"   value="my_db_login" />
          <param name="dbpasswd" value="my_db_password" />
          <param name="dbhost"   value="my_db_host" />
          <param name="dbport"   value="8888" />
          <deny />

Limit commits to three users (larry, moe, and curly)

          <match field="mode">
            <match field="cvsuser" bool="and">
                Only larry, moe, or curly may commit!

...or for a more complex example, we have three engineers (Larry, Shemp, and Curly) with private directories. Commit access to these directories is limited to the individual engineers and to their manager (Moe):

          <match field="mode">

            <match field="path">
                <match field="cvsuser" bool="and">
                    Commit to Larry's directory (private/larry) is denied!

            <match field="path">
                <match field="cvsuser" bool="and">
                    Commit to Shemp's directory (private/shemp) is denied!

            <match field="path">
                <match field="cvsuser" bool="and">
                    Commit to Curly's directory (private/curly) is denied!


Launch an external script for each file. Note the $module, $path, and $_ variables in the command arguments.

          <match field="mode">
            <run each="ary_files">
              /home/curlyjoe/bin/ $module/$path/$_