##// END OF EJS Templates
sidedatacopies: only fetch information once for merge...
sidedatacopies: only fetch information once for merge Before this change, merge would result in reading the data from revlog twice. With this change, we keep the information in memory until we encounter the other parent. When looking at pypy, I see about 1/3 of the changesets with copy information being merge. Not doing duplicated fetch for them provide a significant speedup. revision: large amount; added files: large amount; rename small amount; c3b14617fbd7 9ba6ab77fd29 before: ! wall 0.767042 comb 0.760000 user 0.750000 sys 0.010000 (median of 11) after: ! wall 0.671162 comb 0.670000 user 0.650000 sys 0.020000 (median of 13) revision: large amount; added files: small amount; rename small amount; c3b14617fbd7 f650a9b140d2 before: ! wall 1.170169 comb 1.170000 user 1.130000 sys 0.040000 (median of 10) after: ! wall 1.030596 comb 1.040000 user 1.010000 sys 0.030000 (median of 10) revision: large amount; added files: large amount; rename large amount; 08ea3258278e d9fa043f30c0 before: ! wall 0.209846 comb 0.200000 user 0.200000 sys 0.000000 (median of 46) after: ! wall 0.170981 comb 0.170000 user 0.170000 sys 0.000000 (median of 56) revision: small amount; added files: large amount; rename large amount; df6f7a526b60 a83dc6a2d56f before: ! wall 0.013248 comb 0.010000 user 0.010000 sys 0.000000 (median of 223) after: ! wall 0.013295 comb 0.020000 user 0.020000 sys 0.000000 (median of 222) revision: small amount; added files: large amount; rename small amount; 4aa4e1f8e19a 169138063d63 before: ! wall 0.001672 comb 0.000000 user 0.000000 sys 0.000000 (median of 1000) after: ! wall 0.001666 comb 0.000000 user 0.000000 sys 0.000000 (median of 1000) revision: small amount; added files: small amount; rename small amount; 4bc173b045a6 964879152e2e before: ! wall 0.000119 comb 0.000000 user 0.000000 sys 0.000000 (median of 8010) after: ! wall 0.000119 comb 0.000000 user 0.000000 sys 0.000000 (median of 8007) revision: medium amount; added files: large amount; rename medium amount; c95f1ced15f2 2c68e87c3efe before: ! wall 0.168599 comb 0.160000 user 0.160000 sys 0.000000 (median of 58) after: ! wall 0.133316 comb 0.140000 user 0.140000 sys 0.000000 (median of 73) revision: medium amount; added files: medium amount; rename small amount; d343da0c55a8 d7746d32bf9d before: ! wall 0.036052 comb 0.030000 user 0.030000 sys 0.000000 (median of 100) after: ! wall 0.032558 comb 0.030000 user 0.030000 sys 0.000000 (median of 100) Differential Revision: https://phab.mercurial-scm.org/D7127

File last commit:

r35978:580f7b1b default
r43595:90213d02 default
Show More
hgsh.c
438 lines | 8.5 KiB | text/x-c | CLexer
Vadim Gelfer
contrib: add restricted shell.
r2341 /*
* hgsh.c - restricted login shell for mercurial
*
* Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
*
* This software may be used and distributed according to the terms of the
* GNU General Public License, incorporated herein by reference.
*
* this program is login shell for dedicated mercurial user account. it
* only allows few actions:
*
* 1. run hg in server mode on specific repository. no other hg commands
* are allowed. we try to verify that repo to be accessed exists under
* given top-level directory.
*
* 2. (optional) forward ssh connection from firewall/gateway machine to
* "real" mercurial host, to let users outside intranet pull and push
* changes through firewall.
*
* 3. (optional) run normal shell, to allow to "su" to mercurial user, use
* "sudo" to run programs as that user, or run cron jobs as that user.
*
* only tested on linux yet. patches for non-linux systems welcome.
*/
#ifndef _GNU_SOURCE
#define _GNU_SOURCE /* for asprintf */
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sysexits.h>
#include <unistd.h>
/*
* user config.
*
* if you see a hostname below, just use first part of hostname. example,
* if you have host named foo.bar.com, use "foo".
*/
/*
* HG_GATEWAY: hostname of gateway/firewall machine that people outside your
* intranet ssh into if they need to ssh to other machines. if you do not
* have such machine, set to NULL.
*/
#ifndef HG_GATEWAY
Augie Fackler
hgsh: enable clang-format...
r35978 #define HG_GATEWAY "gateway"
Vadim Gelfer
contrib: add restricted shell.
r2341 #endif
/*
* HG_HOST: hostname of mercurial server. if any machine is allowed, set to
* NULL.
*/
#ifndef HG_HOST
Augie Fackler
hgsh: enable clang-format...
r35978 #define HG_HOST "mercurial"
Vadim Gelfer
contrib: add restricted shell.
r2341 #endif
/*
* HG_USER: username to log in from HG_GATEWAY to HG_HOST. if gateway and
* host username are same, set to NULL.
*/
#ifndef HG_USER
Augie Fackler
hgsh: enable clang-format...
r35978 #define HG_USER "hg"
Vadim Gelfer
contrib: add restricted shell.
r2341 #endif
/*
* HG_ROOT: root of tree full of mercurial repos. if you do not want to
* validate location of repo when someone is try to access, set to NULL.
*/
#ifndef HG_ROOT
Augie Fackler
hgsh: enable clang-format...
r35978 #define HG_ROOT "/home/hg/repos"
Vadim Gelfer
contrib: add restricted shell.
r2341 #endif
/*
* HG: path to the mercurial executable to run.
*/
#ifndef HG
Augie Fackler
hgsh: enable clang-format...
r35978 #define HG "/home/hg/bin/hg"
Vadim Gelfer
contrib: add restricted shell.
r2341 #endif
/*
* HG_SHELL: shell to use for actions like "sudo" and "su" access to
* mercurial user, and cron jobs. if you want to make these things
* impossible, set to NULL.
*/
#ifndef HG_SHELL
Augie Fackler
hgsh: enable clang-format...
r35978 #define HG_SHELL NULL
Matt Mackall
many, many trivial check-code fixups
r10282 /* #define HG_SHELL "/bin/bash" */
Vadim Gelfer
contrib: add restricted shell.
r2341 #endif
/*
* HG_HELP: some way for users to get support if they have problem. if they
* should not get helpful message, set to NULL.
*/
#ifndef HG_HELP
Augie Fackler
hgsh: enable clang-format...
r35978 #define HG_HELP "please contact support@example.com for help."
Vadim Gelfer
contrib: add restricted shell.
r2341 #endif
/*
* SSH: path to ssh executable to run, if forwarding from HG_GATEWAY to
* HG_HOST. if you want to use rsh instead (why?), you need to modify
* arguments it is called with. see forward_through_gateway.
*/
#ifndef SSH
Augie Fackler
hgsh: enable clang-format...
r35978 #define SSH "/usr/bin/ssh"
Vadim Gelfer
contrib: add restricted shell.
r2341 #endif
/*
* tell whether to print command that is to be executed. useful for
* debugging. should not interfere with mercurial operation, since
* mercurial only cares about stdin and stdout, and this prints to stderr.
*/
static const int debug = 0;
static void print_cmdline(int argc, char **argv)
{
Matt Mackall
many, many trivial check-code fixups
r10282 FILE *fp = stderr;
int i;
Vadim Gelfer
contrib: add restricted shell.
r2341
Matt Mackall
many, many trivial check-code fixups
r10282 fputs("command: ", fp);
Vadim Gelfer
contrib: add restricted shell.
r2341
Matt Mackall
many, many trivial check-code fixups
r10282 for (i = 0; i < argc; i++) {
char *spc = strpbrk(argv[i], " \t\r\n");
if (spc) {
fputc('\'', fp);
}
fputs(argv[i], fp);
if (spc) {
fputc('\'', fp);
}
if (i < argc - 1) {
fputc(' ', fp);
}
}
fputc('\n', fp);
fflush(fp);
Vadim Gelfer
contrib: add restricted shell.
r2341 }
static void usage(const char *reason, int exitcode)
{
Matt Mackall
many, many trivial check-code fixups
r10282 char *hg_help = HG_HELP;
Vadim Gelfer
contrib: add restricted shell.
r2341
Matt Mackall
many, many trivial check-code fixups
r10282 if (reason) {
fprintf(stderr, "*** Error: %s.\n", reason);
}
fprintf(stderr, "*** This program has been invoked incorrectly.\n");
if (hg_help) {
fprintf(stderr, "*** %s\n", hg_help);
}
exit(exitcode ? exitcode : EX_USAGE);
Vadim Gelfer
contrib: add restricted shell.
r2341 }
/*
* run on gateway host to make another ssh connection, to "real" mercurial
* server. it sends its command line unmodified to far end.
*
* never called if HG_GATEWAY is NULL.
*/
static void forward_through_gateway(int argc, char **argv)
{
Matt Mackall
many, many trivial check-code fixups
r10282 char *ssh = SSH;
char *hg_host = HG_HOST;
char *hg_user = HG_USER;
char **nargv = alloca((10 + argc) * sizeof(char *));
int i = 0, j;
Vadim Gelfer
contrib: add restricted shell.
r2341
Matt Mackall
many, many trivial check-code fixups
r10282 nargv[i++] = ssh;
nargv[i++] = "-q";
nargv[i++] = "-T";
nargv[i++] = "-x";
if (hg_user) {
nargv[i++] = "-l";
nargv[i++] = hg_user;
}
nargv[i++] = hg_host;
Vadim Gelfer
contrib: add restricted shell.
r2341
Matt Mackall
many, many trivial check-code fixups
r10282 /*
* sshd called us with added "-c", because it thinks we are a shell.
* drop it if we find it.
*/
j = 1;
if (j < argc && strcmp(argv[j], "-c") == 0) {
j++;
}
Vadim Gelfer
contrib: add restricted shell.
r2341
Matt Mackall
many, many trivial check-code fixups
r10282 for (; j < argc; i++, j++) {
nargv[i] = argv[j];
}
nargv[i] = NULL;
Vadim Gelfer
contrib: add restricted shell.
r2341
Matt Mackall
many, many trivial check-code fixups
r10282 if (debug) {
print_cmdline(i, nargv);
}
Vadim Gelfer
contrib: add restricted shell.
r2341
Matt Mackall
many, many trivial check-code fixups
r10282 execv(ssh, nargv);
perror(ssh);
exit(EX_UNAVAILABLE);
Vadim Gelfer
contrib: add restricted shell.
r2341 }
/*
* run shell. let administrator "su" to mercurial user's account to do
* administrative works.
*
* never called if HG_SHELL is NULL.
*/
static void run_shell(int argc, char **argv)
{
Matt Mackall
many, many trivial check-code fixups
r10282 char *hg_shell = HG_SHELL;
char **nargv;
char *c;
int i;
Vadim Gelfer
contrib: add restricted shell.
r2341
Matt Mackall
many, many trivial check-code fixups
r10282 nargv = alloca((argc + 3) * sizeof(char *));
c = strrchr(hg_shell, '/');
Vadim Gelfer
contrib: add restricted shell.
r2341
Matt Mackall
many, many trivial check-code fixups
r10282 /* tell "real" shell it is login shell, if needed. */
Vadim Gelfer
contrib: add restricted shell.
r2341
Matt Mackall
many, many trivial check-code fixups
r10282 if (argv[0][0] == '-' && c) {
nargv[0] = strdup(c);
if (nargv[0] == NULL) {
perror("malloc");
exit(EX_OSERR);
}
nargv[0][0] = '-';
} else {
nargv[0] = hg_shell;
}
Vadim Gelfer
contrib: add restricted shell.
r2341
Matt Mackall
many, many trivial check-code fixups
r10282 for (i = 1; i < argc; i++) {
nargv[i] = argv[i];
}
nargv[i] = NULL;
Vadim Gelfer
contrib: add restricted shell.
r2341
Matt Mackall
many, many trivial check-code fixups
r10282 if (debug) {
print_cmdline(i, nargv);
}
Vadim Gelfer
contrib: add restricted shell.
r2341
Matt Mackall
many, many trivial check-code fixups
r10282 execv(hg_shell, nargv);
perror(hg_shell);
exit(EX_OSFILE);
Vadim Gelfer
contrib: add restricted shell.
r2341 }
Vadim Gelfer
contrib/hgsh: make to work with remote clone over ssh.
r2602 enum cmdline {
Matt Mackall
many, many trivial check-code fixups
r10282 hg_init,
hg_serve,
Vadim Gelfer
contrib/hgsh: make to work with remote clone over ssh.
r2602 };
Vadim Gelfer
contrib: add restricted shell.
r2341 /*
Bryan O'Sullivan
contrib/hgsh: Check for .hg/store as well as .hg/data....
r4419 * attempt to verify that a directory is really a hg repo, by testing
* for the existence of a subdirectory.
*/
static int validate_repo(const char *repo_root, const char *subdir)
{
Matt Mackall
many, many trivial check-code fixups
r10282 char *abs_path;
struct stat st;
int ret;
Bryan O'Sullivan
contrib/hgsh: Check for .hg/store as well as .hg/data....
r4419
Matt Mackall
many, many trivial check-code fixups
r10282 if (asprintf(&abs_path, "%s.hg/%s", repo_root, subdir) == -1) {
ret = -1;
goto bail;
}
Bryan O'Sullivan
contrib/hgsh: Check for .hg/store as well as .hg/data....
r4419
Matt Mackall
many, many trivial check-code fixups
r10282 /* verify that we really are looking at valid repo. */
Bryan O'Sullivan
contrib/hgsh: Check for .hg/store as well as .hg/data....
r4419
Matt Mackall
many, many trivial check-code fixups
r10282 if (stat(abs_path, &st) == -1) {
ret = 0;
} else {
ret = 1;
}
Bryan O'Sullivan
contrib/hgsh: Check for .hg/store as well as .hg/data....
r4419
bail:
Matt Mackall
many, many trivial check-code fixups
r10282 return ret;
Bryan O'Sullivan
contrib/hgsh: Check for .hg/store as well as .hg/data....
r4419 }
/*
Vadim Gelfer
contrib: add restricted shell.
r2341 * paranoid wrapper, runs hg executable in server mode.
*/
static void serve_data(int argc, char **argv)
{
Matt Mackall
many, many trivial check-code fixups
r10282 char *hg_root = HG_ROOT;
char *repo, *repo_root;
enum cmdline cmd;
char *nargv[6];
size_t repolen;
int i;
Vadim Gelfer
contrib: add restricted shell.
r2341
Matt Mackall
many, many trivial check-code fixups
r10282 /*
* check argv for looking okay. we should be invoked with argv
* resembling like this:
*
* hgsh
* -c
* hg -R some/path serve --stdio
*
* the "-c" is added by sshd, because it thinks we are login shell.
*/
Vadim Gelfer
contrib: add restricted shell.
r2341
Matt Mackall
many, many trivial check-code fixups
r10282 if (argc != 3) {
goto badargs;
}
Vadim Gelfer
contrib: add restricted shell.
r2341
Matt Mackall
many, many trivial check-code fixups
r10282 if (strcmp(argv[1], "-c") != 0) {
goto badargs;
}
Vadim Gelfer
contrib: add restricted shell.
r2341
Matt Mackall
many, many trivial check-code fixups
r10282 if (sscanf(argv[2], "hg init %as", &repo) == 1) {
cmd = hg_init;
Augie Fackler
hgsh: enable clang-format...
r35978 } else if (sscanf(argv[2], "hg -R %as serve --stdio", &repo) == 1) {
Matt Mackall
many, many trivial check-code fixups
r10282 cmd = hg_serve;
} else {
Vadim Gelfer
contrib/hgsh: make to work with remote clone over ssh.
r2602 goto badargs;
Matt Mackall
many, many trivial check-code fixups
r10282 }
Thomas Arendsen Hein
Remove trailing spaces
r5081
Matt Mackall
many, many trivial check-code fixups
r10282 repolen = repo ? strlen(repo) : 0;
Vadim Gelfer
contrib/hgsh: make to work with remote clone over ssh.
r2602
Matt Mackall
many, many trivial check-code fixups
r10282 if (repolen == 0) {
goto badargs;
Vadim Gelfer
contrib/hgsh: make to work with remote clone over ssh.
r2602 }
Vadim Gelfer
contrib: add restricted shell.
r2341
Matt Mackall
many, many trivial check-code fixups
r10282 if (hg_root) {
if (asprintf(&repo_root, "%s/%s/", hg_root, repo) == -1) {
goto badargs;
}
/*
* attempt to stop break out from inside the
* repository tree. could do something more clever
* here, because e.g. we could traverse a symlink that
* looks safe, but really breaks us out of tree.
*/
if (strstr(repo_root, "/../") != NULL) {
goto badargs;
}
Vadim Gelfer
contrib: add restricted shell.
r2341
Matt Mackall
many, many trivial check-code fixups
r10282 /* only hg init expects no repo. */
if (cmd != hg_init) {
int valid;
valid = validate_repo(repo_root, "data");
if (valid == -1) {
goto badargs;
}
if (valid == 0) {
valid = validate_repo(repo_root, "store");
if (valid == -1) {
goto badargs;
}
}
Vadim Gelfer
contrib/hgsh: make to work with remote clone over ssh.
r2602
Matt Mackall
many, many trivial check-code fixups
r10282 if (valid == 0) {
perror(repo);
exit(EX_DATAERR);
}
}
if (chdir(hg_root) == -1) {
perror(hg_root);
exit(EX_SOFTWARE);
}
}
i = 0;
Thomas Arendsen Hein
Remove trailing spaces
r5081
Matt Mackall
many, many trivial check-code fixups
r10282 switch (cmd) {
case hg_serve:
nargv[i++] = HG;
nargv[i++] = "-R";
nargv[i++] = repo;
nargv[i++] = "serve";
nargv[i++] = "--stdio";
break;
case hg_init:
nargv[i++] = HG;
nargv[i++] = "init";
nargv[i++] = repo;
break;
}
Vadim Gelfer
contrib: add restricted shell.
r2341
Matt Mackall
many, many trivial check-code fixups
r10282 nargv[i] = NULL;
Vadim Gelfer
contrib: add restricted shell.
r2341
Matt Mackall
many, many trivial check-code fixups
r10282 if (debug) {
print_cmdline(i, nargv);
}
execv(HG, nargv);
perror(HG);
exit(EX_UNAVAILABLE);
Vadim Gelfer
contrib: add restricted shell.
r2341
badargs:
Matt Mackall
many, many trivial check-code fixups
r10282 /* print useless error message. */
Vadim Gelfer
contrib: add restricted shell.
r2341
Matt Mackall
many, many trivial check-code fixups
r10282 usage("invalid arguments", EX_DATAERR);
Vadim Gelfer
contrib: add restricted shell.
r2341 }
int main(int argc, char **argv)
{
Matt Mackall
many, many trivial check-code fixups
r10282 char host[1024];
char *c;
Vadim Gelfer
contrib: add restricted shell.
r2341
Matt Mackall
many, many trivial check-code fixups
r10282 if (gethostname(host, sizeof(host)) == -1) {
perror("gethostname");
exit(EX_OSERR);
}
Vadim Gelfer
contrib: add restricted shell.
r2341
Matt Mackall
many, many trivial check-code fixups
r10282 if ((c = strchr(host, '.')) != NULL) {
*c = '\0';
}
Vadim Gelfer
contrib: add restricted shell.
r2341
Matt Mackall
many, many trivial check-code fixups
r10282 if (getenv("SSH_CLIENT")) {
char *hg_gateway = HG_GATEWAY;
char *hg_host = HG_HOST;
Vadim Gelfer
contrib: add restricted shell.
r2341
Matt Mackall
many, many trivial check-code fixups
r10282 if (hg_gateway && strcmp(host, hg_gateway) == 0) {
forward_through_gateway(argc, argv);
}
Vadim Gelfer
contrib: add restricted shell.
r2341
Matt Mackall
many, many trivial check-code fixups
r10282 if (hg_host && strcmp(host, hg_host) != 0) {
usage("invoked on unexpected host", EX_USAGE);
}
Vadim Gelfer
contrib: add restricted shell.
r2341
Matt Mackall
many, many trivial check-code fixups
r10282 serve_data(argc, argv);
} else if (HG_SHELL) {
run_shell(argc, argv);
} else {
usage("invalid arguments", EX_DATAERR);
}
Vadim Gelfer
contrib: add restricted shell.
r2341
Matt Mackall
many, many trivial check-code fixups
r10282 return 0;
Vadim Gelfer
contrib: add restricted shell.
r2341 }