Node:Top, Next:, Previous:(dir), Up:(dir)


This document is for Savannah administrators, not Savannah users. Savannah is a SourceForge clone based on the SourceForge-2.0 software. It is dedicated the GNU projects.

Because of the highly specific nature of the software, Savannah is a fork of the SourceForge-2.0 software. Attempting to make it modular and configurable is a waste of time. The whole Savannah software is available from CVS and is managed by the Savannah project. The ChangeLog explains the modifications made to the original code.

Node:Introduction, Next:, Previous:Top, Up:Top


Savannah currently provides the CVS frontend. Check the Task List for details on planned developments.

Setting up Savannah is not an easy task because it has to integrate existing habits and projects without breaking anything. However, the SourceForge Installation Guide by Guillaume Morin helps a lot understanding the software.

Node:Installation, Next:, Previous:Introduction, Up:Top


Savannah is installed on the machine The root of the installation is in /subversions/sourceforge. All the software that is not system wide and is needed to run Savannah is installed in this directory. The structure of this directory is similar to FHS-2.1. In the following table the path names are relative to the installation root. All directories covered by the SourceForge Installation Guide are omitted.

Directory to run the backend scripts.
The SourceForge software and the Savannah web pages.
The document root of Savannah web pages.
Savannah specific backend scripts.

The whole Savannah software is available from CVS and is managed by the Savannah project.

In order to install changes commmited in the savannah project CVS tree, proceed as follows:

login subversions
su -
export CVS_RSH=ssh
cd /subversions/sourceforge/src/savannah
cvs -q update

Node:CVS repositories, Next:, Previous:Installation, Up:Top

CVS repositories

For each project registered on Savannah there may be two CVS repositories. One to store the sources of the project and one to store the web of the project. The sources repository is in /subversions/cvs/software and the web repository is in /subversions/cvs/gnuweb. The /webcvs symbolic link points to /subversions/cvs/gnuweb and the /cvsroot symbolic link points to /subversions/cvs/software.

Node:Import repositories, Next:, Previous:CVS repositories, Up:CVS repositories

Import repositories

Existing projects that migrate to Savannah may want their CVS repository to be transfered to subversions. Time is essential for such an operation since the project contributors want to work on the new repository on subversions and stop using the old. When the author asks, ask him to send the tarbal by mail or send a URL from which it can be downloaded. Make an appointement with him and guarantee that the repository will be untared on subversions with 24 hours maximum. The project contributor must first create a project on subversions. When you have the tarbal untar it at /cvsroot/project. Make sure it does not contain a CVSROOT that would override the existing CVSROOT. If it does manualy copy the history and val-tags files only. Make sure the imported repository is untared under /cvsroot/project/project and does not polute the root of the repository.

Node:Sources CVS repositories, Next:, Previous:Import repositories, Up:CVS repositories

Sources CVS repositories

When a project has a license that is not website a source repository is created under /subversions/cvs/software/project with a private CVSROOT that only contains anoncvs. The developers of the project have access to the CVSROOT directory.

The group project is created to grant write access to the repository to all the members of the project.

When a Savannah project is assigned the website license, it only has a portion of the webcvs repository and no source CVS repository.

If the html_cvs field for a given Savannah project is empty, it is not associated with a part of the webcvs repository.

It allows them to add commit notification by doing the following, replacing project with the name of their project:

cvs -d co CVSROOT

In CVSROOT/commitinfo

^project /usr/local/bin/commit_prep -T project -r

In CVSROOT/loginfo

^project /usr/local/bin/log_accum -T project -C -m -s %{sVv}

The email address must exist, it will not be automatically generated.

For compatibility with the cvs setup before Savannah was introduced, /subversions/cvs/common contains repositories that existed before Savannah. When a project is registered in Savannah, a symbolic link is created (/subversions/cvs/software/project/project) that points to the already existing /subversions/cvs/common/project directory.

The /cvs symbolic link points to /subversions/cvs/common so that people already using it to access their repositories can continue to do so. Before Savannah existed a pserver access was available and Savannah continues to maintain it for these projects, updating the CVSROOT/passwd files with user/password pairs that are in the Savannah database.

Node:Web CVS repositories, Next:, Previous:Sources CVS repositories, Up:CVS repositories

