Beispiel #1
0
/*
 * callback proc to run a script when admin finishes.
 */
static int
postadmin_proc (const char *repository, const char *filter, void *closure)
{
    char *cmdline;
    const char *srepos = Short_Repository (repository);

    TRACE (TRACE_FUNCTION, "postadmin_proc (%s, %s)", repository, filter);

    /* %c = cvs_cmd_name
     * %R = referrer
     * %p = shortrepos
     * %r = repository
     */
    /*
     * Cast any NULL arguments as appropriate pointers as this is an
     * stdarg function and we need to be certain the caller gets what
     * is expected.
     */
    cmdline = format_cmdline (
#ifdef SUPPORT_OLD_INFO_FMT_STRINGS
	                      false, srepos,
#endif /* SUPPORT_OLD_INFO_FMT_STRINGS */
	                      filter,
	                      "c", "s", cvs_cmd_name,
#ifdef SERVER_SUPPORT
	                      "R", "s", referrer ? referrer->original : "NONE",
#endif /* SERVER_SUPPORT */
	                      "p", "s", srepos,
	                      "r", "s", current_parsed_root->directory,
	                      (char *) NULL);

    if (!cmdline || !strlen (cmdline))
    {
	if (cmdline) free (cmdline);
	error (0, 0, "postadmin proc resolved to the empty string!");
	return 1;
    }

    run_setup (cmdline);

    free (cmdline);

    /* FIXME - read the comment in verifymsg_proc() about why we use abs()
     * below() and shouldn't.
     */
    return abs (run_exec (RUN_TTY, RUN_TTY, RUN_TTY,
			  RUN_NORMAL | RUN_SIGIGNORE));
}
Beispiel #2
0
/*
 * Uses setup_tmpfile() to pass the updated message on directly to any
 * logfile programs that have a regular expression match for the checked in
 * directory in the source repository.  The log information is fed into the
 * specified program as standard input.
 */
void Update_Logfile (const char *repository, const char *xmessage, FILE *xlogfp, List *xchanges, const char *xbugid)
{
    /* nothing to do if the list is empty */
    if (xchanges == NULL || xchanges->list->next == xchanges->list)
		return;

	loginfo_param_t args;
	args.message = xmessage;
	args.logfp=xlogfp;
	args.changes=xchanges;
	args.directory=Short_Repository(repository);

    /* call Parse_Info to do the actual logfile updates */
	TRACE(3,"run loginfo trigger");
	run_trigger(&args, update_logfile_proc);
}
Beispiel #3
0
/* An empty LogHistory string in CVSROOT/config will turn logging off.
 */
