aboutsummaryrefslogtreecommitdiff
path: root/cvs/sv_sync_www.c
diff options
context:
space:
mode:
authorSergey Poznyakoff <gray@gnu.org.ua>2005-07-31 19:01:14 +0000
committerSergey Poznyakoff <gray@gnu.org.ua>2005-07-31 19:01:14 +0000
commit1be8e172dee8ab5e5e6129e31140722ea8ba0f85 (patch)
tree50b3eb353e2b5955213730223836a053a034e0d7 /cvs/sv_sync_www.c
parenta7f33bff55cd5c7f995412fcb51133b06c92f669 (diff)
downloadgsc-1be8e172dee8ab5e5e6129e31140722ea8ba0f85.tar.gz
gsc-1be8e172dee8ab5e5e6129e31140722ea8ba0f85.tar.bz2
Added to the repository
git-svn-id: file:///svnroot/gsc/trunk@58 d2de0444-eb31-0410-8365-af798a554d48
Diffstat (limited to 'cvs/sv_sync_www.c')
-rw-r--r--cvs/sv_sync_www.c435
1 files changed, 435 insertions, 0 deletions
diff --git a/cvs/sv_sync_www.c b/cvs/sv_sync_www.c
new file mode 100644
index 0000000..c046800
--- /dev/null
+++ b/cvs/sv_sync_www.c
@@ -0,0 +1,435 @@
+/*
+
+ Copyright (C) Gordon Matzigkeit <gord@fig.org>, Loic Dachary
+ <loic@gnu.org>, 2001
+ Copyright (C) 2005, Sergey Poznyakoff
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 2
+ of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ MA 02110-1301, USA.
+
+*/
+
+/* sf_sync_www.c - Allow unprivileged users to do a web update on www.gnu.org
+
+ In CVSROOT/loginfo file
+ ALL /bin/sv_sync_www_schedule ${USER} %s
+
+ In crontab (remove the backslash before /):
+ 0 *\/1 * * * /bin/sv_sync_www_flush
+
+ Or, to synchronize right after commit, in CVSROOT/loginfo:
+
+ ALL (date; cat; (sleep 2; /bin/sf_sync_www %{s} ) &\ ) >> /var/log/sf_sync_www 2>&1
+
+ For test purpose:
+ ./sf_sync_www -n ' bla bla'
+ ./sf_sync_www -n 'foo/bar bla bla'
+ ./sf_sync_www -n 'f'\''oo bla bla'
+ ./sf_sync_www -n 'f'\''oo - New directory'
+ ./sf_sync_www -n 'bla - Imported sources'
+
+ The idea is that it runs a cvs update -l (to prevent recursion) in the
+ directory where the commit was done. Since the command will be called
+ once for each directory where a commit did some action there is no
+ need for recursion. In the case of an import command this does not
+ hold and a recursion must always be done since there is only one
+ call to the script for a whole imported tree (this happens when the
+ argument contains the Imported source string).
+
+ After running update, the program looks for file ".symlinks" and executes
+ it, when present. File format is: empty lines and lines started with ';'
+ or '#' are ignored. Rest of lines are split in two words: first word is
+ the name of an existing file, second one is the link name. Both names
+ should be in the current directory. Care is taken to prevent linking to
+ upper-level directories.
+
+ The %{s} argument in loginfo invocation is a single argument that lists
+ the directory and all the files involved. As a special case if the
+ directory was added the file list is replaced by '- New directory'. This
+ is lame since adding the files -, New and directory will produce the same
+ effect, but it's unlikely. The same applies when a whole source tree is
+ imported using cvs import in which case the file list is replaced by
+ '- Imported sources'.
+
+ There are three cases to take in account (topdir is the absolute path
+ of the directory in which the CVS tree was extracted, subdirectory is
+ the directory given in argument):
+ - commit that modify the top level directory files
+ cd topdir ; cvs update -l
+ - commit that adds a new directory or that import a whole source tree
+ cd topdir ; cvs update 'subdirectory'
+ - commit that modify files in a subdirectory
+ cd topdir/subdirectory ; cvs update -l
+
+ In order to prevent security compromise the directory name is quoted.
+
+ Originaly by
+ Gordon Matzigkeit <gord@fig.org>, 2000-11-28
+
+ Update CVS_COMMANDS to reduce noise
+ Loic Dachary <loic@gnu.org>, 2001-02-26
+
+ Modify to allow generic call from loginfo file in an efficient way
+ Loic Dachary <loic@gnu.org>, 2001-03-10
+
+ Introduce command line options, make program configurable and
+ implement .symlinks functionality
+ Sergey Poznyakoff <gray@gnu.org.ua>, 2005-07-31
+
+*/
+
+#include <sys/types.h>
+#include <ctype.h>
+#include <string.h>
+#include <stdio.h>
+#include <sys/wait.h>
+
+#include <error.h>
+#include <errno.h>
+
+#include <sys/stat.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <pwd.h>
+
+/*
+ * Return a malloc'ed copy of the string argument with all '
+ * substituted by '\'' and a trailing and leading '.
+ * For instance : foo -> 'foo'
+ * fib'ou -> 'fib'\''ou'
+ */
+char *
+quote (char *string)
+{
+ int i;
+ int length;
+ int count = 0;
+ char *out;
+
+ if (!string)
+ return NULL;
+
+ length = strlen (string);
+ for (i = 0; i < length; i++)
+ if (string[i] == '\'')
+ count++;
+
+ /* +1 for null at end, (count * 3) for '\'', +2 for leading ' and
+ trailing ' */
+ out = (char *) malloc (length + 1 + count * 3 + 2);
+ if (count > 0)
+ {
+ char *from_p = string;
+ char *to_p = out;
+ *to_p++ = '\'';
+ while (*from_p)
+ {
+ if (*from_p == '\'')
+ {
+ strcpy (to_p, "'\\''");
+ to_p += 4;
+ from_p++;
+ }
+ else
+ *to_p++ = *from_p++;
+ }
+ *to_p++ = '\'';
+ *to_p = '\0';
+ }
+ else
+ sprintf (out, "'%s'", string);
+ return out;
+}
+
+char *user;
+char *rsh_program = "/usr/bin/ssh";
+char *hostname;
+char *repository = "/webcvs";
+char *www_directory = "/home/puszcza/software";
+char *cvs_command = "CVS_RSH=ssh cvs -q -z3";
+int dry_run = 0;
+int debug = 0;
+
+void
+usage ()
+{
+ printf ("sv_sync_www [OPTIONS] directory\n\n");
+ printf ("OPTIONS are:\n");
+ printf (" -H hostname Set hostname or IP address of the host where WWW directory\n");
+ printf (" resides (default: none, WWW and CVS reside on the same host)\n");
+ printf (" -r rsh_program Set full name of RSH program\n");
+ printf (" (default: %s)\n", rsh_program);
+ printf (" -R repository Set full name of the CVS repository\n");
+ printf (" (default: %s)\n", repository);
+ printf (" -w www_directory Set full name of the WWW directory\n");
+ printf (" (default: %s)\n", www_directory);
+ printf (" -n Dry run: do nothing, only print what would have been done\n");
+ printf (" -h Display this help summary\n");
+}
+
+static int
+run_cmd (char *cmd)
+{
+ pid_t pid;
+ int i;
+ char *xargv[6];
+ int xargc = 0;
+ char *new_envp[] = { NULL };
+ char *p;
+ char *program;
+
+ pid = fork ();
+ if (pid < 0)
+ error (1, errno, "cannot fork");
+
+ if (pid > 0) /* Master process */
+ {
+ int status;
+ waitpid (pid, &status, 0);
+ if (WIFEXITED (status))
+ {
+ status = WEXITSTATUS (status);
+ if (status)
+ error (0, 0, "subprocess returned exit code %d",
+ status);
+ return status;
+ }
+ else if (WIFSIGNALED (status))
+ {
+ error (0, 0, "subprocess terminated on signal %d",
+ WTERMSIG (status));
+ return -1;
+ }
+ else
+ {
+ error (0, 0, "subprocess terminated");
+ return -1;
+ }
+ }
+
+ if (hostname)
+ {
+ program = dry_run ? "/bin/echo" : rsh_program;
+ xargv[xargc++] = rsh_program;
+ xargv[xargc++] = hostname;
+ }
+ else
+ {
+ if (dry_run)
+ {
+ program = "/bin/echo";
+ xargv[xargc++] = "echo";
+ }
+ else
+ {
+ program = "/bin/sh";
+ xargv[xargc++] = "-sh";
+ xargv[xargc++] = "-c";
+ if (debug)
+ xargv[xargc++] = "-x";
+ }
+ }
+
+ xargv[xargc++] = cmd;
+ xargv[xargc++] = NULL;
+
+ /* Pick out the name of the program. */
+ p = strrchr (xargv[0], '/');
+ if (p)
+ xargv[0] = p;
+
+ execve (program, xargv, new_envp);
+ error (1, errno, "Cannot exec `%s'", program);
+}
+
+int
+main (int argc, char **argv)
+{
+ int i;
+ char *unquoted_directory = 0;
+ char *directory = 0;
+ int files = 0;
+ char *cmd;
+ char *tmp;
+ uid_t uid;
+ gid_t gid;
+
+ while ((i = getopt (argc, argv, "hH:nr:R:u:w:x")) != EOF)
+ switch (i)
+ {
+ case 'H':
+ hostname = optarg;
+ break;
+
+ case 'n':
+ dry_run = 1;
+ break;
+
+ case 'r':
+ rsh_program = optarg;
+ break;
+
+ case 'R':
+ repository = optarg;
+ break;
+
+ case 'u':
+ user = optarg;
+ break;
+
+ case 'h':
+ usage ();
+ exit (0);
+
+ case 'w':
+ www_directory = optarg;
+ break;
+
+ case 'x':
+ debug = 1;
+ break;
+
+ default:
+ exit (1);
+ }
+
+ if (debug)
+ {
+ fprintf (stderr, "Invocation:\n");
+ for (i = 0; i < argc; i++)
+ fprintf (stderr, "%s\n", argv[i]);
+ fprintf (stderr, "\n");
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (user)
+ {
+ struct passwd *pw = getpwnam (user);
+ if (!pw)
+ error (1, errno, "cannot get password entry for user %s", user);
+ uid = pw->pw_uid;
+ gid = pw->pw_gid;
+ }
+ else
+ {
+ gid = getegid ();
+ uid = geteuid ();
+ }
+
+ if (setregid (gid, gid))
+ error (1, errno, "cannot assume effective group id");
+
+ if (setreuid (uid, uid))
+ error (1, errno, "cannot assume effective user id");
+
+ if (argc == 1)
+ {
+ tmp = argv[0];
+ /*
+ * First comes the relative directory name or empty string if current.
+ * directory = 0 if no subdirectory
+ * directory = relative path if subdirectory
+ */
+ unquoted_directory = tmp;
+ if ((tmp = strchr (tmp, ' ')))
+ {
+ *tmp = '\0';
+ if (tmp - unquoted_directory <= 0)
+ unquoted_directory = 0;
+ tmp++;
+ }
+ else
+ error (1, 0, "Failed to find directory in first argument: `%s'",
+ argv[0]);
+ }
+ else if (argc == 2)
+ {
+ unquoted_directory = argv[0];
+ tmp = argv[1];
+ }
+ else
+ error (1, 0, "You must specify one or two arguments");
+
+ directory = quote (unquoted_directory);
+
+ if (!strcmp (tmp, "- New directory")
+ || !strcmp (tmp, "- Imported sources"))
+ {
+ /*
+ * If this is a new directory creation or an import command,
+ * there are no files to collect in the argument list.
+ */
+ }
+ else
+ {
+ /*
+ * There are files to collect in the argument list.
+ */
+ files = 1;
+ }
+
+ /* Check that the local directory exists. */
+ if (!dry_run && unquoted_directory)
+ {
+ struct stat stbuf;
+ char *localdir;
+
+ localdir = malloc (i + 1 + strlen (unquoted_directory) + 1);
+ if (!localdir)
+ error (1, 0, "not enough memory");
+
+ i = strlen (repository);
+
+ strcpy (localdir, repository);
+ if (localdir[i - 1] != '/')
+ localdir[i++] = '/';
+ strcpy (localdir + i, unquoted_directory);
+
+ /* Check to see that the local unquoted_directory actually exists. */
+ if (stat (localdir, &stbuf) ||
+ (!S_ISDIR (stbuf.st_mode) && (errno = ENOTDIR)))
+ error (1, errno, "Cannot find `%s'", localdir);
+ }
+
+ /* Update from CVS */
+ if (directory && !files)
+ asprintf (&cmd, "cd %s && ( %s -d %s/%s checkout %s )",
+ www_directory, cvs_command, repository, directory, directory);
+ else if (!directory && files)
+ asprintf (&cmd, "cd %s && ( %s update -l )",
+ www_directory, cvs_command);
+ else if (directory && files)
+ asprintf (&cmd, "cd %s/%s && ( %s update -l )",
+ www_directory, directory, cvs_command);
+ else
+ error (1, 0, "Unexpected null directory and files");
+
+ if (run_cmd (cmd))
+ return 1;
+
+ free (cmd);
+
+ /* Restore symlinks */
+ asprintf (&cmd,
+ "cd %s/%s && test -r .symlinks && "
+ "(grep -v '^[ \t]*[;#]' .symlinks | "
+ "sed 's,^/,_,;s,\\.\\./,__/,g' |"
+ "while read S T; do ln -sf $S $T; done)",
+ www_directory, directory);
+
+ return run_cmd (cmd);
+}

Return to:

Send suggestions and report system problems to the System administrator.