Web CVS repositories

When a project has an html_cvs field that is not empty in the group table, a web repository is created in /subversions/cvs/gnuweb/html_cvs. By default the html_cvs field has the value /software/project/ but it may be edited with the See the gnujobs, greve and bravegw projects for examples.

When a Savannah project is assigned the website license, it only has a portion of the webcvs repository and no source CVS repository.

If the html_cvs field for a given Savannah project is empty, it is not associated with a part of the webcvs repository.

The group webproject is created to grant write access to the repository to all the members of the project.

All the web was imported in /subversions/cvs/gnuweb. When a project is registered on Savannah and there already exists a directory for it in the repository (either .../software/project or the value of the html_cvs field), a chgrp -R webproject is done on this repository to grant the members of the project a write access to this portion of the web repository and only this one.

The www project in Savannah is treated in a special way. All the members of the www project have access to the whole repository in /webcvs. It means that they are always included in every webproject created.

Node:Web CVS Symbolic links, Next:, Previous:Web CVS repositories, Up:CVS repositories

Web CVS Symbolic links

Since CVS is not able to handle symbolic links, a simple mechanism has been implemented on the machine hosting the to allow webmasters to control the symbolic link from the CVS tree.

The special file .symlinks contains a list of file name pairs, one per line. For instance:

foo.html index.html
bar.html other.html

is a valid .symlinks file. Every night a script reads all the .symlinks files, prepend a ln -s in front of each line and execute them. Well, in reality it's not that simple but you get the idea. The .symlinks file can only be used to control the symbolic link in the directory where they are. File names with / will be ignored.

Node:Web CVS top level directories, Next:, Previous:Web CVS Symbolic links, Up:CVS repositories

Web CVS top level directories

The /subversions/cvs/gnuweb/CVSROOT/loginfo file contains triggers that update the directory whenever a commit is done. There is a single CVSROOT for all the projects that have a web repository.

When a top level directory is added in the webcvs repository a line must be added in the loginfo file to run webcvs whenever something is changed in this directory. This must be done manualy. Hopefully adding new top level directories is not a frequent operation and adding this is not too much overhead for the Savannah maintainer.

Node:Web CVS and Projects, Previous:Web CVS top level directories, Up:CVS repositories

Web CVS and Projects

The special project www have write access to all the /webcvs repository. It is possible to create projects that will limit write access of the members of the project to a subdirectory of the /webcvs repository only. For instance the bravegw Savannah project only give write access to the /webcvs/brave-gnu-world part of the repository.

A project bound to a specific subdirectory will grante write access to all the tree under this subdirectory. There is no way, for instance, to grant write access to group B to /webcvs/thispart and write access to group A to /webcvs/thispart/subdir. If you do this group B win and will have write access to /webcvs/thispart recursively and group A will have access to nothing. If you see a way to overcome this limitation, let us know.

The sf_www script generates the map that is published at to Savannah. It writes the file in /subversions/sourceforge/src/server/standards and commits it. The server/standards directory is a read-write checkout of the web CVS. The sf_www script is run once a day by the crontab.

A more webmaster oriented documentation explains the organisation of the CVS tree and the rationale of its usage.

Node:Database, Next:, Previous:CVS repositories, Up:Top


Savannah uses MySQL and the sourceforge database. The root user has a ~/.my.cnf file that defines the user/passwd. It is not necessary to specify them on the command line.

Node:Skill List, Previous:Database, Up:Database

Skill List