void
history_write (int type, const char *update_dir, const char *revs,
               const char *name, const char *repository)
{
    const char *fname;
    char *workdir;
    char *username = getcaller ();
    int fd;
    char *line;
    char *slash = "", *cp;
    const char *cp2, *repos;
    int i;
    static char *tilde = "";
    static char *PrCurDir = NULL;
    time_t now;

    if (logoff)			/* History is turned off by noexec or
				 * readonlyfs.
				 */
	return;
    if (!strchr (config->logHistory, type))	
	return;

    if (nolock)
	goto out;

    repos = Short_Repository (repository);

    if (!PrCurDir)
    {
	char *pwdir;

	pwdir = get_homedir ();
	PrCurDir = CurDir;
	if (pwdir != NULL)
	{
	    /* Assumes neither CurDir nor pwdir ends in '/' */
	    i = strlen (pwdir);
	    if (!strncmp (CurDir, pwdir, i))
	    {
		PrCurDir += i;		/* Point to '/' separator */
		tilde = "~";
	    }
	    else
	    {
		/* Try harder to find a "homedir" */
		struct saved_cwd cwd;
		char *homedir;

		if (save_cwd (&cwd))
		    error (1, errno, "Failed to save current directory.");

		if (CVS_CHDIR (pwdir) < 0 || (homedir = xgetcwd ()) == NULL)
		    homedir = pwdir;

		if (restore_cwd (&cwd))
		    error (1, errno,
		           "Failed to restore current directory, `%s'.",
		           cwd.name);
		free_cwd (&cwd);

		i = strlen (homedir);
		if (!strncmp (CurDir, homedir, i))
		{
		    PrCurDir += i;	/* Point to '/' separator */
		    tilde = "~";
		}

		if (homedir != pwdir)
		    free (homedir);
	    }
	}
    }

    if (type == 'T')
    {
	repos = update_dir;
	update_dir = "";
    }
    else if (update_dir && *update_dir)
	slash = "/";
    else
	update_dir = "";

    workdir = Xasprintf ("%s%s%s%s", tilde, PrCurDir, slash, update_dir);

    /*
     * "workdir" is the directory where the file "name" is. ("^~" == $HOME)
     * "repos"	is the Repository, relative to $CVSROOT where the RCS file is.
     *
     * "$workdir/$name" is the working file name.
     * "$CVSROOT/$repos/$name,v" is the RCS file in the Repository.
     *
     * First, note that the history format was intended to save space, not
     * to be human readable.
     *
     * The working file directory ("workdir") and the Repository ("repos")
     * usually end with the same one or more directory elements.  To avoid
     * duplication (and save space), the "workdir" field ends with
     * an integer offset into the "repos" field.  This offset indicates the
     * beginning of the "tail" of "repos", after which all characters are
     * duplicates.
     *
     * In other words, if the "workdir" field has a '*' (a very stupid thing
     * to put in a filename) in it, then every thing following the last '*'
     * is a hex offset into "repos" of the first character from "repos" to
     * append to "workdir" to finish the pathname.
     *
     * It might be easier to look at an example:
     *
     *  M273b3463|dgg|~/work*9|usr/local/cvs/examples|1.2|loginfo
     *
     * Indicates that the workdir is really "~/work/cvs/examples", saving
     * 10 characters, where "~/work*d" would save 6 characters and mean that
     * the workdir is really "~/work/examples".  It will mean more on
     * directories like: usr/local/gnu/emacs/dist-19.17/lisp/term
     *
     * "workdir" is always an absolute pathname (~/xxx is an absolute path)
     * "repos" is always a relative pathname.  So we can assume that we will
     * never run into the top of "workdir" -- there will always be a '/' or
     * a '~' at the head of "workdir" that is not matched by anything in
     * "repos".  On the other hand, we *can* run off the top of "repos".
     *
     * Only "compress" if we save characters.
     */

    cp = workdir + strlen (workdir) - 1;
    cp2 = repos + strlen (repos) - 1;
    for (i = 0; cp2 >= repos && cp > workdir && *cp == *cp2--; cp--)
	i++;

    if (i > 2)
    {
	i = strlen (repos) - i;
	(void) sprintf ((cp + 1), "*%x", i);
    }

    if (!revs)
	revs = "";
    now = time (NULL);
    line = Xasprintf ("%c%08lx|%s|%s|%s|%s|%s\n", type, (long) now,
		      username, workdir, repos, revs, name);

    fname = get_history_log_name (now);

    if (!history_lock (current_parsed_root->directory))
	/* history_lock() will already have printed an error on failure.  */
	goto out;

    fd = CVS_OPEN (fname, O_WRONLY | O_APPEND | O_CREAT | OPEN_BINARY, 0666);
    if (fd < 0)
    {
	if (!really_quiet)
            error (0, errno,
		   "warning: cannot open history file `%s' for write", fname);
        goto out;
    }

    TRACE (TRACE_FUNCTION, "open (`%s', a)", fname);

    /* Lessen some race conditions on non-Posix-compliant hosts.
     *
     * FIXME:  I'm guessing the following was necessary for NFS when multiple
     * simultaneous writes to the same file are possible, since NFS does not
     * natively support append mode and it must be emulated via lseek().  Now
     * that the history file is locked for write, the following lseek() may be
     * unnecessary.
     */
    if (lseek (fd, (off_t) 0, SEEK_END) == -1)
	error (1, errno, "cannot seek to end of history file: %s", fname);

    if (write (fd, line, strlen (line)) < 0)
	error (1, errno, "cannot write to history file: %s", fname);
    free (line);
    if (close (fd) != 0)
	error (1, errno, "cannot close history file: %s", fname);
    free (workdir);
 out:
    clear_history_lock ();
}
Beispiel #4
0
/* Runs the user-defined verification script as part of the commit or import 
   process.  This verification is meant to be run whether or not the user 
   included the -m atribute.  unlike the do_editor function, this is 
   independant of the running of an editor for getting a message.
 */
