aboutsummaryrefslogtreecommitdiff
path: root/cvs/sv_sync_www.c
diff options
context:
space:
mode:
authorSergey Poznyakoff <gray@gnu.org.ua>2006-02-09 16:37:31 +0000
committerSergey Poznyakoff <gray@gnu.org.ua>2006-02-09 16:37:31 +0000
commitc055a4be5a74b9fb3dc4ab17f921c545988b84e8 (patch)
tree4c58d4e08ed821a701d1117f0f09f3035a457bb3 /cvs/sv_sync_www.c
parentfbb6f17b55d7c0babb528a75c747f4280d75e264 (diff)
downloadgsc-c055a4be5a74b9fb3dc4ab17f921c545988b84e8.tar.gz
gsc-c055a4be5a74b9fb3dc4ab17f921c545988b84e8.tar.bz2
Rewritten to incorporate the functionality of
sv_sync_www_flush and to make updates more safe. git-svn-id: file:///svnroot/gsc/trunk@181 d2de0444-eb31-0410-8365-af798a554d48
Diffstat (limited to 'cvs/sv_sync_www.c')
-rw-r--r--cvs/sv_sync_www.c567
1 files changed, 447 insertions, 120 deletions
diff --git a/cvs/sv_sync_www.c b/cvs/sv_sync_www.c
index e61aab1..3db8150 100644
--- a/cvs/sv_sync_www.c
+++ b/cvs/sv_sync_www.c
@@ -2,7 +2,7 @@
Copyright (C) Gordon Matzigkeit <gord@fig.org>, Loic Dachary
<loic@gnu.org>, 2001
- Copyright (C) 2005, Sergey Poznyakoff
+ Copyright (C) 2005, 2006 Sergey Poznyakoff
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
@@ -21,24 +21,24 @@
*/
-/* sf_sync_www.c - Allow unprivileged users to do a web update on www.gnu.org
+/* sv_sync_www.c - Allow unprivileged users to do a web update on www.gnu.org.ua
In CVSROOT/loginfo file
ALL /bin/sv_sync_www_schedule ${USER} ${CVSROOT} %s
In crontab (remove the backslash before /):
- 0 *\/1 * * * /bin/sv_sync_www_flush
+ 0 *\/1 * * * /bin/sv_sync_www -s /var/spool/savane
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
+ ALL (date; cat; (sleep 2; /bin/sv_sync_www %{s} ) &\ ) >> /var/log/sv_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'
+ ./sv_sync_www -n ' bla bla'
+ ./sv_sync_www -n 'foo/bar bla bla'
+ ./sv_sync_www -n 'f'\''oo bla bla'
+ ./sv_sync_www -n 'f'\''oo - New directory'
+ ./sv_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
@@ -87,6 +87,9 @@
Introduce command line options, make program configurable and
implement .symlinks functionality
Sergey Poznyakoff <gray@gnu.org.ua>, 2005-07-31
+
+ Implement 'scan directory' mode
+ Sergey Poznyakoff <gray@gnu.org.ua>, 2005-02-09
*/
@@ -95,19 +98,24 @@
#endif
#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
#include <ctype.h>
-#include <string.h>
+#include <unistd.h>
+#include <stdlib.h>
#include <stdio.h>
-#include <sys/wait.h>
+#include <string.h>
+#include <dirent.h>
#include <error.h>
#include <errno.h>
-#include <sys/stat.h>
-#include <unistd.h>
-#include <stdlib.h>
#include <pwd.h>
+#define obstack_chunk_alloc malloc
+#define obstack_chunk_free free
+#include <obstack.h>
+
/*
* Return a malloc'ed copy of the string argument with all '
* substituted by '\'' and a trailing and leading '.
@@ -115,7 +123,7 @@
* fib'ou -> 'fib'\''ou'
*/
char *
-quote (char *string)
+quote (const char *string)
{
int i;
int length;
@@ -135,7 +143,7 @@ quote (char *string)
out = (char *) malloc (length + 1 + count * 3 + 2);
if (count > 0)
{
- char *from_p = string;
+ const char *from_p = string;
char *to_p = out;
*to_p++ = '\'';
while (*from_p)
@@ -157,37 +165,45 @@ quote (char *string)
return out;
}
-char *user;
char *rsh_program = "/usr/bin/ssh";
char *hostname;
-char *repository = "/webcvs";
+char *default_repository = "/webcvs";
+char *default_spooldir = "/var/spool/savane";
char *access_method = NULL;
char *www_directory = "/home/puszcza/software";
char *cvs_command = "CVS_RSH=ssh cvs -q -z3";
+size_t min_uid = 100;
+size_t min_gid = 100;
int dry_run = 0;
int debug = 0;
void
usage ()
{
- printf ("sv_sync_www [OPTIONS] directory\n\n");
+ printf ("sv_sync_www [OPTIONS] DIRECTORY\n\n");
printf ("OPTIONS are:\n");
+ printf ("\nMode selection:\n");
+ printf (" -h Display this help summary\n");
+ printf (" -s Scan mode: process each file in DIRECTORY\n");
+ printf ("\nMode modification:\n");
+ printf (" -n Dry run: do nothing, only print what would have been done\n");
+ printf (" -x Enable debugging output\n");
+ printf ("\nConfiguration:\n");
printf (" -c COMMAND Set cvs command to use (default %s)\n",
cvs_command);
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 (" -h Display this help summary\n");
- printf (" -n Dry run: do nothing, only print what would have been done\n");
printf (" -m METHOD Set default access method. METHOD will be prepended to\n");
printf (" REPOSITORY (see -R option below) before accessing the\n");
printf (" repository\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 (" (default: %s)\n", default_repository);
printf (" -w WWW_DIRECTORY Set full name of the WWW directory\n");
printf (" (default: %s)\n", www_directory);
- printf (" -x Enable debugging output");
+ printf (" -U UID Set minimum allowable UID value\n");
+ printf (" -G UID Set minimum allowable GID value\n");
}
static int
@@ -220,7 +236,7 @@ run_cmd (char *cmd)
else if (WIFSIGNALED (status))
{
error (0, 0, "subprocess terminated on signal %d",
- WTERMSIG (status));
+ WTERMSIG (status));
return -1;
}
else
@@ -266,24 +282,377 @@ run_cmd (char *cmd)
}
int
+has_file_list (const char *arg)
+{
+ /*
+ * If this is a new directory creation or an import command,
+ * there are no files to collect in the argument list.
+ * Otherwise, there are files to collect.
+ */
+ return (strcmp (arg, "- New directory")
+ && strcmp (arg, "- Imported sources"));
+}
+
+static int
+split_arg (const char *arg, char **pdir, char **pfiles)
+{
+ char *tmp;
+ /*
+ * First comes the relative directory name or empty string if current.
+ * directory = 0 if no subdirectory
+ * directory = relative path if subdirectory
+ */
+ if ((tmp = strchr (arg, ' ')))
+ {
+ size_t len = tmp - arg;
+ if (len > 0)
+ {
+ *pdir = malloc (len + 1);
+ if (!*pdir)
+ return -1;
+ memcpy (*pdir, arg, len);
+ (*pdir)[len] = 0;
+ }
+ else
+ *pdir = NULL;
+ *pfiles = strdup (tmp + 1);
+ if (!*pfiles)
+ {
+ free (*pdir);
+ return -1;
+ }
+ return 0;
+ }
+ return 1;
+}
+
+
+int
+sync_www (const char *repository, const char *unquoted_directory, int files)
+{
+ int rc;
+ char *directory = quote (unquoted_directory);
+ char *cmd;
+
+ /* Check that the local directory exists. */
+ if (!dry_run && files && unquoted_directory)
+ {
+ struct stat stbuf;
+ char *localdir;
+ size_t len = strlen (repository);
+
+ localdir = malloc (len + 1 + strlen (unquoted_directory) + 1);
+ if (!localdir)
+ {
+ error (0, 0, "not enough memory");
+ return 1;
+ }
+
+ strcpy (localdir, repository);
+ if (localdir[len - 1] != '/')
+ localdir[len++] = '/';
+ strcpy (localdir + len, unquoted_directory);
+
+ /* Check to see that the local unquoted_directory actually exists. */
+ if (stat (localdir, &stbuf) ||
+ (!S_ISDIR (stbuf.st_mode) && (errno = ENOTDIR)))
+ {
+ error (0, errno, "Cannot find `%s'", localdir);
+ return 1;
+ }
+ }
+
+ /* Update from CVS */
+ if (directory && !files)
+ asprintf (&cmd, "cd %s && ( %s -d %s%s checkout %s )",
+ www_directory, cvs_command, access_method ? access_method : "",
+ repository, 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 (0, 0, "Unexpected null directory and files");
+ return 1;
+ }
+
+ 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);
+
+ rc = run_cmd (cmd);
+ free (cmd);
+ return rc;
+}
+
+struct sync_entry
+{
+ char *repo;
+ char *dir;
+ int files;
+};
+
+static int
+dir_cmp (const char *a, const char *b)
+{
+ if (!a)
+ {
+ if (b)
+ return -1;
+ else
+ return 0;
+ }
+ else if (!b)
+ return 1;
+ return strcmp (a, b);
+}
+
+static int
+ent_cmp (const void *a, const void *b)
+{
+ const struct sync_entry *sa = a;
+ const struct sync_entry *sb = b;
+ int rc;
+
+ rc = strcmp (sa->repo, sb->repo);
+ if (rc)
+ return rc;
+ rc = dir_cmp (sa->dir, sb->dir);
+ if (rc)
+ return rc;
+ return sa->files - sb->files;
+}
+
+int
+sync_from_file (uid_t uid, const char *spooldir, const char *file)
+{
+ char *buf = NULL;
+ size_t size = 0;
+ size_t line = 0;
+ char *user, *dir, *filelist;
+ int err = 0;
+ struct obstack stk;
+ struct sync_entry *sp;
+ struct sync_entry ent;
+ struct sync_entry *sync_array;
+ FILE *fp = fopen (file, "r");
+
+ if (!fp)
+ {
+ error (0, errno, "Cannot open file `%s/%s'", spooldir, file);
+ return 1;
+ }
+
+ obstack_init (&stk);
+ while (getline (&buf, &size, fp) > 0)
+ {
+ char *p;
+ struct passwd *pw;
+
+
+ p = buf + strlen (buf) - 1;
+ if (*p == '\n')
+ *p = 0;
+
+ line++;
+
+ p = strtok (buf, " \t");
+ if (!p)
+ {
+ error (0, 0, "%s/%s:%lu: Invalid line",
+ spooldir, file,
+ (unsigned long) line);
+ err = 1;
+ break;
+ }
+
+ pw = getpwnam (p);
+ if (!pw)
+ {
+ error (0, 0, "%s/%s:%lu: cannot get password entry for user %s",
+ spooldir, file,
+ (unsigned long) line, p);
+ err = 1;
+ break;
+ }
+ if (uid != pw->pw_uid)
+ {
+ error (0, 0, "%s/%s:%lu: invalid uid",
+ spooldir, file,
+ (unsigned long) line);
+ err = 1;
+ break;
+ }
+
+ p = strtok (NULL, " \t");
+ if (!p)
+ {
+ error (0, 0, "%s/%s:%lu: Invalid line, second field is missing",
+ spooldir, file,
+ (unsigned long) line);
+ err = 1;
+ break;
+ }
+ ent.repo = strdup (p);
+ if (!ent.repo)
+ {
+ error (0, ENOMEM, "%s/%s:%lu",
+ spooldir, file,
+ (unsigned long) line);
+ err = 1;
+ break;
+ }
+
+ p = strtok (NULL, "");
+ if (!p)
+ {
+ error (0, 0, "%s/%s:%lu: Invalid line, only two fields",
+ spooldir, file,
+ (unsigned long) line);
+ err = 1;
+ break;
+ }
+ err = split_arg (p, &ent.dir, &p);
+ if (err)
+ {
+ error (0, err < 0 ? errno : 0, "%s/%s:%lu: Cannot split argument",
+ spooldir, file,
+ (unsigned long) line);
+ break;
+ }
+ ent.files = has_file_list (p);
+ free (p);
+
+ obstack_grow (&stk, &ent, sizeof (ent));
+ }
+ fclose (fp);
+
+ sync_array = obstack_finish (&stk);
+
+ if (err == 0)
+ {
+ struct sync_entry *sp, *lastp = NULL;
+
+ /* Sort the array */
+ qsort (sync_array, line, sizeof ent, ent_cmp);
+
+ for (sp = sync_array; sp < sync_array + line; sp++)
+ {
+ if (lastp)
+ {
+ if (strcmp (lastp->repo, sp->repo) == 0
+ && dir_cmp (lastp->dir, sp->dir) == 0)
+ continue;
+ }
+ sync_www (sp->repo, sp->dir, sp->files);
+ lastp = sp;
+ }
+ }
+
+ for (sp = sync_array; sp < sync_array + line; sp++)
+ {
+ free (sp->repo);
+ free (sp->dir);
+ }
+
+ obstack_free (&stk, NULL);
+
+ return err;
+}
+
+int
+scan_spool (const char *spooldir)
+{
+ DIR *dir;
+ struct dirent *ent;
+ size_t total_count = 0; /* Total number of jobs seen */
+ size_t success_count = 0; /* Number of successfully processed jobs */
+
+ if (chdir (spooldir))
+ {
+ error (0, errno, "Cannot change to `%s'", spooldir);
+ return 1;
+ }
+
+ dir = opendir (".");
+ if (!dir)
+ {
+ error (0, errno, "Cannot open directory `%s'", spooldir);
+ return 1;
+ }
+
+ while ((ent = readdir (dir)))
+ {
+ struct stat st;
+
+ if (ent->d_name[0] == '.')
+ continue;
+
+ total_count++;
+ if (stat (ent->d_name, &st))
+ {
+ error (0, errno, "Cannot stat file `%s/%s'", spooldir, ent->d_name);
+ continue;
+ }
+
+ if (S_ISREG (st.st_mode))
+ {
+ if (st.st_uid < min_uid || st.st_gid < min_gid)
+ {
+ error (0, 0, "Ignoring file `%s/%s': UID or GID out of range",
+ spooldir, ent->d_name);
+ continue;
+ }
+
+ if (sync_from_file (st.st_uid, spooldir, ent->d_name) == 0)
+ {
+ if (!dry_run && unlink (ent->d_name))
+ error (0, errno, "Cannot unlink file `%s/%s'",
+ spooldir, ent->d_name);
+
+ success_count++;
+ }
+ }
+ }
+
+ printf ("Total files: %lu; Jobs processed: %lu\n",
+ (unsigned long) total_count,
+ (unsigned long) success_count);
+
+ closedir (dir);
+ return 0;
+}
+
+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;
+ char *p;
+ int spooldir_mode;
+ char *user = NULL;
+ char *repository = default_repository;
- while ((i = getopt (argc, argv, "c:hH:m:nr:R:u:w:x")) != EOF)
+ while ((i = getopt (argc, argv, "c:hH:m:nr:R:su:U:G:w:x")) != EOF)
switch (i)
{
case 'c':
cvs_command = optarg;
break;
-
+
case 'H':
hostname = optarg;
break;
@@ -292,6 +661,12 @@ main (int argc, char **argv)
usage ();
exit (0);
+ case 'G':
+ min_gid = strtoul (optarg, &p, 0);
+ if (p)
+ error (1, 0, "invalid numeric value (near %s)", p);
+ break;
+
case 'm':
access_method = optarg;
break;
@@ -308,6 +683,16 @@ main (int argc, char **argv)
repository = optarg;
break;
+ case 's':
+ spooldir_mode = 1;
+ break;
+
+ case 'U':
+ min_uid = strtoul (optarg, &p, 0);
+ if (p)
+ error (1, 0, "invalid numeric value (near %s)", p);
+ break;
+
case 'u':
user = optarg;
break;
@@ -355,101 +740,43 @@ main (int argc, char **argv)
if (setreuid (uid, uid))
error (1, errno, "cannot assume effective user id");
- if (argc == 1)
+ if (spooldir_mode)
{
- 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++;
- }
+ char *spooldir = NULL;
+
+ if (argc > 1)
+ error (1, 0, "Extra arguments in scan mode");
+ else if (argc == 1)
+ spooldir = argv[0];
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");
+ spooldir = default_spooldir;
- 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.
- */
+ return scan_spool (spooldir);
}
else
{
- /*
- * There are files to collect in the argument list.
- */
- files = 1;
- }
-
- /* Check that the local directory exists. */
- if (!dry_run && files && 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);
+ char *unquoted_directory = NULL;
+ char *tmp;
+
+ if (argc == 1)
+ {
+ int rc = split_arg (argv[0], &unquoted_directory, &tmp);
+ if (rc < 0)
+ error (1, errno, "Cannot split argument");
+ else if (rc > 0)
+ 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");
- /* 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);
+ return sync_www (repository, unquoted_directory, has_file_list (tmp));
}
-
- /* Update from CVS */
- if (directory && !files)
- asprintf (&cmd, "cd %s && ( %s -d %s%s checkout %s )",
- www_directory, cvs_command, access_method ? access_method : "",
- repository, 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);
+ /*NOTREACHED*/
+ return 0;
}

Return to:

Send suggestions and report system problems to the System administrator.