The tables people_skill and people_skill_level are loaded from the skill database maintained by CJN ( The script /subversions/sourceforge/src/savannah/gnuscripts/sf_skill loads the XML skill files from CJN and replace the content of the tables in the sourceforge database.

If some proprietary software shows on the skill list, add it to the %ignore table in the sf_skill script and re-run it.

cd /subversions/sourceforge/src/savannah/gnuscripts
edit sf_skill
cvs commit -m 'Ignore proprietary software xxxx'

Node:Mails and aliases, Next:, Previous:Database, Up:Top

Mails and aliases

Savannah will try to send mail to users under various circumstances (bug reports notification, account creation etc.). In some cases it will use the real mail address of the user, in others it will use In order for the address to work properly for outgoing mails, the /etc/aliases file is updated automatically every 5 minutes with the following command:


The can never be used to recieve mail for the good reason that does not listen on the SMTP port.

Node:Unlock alias at account, Next:, Previous:Mails and aliases, Up:Top

Unlock alias at account

People who have a simple alias but no account on Kerberos cannot create an account on Savannah. When they ask to unlock the account name on, tell them to create an account using a fake username and to send this username to When receiving that user name substitute the fake login name by the desired one:

mysql -e "update user set user_name = 'desired' where user_name = 'fake'" sourceforge

Node:Migration, Next:, Previous:Unlock alias at account, Up:Top


Must be root to run this script. You are advised to run it in /subversions/sourceforge/tmp, although it is not mandatory.

The sf_migrate script creates a Savannah project for an existing project in the /subversions/cvs/common directory. It is done in three steps:

Will create the project in Savannah if it does not yet exists. It will also scan the /etc/passwd and /etc/group files and create users in Savannah.

When explaining the situation to a user added to Savannah in this way, one could say it like this. If you have a Kerberos account on, use the same login and password on Savannah and change the password immediately afterwards: it will not change your Kerberos password, just the Savannah password. If you only have a pserver account, use the same login and password on Savannah. If you have both, use the Kerberos account login and password. If you have none and access CVS using SSH public keys, ask to to give you a password. This last case requires human interaction to prevent someone from stealing your account name.

Will bind the users created to the project. This cannot be done in one step (-add -bind) because of unique identifiers allocations by the MySQL database.
Generate a Emacs-VM formated mail ready to send to all contributors of the project that explains what has been done.

When a user with SSH access thru public key was added by sf_migrate, she/he will be instructed to ask for a password to The sf_pass script can be used to set her/his password. A mail must be sent by the user requesting the password with the encrypted password. Instruct the user to generate the password using the following command:

perl -e 'print crypt("mypassword",
  join "", (".", "/", 0..9, "A".."Z", "a".."z")[rand 64, rand 64])'

When the user send the password crypted, set it using:

sf_pass --set thename cryptpass | mysql sourceforge

After 24 hours, check user logged in and lock the user if it is not the case. This is to prevent obvious holes.

sf_pass --unset thename | mysql sourceforge

Node:Users and CVS synchronization, Next:, Previous:Migration, Up:Top

Users and CVS synchronization

Must be root to run this script. Must export CVS_RSH=ssh. You are advised to run it in /subversions/sourceforge/tmp, although it is not mandatory.

The sf_cvs script generates a shell script that will synchronize the system files with the state of the Savannah database (sourceforge).

This script only generates lines if something needs to be done. When the resulting script is executed, another run must not display any action, unless the database was modified in the meantime.

It performs the following tasks, in this order.

Add new projects
/subversions/cvs/software/project and /subversions/cvs/gnuweb/software/project directories are created.
Update existing projects
Fixes projects that do not have a /subversions/cvs/gnuweb/software/project directory.
Add missing users
Create users that are bound to at least a project in Savannah.
Remove users
Remove users not bound to any project in Savannah If user belongs to groups not managed by Savannah, just redefine its group list.
Update existing users
Modify the system files if they are not in sync with the Savannah database.
Update the CVS password file
Update the /subversions/cvs/CVSROOT/passwd file to reflect the passwd and users bound to projects in Savannah.
Update xinetd.conf
Update the cvspserver/cvskserver with all possible pserver roots

Node:Publishing this document, Next:, Previous:Users and CVS synchronization, Up:Top

Publishing this document

The HTML version of this document is published in two places: Savannah Administration Guide and Savannah Administration Guide. The source is stored in the co gnudocs repository. To facilitate the publication process you can edit it in the directory and then issue a

make publish

The publish goal assumes that the Savannah document root is in ../savannah/www and a read-write checkout of the directory is in ../server/standards. It will format the document to HTML and commit the changes to the repository.

Node:System Administration, Next:, Previous:Publishing this document, Up:Top

System Administration

Node:SSL certificate, Next:, Previous:System Administration, Up:System Administration

SSL certificate