int do_verify (char **message, const char *repository)
{
    FILE *fp;
    char *fname = NULL;
    int retcode = 0;
    int len;
	char boughtsuite[100];

    if (current_parsed_root->isremote)
	/* The verification will happen on the server.  */
		return 0;

    /* FIXME? Do we really want to skip this on noexec?  What do we do
       for the other administrative files?  */
    if (noexec)
		return 0;
	
    /* If there's no message, then we have nothing to verify.  Can this
       case happen?  And if so why would we print a message?  */
    if (message == NULL)
    {
		cvs_output ("No message to verify\n", 0);
		return 0;
    }

    /* open a temporary file, write the message to the 
       temp file, and close the file.  */

    if ((fp = cvs_temp_file (&fname)) == NULL)
		error (1, errno, "cannot create temporary file %s", fname);
    else
    {
		fprintf (fp, "%s", *message);
		if ((*message)[0] == '\0' ||
			(*message)[strlen (*message) - 1] != '\n')
			fprintf (fp, "%s", "\n");
		if (fclose (fp) == EOF)
			error (1, errno, "%s", fname);

		/* Run the verify message  */

		if (repository != NULL)
		{
			verifymsg_param_t args;
			args.directory = Short_Repository(repository);
			args.filename = fname;

			TRACE(3,"run verifymsg trigger");
			retcode = run_trigger(&args,verifymsg_proc);

			if(!retcode && reread_log_after_verify)
			{
				if((fp=fopen(fname,"r"))==NULL)
					error(1, errno, "Couldn't reread message file");
				fseek(fp,0,SEEK_END);
				len = ftell(fp);
				fseek(fp,0,SEEK_SET);
				*message = (char*)xmalloc(len+1);
				if(!*message)
					error(1,errno,"Out of memory rereading message");
				len = fread(*message, 1, len, fp);
				if(len < 0)
					error(1, errno, "Couldn't reread message file");
				(*message)[len]='\0';
				if (fclose (fp) == EOF)
	    				error (1, errno, "Couldn't close %s", fname);
			}
#ifdef _WIN32
#if (CVSNT_SPECIAL_BUILD_FLAG != 0)
	if (strcasecmp(CVSNT_SPECIAL_BUILD,"Suite")!=0)
#endif
	{
			if (message!=NULL)
			{
				if (strstr(*message,"Committed on the Free edition of March Hare Software CVSNT")==NULL)
				{
				    if(CGlobalSettings::GetGlobalValue("cvsnt","PServer","HaveBoughtSuite",boughtsuite,sizeof(boughtsuite)))
						strcpy(boughtsuite,"no");
					if (strcasecmp(boughtsuite,"yes"))
					{
						len=strlen(*message);
						*message=(char *)xrealloc(*message,len+400);
						strcat(*message,"\nCommitted on the Free edition of March Hare Software CVSNT Server.\nUpgrade to CVS Suite for more features and support:\nhttp://march-hare.com/cvsnt/");
					}
				}
			}
    }
#endif
		}

		/* Delete the temp file  */

		if (unlink_file (fname) < 0)
			error (0, errno, "cannot remove %s", fname);
		xfree (fname);
    }
	return retcode;
}
Beispiel #5
0
/*
 * Builds a temporary file using setup_tmpfile() and invokes the user's
 * editor on the file.  The header garbage in the resultant file is then
 * stripped and the log message is stored in the "message" argument.
 * 
 * If REPOSITORY is non-NULL, process rcsinfo for that repository; if it
 * is NULL, use the CVSADM_TEMPLATE file instead.
 */
