<?php // It is called by a remote client that does a push/fetch by git/ssh. error_reporting(E_ALL); ini_set("track_errors", "On"); require_once("/etc/rocketgit/config.php"); $INC = dirname(__FILE__) . "/../inc"; require_once($INC . "/init.inc.php"); require_once($INC . "/util.inc.php"); require_once($INC . "/log.inc.php"); require_once($INC . "/sql.inc.php"); require_once($INC . "/struct.inc.php"); require_once($INC . "/repo.inc.php"); require_once($INC . "/prof.inc.php"); require_once($INC . "/ssh.inc.php"); require_once($INC . "/keys.inc.php"); require_once($INC . "/fixes.inc.php"); require_once($INC . "/plan.inc.php"); require_once($INC . "/ver.php"); rg_prof_start("remote.php"); rg_log_set_file($rg_log_dir . "/remote.log"); function info($str) { rg_log("Sending: " . $str); $str2 = "RocketGit: Info: " . $str . "\n"; if (isset($_SERVER['SSH_CONNECTION'])) { // ssh fwrite(STDERR, $str2); } else { // Keep in mind we did not negotiated yet the side-band feat. // So, seems we cannot send nice messages to the user. } } function fatal($str) { rg_log("Sending: " . $str); $str2 = "RocketGit: Error: " . $str . "\n"; if (isset($_SERVER['SSH_CONNECTION'])) { // ssh fwrite(STDERR, $str2); } else { // See above for 'info' function // But, keep in mind that at least git archive --remote // expects a 4 byte len! //echo "\n" . $str2; echo rg_git_pack('ERR ' . $str2); } exit(1); } umask(0022); rg_log("Start ($rocketgit_version)..."); rg_log("_SERVER: " . rg_array2string($_SERVER)); // DEBUG SELinux $label = @file_get_contents("/proc/self/attr/current"); rg_log("SELINUX: " . $label); rg_sql_app("rg-remote"); $db = rg_sql_open($rg_sql); if ($db === FALSE) fatal("Internal error (db)!"); if (rg_struct_ok($db) === FALSE) fatal("We are in a short maintenance window. Try again later."); if (isset($_SERVER['SSH_CONNECTION'])) { rg_log("SSH connection: " . $_SERVER['SSH_CONNECTION']); // we do not have host info $host = ''; // first parameter must be uid of the user $login_uid = isset($_SERVER['argv'][1]) ? $_SERVER['argv'][1] : 0; rg_log("uid is $login_uid."); // second parameter must be the ssh key id $key_id = isset($_SERVER['argv'][2]) ? $_SERVER['argv'][2] : 0; // TODO: because of build system, 0 may be valid. //if ($key_id == 0) // fatal("key_id not provided!"); rg_log("key_id is $key_id."); // Third para is 'flags' $flags = isset($_SERVER['argv'][3]) ? $_SERVER['argv'][3] : ''; rg_log('flags=' . $flags); if (!isset($_SERVER['SSH_ORIGINAL_COMMAND'])) $cmd_repo = ""; else $cmd_repo = trim($_SERVER['SSH_ORIGINAL_COMMAND']); $ssh_client = getenv("SSH_CLIENT"); $_t = explode(" ", $ssh_client); $ip = rg_fix_ip($_t[0]); info('== Welcome to RocketGit! =='); info('you are connecting from IP ' . $ip . '.'); $login_ui = rg_user_info($db, $login_uid, '', ''); if ($login_ui['exists'] != 1) fatal("User does not exists (conn)."); info('you are connecting as user \'' . $login_ui['username'] . '\'.'); putenv('ROCKETGIT_SHOW_INFO=0'); // Only normal keys can execute some commands if (strstr($flags, 'N')) $must_exit = rg_ssh_dispatch($db, $ip, $login_uid, $cmd_repo); else $must_exit = FALSE; // Only normal keys can update stats if (strstr($flags, 'N')) { // We do this operation after dispatch to not impact the latency. // TODO: This should be put in a queue for performance reasons // At the same time, update stats? events? $_r = rg_keys_update_use($db, $login_ui['uid'], $key_id, $ip, $cmd_repo); if ($_r !== TRUE) rg_internal_error("Cannot update key last_use!"); } if ($must_exit) { rg_prof_end("remote.php"); rg_prof_log(); exit(0); } } else { rg_log("git-daemon connection..."); // we have no client info $login_uid = 0; $key_id = 0; $flags = ''; $login_ui = array('uid' => 0, 'username' => 'anonymous user', 'organization' => 0); $line = @fread(STDIN, 8000); $line_len = strlen($line); rg_log("line=[$line]"); if ($line_len < 4) fatal("Line is too short!"); $len = @hexdec(substr($line, 0, 4)); if ($line_len < $len) fatal("Too less data ($line_len/$len) received!"); // parse something like: 002bgit-upload-pack /aa.git[0x00]host=localhost:9418[0x00] $line = substr($line, 4); $v = explode("\0", $line); $cmd_repo = trim($v[0]); $host_port = isset($v[1]) ? trim(substr($v[1], 5)) : ''; $v = explode(':', $host_port); $host = $v[0]; $ip = rg_fix_ip(getenv("REMOTE_HOST")); } if (strncasecmp($cmd_repo, 'git-upload-pack', 15) == 0) { $cmd = 'git-upload-pack'; } else if (strncasecmp($cmd_repo, 'git-receive-pack', 16) == 0) { $cmd = 'git-receive-pack'; } else { $cmd = $cmd_repo; } // extract repository name $_t = substr($cmd_repo, strlen($cmd)); $_t = trim($_t, "' "); $_t = ltrim($_t, "/"); $_t = preg_replace('/\.git$/' , '', $_t); $_t = explode("/", $_t); if (strcmp($_t[0], "user") == 0) { $prefix = "/user"; $user = isset($_t[1]) ? $_t[1] : ""; $repo = isset($_t[2]) ? $_t[2] : ""; } else { $prefix = ""; $user = isset($_t[0]) ? $_t[0] : ""; $repo = isset($_t[1]) ? $_t[1] : ""; } rg_log("host=[$host] cmd=[$cmd] prefix=[$prefix] user=[$user] repo=[$repo]."); if (strstr($flags, 'W')) { // We are a worker, the command may be only for clonning! if (strcmp($cmd, 'git-upload-pack') != 0) fatal('A worker can only clone!'); } $r = rg_repo_fetch_push_helper($db, $host, $ip, $login_ui, $prefix, $user, $repo, $cmd, TRUE /*need_namespace_copy*/); if ($r['ok'] != 1) fatal($r['error']); $run = "git-shell -c \"" . $cmd . " " . escapeshellarg($r['repo_path']) . "\""; //$run = $cmd . ' ' . escapeshellarg($r['repo_path']); rg_log("Running [$run]..."); rg_prof_start($cmd); // TODO: shouldn't we use rg_exec to capture stderr? passthru($run, $ret); rg_prof_end($cmd); rg_log("[$run] returned $ret."); rg_prof_end("remote.php"); rg_prof_log(); ?>