The SSL certificate for was generated in /etc/apache-ssl/. Check the README file for a log of the command. There has been a lot of discussions regarding the root certificate for GNU, the use of a PKI. At some point the savannah certificate will be generated using a proper root certificate.

Node:Savannah crontab, Next:, Previous:SSL certificate, Up:System Administration

Savannah crontab

The Savannah crontab jobs are in /etc/cron.d/savannah. Every cron command output is sent to

*/5 * * * *	root	sf_aliases
10 4 * * *	root	sf_www
17 * * * *	root	cd /subversions/sourceforge/tmp ; sf_cvs | ( date ; sh -x ) >> /var/log/sf_cvs.log 2>&1

Node:Savannah software root, Next:, Previous:Savannah crontab, Up:System Administration

Savannah software root

All software that is not system wide but only used for the purpose of Savannah must be installed in the prefix /subversions/sourceforge.

The MySQL installation is an exception that must be fixed. It is installed with the /usr/local/mysql prefix. It was not installed from the debian package because I ( was not able to fix the MySQL-3.23 package to make it work on potato.

Node:NGROUPS_MAX, Previous:Savannah software root, Up:System Administration


The large number of groups a user can have (>32) implies to modify some basic programs (namely useradd and usermod).

Gordon Matzigkeit <> modified /usr/local/src/cvs-1.10.8/src/server.c to overcome the limit builtin glibc. He also installed the /boot/vmlinuz-2.2.12-256groups kernel that was compiled with NGROUPS_MAX set to 256. The system files have not been reinstalled to match this version.

Here is the patch applied to /usr/local/src/shadow-19990827. The modified usermod and useradd have been installed in /subversions/sourceforge/bin.

*** ./debian/rules.~1~	Fri Feb  9 02:05:06 2001
--- ./debian/rules	Fri Feb  9 02:05:41 2001
*** 38,44 ****
  ifneq ($(DEB_HOST_GNU_SYSTEM),gnu)
    include debian/scripts/
    package-list += binary-login
!   config_options += --with-libpam
    control_defs += -DMODDEP="(>= 0.72-5)"

--- 38,44 ----
  ifneq ($(DEB_HOST_GNU_SYSTEM),gnu)
    include debian/scripts/
    package-list += binary-login
! #  config_options += --with-libpam
    control_defs += -DMODDEP="(>= 0.72-5)"

*** ./build-tree/shadow-19990827/libmisc/addgrps.c.~1~	Mon Dec 28 12:34:41 1998
--- ./build-tree/shadow-19990827/libmisc/addgrps.c	Fri Feb  9 03:04:47 2001
*** 20,25 ****
--- 20,28 ----
   * already there.  Warning: uses strtok().

+ #undef NGROUPS_MAX
+ #define NGROUPS_MAX 256
  add_groups(const char *list)
*** ./build-tree/shadow-19990827/src/usermod.c.~1~	Fri Jul  9 09:27:38 1999
--- ./build-tree/shadow-19990827/src/usermod.c	Fri Feb  9 03:05:52 2001
*** 74,79 ****
--- 74,82 ----

  #define	VALID(s)	(strcspn (s, ":\n") == strlen (s))

+ #undef NGROUPS_MAX
+ #define NGROUPS_MAX 256
  static char *user_name;
  static char *user_newname;
  static char *user_pass;
*** ./build-tree/shadow-19990827/src/groups.c.~1~	Mon Jun  7 09:40:45 1999
--- ./build-tree/shadow-19990827/src/groups.c	Fri Feb  9 03:15:54 2001
*** 42,47 ****
--- 42,50 ----
  static void print_groups P_((const char *));
  int main P_((int, char **));

+ #undef NGROUPS_MAX
+ #define NGROUPS_MAX 256
   * print_groups - print the groups which the named user is a member of
*** ./build-tree/shadow-19990827/src/id.c.~1~	Mon Jun  7 09:40:45 1999
--- ./build-tree/shadow-19990827/src/id.c	Fri Feb  9 03:16:34 2001
*** 50,55 ****
--- 50,58 ----
  static void usage P_((void));
  int main P_((int, char **));

+ #undef NGROUPS_MAX
+ #define NGROUPS_MAX 256
  static void
*** ./build-tree/shadow-19990827/src/useradd.c.~1~	Fri Feb  9 02:06:01 2001
--- ./build-tree/shadow-19990827/src/useradd.c	Fri Feb  9 03:28:52 2001
*** 53,58 ****
--- 53,61 ----
  #include "faillog.h"

+ #undef NGROUPS_MAX
+ #define NGROUPS_MAX 256
  #ifndef SKEL_DIR
  #define SKEL_DIR "/etc/skel"
*** ./build-tree/shadow-19990827/src/newgrp.c.~1~	Fri Feb  9 02:06:00 2001
--- ./build-tree/shadow-19990827/src/newgrp.c	Fri Feb  9 03:29:10 2001
*** 49,54 ****
--- 49,57 ----
  static GETGROUPS_T *grouplist;

+ #undef NGROUPS_MAX
+ #define NGROUPS_MAX 256
  static char *Prog;
  static int is_newgrp;

The sshd daemon has been rebuilt with the following patch so that CVS ssh operations have the proper set of groups. The sources are in /usr/local/src/openssh-1.2.3/ and the corresponding debian package is at /usr/local/src/ssh_1.2.3-9.2loic_i386.deb. The package was tagged on hold using dselect to prevent accidental upgrade. Note that this patch may have hideous impact for users who have real account and use ssh since most of the commands that deal with groups have not been recompiled to handle more than the limit of 32 groups. For instance the id command will core dump. Here is the patch applied on the distribution:

*** sshd.c.~1~	Fri Mar 17 04:40:18 2000
--- sshd.c	Tue Feb 13 06:32:17 2001
*** 147,152 ****
--- 151,240 ----
  	      const char *display, const char *auth_proto,
  	      const char *auth_data, const char *ttyname);