void do_editor (const char *dir, char **messagep, const char *repository, List *changes)
{
    static int reuse_log_message = 0;
    char *line;
    int line_length;
    size_t line_chars_allocated;
    char *fname;
    struct stat pre_stbuf, post_stbuf;
    int retcode = 0;

    if (noexec || reuse_log_message)
		return;

    /* Abort creation of temp file if no editor is defined */
    if (strcmp (Editor, "") == 0)
		error(1, 0, "no editor defined, must use -e or -m");

	/* Create a temporary file */
	/* FIXME - It's possible we should be relying on cvs_temp_file to open
	* the file here - we get race conditions otherwise.
	*/
	fname = cvs_temp_name ();
again:
	if ((fp = CVS_FOPEN (fname, "w+")) == NULL)
		error (1, 0, "cannot create temporary file %s", fname);

	if (*messagep)
	{
		fprintf (fp, "%s", *messagep);

		if ((*messagep)[0] == '\0' ||
			(*messagep)[strlen (*messagep) - 1] != '\n')
			fprintf (fp, "\n");
	}

    if (repository != NULL)
	{
		rcsinfo_param_t args;
		args.directory = Short_Repository(repository);
		args.message=NULL;
		/* tack templates on if necessary */
		TRACE(3,"run rcsinfo trigger");
		if(!run_trigger(&args,rcsinfo_proc) && args.message)
		{
			fprintf(fp,"%s",args.message);
			if (args.message[0] == '\0' || args.message[strlen(args.message) - 1] != '\n')
				fprintf (fp, "\n");
		}
	}
    else
    {
		FILE *tfp;
		char buf[1024];
		size_t n;
		size_t nwrite;

		/* Why "b"?  */
		tfp = CVS_FOPEN (CVSADM_TEMPLATE, "rb");
		if (tfp == NULL)
		{
			if (!existence_error (errno))
			error (1, errno, "cannot read %s", CVSADM_TEMPLATE);
		}
		else
		{
			while (!feof (tfp))
			{
				char *p = buf;
				n = fread (buf, 1, sizeof buf, tfp);
				nwrite = n;
				while (nwrite > 0)
				{
					n = fwrite (p, 1, nwrite, fp);
					nwrite -= n;
					p += n;
				}
				if (ferror (tfp))
					error (1, errno, "cannot read %s", CVSADM_TEMPLATE);
			}
			if (fclose (tfp) < 0)
				error (0, errno, "cannot close %s", CVSADM_TEMPLATE);
		}
	}

	fprintf (fp,"%s----------------------------------------------------------------------\n",CVSEDITPREFIX);
#ifdef _WIN32
#if (CVSNT_SPECIAL_BUILD_FLAG != 0)
	if (strcasecmp(CVSNT_SPECIAL_BUILD,"Suite")!=0)
#endif
	{
	fprintf (fp,"%s     Committed on the Free edition of March Hare Software CVSNT Server\n",CVSEDITPREFIX);
	fprintf (fp,"%s     Upgrade to CVS Suite for more features and support:\n",CVSEDITPREFIX);
	fprintf (fp,"%s           http://march-hare.com/cvsnt/\n",CVSEDITPREFIX);
	fprintf (fp,"%s----------------------------------------------------------------------\n",CVSEDITPREFIX);
    }
#endif
	fprintf (fp,"%sEnter Log.  Lines beginning with `%.*s' are removed automatically\n%s\n",
				CVSEDITPREFIX, CVSEDITPREFIXLEN, CVSEDITPREFIX,CVSEDITPREFIX);
	if (dir != NULL && *dir)
		fprintf (fp, "%sCommitting in %s\n%s\n", CVSEDITPREFIX,	dir, CVSEDITPREFIX);
	if (changes != NULL)
		setup_tmpfile (fp, CVSEDITPREFIX, changes);
	fprintf (fp,"%s----------------------------------------------------------------------\n",
				CVSEDITPREFIX);

	/* finish off the temp file */
	if (fclose (fp) == EOF)
		error (1, errno, "%s", fname);
	if ( CVS_STAT (fname, &pre_stbuf) == -1)
		pre_stbuf.st_mtime = 0;

	/* run the editor */
	run_setup (Editor);
	run_arg (fname);
	if ((retcode = run_exec (true)) != 0)
		error (0, retcode == -1 ? errno : 0, "warning: editor session failed");

	/* put the entire message back into the *messagep variable */
	fp = open_file (fname, "r");

	if (*messagep)
		xfree (*messagep);

	if ( CVS_STAT (fname, &post_stbuf) != 0)
		error (1, errno, "cannot find size of temp file %s", fname);

	if (post_stbuf.st_size == 0)
		*messagep = NULL;
	else
	{
		/* On NT, we might read less than st_size bytes, but we won't
		read more.  So this works.  */
		*messagep = (char *) xmalloc (post_stbuf.st_size + 1);
 		*messagep[0] = '\0';
	}

	line = NULL;
	line_chars_allocated = 0;

	if (*messagep)
	{
		size_t message_len = post_stbuf.st_size + 1;
		size_t offset = 0;
		while (1)
		{
			line_length = getline (&line, &line_chars_allocated, fp);
			if (line_length == -1)
			{
				if (ferror (fp))
					error (0, errno, "warning: cannot read %s", fname);
				break;
			}
			if (strncmp (line, CVSEDITPREFIX, CVSEDITPREFIXLEN) == 0)
				continue;
			if (offset + line_length >= message_len)
			expand_string (messagep, &message_len, offset + line_length + 1);
			strcpy (*messagep + offset, line);
			offset += line_length;
		}
	}
	
	if (fclose (fp) < 0)
		error (0, errno, "warning: cannot close %s", fname);

	if (pre_stbuf.st_mtime == post_stbuf.st_mtime || *messagep == NULL || strcmp (*messagep, "\n") == 0)
	{
		for (;;)
		{
			printf ("\nLog message unchanged or not specified\n");
			printf ("a)bort, c)ontinue, e)dit, !)reuse this message unchanged for remaining dirs\n");
			printf ("Action: (continue) ");
			fflush (stderr);
			fflush (stdout);
			line_length = getline (&line, &line_chars_allocated, stdin);
			if (line_length < 0)
			{
				error (0, errno, "cannot read from stdin");
				if (unlink_file (fname) < 0)
					error (0, errno, "warning: cannot remove temp file %s", fname);
				error (1, 0, "aborting");
			}
			else if (line_length == 0 || *line == '\n' || *line == 'c' || *line == 'C')
				break;
			if (*line == 'a' || *line == 'A')
			{
				if (unlink_file (fname) < 0)
					error (0, errno, "warning: cannot remove temp file %s", fname);
				error (1, 0, "aborted by user");
			}
			if (*line == 'e' || *line == 'E')
				goto again;
			if (*line == '!')
			{
				reuse_log_message = 1;
				break;
			}
			printf ("Unknown input\n");
		}
    }
    if (line)
		xfree (line);
    if (unlink_file (fname) < 0)
		error (0, errno, "warning: cannot remove temp file %s", fname);
    xfree (fname);
}