问题1:在进行进程捕获的过程中,本身的subversion进程的操作是能够进行正确的捕获的,我们使用ifsvnadmin去创建版本库,却没有对相关的任何进程进行捕获
问题2:在问题1的基础上,使用ifsvnadmin创建的版本库,在服务器上没有启动正常的subversion服务,但是依然能够使用小乌龟工具进行文件上传下载
带着这两个问题,我们来看下ifsvnadmin的源码,正所谓源码之下无秘密。
1.由于要讨论版本库创建问题,先在svnadmin文件下找到repositorycreate.php文件,我们找到如下内容
//
// Actions
//
if (check_request_var('create'))
{
$engine->handleAction('create_repository');
}
发现这里是创建版本库的,找到engine的定义
//
// Authentication
//
$engine = \svnadmin\core\Engine::getInstance();
2.去svnadmin\classes\core\目录下找到Engine.class.php文件,找到
/**
* Gets the singelton instance of this class.
*
* @return \svnadmin\core\Engine
*/
public static function getInstance()
{
if (self::$m_instance == null)
{
self::$m_instance = new Engine;
}
return self::$m_instance;
}
这个接口就是自己的实例,然后我们在下面找到handleAction方法
/**
* Loads the given action file and handles it.
*
* @param string $action The name of the action
* @return void
*/
public function handleAction( $action )
{
global $appEngine;
global $appTemplate; // @todo Remove this variable from this place.
global $appTR; // @todo Remove this variable from this place.
$appTR->loadModule("actions");
if(!defined('ACTION_HANDLING'))
{
define('ACTION_HANDLING', true);
}
// Path to action implementation.
$filename = 'actions/'.$action.'.php';
if(file_exists($filename))
{
$code = file_get_contents($filename);
eval(' ?>'.$code.'<?php ');
}
else
{
throw new Exception('Can not find implementation for action: '.$action);
}
}
这段代码意思加载了actions目录下的php文件,所以我们去找create_repository.php文件
3.在create_repository.php文件中,我们找到如下内容
// Create a initial repository structure.
try {
$repoPredefinedStructure = get_request_var("repostructuretype");
if ($repoPredefinedStructure != NULL) {
switch ($repoPredefinedStructure) {
case "single":
$engine->getRepositoryEditProvider()
->mkdir($r, array('trunk', 'branches', 'tags'));
break;
case "multi":
$projectName = get_request_var("projectname");
if ($projectName != NULL) {
$engine->getRepositoryEditProvider()
->mkdir($r, array(
$projectName . '/trunk',
$projectName . '/branches',
$projectName . '/tags'
));
}
else {
throw new ValidationException(tr("Missing project name"));
}
break;
}
}
}
catch (Exception $e3) {
$engine->addException($e3);
}
显然,这就是ifsvnadmin创建版本库的地方,在这段代码中,我们注意到有两个地方需要再细看一下,get_request_var和getRepositoryEditProvider这两个函数
4.通过全局查找发现,get_request_var函数再include\ifcorelib\globals.php中定义
function get_request_var( $varname )
{
$method = null;
if (check_request_var($varname, $method))
{
switch($method)
{
case 'get':
if (is_array($_GET[$varname]))
{
if (count($_GET[$varname]) == 1 && empty($_GET[$varname][0]))
{
return null;
}
}
return $_GET[$varname];
case 'post':
if (is_array($_POST[$varname]))
{
if (count($_POST[$varname]) == 1 && empty($_POST[$varname][0]))
{
return null;
}
}
return $_POST[$varname];
}
}
return null;
}
通过这个函数,我们可以看到,版本库的名字是通过http的方式传到后台的。
5.在Engine.class.php文件中找到getRepositoryEditProvider函数如下
public function isRepositoryEditActive()
{
return $this->m_repositoryEditProvider == null ? false : true;
}
public function setRepositoryEditProvider( $o )
{
$this->m_repositoryEditProvider = $o;
}
public function getRepositoryEditProvider()
{
if( $this->m_repositoryEditProvider != null )
{
$this->m_repositoryEditProvider->init();
}
return $this->m_repositoryEditProvider;
}
这种写法,面向对象常用的写法,所以我们去找在哪里调用了setRepositoryEditProvider这个函数
6.通过查找,我们发现调用出
# grep "setRepositoryEditProvider" ./* -r
./classes/core/Engine.class.php: public function setRepositoryEditProvider( $o )
./include/config.inc.php: $appEngine->setRepositoryEditProvider( $repoEdit );
所以去看下include/config.inc.php文件中是怎么样的,在其中找到了下面的代码
/**
* Repository edit provider.
*/
if ($cfg->getValue("Engine:Providers", "RepositoryEditProviderType") == "svnclient")
{
include_once( "./classes/providers/RepositoryEditProvider.class.php" );
$repoEdit = \svnadmin\providers\RepositoryEditProvider::getInstance();
$appEngine->setRepositoryEditProvider( $repoEdit );
}
从这段代码可以看到,repoEdit的值是从providers\RepositoryEditProvider::getInstance()中来的,我们再去看这个文件,providers文件夹在classes文件夹下面
7.在RepositoryEditProvider.class.php文件中,我们找到getInstance,如下
/**
* Gets the singelton instance of this object.
*
* @return svnadmin\providers\RepositoryEditProvider
*/
public static function getInstance()
{
if (self::$_instance == NULL) {
self::$_instance = new self();
}
return self::$_instance;
}
创建一个自己的实例,所以我们要仔细的看看其中到底有什么?
8.在其中我们找到了如下代码
/**
* (non-PHPdoc)
* @see svnadmin\core\interfaces.IRepositoryEditProvider::mkdir()
*/
public function mkdir(\svnadmin\core\entities\Repository $oRepository, array $paths)
{
$svnParentPath = $this->getRepositoryConfigValue($oRepository, 'SVNParentPath');
if ($svnParentPath == NULL) {
throw new \Exception('Invalid parent-identifier: ' .
$oRepository->getParentIdentifier());
}
// Create absolute paths.
for ($i = 0; $i < count($paths); ++$i) {
$paths[$i] = $svnParentPath . '/' . $oRepository->name . '/' . $paths[$i];
}
$this->_svnClient->svn_mkdir($paths);
return true;
}
因为我们关注的版本库的创建,所以看的是mkdir部分,可以看到,指向了_svnClient这个方法,看一下
/**
* Initializes the object by Engine configuration.
*/
public function __construct()
{
$engine = \svnadmin\core\Engine::getInstance();
$config = $engine->getConfig();
// Subversion class for browsing.
$this->_svnClient = new \IF_SVNClientC($engine->getConfig()
->getValue('Repositories:svnclient', 'SvnExecutable'));
....
这里就不粘贴完了,又调用了getConfig这个函数再往下看
9.在Engine.class.php中又找到
/**
* Gets the global configuration object.
*
* @return \IF_IniFile
*/
public function getConfig()
{
return $this->m_config;
}
...
那么这个m_config又是什么,继续找
/**
* Private constructor, because the class is a singelton instance.
* @return \svnadmin\core\Engine
*/
private function __construct()
{
// Load the global configuration.
$this->m_config = new \IF_IniFile();
$this->m_config->loadFromFile("./data/config.ini");
}
在这里可以看到,加载了data/config.ini这个配置文件,我们去看看这个配置文件
10.在data/目录下找到了config.ini文件,下面是配置文件的内容
[Common]
FirstStart=0
BackupFolder=./data/backup/
[Translation]
Directory=./translations/
[Engine:Providers]
AuthenticationStatus=basic
UserViewProviderType=passwd
UserEditProviderType=passwd
GroupViewProviderType=svnauthfile
GroupEditProviderType=svnauthfile
AccessPathViewProviderType=svnauthfile
AccessPathEditProviderType=svnauthfile
RepositoryViewProviderType=svnclient
RepositoryEditProviderType=svnclient
[ACLManager]
UserRoleAssignmentFile=./data/userroleassignments.ini
[Subversion]
SVNAuthFile=/var/www/html/svnadmin/accessfile
[Repositories:svnclient]
SVNParentPath=/usr/local/svn/svnrepos
SvnExecutable=/usr/bin/svn
SvnAdminExecutable=/usr/bin/svnadmin
[Users:passwd]
SVNUserFile=/var/www/html/svnadmin/passwdfile
[Users:digest]
SVNUserDigestFile=
SVNDigestRealm=SVN Privat
[Ldap]
HostAddress=ldap://192.168.136.130:389/
ProtocolVersion=3
BindDN=CN=Manuel Freiholz,CN=Users,DC=insanefactory,DC=com
BindPassword=root
CacheEnabled=false
CacheFile=./data/ldap.cache.json
[Users:ldap]
BaseDN=DC=insanefactory,DC=com
SearchFilter=(&(objectClass=person)(objectClass=user))
Attributes=sAMAccountName
[Groups:ldap]
BaseDN=DC=insanefactory,DC=com
SearchFilter=(objectClass=group)
Attributes=sAMAccountName
GroupsToUserAttribute=member
GroupsToUserAttributeValue=distinguishedName
[Update:ldap]
AutoRemoveUsers=true
AutoRemoveGroups=true
[GUI]
RepositoryDeleteEnabled=false
RepositoryDumpEnabled=false
AllowUpdateByGui=true
根据第8步知道,要找Repositories:svnclient,在配置文件中看到
[Repositories:svnclient]
SVNParentPath=/usr/local/svn/svnrepos
SvnExecutable=/usr/bin/svn
SvnAdminExecutable=/usr/bin/svnadmin
所以,到这里,我们找到了这里调用的mkdir命令是由/usr/bin/svn进程实现的,现在返回到我们的实际环境中看一下
11.在实际环境中,看到了所有相关的svn命令
# ls /usr/bin/svn
svn svnadmin svndumpfilter svnlook svnrdump svnserve svnsync svnversion
也就是说,直接调用svn命令,导致我的程序没有捕获相应的操作,下面来看看svn这个命令做了什么
12.在环境中发现我的目录结构和第3步的目录结构不一样,我的目录结构是
#ls
conf dav db format hooks locks README.txt
意识到我的出发点有错误,于是继续查找,在create_repository.php文件中找到如下内容
if ($reponame == NULL) {
$engine->addException(new ValidationException(tr("You have to fill out all fields.")));
}
else {
$r = new \svnadmin\core\entities\Repository($reponame, $varParentIdentifier);
// Create repository.
try {
$engine->getRepositoryEditProvider()->create($r, $repotype);
$engine->getRepositoryEditProvider()->save();
$engine->addMessage(tr("The repository %0 has been created successfully", array($reponame)));
...
这里才是初始化创建库的地方,这里就可以发现,同样是调用的getRepositoryEditProvider函数,所以接下来就简单了,到RepositoryEditProvider.class.php文件中去看
13.在RepositoryEditProvider.class.php文件中找到
/**
* (non-PHPdoc)
* @see svnadmin\core\interfaces.IProvider::init()
*/
public function init()
{
return true;
}
/**
* (non-PHPdoc)
* @see svnadmin\core\interfaces.IEditProvider::save()
*/
public function save()
{
return true;
}
/**
* (non-PHPdoc)
* @see svnadmin\core\interfaces.IRepositoryEditProvider::create()
*/
public function create(\svnadmin\core\entities\Repository $oRepository, $type = "fsfs")
{
$svnParentPath = $this->getRepositoryConfigValue($oRepository, 'SVNParentPath');
if (!file_exists($svnParentPath)) {
throw new \Exception("The repository parent path doesn't exists: " .
$svnParentPath);
}
$path = $svnParentPath . '/' . $oRepository->name;
$this->_svnAdmin->create($path, $type);
return true;
}
这个类中,做了一些封装,查看一下svnAdmin的定义,如下
// Subversion class for administration.
$this->_svnAdmin = new \IF_SVNAdminC($engine->getConfig()
->getValue('Repositories:svnclient', 'SvnAdminExecutable'));
同样是取配置,调用的是SvnAdminExecutable=/usr/bin/svnadmin
到此为止,我们弄清楚了ifsvnadmin的工作方式,数据流向以及程序逻辑。但是我的问题似乎并不在这里,还要继续查看,应该是其他问题。
14.