+ #include <shadow.h>
+ #endif
+ #endif /* AUTH_SERVER_SUPPORT */
+ /* The GNU C Library currently has a compile-time limit on the number of
+    groups a user may be a part of, even if the underlying kernel has been
+    fixed, and so we define our own initgroups. */
+ #include <grp.h>
+ static int
+ xinitgroups (char *user, gid_t gid)
+ {
+   struct group *grp;
+   gid_t *buf;
+   int buflen, ngroups;
+   /* Initialise the list with the specified GID. */
+   ngroups = 0;
+   buflen = 16;
+   buf = malloc (buflen * sizeof (*buf));
+   buf[ngroups ++] = gid;
+   setgrent ();
+   while ((grp = getgrent ()))
+     {
+       /* Scan the member list for our user. */
+       char **p = grp->gr_mem;
+       while (*p && strcmp (*p, user))
+ 	p ++;
+       if (*p)
+ 	{
+ 	  /* We found the user in this group. */
+ 	  if (ngroups == buflen)
+ 	    {
+ 	      /* Enlarge the group list. */
+ 	      buflen *= 2;
+ 	      buf = realloc (buf, buflen * sizeof (*buf));
+ 	    }
+ 	  /* Add the group id to our list. */
+ 	  buf[ngroups ++] = grp->gr_gid;
+ 	}
+     }
+   endgrent ();
+   /* Return whatever setgroups says. */
+   buflen = setgroups (ngroups, buf);
+   free (buf);
+   return buflen;
+ }
+ #define initgroups xinitgroups
+ /* This worked fine, and was adopted into glibc, until setgroups got a
+    similar limitation, so we override it as well. */
+ #include <linux/posix_types.h>
+ #include <sys/syscall.h>
+ #include <errno.h>
+ int
+ setgroups (size_t n, const gid_t *groups)
+ {
+   size_t i;
+   __kernel_gid_t kernel_groups[n];
+   for (i = 0; i < n; i ++)
+     kernel_groups[i] = groups[i];
+   {
+     long res;
+     __asm__ volatile ("int $0x80"
+ 		      : "=a" (res)
+ 		      : "0" (__NR_setgroups),"b" ((long)(n)),
+ 		      "c" ((long)(kernel_groups)));
+     if ((unsigned long)(res) >= (unsigned long)(-125)) {
+       errno = -res;
+       res = -1;
+     }
+     return (int) (res);
+   }
+ }
   * Remove local Xauthority file.

Node:Concept Index, Previous:System Administration, Up:Top

Index of Concepts

Table of Contents