1 /* 2 * Copyright (C) 1986-2005 The Free Software Foundation, Inc. 3 * 4 * Portions Copyright (C) 1998-2005 Derek Price, Ximbiot <http://ximbiot.com>, 5 * and others. 6 * 7 * Portions Copyright (C) 1992, Brian Berliner and Jeff Polk 8 * Portions Copyright (C) 1989-1992, Brian Berliner 9 * 10 * You may distribute under the terms of the GNU General Public License as 11 * specified in the README file that comes with the CVS kit. */ 12 13 #include "cvs.h" 14 #include "getline.h" 15 #include "history.h" 16 #include "save-cwd.h" 17 18 #ifndef DBLKSIZ 19 #define DBLKSIZ 4096 /* since GNU ndbm doesn't define it */ 20 #endif 21 22 static int checkout_file (char *file, char *temp); 23 static char *make_tempfile (void); 24 static void rename_rcsfile (char *temp, char *real); 25 26 #ifndef MY_NDBM 27 static void rename_dbmfile (char *temp); 28 static void write_dbmfile (char *temp); 29 #endif /* !MY_NDBM */ 30 31 /* cvsacl patch */ 32 static const char *const aclconfig_contents[] = { 33 "# Set `UseCVSACL' to yes to use CVSACL feature.\n", 34 "UseCVSACL=yes\n", 35 "\n", 36 "# Default CVSACL Permission to use.\n", 37 "#CVSACLDefaultPermissions=p\n", 38 "\n", 39 "# Default file location for CVS ACL file (access) is CVSROOT/access.\n", 40 "# If you want to use a different location, define it below.\n", 41 "#CVSACLFileLocation=/path/to/cvs/access\n", 42 "\n", 43 "# Set `UseSystemGroups' to yes to use system group definitions (/etc/group).\n", 44 "UseSystemGroups=yes\n", 45 "\n", 46 "# Set `UseCVSGroups' to yes to use another group file.\n", 47 "#UseCVSGroups=yes\n", 48 "\n", 49 "# Default file location for CVS groups file is CVSROOT/group.\n", 50 "# If you want to use a different location, define it below.\n", 51 "#CVSGroupsFileLocation=/path/to/cvs/group\n", 52 "\n", 53 "# Set UseSeperateACLFileForEachDir to yes in order to use a\n", 54 "# seperate 'access' file for each directory.\n", 55 "# This increased the performance if you have really big repository.\n", 56 "#UseSeperateACLFileForEachDir=no\n", 57 "\n", 58 "# If StopAtFirstPermissionDenied is set to yes\n", 59 "# operation will stop at first permission denied message.\n", 60 "# Default is no.\n", 61 "#StopAtFirstPermissionDenied=no\n", 62 "\n", 63 "# Set CVSServerRunAsUser to a system user, in order CVS server\n", 64 "# to run as.\n", 65 "#CVSServerRunAsUser=runascvsuser", 66 NULL 67 }; 68 69 static const char *const access_contents[] = { 70 "# CVS ACL definitions file. DO NOT EDIT MANUALLY\n", 71 "#\n", 72 "# BNF:\n", 73 "# <entry>: " 74 "<filetype>:<path>:<tag-branch>:<permission-list>:\n", 75 "# <filetype>: d|f\n", 76 "# <path>: ALL | unix-file-path\n", 77 "# <tag-branch>: ALL | HEAD | name\n", 78 "# <permission-list>: <permission>\n", 79 "# | <permission-list> , <permission>\n", 80 "# <permission>: <actor>!<privilege>\n", 81 "# <actor>: ALL | user | group\n", 82 "# <privilege> n | a | w | t | r | c | d | p\n", 83 "#\n", 84 "# Valid privileges are:\n", 85 "# * - no permission (n) (1)\n", 86 "# * - all permissions (a) (2)\n", 87 "# * - write permission (w) (3)\n", 88 "# * - tag permission (t) (4)\n", 89 "# * - read permission (r) (5)\n", 90 "# * - add permission (c) (6)\n", 91 "# * - remove permission (d) (7)\n", 92 "# * - permission change (p) (8)\n", 93 "#\n", 94 "# Valid filetypes are:\n", 95 "# * - plain file (f)\n", 96 "# * - directory (d)\n", 97 "#\n", 98 NULL 99 }; 100 101 static const char *const group_contents[] = { 102 NULL 103 }; 104 105 /* Structure which describes an administrative file. */ 106 struct admin_file { 107 /* Name of the file, within the CVSROOT directory. */ 108 char *filename; 109 110 /* This is a one line description of what the file is for. It is not 111 currently used, although one wonders whether it should be, somehow. 112 If NULL, then don't process this file in mkmodules (FIXME?: a bit of 113 a kludge; probably should replace this with a flags field). */ 114 char *errormsg; 115 116 /* Contents which the file should have in a new repository. To avoid 117 problems with brain-dead compilers which choke on long string constants, 118 this is a pointer to an array of char * terminated by NULL--each of 119 the strings is concatenated. 120 121 If this field is NULL, the file is not created in a new 122 repository, but it can be added with "cvs add" (just as if one 123 had created the repository with a version of CVS which didn't 124 know about the file) and the checked-out copy will be updated 125 without having to add it to checkoutlist. */ 126 const char * const *contents; 127 }; 128 129 static const char *const loginfo_contents[] = { 130 "# The \"loginfo\" file controls where \"cvs commit\" log information is\n", 131 "# sent. The first entry on a line is a regular expression which must\n", 132 "# match the directory that the change is being made to, relative to the\n", 133 "# $CVSROOT. If a match is found, then the remainder of the line is a\n", 134 "# filter program that should expect log information on its standard input.\n", 135 "#\n", 136 "# If the repository name does not match any of the regular expressions in this\n", 137 "# file, the \"DEFAULT\" line is used, if it is specified.\n", 138 "#\n", 139 "# If the name ALL appears as a regular expression it is always used\n", 140 "# in addition to the first matching regex or DEFAULT.\n", 141 "#\n", 142 "# If any format strings are present in the filter, they will be replaced\n", 143 "# as follows:\n", 144 "# %c = canonical name of the command being executed\n", 145 #ifdef PROXY_SUPPORT 146 "# %R = the name of the referrer, if any, otherwise the value NONE\n", 147 #endif 148 "# %p = path relative to repository\n", 149 "# %r = repository (path portion of $CVSROOT)\n", 150 "# %t = tagname\n", 151 "# %{sVv} = attribute list = file name, old version number (pre-checkin),\n", 152 "# new version number (post-checkin). When either old or new revision\n", 153 "# is unknown, doesn't exist, or isn't applicable, the string \"NONE\"\n", 154 "# will be placed on the command line instead.\n", 155 "#\n", 156 "# Note that %{sVv} is a list operator and not all elements are necessary.\n", 157 "# Thus %{sv} is a legal format string, but will only be replaced with\n", 158 "# file name and new revision.\n", 159 "# It also generates multiple arguments for each file being operated upon.\n", 160 "# That is, if two files, file1 & file2, are being commited from 1.1 to\n", 161 "# version 1.1.2.1 and from 1.1.2.2 to 1.1.2.3, respectively, %{sVv} will\n", 162 "# generate the following six arguments in this order:\n", 163 "# file1, 1.1, 1.1.2.1, file2, 1.1.2.2, 1.1.2.3.\n", 164 "#\n", 165 "# For example:\n", 166 "#DEFAULT (echo \"\"; id; echo %s; date; cat) >> $CVSROOT/CVSROOT/commitlog\n", 167 "# or\n", 168 "#DEFAULT (echo \"\"; id; echo %{sVv}; date; cat) >> $CVSROOT/CVSROOT/commitlog\n", 169 NULL 170 }; 171 172 static const char *const rcsinfo_contents[] = { 173 "# The \"rcsinfo\" file is used to control templates with which the editor\n", 174 "# is invoked on commit and import.\n", 175 "#\n", 176 "# The first entry on a line is a regular expression which is tested\n", 177 "# against the directory that the change is being made to, relative to the\n", 178 "# $CVSROOT. For the first match that is found, then the remainder of the\n", 179 "# line is the name of the file that contains the template.\n", 180 "#\n", 181 "# If the repository name does not match any of the regular expressions in this\n", 182 "# file, the \"DEFAULT\" line is used, if it is specified.\n", 183 "#\n", 184 "# If the name \"ALL\" appears as a regular expression it is always used\n", 185 "# in addition to the first matching regex or \"DEFAULT\".\n", 186 NULL 187 }; 188 189 190 191 static const char *const verifymsg_contents[] = { 192 "# The \"verifymsg\" file is used to allow verification of logging\n", 193 "# information. It works best when a template (as specified in the\n", 194 "# rcsinfo file) is provided for the logging procedure. Given a\n", 195 "# template with locations for, a bug-id number, a list of people who\n", 196 "# reviewed the code before it can be checked in, and an external\n", 197 "# process to catalog the differences that were code reviewed, the\n", 198 "# following test can be applied to the code:\n", 199 "#\n", 200 "# Making sure that the entered bug-id number is correct.\n", 201 "# Validating that the code that was reviewed is indeed the code being\n", 202 "# checked in (using the bug-id number or a seperate review\n", 203 "# number to identify this particular code set.).\n", 204 "#\n", 205 "# If any of the above test failed, then the commit would be aborted.\n", 206 "#\n", 207 "# Format strings present in the filter will be replaced as follows:\n", 208 "# %c = canonical name of the command being executed\n", 209 #ifdef PROXY_SUPPORT 210 "# %R = the name of the referrer, if any, otherwise the value NONE\n", 211 #endif 212 "# %p = path relative to repository\n", 213 "# %r = repository (path portion of $CVSROOT)\n", 214 "# %l = name of log file to be verified.\n", 215 "#\n", 216 "# If no format strings are present in the filter, a default \" %l\" will\n", 217 "# be appended to the filter, but this usage is deprecated.\n", 218 "#\n", 219 "# Actions such as mailing a copy of the report to each reviewer are\n", 220 "# better handled by an entry in the loginfo file.\n", 221 "#\n", 222 "# One thing that should be noted is the the ALL keyword is not\n", 223 "# supported. There can be only one entry that matches a given\n", 224 "# repository.\n", 225 NULL 226 }; 227 228 static const char *const commitinfo_contents[] = { 229 "# The \"commitinfo\" file is used to control pre-commit checks.\n", 230 "# The filter on the right is invoked with the repository and a list \n", 231 "# of files to check. A non-zero exit of the filter program will \n", 232 "# cause the commit to be aborted.\n", 233 "#\n", 234 "# The first entry on a line is a regular expression which is tested\n", 235 "# against the directory that the change is being committed to, relative\n", 236 "# to the $CVSROOT. For the first match that is found, then the remainder\n", 237 "# of the line is the name of the filter to run.\n", 238 "#\n", 239 "# Format strings present in the filter will be replaced as follows:\n", 240 "# %c = canonical name of the command being executed\n", 241 #ifdef PROXY_SUPPORT 242 "# %R = the name of the referrer, if any, otherwise the value NONE\n", 243 #endif 244 "# %p = path relative to repository\n", 245 "# %r = repository (path portion of $CVSROOT)\n", 246 "# %t = tagname\n", 247 "# %{s} = file name, file name, ...\n", 248 "#\n", 249 "# If no format strings are present in the filter string, a default of\n", 250 "# \" %r %s\" will be appended to the filter string, but this usage is\n", 251 "# deprecated.\n", 252 "#\n", 253 "# If the repository name does not match any of the regular expressions in this\n", 254 "# file, the \"DEFAULT\" line is used, if it is specified.\n", 255 "#\n", 256 "# If the name \"ALL\" appears as a regular expression it is always used\n", 257 "# in addition to the first matching regex or \"DEFAULT\".\n", 258 NULL 259 }; 260 261 static const char *const taginfo_contents[] = { 262 "# The \"taginfo\" file is used to control pre-tag checks.\n", 263 "# The filter on the right is invoked with the following arguments\n", 264 "# if no format strings are present:\n", 265 "#\n", 266 "# $1 -- tagname\n", 267 "# $2 -- operation \"add\" for tag, \"mov\" for tag -F, and \"del\" for tag -d\n", 268 "# $3 -- tagtype \"?\" on delete, \"T\" for branch, \"N\" for static\n", 269 "# $4 -- repository\n", 270 "# $5-> file revision [file revision ...]\n", 271 "#\n", 272 "# If any format strings are present in the filter, they will be replaced\n", 273 "# as follows:\n", 274 "# %b = branch mode = \"?\" (delete ops - unknown) | \"T\" (branch)\n", 275 "# | \"N\" (not branch)\n", 276 "# %o = operation = \"add\" | \"mov\" | \"del\"\n", 277 "# %c = canonical name of the command being executed\n", 278 #ifdef PROXY_SUPPORT 279 "# %R = the name of the referrer, if any, otherwise the value NONE\n", 280 #endif 281 "# %p = path relative to repository\n", 282 "# %r = repository (path portion of $CVSROOT)\n", 283 "# %t = tagname\n", 284 "# %{sVv} = attribute list = file name, old version tag will be deleted\n", 285 "# from, new version tag will be added to (or deleted from, but\n", 286 "# this feature is deprecated. When either old or new revision is\n", 287 "# unknown, doesn't exist, or isn't applicable, the string \"NONE\"\n", 288 "# will be placed on the command line.\n", 289 "#\n", 290 "# Note that %{sVv} is a list operator and not all elements are necessary.\n", 291 "# Thus %{sV} is a legal format string, but will only be replaced with file\n", 292 "# name and old revision. it also generates multiple arguments for each file\n", 293 "# being operated upon. i.e. if two files, file1 & file2, are having a tag\n", 294 "# moved from version 1.1 to version 1.1.2.9, %{sVv} will generate the\n", 295 "# following six arguments in this order:\n", 296 "# file1, 1.1, 1.1.2.9, file2, 1.1, 1.1.2.9.\n", 297 "#\n", 298 "# A non-zero exit of the filter program will cause the tag to be aborted.\n", 299 "#\n", 300 "# The first entry on a line is a regular expression which is tested\n", 301 "# against the directory that the change is being committed to, relative\n", 302 "# to the $CVSROOT. For the first match that is found, then the remainder\n", 303 "# of the line is the name of the filter to run.\n", 304 "#\n", 305 "# If the repository name does not match any of the regular expressions in this\n", 306 "# file, the \"DEFAULT\" line is used, if it is specified.\n", 307 "#\n", 308 "# If the name \"ALL\" appears as a regular expression it is always used\n", 309 "# in addition to the first matching regex or \"DEFAULT\".\n", 310 NULL 311 }; 312 313 static const char *const preproxy_contents[] = { 314 "# The \"preproxy\" file is called form the secondary server as soon as\n", 315 "# the secondary server determines that it will be proxying a write\n", 316 "# command to a primary server and immediately before it opens a\n", 317 "# connection to the primary server. This script might, for example, be\n", 318 "# used to launch a dial up or VPN connection to the primary server's\n", 319 "# network.\n", 320 "#\n", 321 "# If any format strings are present in the filter, they will be replaced\n", 322 "# as follows:\n", 323 "# %c = canonical name of the command being executed\n", 324 #ifdef PROXY_SUPPORT 325 "# %R = the name of the referrer, if any, otherwise the value NONE\n", 326 #endif 327 "# %p = path relative to repository (currently always \".\")\n", 328 "# %r = repository (path portion of $CVSROOT)\n", 329 "#\n", 330 "# The first entry on a line is a regular expression which is tested\n", 331 "# against the directory that the change is being committed to, relative\n", 332 "# to the $CVSROOT. For the first match that is found, then the remainder\n", 333 "# of the line is the name of the filter to run.\n", 334 "#\n", 335 "# If the repository name does not match any of the regular expressions in this\n", 336 "# file, the \"DEFAULT\" line is used, if it is specified.\n", 337 "#\n", 338 "# If the name \"ALL\" appears as a regular expression it is always used\n", 339 "# in addition to the first matching regex or \"DEFAULT\".\n", 340 NULL 341 }; 342 343 static const char *const postadmin_contents[] = { 344 "# The \"postadmin\" file is called after the \"admin\" command finishes\n", 345 "# processing a directory.\n", 346 "#\n", 347 "# If any format strings are present in the filter, they will be replaced\n", 348 "# as follows:\n", 349 "# %c = canonical name of the command being executed\n", 350 #ifdef PROXY_SUPPORT 351 "# %R = the name of the referrer, if any, otherwise the value NONE\n", 352 #endif 353 "# %p = path relative to repository\n", 354 "# %r = repository (path portion of $CVSROOT)\n", 355 "#\n", 356 "# The first entry on a line is a regular expression which is tested\n", 357 "# against the directory that the change is being committed to, relative\n", 358 "# to the $CVSROOT. For the first match that is found, then the remainder\n", 359 "# of the line is the name of the filter to run.\n", 360 "#\n", 361 "# If the repository name does not match any of the regular expressions in this\n", 362 "# file, the \"DEFAULT\" line is used, if it is specified.\n", 363 "#\n", 364 "# If the name \"ALL\" appears as a regular expression it is always used\n", 365 "# in addition to the first matching regex or \"DEFAULT\".\n", 366 NULL 367 }; 368 369 static const char *const postproxy_contents[] = { 370 "# The \"postproxy\" file is called from a secondary server as soon as\n", 371 "# the secondary server closes its connection to the primary server.\n", 372 "# This script might, for example, be used to shut down a dial up\n", 373 "# or VPN connection to the primary server's network.\n", 374 "#\n", 375 "# If any format strings are present in the filter, they will be replaced\n", 376 "# as follows:\n", 377 "# %c = canonical name of the command being executed\n", 378 #ifdef PROXY_SUPPORT 379 "# %R = the name of the referrer, if any, otherwise the value NONE\n", 380 #endif 381 "# %p = path relative to repository (currently always \".\")\n", 382 "# %r = repository (path portion of $CVSROOT)\n", 383 "#\n", 384 "# The first entry on a line is a regular expression which is tested\n", 385 "# against the directory that the change is being committed to, relative\n", 386 "# to the $CVSROOT. For the first match that is found, then the remainder\n", 387 "# of the line is the name of the filter to run.\n", 388 "#\n", 389 "# If the repository name does not match any of the regular expressions in this\n", 390 "# file, the \"DEFAULT\" line is used, if it is specified.\n", 391 "#\n", 392 "# If the name \"ALL\" appears as a regular expression it is always used\n", 393 "# in addition to the first matching regex or \"DEFAULT\".\n", 394 NULL 395 }; 396 397 static const char *const posttag_contents[] = { 398 "# The \"posttag\" file is called after the \"tag\" command finishes\n", 399 "# processing a directory.\n", 400 "#\n", 401 "# If any format strings are present in the filter, they will be replaced\n", 402 "# as follows:\n", 403 "# %b = branch mode = \"?\" (delete ops - unknown) | \"T\" (branch)\n", 404 "# | \"N\" (not branch)\n", 405 "# %o = operation = \"add\" | \"mov\" | \"del\"\n", 406 "# %c = canonical name of the command being executed\n", 407 #ifdef PROXY_SUPPORT 408 "# %R = the name of the referrer, if any, otherwise the value NONE\n", 409 #endif 410 "# %p = path relative to repository\n", 411 "# %r = repository (path portion of $CVSROOT)\n", 412 "# %t = tagname\n", 413 "# %{sVv} = attribute list = file name, old version tag will be deleted\n", 414 "# from, new version tag will be added to (or deleted from, but\n", 415 "# this feature is deprecated. When either old or new revision is\n", 416 "# unknown, doesn't exist, or isn't applicable, the string \"NONE\"\n", 417 "# will be placed on the command line.\n", 418 "#\n", 419 "# Note that %{sVv} is a list operator and not all elements are necessary.\n", 420 "# Thus %{sV} is a legal format string, but will only be replaced with file\n", 421 "# name and old revision. it also generates multiple arguments for each file\n", 422 "# being operated upon. i.e. if two files, file1 & file2, are having a tag\n", 423 "# moved from version 1.1 to version 1.1.2.9, %{sVv} will generate the\n", 424 "# following six arguments in this order:\n", 425 "# file1, 1.1, 1.1.2.9, file2, 1.1, 1.1.2.9.\n", 426 "#\n", 427 "# The first entry on a line is a regular expression which is tested\n", 428 "# against the directory that the change is being committed to, relative\n", 429 "# to the $CVSROOT. For the first match that is found, then the remainder\n", 430 "# of the line is the name of the filter to run.\n", 431 "#\n", 432 "# If the repository name does not match any of the regular expressions in this\n", 433 "# file, the \"DEFAULT\" line is used, if it is specified.\n", 434 "#\n", 435 "# If the name \"ALL\" appears as a regular expression it is always used\n", 436 "# in addition to the first matching regex or \"DEFAULT\".\n", 437 NULL 438 }; 439 440 static const char *const postwatch_contents[] = { 441 "# The \"postwatch\" file is called after any command finishes writing new\n", 442 "# file attibute (watch/edit) information in a directory.\n", 443 "#\n", 444 "# If any format strings are present in the filter, they will be replaced\n", 445 "# as follows:\n", 446 "# %c = canonical name of the command being executed\n", 447 #ifdef PROXY_SUPPORT 448 "# %R = the name of the referrer, if any, otherwise the value NONE\n", 449 #endif 450 "# %p = path relative to repository\n", 451 "# %r = repository (path portion of $CVSROOT)\n", 452 "#\n", 453 "# The first entry on a line is a regular expression which is tested\n", 454 "# against the directory that the change is being committed to, relative\n", 455 "# to the $CVSROOT. For the first match that is found, then the remainder\n", 456 "# of the line is the name of the filter to run.\n", 457 "#\n", 458 "# If the repository name does not match any of the regular expressions in this\n", 459 "# file, the \"DEFAULT\" line is used, if it is specified.\n", 460 "#\n", 461 "# If the name \"ALL\" appears as a regular expression it is always used\n", 462 "# in addition to the first matching regex or \"DEFAULT\".\n", 463 NULL 464 }; 465 466 static const char *const checkoutlist_contents[] = { 467 "# The \"checkoutlist\" file is used to support additional version controlled\n", 468 "# administrative files in $CVSROOT/CVSROOT, such as template files.\n", 469 "#\n", 470 "# The first entry on a line is a filename which will be checked out from\n", 471 "# the corresponding RCS file in the $CVSROOT/CVSROOT directory.\n", 472 "# The remainder of the line is an error message to use if the file cannot\n", 473 "# be checked out.\n", 474 "#\n", 475 "# File format:\n", 476 "#\n", 477 "# [<whitespace>]<filename>[<whitespace><error message>]<end-of-line>\n", 478 "#\n", 479 "# comment lines begin with '#'\n", 480 NULL 481 }; 482 483 static const char *const cvswrappers_contents[] = { 484 "# This file affects handling of files based on their names.\n", 485 "#\n", 486 #if 0 /* see comments in wrap_add in wrapper.c */ 487 "# The -t/-f options allow one to treat directories of files\n", 488 "# as a single file, or to transform a file in other ways on\n", 489 "# its way in and out of CVS.\n", 490 "#\n", 491 #endif 492 "# The -m option specifies whether CVS attempts to merge files.\n", 493 "#\n", 494 "# The -k option specifies keyword expansion (e.g. -kb for binary).\n", 495 "#\n", 496 "# Format of wrapper file ($CVSROOT/CVSROOT/cvswrappers or .cvswrappers)\n", 497 "#\n", 498 "# wildcard [option value][option value]...\n", 499 "#\n", 500 "# where option is one of\n", 501 "# -f from cvs filter value: path to filter\n", 502 "# -t to cvs filter value: path to filter\n", 503 "# -m update methodology value: MERGE or COPY\n", 504 "# -k expansion mode value: b, o, kkv, &c\n", 505 "#\n", 506 "# and value is a single-quote delimited value.\n", 507 "# For example:\n", 508 "#*.gif -k 'b'\n", 509 NULL 510 }; 511 512 static const char *const notify_contents[] = { 513 "# The \"notify\" file controls where notifications from watches set by\n", 514 "# \"cvs watch add\" or \"cvs edit\" are sent. The first entry on a line is\n", 515 "# a regular expression which is tested against the directory that the\n", 516 "# change is being made to, relative to the $CVSROOT. If it matches,\n", 517 "# then the remainder of the line is a filter program that should contain\n", 518 "# one occurrence of %s for the user to notify, and information on its\n", 519 "# standard input.\n", 520 "#\n", 521 "# \"ALL\" or \"DEFAULT\" can be used in place of the regular expression.\n", 522 "#\n", 523 "# format strings are replaceed as follows:\n", 524 "# %c = canonical name of the command being executed\n", 525 #ifdef PROXY_SUPPORT 526 "# %R = the name of the referrer, if any, otherwise the value NONE\n", 527 #endif 528 "# %p = path relative to repository\n", 529 "# %r = repository (path portion of $CVSROOT)\n", 530 "# %s = user to notify\n", 531 "#\n", 532 "# For example:\n", 533 "#ALL (echo Committed to %r/%p; cat) |mail %s -s \"CVS notification\"\n", 534 NULL 535 }; 536 537 static const char *const modules_contents[] = { 538 "# Three different line formats are valid:\n", 539 "# key -a aliases...\n", 540 "# key [options] directory\n", 541 "# key [options] directory files...\n", 542 "#\n", 543 "# Where \"options\" are composed of:\n", 544 "# -i prog Run \"prog\" on \"cvs commit\" from top-level of module.\n", 545 "# -o prog Run \"prog\" on \"cvs checkout\" of module.\n", 546 "# -e prog Run \"prog\" on \"cvs export\" of module.\n", 547 "# -t prog Run \"prog\" on \"cvs rtag\" of module.\n", 548 "# -u prog Run \"prog\" on \"cvs update\" of module.\n", 549 "# -d dir Place module in directory \"dir\" instead of module name.\n", 550 "# -l Top-level directory only -- do not recurse.\n", 551 "#\n", 552 "# NOTE: If you change any of the \"Run\" options above, you'll have to\n", 553 "# release and re-checkout any working directories of these modules.\n", 554 "#\n", 555 "# And \"directory\" is a path to a directory relative to $CVSROOT.\n", 556 "#\n", 557 "# The \"-a\" option specifies an alias. An alias is interpreted as if\n", 558 "# everything on the right of the \"-a\" had been typed on the command line.\n", 559 "#\n", 560 "# You can encode a module within a module by using the special '&'\n", 561 "# character to interpose another module into the current module. This\n", 562 "# can be useful for creating a module that consists of many directories\n", 563 "# spread out over the entire source repository.\n", 564 NULL 565 }; 566 567 static const char *const config_contents[] = { 568 "# Set `SystemAuth' to `no' if pserver shouldn't check system users/passwords.\n", 569 "#SystemAuth=no\n", 570 "\n", 571 "# Set `LocalKeyword' to specify a local alias for a standard keyword.\n", 572 "#LocalKeyword=MYCVS=CVSHeader\n", 573 "\n", 574 "# Set `KeywordExpand' to `i' followed by a list of keywords to expand or\n", 575 "# `e' followed by a list of keywords to not expand.\n" 576 "#KeywordExpand=iMYCVS,Name,Date\n", 577 "#KeywordExpand=eCVSHeader\n", 578 "\n", 579 #ifdef PRESERVE_PERMISSIONS_SUPPORT 580 "# Set `PreservePermissions' to `yes' to save file status information\n", 581 "# in the repository.\n", 582 "#PreservePermissions=no\n", 583 "\n", 584 #endif 585 "# Set `TopLevelAdmin' to `yes' to create a CVS directory at the top\n", 586 "# level of the new working directory when using the `cvs checkout'\n", 587 "# command.\n", 588 "#TopLevelAdmin=no\n", 589 "\n", 590 "# Put CVS lock files in this directory rather than directly in the repository.\n", 591 "#LockDir=/var/lock/cvs\n", 592 "\n", 593 "# Set `LogHistory' to `all' or `" ALL_HISTORY_REC_TYPES "' to log all transactions to the\n", 594 "# history file, or a subset as needed (ie `TMAR' logs all write operations)\n", 595 "#LogHistory=" ALL_HISTORY_REC_TYPES "\n", 596 "\n", 597 "# Set `RereadLogAfterVerify' to `always' (the default) to allow the verifymsg\n", 598 "# script to change the log message. Set it to `stat' to force CVS to verify\n", 599 "# that the file has changed before reading it (this can take up to an extra\n", 600 "# second per directory being committed, so it is not recommended for large\n", 601 "# repositories. Set it to `never' (the previous CVS behavior) to prevent\n", 602 "# verifymsg scripts from changing the log message.\n", 603 "#RereadLogAfterVerify=always\n", 604 "\n", 605 "# Set `UserAdminOptions' to the list of `cvs admin' commands (options)\n", 606 "# that users not in the `cvsadmin' group are allowed to run. This\n", 607 "# defaults to `k', or only allowing the changing of the default\n", 608 "# keyword expansion mode for files for users not in the `cvsadmin' group.\n", 609 "# This value is ignored if the `cvsadmin' group does not exist.\n", 610 "#\n", 611 "# The following string would enable all `cvs admin' commands for all\n", 612 "# users:\n", 613 "#AdminGroup=wheel\n", 614 "#AdminOptions=aAbceIklLmnNostuU\n", 615 #ifdef SUPPORT_OLD_INFO_FMT_STRINGS 616 "\n", 617 "# Set `UseNewInfoFmtStrings' to `no' if you must support a legacy system by\n", 618 "# enabling the deprecated old style info file command line format strings.\n", 619 "# Be warned that these strings could be disabled in any new version of CVS.\n", 620 "UseNewInfoFmtStrings=yes\n", 621 #endif /* SUPPORT_OLD_INFO_FMT_STRINGS */ 622 "\n", 623 "# Set `ImportNewFilesToVendorBranchOnly' to `yes' if you wish to force\n", 624 "# every `cvs import' command to behave as if the `-X' flag was\n", 625 "# specified.\n", 626 "#ImportNewFilesToVendorBranchOnly=no\n", 627 #ifdef PROXY_SUPPORT 628 "\n", 629 "# Set `PrimaryServer' to the CVSROOT to the primary, or write, server when\n", 630 "# establishing one or more read-only mirrors which serve as proxies for\n", 631 "# the write server in write mode or redirect the client to the primary for\n", 632 "# write requests.\n", 633 "#\n", 634 "# For example:\n", 635 "#\n", 636 "# PrimaryServer=:fork:localhost/cvsroot\n", 637 "\n", 638 "# Set `MaxProxyBufferSize' to the the maximum allowable secondary\n", 639 "# buffer memory cache size before the buffer begins being stored to disk, in\n", 640 "# bytes. Must be a positive integer but may end in `k', `M', `G', or `T' (for\n", 641 "# kiilo, mega, giga, & tera, respectively). If an otherwise valid number you\n", 642 "# specify is greater than the SIZE_MAX defined by your system's C compiler,\n", 643 "# then it will be resolved to SIZE_MAX without a warning. Defaults to 8M (8\n", 644 "# megabytes).\n", 645 "#\n", 646 "# High values for MaxProxyBufferSize may speed up a secondary server\n", 647 "# with old hardware and a lot of available memory but can actually slow a\n", 648 "# modern system down slightly.\n", 649 "#\n", 650 "# For example:\n", 651 "#\n", 652 "# MaxProxyBufferSize=1G\n", 653 #endif /* PROXY_SUPPORT */ 654 "\n", 655 "# Set `MaxCommentLeaderLength' to the maximum length permitted for the\n", 656 "# automagically determined comment leader used when expanding the Log\n", 657 "# keyword, in bytes. CVS's behavior when the automagically determined\n", 658 "# comment leader exceeds this length is dependant on the value of\n", 659 "# `UseArchiveCommentLeader' set in this file. `unlimited' is a valid\n", 660 "# setting for this value. Defaults to 20 bytes.\n", 661 "#\n", 662 "# For example:\n", 663 "#\n", 664 "# MaxCommentLeaderLength=20\n", 665 "\n", 666 "# Set `UseArchiveCommentLeader' to `yes' to cause CVS to fall back on\n", 667 "# the comment leader set in the RCS archive file, if any, when the\n", 668 "# automagically determined comment leader exceeds `MaxCommentLeaderLength'\n", 669 "# bytes. If `UseArchiveCommentLeader' is not set and a comment leader\n", 670 "# greater than `MaxCommentLeaderLength' is calculated, the Log keyword\n", 671 "# being examined will not be expanded. Defaults to `no'.\n", 672 "#\n", 673 "# For example:\n", 674 "#\n", 675 "# UseArchiveCommentLeader=no\n", 676 "#\n", 677 "# Set this to the name of a local tag to use in addition to Id\n", 678 "#tag=OurTag\n", 679 NULL 680 }; 681 682 static const struct admin_file filelist[] = { 683 {CVSROOTADM_CHECKOUTLIST, 684 "a %s file can specify extra CVSROOT files to auto-checkout", 685 checkoutlist_contents}, 686 {CVSROOTADM_COMMITINFO, 687 "a %s file can be used to configure 'cvs commit' checking", 688 commitinfo_contents}, 689 {CVSROOTADM_IGNORE, 690 "a %s file can be used to specify files to ignore", 691 NULL}, 692 {CVSROOTADM_LOGINFO, 693 "no logging of 'cvs commit' messages is done without a %s file", 694 &loginfo_contents[0]}, 695 {CVSROOTADM_MODULES, 696 /* modules is special-cased in mkmodules. */ 697 NULL, 698 modules_contents}, 699 {CVSROOTADM_NOTIFY, 700 "a %s file can be used to specify where notifications go", 701 notify_contents}, 702 {CVSROOTADM_POSTADMIN, 703 "a %s file can be used to configure 'cvs admin' logging", 704 postadmin_contents}, 705 {CVSROOTADM_POSTPROXY, 706 "a %s file can be used to close or log connections to a primary server", 707 postproxy_contents}, 708 {CVSROOTADM_POSTTAG, 709 "a %s file can be used to configure 'cvs tag' logging", 710 posttag_contents}, 711 {CVSROOTADM_POSTWATCH, 712 "a %s file can be used to configure 'cvs watch' logging", 713 postwatch_contents}, 714 {CVSROOTADM_PREPROXY, 715 "a %s file can be used to open or log connections to a primary server", 716 preproxy_contents}, 717 {CVSROOTADM_RCSINFO, 718 "a %s file can be used to configure 'cvs commit' templates", 719 rcsinfo_contents}, 720 {CVSROOTADM_READERS, 721 "a %s file specifies read-only users", 722 NULL}, 723 {CVSROOTADM_TAGINFO, 724 "a %s file can be used to configure 'cvs tag' checking", 725 taginfo_contents}, 726 {CVSROOTADM_VERIFYMSG, 727 "a %s file can be used to validate log messages", 728 verifymsg_contents}, 729 {CVSROOTADM_WRAPPER, 730 "a %s file can be used to specify files to treat as wrappers", 731 cvswrappers_contents}, 732 {CVSROOTADM_WRITERS, 733 "a %s file specifies read/write users", 734 NULL}, 735 736 /* Some have suggested listing CVSROOTADM_PASSWD here too. This 737 would mean that CVS commands which operate on the 738 CVSROOTADM_PASSWD file would transmit hashed passwords over the 739 net. This might seem to be no big deal, as pserver normally 740 transmits cleartext passwords, but the difference is that 741 CVSROOTADM_PASSWD contains *all* passwords, not just the ones 742 currently being used. For example, it could be too easy to 743 accidentally give someone readonly access to CVSROOTADM_PASSWD 744 (e.g. via anonymous CVS or cvsweb), and then if there are any 745 guessable passwords for read/write access (usually there will be) 746 they get read/write access. 747 748 Another worry is the implications of storing old passwords--if 749 someone used a password in the past they might be using it 750 elsewhere, using a similar password, etc, and so saving old 751 passwords, even hashed, is probably not a good idea. */ 752 753 {CVSROOTADM_CONFIG, 754 "a %s file configures various behaviors", 755 config_contents}, 756 757 /* cvsacl patch */ 758 {CVSROOTADM_ACLCONFIG, 759 "a %s file configures Access Control List behaviors", 760 aclconfig_contents}, 761 762 {CVSROOTADM_ACCESS, 763 "a %s file configures Access Control Lists", 764 access_contents}, 765 766 {CVSROOTADM_GROUP, 767 "a %s file configures Access Control List group definitions", 768 group_contents}, 769 770 {NULL, NULL, NULL} 771 }; 772 773 /* Rebuild the checked out administrative files in directory DIR. */ 774 int 775 mkmodules (char *dir) 776 { 777 struct saved_cwd cwd; 778 char *temp; 779 char *cp, *last, *fname; 780 #ifdef MY_NDBM 781 DBM *db; 782 #endif 783 FILE *fp; 784 char *line = NULL; 785 size_t line_allocated = 0; 786 const struct admin_file *fileptr; 787 788 if (noexec) 789 return 0; 790 791 if (save_cwd (&cwd)) 792 error (1, errno, "Failed to save current directory."); 793 794 if (CVS_CHDIR (dir) < 0) 795 error (1, errno, "cannot chdir to %s", dir); 796 797 /* 798 * First, do the work necessary to update the "modules" database. 799 */ 800 temp = make_tempfile (); 801 switch (checkout_file (CVSROOTADM_MODULES, temp)) 802 { 803 804 case 0: /* everything ok */ 805 #ifdef MY_NDBM 806 /* open it, to generate any duplicate errors */ 807 if ((db = dbm_open (temp, O_RDONLY, 0666)) != NULL) 808 dbm_close (db); 809 #else 810 write_dbmfile (temp); 811 rename_dbmfile (temp); 812 #endif 813 rename_rcsfile (temp, CVSROOTADM_MODULES); 814 break; 815 816 default: 817 error (0, 0, 818 "'cvs checkout' is less functional without a %s file", 819 CVSROOTADM_MODULES); 820 break; 821 } /* switch on checkout_file() */ 822 823 if (unlink_file (temp) < 0 824 && !existence_error (errno)) 825 error (0, errno, "cannot remove %s", temp); 826 free (temp); 827 828 /* Checkout the files that need it in CVSROOT dir */ 829 for (fileptr = filelist; fileptr && fileptr->filename; fileptr++) { 830 if (fileptr->errormsg == NULL) 831 continue; 832 temp = make_tempfile (); 833 if (checkout_file (fileptr->filename, temp) == 0) 834 rename_rcsfile (temp, fileptr->filename); 835 /* else 836 * If there was some problem other than the file not existing, 837 * checkout_file already printed a real error message. If the 838 * file does not exist, it is harmless--it probably just means 839 * that the repository was created with an old version of CVS 840 * which didn't have so many files in CVSROOT. 841 */ 842 843 if (unlink_file (temp) < 0 844 && !existence_error (errno)) 845 error (0, errno, "cannot remove %s", temp); 846 free (temp); 847 } 848 849 fp = CVS_FOPEN (CVSROOTADM_CHECKOUTLIST, "r"); 850 if (fp) 851 { 852 /* 853 * File format: 854 * [<whitespace>]<filename>[<whitespace><error message>]<end-of-line> 855 * 856 * comment lines begin with '#' 857 */ 858 while (getline (&line, &line_allocated, fp) >= 0) 859 { 860 /* skip lines starting with # */ 861 if (line[0] == '#') 862 continue; 863 864 if ((last = strrchr (line, '\n')) != NULL) 865 *last = '\0'; /* strip the newline */ 866 867 /* Skip leading white space. */ 868 for (fname = line; 869 *fname && isspace ((unsigned char) *fname); 870 fname++) 871 ; 872 873 /* Find end of filename. */ 874 for (cp = fname; *cp && !isspace ((unsigned char) *cp); cp++) 875 ; 876 *cp = '\0'; 877 878 temp = make_tempfile (); 879 if (checkout_file (fname, temp) == 0) 880 { 881 rename_rcsfile (temp, fname); 882 } 883 else 884 { 885 /* Skip leading white space before the error message. */ 886 for (cp++; 887 cp < last && *cp && isspace ((unsigned char) *cp); 888 cp++) 889 ; 890 if (cp < last && *cp) 891 error (0, 0, "%s", cp); 892 } 893 if (unlink_file (temp) < 0 894 && !existence_error (errno)) 895 error (0, errno, "cannot remove %s", temp); 896 free (temp); 897 } 898 if (line) 899 free (line); 900 if (ferror (fp)) 901 error (0, errno, "cannot read %s", CVSROOTADM_CHECKOUTLIST); 902 if (fclose (fp) < 0) 903 error (0, errno, "cannot close %s", CVSROOTADM_CHECKOUTLIST); 904 } 905 else 906 { 907 /* Error from CVS_FOPEN. */ 908 if (!existence_error (errno)) 909 error (0, errno, "cannot open %s", CVSROOTADM_CHECKOUTLIST); 910 } 911 912 if (restore_cwd (&cwd)) 913 error (1, errno, "Failed to restore current directory, `%s'.", 914 cwd.name); 915 free_cwd (&cwd); 916 917 return 0; 918 } 919 920 921 922 /* 923 * Yeah, I know, there are NFS race conditions here. 924 */ 925 static char * 926 make_tempfile (void) 927 { 928 static int seed = 0; 929 int fd; 930 char *temp; 931 932 if (seed == 0) 933 seed = getpid (); 934 temp = xmalloc (sizeof (BAKPREFIX) + 40); 935 while (1) 936 { 937 (void) sprintf (temp, "%s%d", BAKPREFIX, seed++); 938 if ((fd = CVS_OPEN (temp, O_CREAT|O_EXCL|O_RDWR, 0666)) != -1) 939 break; 940 if (errno != EEXIST) 941 error (1, errno, "cannot create temporary file %s", temp); 942 } 943 if (close(fd) < 0) 944 error(1, errno, "cannot close temporary file %s", temp); 945 return temp; 946 } 947 948 949 950 /* Get a file. If the file does not exist, return 1 silently. If 951 there is an error, print a message and return 1 (FIXME: probably 952 not a very clean convention). On success, return 0. */ 953 static int 954 checkout_file (char *file, char *temp) 955 { 956 char *rcs; 957 RCSNode *rcsnode; 958 int retcode = 0; 959 960 if (noexec) 961 return 0; 962 963 rcs = Xasprintf ("%s%s", file, RCSEXT); 964 if (!isfile (rcs)) 965 { 966 free (rcs); 967 return 1; 968 } 969 970 rcsnode = RCS_parsercsfile (rcs); 971 if (!rcsnode) 972 { 973 /* Probably not necessary (?); RCS_parsercsfile already printed a 974 message. */ 975 error (0, 0, "Failed to parse `%s'.", rcs); 976 free (rcs); 977 return 1; 978 } 979 980 retcode = RCS_checkout (rcsnode, NULL, NULL, NULL, NULL, temp, NULL, NULL); 981 if (retcode != 0) 982 { 983 /* Probably not necessary (?); RCS_checkout already printed a 984 message. */ 985 error (0, 0, "failed to check out %s file", 986 file); 987 } 988 freercsnode (&rcsnode); 989 free (rcs); 990 return retcode; 991 } 992 993 994 995 #ifndef MY_NDBM 996 997 static void 998 write_dbmfile( char *temp ) 999 { 1000 char line[DBLKSIZ], value[DBLKSIZ]; 1001 FILE *fp; 1002 DBM *db; 1003 char *cp, *vp; 1004 datum key, val; 1005 int len, cont, err = 0; 1006 1007 fp = xfopen (temp, "r"); 1008 if ((db = dbm_open (temp, O_RDWR | O_CREAT | O_TRUNC, 0666)) == NULL) 1009 error (1, errno, "cannot open dbm file %s for creation", temp); 1010 for (cont = 0; fgets (line, sizeof (line), fp) != NULL;) 1011 { 1012 if ((cp = strrchr (line, '\n')) != NULL) 1013 *cp = '\0'; /* strip the newline */ 1014 1015 /* 1016 * Add the line to the value, at the end if this is a continuation 1017 * line; otherwise at the beginning, but only after any trailing 1018 * backslash is removed. 1019 */ 1020 vp = value; 1021 if (cont) 1022 vp += strlen (value); 1023 1024 /* 1025 * See if the line we read is a continuation line, and strip the 1026 * backslash if so. 1027 */ 1028 len = strlen (line); 1029 if (len > 0) 1030 cp = &line[len - 1]; 1031 else 1032 cp = line; 1033 if (*cp == '\\') 1034 { 1035 cont = 1; 1036 *cp = '\0'; 1037 } 1038 else 1039 { 1040 cont = 0; 1041 } 1042 (void) strcpy (vp, line); 1043 if (value[0] == '#') 1044 continue; /* comment line */ 1045 vp = value; 1046 while (*vp && isspace ((unsigned char) *vp)) 1047 vp++; 1048 if (*vp == '\0') 1049 continue; /* empty line */ 1050 1051 /* 1052 * If this was not a continuation line, add the entry to the database 1053 */ 1054 if (!cont) 1055 { 1056 key.dptr = vp; 1057 while (*vp && !isspace ((unsigned char) *vp)) 1058 vp++; 1059 key.dsize = vp - key.dptr; 1060 *vp++ = '\0'; /* NULL terminate the key */ 1061 while (*vp && isspace ((unsigned char) *vp)) 1062 vp++; /* skip whitespace to value */ 1063 if (*vp == '\0') 1064 { 1065 error (0, 0, "warning: NULL value for key `%s'", key.dptr); 1066 continue; 1067 } 1068 val.dptr = vp; 1069 val.dsize = strlen (vp); 1070 if (dbm_store (db, key, val, DBM_INSERT) == 1) 1071 { 1072 error (0, 0, "duplicate key found for `%s'", key.dptr); 1073 err++; 1074 } 1075 } 1076 } 1077 dbm_close (db); 1078 if (fclose (fp) < 0) 1079 error (0, errno, "cannot close %s", temp); 1080 if (err) 1081 { 1082 /* I think that the size of the buffer needed here is 1083 just determined by sizeof (CVSROOTADM_MODULES), the 1084 filenames created by make_tempfile, and other things that won't 1085 overflow. */ 1086 char dotdir[50], dotpag[50], dotdb[50]; 1087 1088 (void) sprintf (dotdir, "%s.dir", temp); 1089 (void) sprintf (dotpag, "%s.pag", temp); 1090 (void) sprintf (dotdb, "%s.db", temp); 1091 if (unlink_file (dotdir) < 0 1092 && !existence_error (errno)) 1093 error (0, errno, "cannot remove %s", dotdir); 1094 if (unlink_file (dotpag) < 0 1095 && !existence_error (errno)) 1096 error (0, errno, "cannot remove %s", dotpag); 1097 if (unlink_file (dotdb) < 0 1098 && !existence_error (errno)) 1099 error (0, errno, "cannot remove %s", dotdb); 1100 error (1, 0, "DBM creation failed; correct above errors"); 1101 } 1102 } 1103 1104 static void 1105 rename_dbmfile( char *temp ) 1106 { 1107 /* I think that the size of the buffer needed here is 1108 just determined by sizeof (CVSROOTADM_MODULES), the 1109 filenames created by make_tempfile, and other things that won't 1110 overflow. */ 1111 char newdir[50], newpag[50], newdb[50]; 1112 char dotdir[50], dotpag[50], dotdb[50]; 1113 char bakdir[50], bakpag[50], bakdb[50]; 1114 1115 int dir1_errno = 0, pag1_errno = 0, db1_errno = 0; 1116 int dir2_errno = 0, pag2_errno = 0, db2_errno = 0; 1117 int dir3_errno = 0, pag3_errno = 0, db3_errno = 0; 1118 1119 (void) sprintf (dotdir, "%s.dir", CVSROOTADM_MODULES); 1120 (void) sprintf (dotpag, "%s.pag", CVSROOTADM_MODULES); 1121 (void) sprintf (dotdb, "%s.db", CVSROOTADM_MODULES); 1122 (void) sprintf (bakdir, "%s%s.dir", BAKPREFIX, CVSROOTADM_MODULES); 1123 (void) sprintf (bakpag, "%s%s.pag", BAKPREFIX, CVSROOTADM_MODULES); 1124 (void) sprintf (bakdb, "%s%s.db", BAKPREFIX, CVSROOTADM_MODULES); 1125 (void) sprintf (newdir, "%s.dir", temp); 1126 (void) sprintf (newpag, "%s.pag", temp); 1127 (void) sprintf (newdb, "%s.db", temp); 1128 1129 (void) chmod (newdir, 0666); 1130 (void) chmod (newpag, 0666); 1131 (void) chmod (newdb, 0666); 1132 1133 /* don't mess with me */ 1134 SIG_beginCrSect (); 1135 1136 /* rm .#modules.dir .#modules.pag */ 1137 if (unlink_file (bakdir) < 0) 1138 dir1_errno = errno; 1139 if (unlink_file (bakpag) < 0) 1140 pag1_errno = errno; 1141 if (unlink_file (bakdb) < 0) 1142 db1_errno = errno; 1143 1144 /* mv modules.dir .#modules.dir */ 1145 if (CVS_RENAME (dotdir, bakdir) < 0) 1146 dir2_errno = errno; 1147 /* mv modules.pag .#modules.pag */ 1148 if (CVS_RENAME (dotpag, bakpag) < 0) 1149 pag2_errno = errno; 1150 /* mv modules.db .#modules.db */ 1151 if (CVS_RENAME (dotdb, bakdb) < 0) 1152 db2_errno = errno; 1153 1154 /* mv "temp".dir modules.dir */ 1155 if (CVS_RENAME (newdir, dotdir) < 0) 1156 dir3_errno = errno; 1157 /* mv "temp".pag modules.pag */ 1158 if (CVS_RENAME (newpag, dotpag) < 0) 1159 pag3_errno = errno; 1160 /* mv "temp".db modules.db */ 1161 if (CVS_RENAME (newdb, dotdb) < 0) 1162 db3_errno = errno; 1163 1164 /* OK -- make my day */ 1165 SIG_endCrSect (); 1166 1167 /* I didn't want to call error() when we had signals blocked 1168 (unnecessary?), but do it now. */ 1169 if (dir1_errno && !existence_error (dir1_errno)) 1170 error (0, dir1_errno, "cannot remove %s", bakdir); 1171 if (pag1_errno && !existence_error (pag1_errno)) 1172 error (0, pag1_errno, "cannot remove %s", bakpag); 1173 if (db1_errno && !existence_error (db1_errno)) 1174 error (0, db1_errno, "cannot remove %s", bakdb); 1175 1176 if (dir2_errno && !existence_error (dir2_errno)) 1177 error (0, dir2_errno, "cannot remove %s", bakdir); 1178 if (pag2_errno && !existence_error (pag2_errno)) 1179 error (0, pag2_errno, "cannot remove %s", bakpag); 1180 if (db2_errno && !existence_error (db2_errno)) 1181 error (0, db2_errno, "cannot remove %s", bakdb); 1182 1183 if (dir3_errno && !existence_error (dir3_errno)) 1184 error (0, dir3_errno, "cannot remove %s", bakdir); 1185 if (pag3_errno && !existence_error (pag3_errno)) 1186 error (0, pag3_errno, "cannot remove %s", bakpag); 1187 if (db3_errno && !existence_error (db3_errno)) 1188 error (0, db3_errno, "cannot remove %s", bakdb); 1189 } 1190 1191 #endif /* !MY_NDBM */ 1192 1193 static void 1194 rename_rcsfile (char *temp, char *real) 1195 { 1196 char *bak; 1197 struct stat statbuf; 1198 char *rcs; 1199 1200 /* Set "x" bits if set in original. */ 1201 rcs = Xasprintf ("%s%s", real, RCSEXT); 1202 statbuf.st_mode = 0; /* in case rcs file doesn't exist, but it should... */ 1203 if (stat (rcs, &statbuf) < 0 1204 && !existence_error (errno)) 1205 error (0, errno, "cannot stat %s", rcs); 1206 free (rcs); 1207 1208 if (chmod (temp, 0444 | (statbuf.st_mode & 0111)) < 0) 1209 error (0, errno, "warning: cannot chmod %s", temp); 1210 bak = Xasprintf ("%s%s", BAKPREFIX, real); 1211 1212 /* rm .#loginfo */ 1213 if (unlink_file (bak) < 0 1214 && !existence_error (errno)) 1215 error (0, errno, "cannot remove %s", bak); 1216 1217 /* mv loginfo .#loginfo */ 1218 if (CVS_RENAME (real, bak) < 0 1219 && !existence_error (errno)) 1220 error (0, errno, "cannot rename %s to %s", real, bak); 1221 1222 /* mv "temp" loginfo */ 1223 if (CVS_RENAME (temp, real) < 0 1224 && !existence_error (errno)) 1225 error (0, errno, "cannot rename %s to %s", temp, real); 1226 1227 free (bak); 1228 } 1229 1230 const char *const init_usage[] = { 1231 "Usage: %s %s\n", 1232 "(Specify the --help global option for a list of other help options)\n", 1233 NULL 1234 }; 1235 1236 int 1237 init (int argc, char **argv) 1238 { 1239 /* Name of CVSROOT directory. */ 1240 char *adm; 1241 /* Name of this administrative file. */ 1242 char *info; 1243 /* Name of ,v file for this administrative file. */ 1244 char *info_v; 1245 /* Exit status. */ 1246 int err = 0; 1247 struct stat st; 1248 1249 const struct admin_file *fileptr; 1250 1251 umask (cvsumask); 1252 1253 if (argc == -1 || argc > 1) 1254 usage (init_usage); 1255 1256 #ifdef CLIENT_SUPPORT 1257 if (current_parsed_root->isremote) 1258 { 1259 start_server (); 1260 1261 ign_setup (); 1262 send_init_command (); 1263 return get_responses_and_close (); 1264 } 1265 #endif /* CLIENT_SUPPORT */ 1266 1267 if (stat (current_parsed_root->directory, &st) != -1) 1268 if (!admin_group_member ()) 1269 error (1, 0, "init to an existing repository is restricted to" 1270 " members of the group %s", config->UserAdminGroup); 1271 1272 /* Note: we do *not* create parent directories as needed like the 1273 old cvsinit.sh script did. Few utilities do that, and a 1274 non-existent parent directory is as likely to be a typo as something 1275 which needs to be created. */ 1276 mkdir_if_needed (current_parsed_root->directory); 1277 1278 adm = Xasprintf ("%s/%s", current_parsed_root->directory, CVSROOTADM); 1279 mkdir_if_needed (adm); 1280 1281 /* This is needed because we pass "fileptr->filename" not "info" 1282 to add_rcs_file below. I think this would be easy to change, 1283 thus nuking the need for CVS_CHDIR here, but I haven't looked 1284 closely (e.g. see wrappers calls within add_rcs_file). */ 1285 if ( CVS_CHDIR (adm) < 0) 1286 error (1, errno, "cannot change to directory %s", adm); 1287 1288 /* Make Emptydir so it's there if we need it */ 1289 mkdir_if_needed (CVSNULLREPOS); 1290 1291 /* 80 is long enough for all the administrative file names, plus 1292 "/" and so on. */ 1293 info = xmalloc (strlen (adm) + 80); 1294 info_v = xmalloc (strlen (adm) + 80); 1295 for (fileptr = filelist; fileptr && fileptr->filename; ++fileptr) 1296 { 1297 if (fileptr->contents == NULL) 1298 continue; 1299 strcpy (info, adm); 1300 strcat (info, "/"); 1301 strcat (info, fileptr->filename); 1302 strcpy (info_v, info); 1303 strcat (info_v, RCSEXT); 1304 if (isfile (info_v)) 1305 /* We will check out this file in the mkmodules step. 1306 Nothing else is required. */ 1307 ; 1308 else 1309 { 1310 int retcode; 1311 1312 if (!isfile (info)) 1313 { 1314 FILE *fp; 1315 const char * const *p; 1316 1317 fp = xfopen (info, "w"); 1318 for (p = fileptr->contents; *p != NULL; ++p) 1319 if (fputs (*p, fp) < 0) 1320 error (1, errno, "cannot write %s", info); 1321 if (fclose (fp) < 0) 1322 error (1, errno, "cannot close %s", info); 1323 } 1324 /* The message used to say " of " and fileptr->filename after 1325 "initial checkin" but I fail to see the point as we know what 1326 file it is from the name. */ 1327 retcode = add_rcs_file ("initial checkin", info_v, 1328 fileptr->filename, "1.1", NULL, 1329 1330 /* No vendor branch. */ 1331 NULL, NULL, 0, NULL, 1332 1333 NULL, 0, NULL, 0); 1334 if (retcode != 0) 1335 /* add_rcs_file already printed an error message. */ 1336 err = 1; 1337 } 1338 } 1339 1340 /* Turn on history logging by default. The user can remove the file 1341 to disable it. */ 1342 strcpy (info, adm); 1343 strcat (info, "/"); 1344 strcat (info, CVSROOTADM_HISTORY); 1345 if (!isfile (info)) 1346 { 1347 FILE *fp; 1348 1349 fp = xfopen (info, "w"); 1350 if (fclose (fp) < 0) 1351 error (1, errno, "cannot close %s", info); 1352 1353 /* Make the new history file world-writeable, since every CVS 1354 user will need to be able to write to it. We use chmod() 1355 because xchmod() is too shy. */ 1356 chmod (info, 0666); 1357 } 1358 1359 /* Make an empty val-tags file to prevent problems creating it later. */ 1360 strcpy (info, adm); 1361 strcat (info, "/"); 1362 strcat (info, CVSROOTADM_VALTAGS); 1363 if (!isfile (info)) 1364 { 1365 FILE *fp; 1366 1367 fp = xfopen (info, "w"); 1368 if (fclose (fp) < 0) 1369 error (1, errno, "cannot close %s", info); 1370 1371 /* Make the new val-tags file world-writeable, since every CVS 1372 user will need to be able to write to it. We use chmod() 1373 because xchmod() is too shy. */ 1374 chmod (info, 0666); 1375 } 1376 1377 free (info); 1378 free (info_v); 1379 1380 mkmodules (adm); 1381 1382 free (adm); 1383 return err; 1384 } 1385