This is an explanation of the implementation of vcheckout. The indended audience is anyone interested in the workings of the repository user-interface tools, and the Vesta repository APIs in general. This document assumes that you're already fairly familiar with how vcheckout works, as well as various Vesta concepts such as mutable attributes, replication, and mastership transfer. No time will be spent here explaining those concepts.
vcheckout is one of the most complicated repository tools. It may not be the best place to start learning about the Vesta APIs, but it does illustrate more precisely because it is complicated.
The code listings in this document are taken from /vesta/vestasys.org/vesta/repos_ui/22/src/vcheckout.C. At several points, APIs from other packages are mentioned. The specifiec versions this document is written relative to are:
[Note: not every line of code is covered here. In some cases, enclosing ifs are not shown, so be sure to follow along in the complete source. The point of this document is to illustrate how vcheckout uses the Vesta APIs, not to go over every single line of code.]
29 30 31 32 33 34 35 36 37 |
#include <Basics.H> #include <Text.H> #include <VestaConfig.H> #include <VestaSource.H> #include <VDirSurrogate.H> #include <VestaSourceAtomic.H> #include <Replicator.H> #include <signal.h> #include "ReposUI.H" |
Inclusion of header files mostly from Vesta:
72 73 74 75 76 77 |
Text defpkgpar, defworkpar, timefmt; defpkgpar = VestaConfig::get_Text("UserInterface", "DefaultPackageParent"); defworkpar = VestaConfig::get_Text("UserInterface", "DefaultWorkParent"); timefmt = VestaConfig::get_Text("UserInterface", "TimeFormat"); |
Read settings from the Vesta configuration file using VestaConfig::get_Text into Text variables of See The vcheckout man page for how these are used.
79 80 81 |
time_t now = time(NULL); char timebuf[256]; strftime(timebuf, sizeof(timebuf), timefmt.cchars(), localtime(&now)); |
Format the current time into a string. Used for setting "checkout-time" attribute. See the strftime(3) man page.
82 83 84 85 |
Text cuser(AccessControl::self()->user()); int atpos = cuser.FindChar('@'); Text user(cuser.Sub(0, atpos)); Text uuser(user + "_" + cuser.Sub(atpos+1)); // cuser with @ replaced by _ |
Get the user's global identifier (username@realm). (AccessControl::self returns the user's identity object, and its user member function returns the global username as a text string.) From that, construct a string that can be used in generating a unique session directory name for non-exclusive checkouts by replacing "@" with "_".
86 87 88 89 |
char mode[8]; mode_t oumask; umask(oumask = umask(0)); sprintf(mode, "%03o", 0777 & ~oumask); |
Generate a text string representing the umask. Used in setting "#mode" attribute on created objects. See the umask(2) man page.
90 91 |
Text lhost(VDirSurrogate::defaultHost()); Text lport(VDirSurrogate::defaultPort()); |
Get the default repository host and port as text strings using the static member functions VDirSurrogate::defaultHost and VDirSurrogate::defaultHost.
93 94 95 |
const Replicator::Flags rflags = (Replicator::Flags) (Replicator::attrNew | Replicator::attrOld | Replicator::attrAccess | Replicator::revive | Replicator::inclStubs | Replicator::latest); |
Initialize a Replicator::Flags. These replicator options are used when replicating the old version and the created new version and session directory to the repository where the checkout is performed.
97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 |
// // Parse command line // Text oldver, newver, sessdir, workdir, pkg, hints, repos; bool default_old = true, default_new = true, default_sess = true; bool uniquify_nondefault_sess = false; bool default_work = true, default_repos = true; bool omit_old = false, omit_new = false, omit_sess = false; bool omit_work = false; bool quiet = false, query = false, force = false; opterr = 0; for (;;) { char* slash; int c = getopt(argc, argv, "qQfo:On:Ns:Suw:Wh:R:"); if (c == EOF) break; switch (c) { case 'q': quiet = true; break; case 'Q': query = true; break; case 'f': force = true; break; case 'o': oldver = optarg; default_old = false; break; case 'O': omit_old = true; oldver = "0"; default_old = false; break; case 'n': newver = optarg; default_new = false; break; case 'N': omit_new = true; newver = "1"; default_new = false; break; case 's': sessdir = optarg; default_sess = false; break; case 'S': omit_sess = true; break; case 'u': uniquify_nondefault_sess = true; break; case 'w': workdir = optarg; default_work = false; break; case 'W': omit_work = true; break; case 'h': hints = optarg; break; case 'R': repos = optarg; default_repos = false; break; case '?': default: usage(); /* not reached */ } } |
Command line parsing. See getopt(3) man page. See The vcheckout man page for the meaning of the different options.
171 172 173 174 175 176 177 178 179 180 181 |
switch (argc - optind) { case 1: pkg = argv[optind]; break; case 0: pkg = "."; break; default: usage(); /* not reached */ } |
Determine the package to check out. If there's a command-line argument left, use that. Otherwise default to the current working directory.
183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 |
// // Use a nondefault repository if specified, and automatically // add it to the hints if not already present. // if (!default_repos) { int colon = repos.FindCharR(':'); if (colon == -1) { lhost = repos; repos = repos + ":" + lport; } else { lhost = repos.Sub(0, colon); lport = repos.Sub(colon+1); } hints = hints + " " + repos; } |
If a non-default repository was specified as local, parse it into a hostname and port. The port is optional, and if not supplied the default will be used.
A non-default repository is also added to the "hints" list, which is used when searching for the master repository of the package and its checkout subdirectory, as well as when searching for a copy of the old version. (Normally only the default repository and any repositories mentioned in "master-repository" attributes will be searched, which is why it's necessary to add a non-default repository to the hints.)
199 200 201 202 |
// // Canonicalize pkg // Text cpkg = ReposUI::canonicalize(pkg, defpkgpar); |
Convert the package path to a canonical form (a fully-qualified absolute pathname that starts with "/vesta/") using ReposUI::canonicalize. A relative pathname that does not start with "." or ".." will be interpreted relative to [UserInterface]DefaultPackageParent.
210 211 |
VestaSource* vs_mpkg = ReposUI::filenameToMasterVS(cpkg, hints); long high = ReposUI::highver(vs_mpkg); |
Locate the master copy of the package with ReposUI::filenameToMasterVS and find the highest version that exists there with ReposUI::highver. Consulting the master copy is necessary for determining the new version to reserve and/or the basis version of the checkout. Only the master repository is guaranteed to know all names that exist in the package, so looking at a non-master repository might overlook versions not replicated there.
212 213 214 215 216 217 218 219 220 221 222 |
if (default_old) { if (high == -1) { // No existing versions, pretend old was version 0 omit_old = true; oldver = "0"; } else { char valbuf[64]; sprintf(valbuf, "%ld", high); oldver = valbuf; } } |
If the old version should be determined automatically (the default), do so based on the highest version in the package. If no versions exist in the package, remember that we will have no old version.
223 224 225 226 227 228 229 |
if (default_new) { // Set newver to next version after highest in use, // minimum 1. char valbuf[64]; sprintf(valbuf, "%ld", (high < 0) ? 1 : high + 1); newver = valbuf; } |
If the new version should be determined automatically (the default), do so based on the highest version in the package. If no versions exist in the package, the new version will be version 1.
232 233 234 235 236 237 238 239 240 241 242 |
// // Canonicalize oldver and newver // Text coldver, coldverpar, oldverarc; Text cnewver, cnewverpar, newverarc; coldver = ReposUI::canonicalize(oldver, cpkg); ReposUI::split(coldver, coldverpar, oldverarc, "old-version"); if (!omit_new) { cnewver = ReposUI::canonicalize(newver, cpkg); ReposUI::split(cnewver, cnewverpar, newverarc, "new-version"); } |
Convert the old and new versions to fully-qualified pathnames that start with "/vesta/" using ReposUI::canonicalize. If either are relative paths that don't start with "." or "..", they will interpreted relative to the package to be checked out. After this conversion, split them into their parent directory and last pathname component with ReposUI::splt. (It may seem like ReposUI::splt is being used to perform the reverse of ReposUI::canonicalize here, but if the new or old versions are supplied explicitly on the command-line, both steps must be performed as the arguments could be either absolute or relative paths.)
244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 |
// // Compute default for sessdir // if (default_sess) { if (!omit_new) { // Set sessdir to newver with "checkout/" inserted before // last component. sessdir = cnewverpar + "/checkout/" + newverarc; } else { // Need to make up a unique name, since newver is // meaningless. First, test whether oldver comes from a // checkout session. if (coldver.FindText("/checkout/") >= 0) { // Oldver is from a checkout session, so set sessdir to // oldver with the last "/" changed to a ".", and "." + // uuser appended. Later we will further append a // uniquifying integer ".u" (starting at 1) to this. sessdir = coldverpar + "." + oldverarc + "." + uuser; } else { // Oldver is not from a checkout session, so set sessdir // to oldver with "checkout/" inserted before the last // component and "." + uuser appended. Later we will // further append a uniquifying integer ".u" (starting // at 1) to this. sessdir = coldverpar + "/checkout/" + oldverarc + "." + uuser; } } } |
Determine the session directory name to create.
The simplest case (handled on line 251) is when we are reserving a new version, in which case the session directory is just:
package/checkout/newverarc
In the case of non-exclusive checkouts, if the old version is itself a snapshot in a checkout session, the name of the new session is based on the old version, in an attempt to make the naming show the derivation. uuser (set on line 85) is appended to the session directory to form a more unique name. For example, if the old-version is:
package/checkout/1/2
The value of sessdir will be:
package/checkout/1.2.uuser
This is handled on line 261.
For a non-exclusive checkout where the old-version is not itself in a checkout session (its path doesn't contain "/checkout/"), the value of sessdir will be:
package/checkout/oldverarc.uuser
This is handled on line 268.
279 280 |
csessdir = ReposUI::canonicalize(sessdir, cpkg); ReposUI::split(csessdir, csessdirpar, sessdirarc, "session-dir"); |
Convert the session directory to a fully-qualified absolute pathname that starts with "/vesta/" (with ReposUI::canonicalize), and split it into its parent and parent directory and last pathname arc (with ReposUI::split). If sessdir is a relative path that doesn't start with "." or "..", it will interpreted relative to the package to be checked out.
283 284 285 286 287 288 289 |
if ((default_sess && omit_new) || (!default_sess && uniquify_nondefault_sess)) { // Need to uniquify the session dir vs_msessdirpar = ReposUI::filenameToMasterVS(csessdirpar, hints); sessdirarc = ReposUI::uniquify(vs_msessdirpar, sessdirarc); csessdir = csessdirpar + "/" + sessdirarc; } |
There are two cases where ".N" (for some small integer N) is appended to make a session directory unique:
In order to determine a unique name to use, the master copy of the session directory parent must be consulted (as only the master copy of an appendable directory knows all the names that exist in it). On line 286, ReposUI::filenameToMasterVS is used to find the master copy.
On line 287, ReposUI::uniquify is used to generate a unique session directory name by appending ".N".
292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 |
// // Compute default for workdir // if (default_work) { // Get last arc of absolute package name that does not // begin with a digit int i, j; j = cpkg.Length(); for (;;) { i = cpkg.FindCharR('/', j-1); if (i == -1) { // Should not happen cerr << program_name << ": can't find base package name in " << cpkg << endl; exit(2); } if (!isdigit(cpkg[i+1])) { workdir = cpkg.Sub(i+1, j-i-1); break; } j = i; } } |
Determine the name of the working directory. This is a simple text search upward through directory path components starting with the package name for an arc that starts with a non-digit. (I believe this is intended to get the name of the package rather than a branch within it, although it isn't a particularly precise algorithm.)
316 317 318 319 320 321 322 323 |
// // Canonicalize workdir // Text cworkdir, cworkdirpar, workdirarc; if (!omit_work) { cworkdir = ReposUI::canonicalize(workdir, Text("/vesta-work/") + user); ReposUI::split(cworkdir, cworkdirpar, workdirarc, "work-dir"); } |
Similar to what we've seen earlier, canonicalize and split the working directory name.
325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 |
// // Look up oldver, first replicating here if needed // VestaSource* vs_oldver = NULL; if (!omit_old) { VestaSource* vs_roldver = ReposUI::filenameToRealVS(coldver, hints); if (vs_roldver->type == VestaSource::stub) { cerr << program_name << ": " << coldver << " is not checked in yet" << endl; exit(2); } else if (vs_roldver->type == VestaSource::ghost) { cerr << program_name << ": " << coldver << " has been deleted" << endl; exit(2); } if (vs_roldver->host() == lhost && vs_roldver->port() == lport) { // A local replica exists vs_oldver = vs_roldver; } else if (!query) { // Get a local replica if (!quiet) { cout << "Replicating " << coldver << " from " << vs_roldver->host() << ":" << vs_roldver->port() << endl; } Replicator repl(vs_roldver->host(), vs_roldver->port(), lhost, lport); Replicator::DirectiveSeq direcs; Replicator::Directive d('+', coldver.Sub(vlen)); direcs.addhi(d); repl.replicate(&direcs, rflags); vs_oldver = ReposUI::filenameToVS(coldver, lhost, lport); } } |
Locate and possibly replicate the old version to the local repository.
On line 330, ReposUI::filenameToRealVS is used to find a "real" (not a non-master ghost and not a non-master stub) copy of the old version. If the object located is of type ghost or stub (which must be a master ghost or stub), print an error message and exit.
If the object is in the local repository (line 340), use it. Otherwise, use the replicator to copy it to the local repository (lines 349-353), unless operating in "query" mode. An instance of Replicator is created to perform the replication, using a single Replicator::Directive to copy the old version.
After replicating it, look it up in the local repository with ReposUI::filenameToVS.
358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 |
// // Look up master of newverpar and sanity check // VestaSource* vs_mnewverpar = NULL; if (!omit_new) { vs_mnewverpar = ReposUI::filenameToMasterVS(cnewverpar, hints); if (!force && !vs_mnewverpar->inAttribs("type", "package")) { cerr << program_name << ": " << cnewverpar << " is not a package or branch" << endl; exit(2); } if (!quiet) { cout << "Reserving version " << cnewver; if (vs_mnewverpar->host() != lhost || vs_mnewverpar->port() != lport) { cout << " at " << vs_mnewverpar->host() << ":" << vs_mnewverpar->port(); } cout << endl; } } |
If we're reserving a new version, find the master copy of the new version's parent (line 363).
Check whether the new version's parent's "type" attribute contain the value "package" with the VestaSource::inAttribs member function. If it doesn't contain this value, print an error message and exit unless the "force" command-line option was given.
380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 |
// // Look up master of sessdirpar and sanity check // if (!omit_sess) { if (!vs_msessdirpar) { vs_msessdirpar = ReposUI::filenameToMasterVS(csessdirpar, hints); } if (!force && !vs_msessdirpar->inAttribs("type", "checkout")) { cerr << program_name << ": " << csessdirpar << " is not a checkout directory" << endl; exit(2); } if (!quiet) { cout << "Creating session " << csessdir; if (vs_msessdirpar->host() != lhost || vs_msessdirpar->port() != lport) { cout << " at " << vs_msessdirpar->host() << ":" << vs_msessdirpar->port(); } cout << endl; } } |
If we're creating a session directory and we haven't already located the master copy of the session directory parent, find it now (line 385).
If the session directory parent's "type" attribute doesn't contain the value "checkout", print an error message and exit, unless the "force" command-line option was given.
409 |
vs_workdirpar = ReposUI::filenameToVS(cworkdirpar, lhost, lport); |
Look up the working directory parent in the local repository.
414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 |
Text cworkdirparpar, workdirpararc; ReposUI::split(cworkdirpar, cworkdirparpar, workdirpararc, "work-dir parent"); VestaSource* vs_workdirparpar = ReposUI::filenameToVS(cworkdirparpar, lhost, lport); VestaSource::errorCode err = vs_workdirparpar-> insertMutableDirectory(workdirpararc.cchars(), NULL, true, NULL, VestaSource::dontReplace, &vs_workdirpar); if (err != VestaSource::ok) { cerr << program_name << ": error creating " << cworkdirpar << ": " << ReposUI::errorCodeText(err) << endl; exit(2); } err = vs_workdirpar->setAttrib("#mode", mode); assert(err == VestaSource::ok); |
If the working directory parent doesn't exist, split its path to get its parent (lines 415-416), look up the parent (lines 417-418), and create a new mutable directory for the working directory parent with the VestaSource::insertMutableDirectory member function (lines 419-422). After creating it, give it a "#mode" attribute with the VestaSource::setAttrib member function (line 429).
This handles the case of a new user performing their first checkout. Unless they've explicitly created the directory "/vesta-work/username", it won't exist, which is why vcheckout creates it here.
435 436 437 438 439 440 441 442 443 444 |
if (vs_workdirpar && default_work) { // Make name unique by adding numeric suffix if needed VestaSource* vs_dummy; VestaSource::errorCode err = vs_workdirpar->lookup(workdirarc.cchars(), vs_dummy); if (err != VestaSource::notFound) { workdirarc = ReposUI::uniquify(vs_workdirpar, workdirarc); } cworkdir = cworkdirpar + "/" + workdirarc; } |
If the working directory name is to be automatically determined, see if the working directory name already exists by looking it up with the VestaSource::lookup member function (lines 438-439). If it does exist, uniquify it by appending ".N" (line 441).
450 451 452 453 |
if (query) { cerr << program_name << ": nothing done (-Q flag given)" << endl; exit(0); } |
If operating in "query" mode, exit without making any more changes.
468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 |
// // If needed, construct action to create newver remotely // and replicator directives to replicate it here. // VestaSource* vs_newverpar = NULL; VestaSource* vs_newver = NULL; if (!omit_new) { if (vs_mnewverpar->host() == lhost && vs_mnewverpar->port() == lport) { // Master is local vs_newverpar = vs_mnewverpar; } else { // Master is remote rvsa1 = new VestaSourceAtomic(vs_mnewverpar->host(), vs_mnewverpar->port()); VestaSourceAtomic::VSIndex vsi_mnewverpar = rvsa1->decl(cnewverpar, vs_mnewverpar); VestaSourceAtomic::VSIndex vsi_mnewver = rvsa1->insertStub(cnewver, vsi_mnewverpar, newverarc, true, VestaSource::dontReplace); if (!omit_old) { rvsa1->setAttrib(cnewver, vsi_mnewver, "old-version", coldver); } if (!omit_sess) { rvsa1->setAttrib(cnewver, vsi_mnewver, "session-dir", csessdir); } rvsa1->setAttrib(cnewver, vsi_mnewver, "checkout-time", timebuf); rvsa1->setAttrib(cnewver, vsi_mnewver, "checkout-by", cuser); rvsa1->setAttrib(cnewver, vsi_mnewver, "checkout-from", vs_mnewverpar->host() + ":" + vs_mnewverpar->port()); rvsa1->setAttrib(cnewver, vsi_mnewver, "checkout-to", lhost + ":" + lport); rvsa1->setAttrib(cnewver, vsi_mnewver, "#mode", mode); repl1 = new Replicator(vs_mnewverpar->host(), vs_mnewverpar->port(), lhost, lport); direcs1 = new Replicator::DirectiveSeq; Replicator::Directive d('+', cnewver.Sub(vlen)); direcs1->addhi(d); } } |
Determine whether the master copy of the new version parent is local. If it isn't, construct a repository atomic action and replication objects to create the new version remotely and replicate it to the local repository.
Lines 475-476 check whether the master new version parent is local. If it is, there's no remote atomic action or replication required, as the new version can be created in the local repository.
Lines 481-482 create an instance of VestaSourceAtomic that will be used to create the new version in the remote master repository. The first step in the atomic action (lines 483-484) is to declare the object we'll be working with (the new version's parent). The second step (lines 485-487) is to create the new version reservation stub. Subsequent actions (lines 488-500) set attributes on the newly created version reservation.
Finally, instances of Replicator and Replicator::DirectiveSeq are created to replicate the new version reservation stub to the local repository (lines 502-506).
510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 |
// // If needed, construct action to create sessdir remotely // and replicator directives to replicate it here. // VestaSource* vs_sessdirpar = NULL; VestaSource* vs_sessdir = NULL; if (!omit_sess) { if (vs_msessdirpar->host() == lhost && vs_msessdirpar->port() == lport) { // Master is local vs_sessdirpar = vs_msessdirpar; } else { // Master is remote if (rvsa1 != NULL && vs_msessdirpar->host() == vs_mnewverpar->host() && vs_msessdirpar->port() == vs_mnewverpar->port()) { rvsa2 = rvsa1; repl2 = repl1; direcs2 = direcs1; } else { rvsa2 = new VestaSourceAtomic(vs_msessdirpar->host(), vs_msessdirpar->port()); repl2 = new Replicator(vs_msessdirpar->host(), vs_msessdirpar->port(), lhost, lport); direcs2 = new Replicator::DirectiveSeq; } VestaSourceAtomic::VSIndex vsi_msessdirpar = rvsa2->decl(csessdirpar, vs_msessdirpar); VestaSourceAtomic::VSIndex vsi_msessdir = rvsa2->insertAppendableDirectory(csessdir, vsi_msessdirpar, sessdirarc, true, VestaSource::dontReplace); if (!omit_old) { rvsa2->setAttrib(csessdir, vsi_msessdir, "old-version", coldver); } if (!omit_new) { rvsa2->setAttrib(csessdir, vsi_msessdir, "new-version", cnewver); } rvsa2->setAttrib(csessdir, vsi_msessdir, "checkout-time", timebuf); rvsa2->setAttrib(csessdir, vsi_msessdir, "checkout-by", cuser); rvsa2->setAttrib(csessdir, vsi_msessdir, "checkout-from", (vs_msessdirpar->host() + ":" + vs_msessdirpar->port())); rvsa2->setAttrib(csessdir, vsi_msessdir, "checkout-to", (lhost + ":" + lport)); rvsa2->setAttrib(csessdir, vsi_msessdir, "type", "session"); rvsa2->setAttrib(csessdir, vsi_msessdir, "#mode", mode); Replicator::Directive d1('+', csessdir.Sub(vlen)); Replicator::Directive d2('+', cslatest.Sub(vlen)); direcs2->addhi(d1); direcs2->addhi(d2); } } |
Similar to lines 472-508, determine whether the master copy of the session directory parent is local or in the same repository as the master copy of the new version parent. If necessary, construct a repository atomic action and replication objects, or append to the existing ones.
Lines 517-518 check whether the the master copy of the session directory parent is local. If it is, there's no remote atomic action or replication required.
Lines 523-528 handle the case where the master copy of the session directory parent is in the same repository as the master copy of the new version parent. In this case, we add more atomic actions and replication directives to the existing objects.
Lines 530-534 handle the case where the master copy of the session directory parent is not in the local repository and not in the repository with the master copy of the new version parent. In this case, new instances of VestaSourceAtomic, Replicator, and Replicator::DirectiveSeq are created.
Lines 536-556 add steps to the atomic action for creating and adding attributes to the session directory.
Lines 558-561 add replication directives to the replication object.
565 566 567 568 569 570 |
// // Disable ^C signal to make it less likely that we will be // killed after having done one or more remote actions but // before having finished the whole job. // signal(SIGINT, SIG_IGN); |
Ignore the interrupt signal (generated by the user hitting control-C). This provides some minor protection against a half-completed checkout when multiple repositories are involved.
See the signal(2) man page.
572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 |
// // Run the remote action(s) and replication(s) // if (rvsa1) { if (!rvsa1->run()) { VestaSource::errorCode err; if (rvsa1->lasterr == VestaSource::ok) { err = rvsa1->okreplace; } else { err = rvsa1->lasterr; } cerr << program_name << ": " << rvsa1->name(rvsa1->ndone) << " at " << vs_mnewverpar->host() << ":" << vs_mnewverpar->port() << ": " << ReposUI::errorCodeText(err) << endl; exit(2); } repl1->replicate(direcs1, rflags); } if (rvsa2 && rvsa2 != rvsa1) { if (!rvsa2->run()) { VestaSource::errorCode err; if (rvsa2->lasterr == VestaSource::ok) { err = rvsa2->okreplace; } else { err = rvsa2->lasterr; } cerr << program_name << ": " << rvsa2->name(rvsa2->ndone) << " at " << vs_msessdirpar->host() << ":" << vs_msessdirpar->port() << ": " << ReposUI::errorCodeText(err) << endl; exit(2); } repl2->replicate(direcs2, rflags); } |
Run the one or two remote atomic actions and perform the one or two replications to the local repository.
606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 |
// // Do the mastership transfer(s) // if (!omit_new && vs_newverpar == NULL) { VestaSource::errorCode err = VDirSurrogate::acquireMastership(cnewver.Sub(vlen).cchars(), lhost, lport, vs_mnewverpar->host(), vs_mnewverpar->port()); if (err != VestaSource::ok) { cerr << program_name << ": error acquiring mastership of " << cnewver << ": " << ReposUI::errorCodeText(err) << endl; exit(2); } vs_newverpar = ReposUI::filenameToVS(cnewverpar, lhost, lport); vs_newver = ReposUI::filenameToVS(cnewver, lhost, lport); } if (!omit_sess && vs_sessdirpar == NULL) { VestaSource::errorCode err = VDirSurrogate::acquireMastership(csessdir.Sub(vlen).cchars(), lhost, lport, vs_msessdirpar->host(), vs_msessdirpar->port()); if (err != VestaSource::ok) { cerr << program_name << ": error acquiring mastership of " << csessdir << ": " << ReposUI::errorCodeText(err) << endl; exit(2); } vs_sessdirpar = ReposUI::filenameToVS(csessdirpar, lhost, lport); vs_sessdir = ReposUI::filenameToVS(csessdir, lhost, lport); } |
Transfer mastership of the remotely created objects to the local repository using VDirSurrogate::acquireMastership. First, the local repository acquires mastership of the new version (lines 610-614). Second, the local repository acquires mastership of the session directory (lines 624-628).
The local repository needs mastership of the session directory so that vadvance can take immutable snapshots of the working directory. The local repository needs mastership of the new version reservation stub so that vcheckin can replace it with an immutable directory.
638 639 640 641 642 643 644 |
// // Construct the local atomic action // VestaSourceAtomic vsa(lhost, lport); VestaSourceAtomic::VSIndex vsi_oldver = -1, vsi_newverpar = -1, vsi_sessdirpar = -1, vsi_workdirpar = -1, vsi_newver = -1, vsi_sessdir = -1; |
Construct a VestaSourceAtomic object for performing the local action. (This may include all the work of vcheckout if all objects are mastered locally.) Declare several VestaSourceAtomic::VSIndex variables that will be used while constructing the local atomic action.
649 650 651 652 653 654 655 656 |
if (!omit_old) { vsi_oldver = vsa.decl(coldver, vs_oldver); vsa.typeCheck(coldver, vsi_oldver, VestaSourceAtomic:: typebit(VestaSource::immutableDirectory), VestaSource::inappropriateOp); vsa.accessCheck(coldver, vsi_oldver, AccessControl::read, true, VestaSource::noPermission); } |
If we have an old version, declare the old version to the atomic action. Add steps to check that it is an immutable directory and that the user has read access to it.
658 |
vsi_newverpar = vsa.decl(cnewverpar, vs_newverpar); |
Declare the new version parent to the atomic action.
660 661 662 663 664 665 666 667 |
// newver already exists vsi_newver = vsa.decl(cnewver, vs_newver); vsa.typeCheck(cnewver, vsi_newver, VestaSourceAtomic:: typebit(VestaSource::stub), VestaSource::inappropriateOp); vsa.testMaster(cnewver, vsi_newver, true, VestaSource::notMaster); vsa.accessCheck(cnewver, vsi_newver, AccessControl::write, true, VestaSource::noPermission); |
In the case where the new version already exists (because it was created in a remote repository), declare it to the atomic action. Also, check that it is a stub, that its master flag is set, and that the user has write access to it.
669 670 671 672 673 674 675 676 677 678 679 680 681 |
// newver will be created in newverpar vsa.typeCheck(cnewverpar, vsi_newverpar, VestaSourceAtomic:: typebit(VestaSource::appendableDirectory), VestaSource::inappropriateOp); vsa.testMaster(cnewverpar, vsi_newverpar, true, VestaSource::notMaster); vsa.accessCheck(cnewverpar, vsi_newverpar, AccessControl::write, true, VestaSource::noPermission); // Be sure new version doesn't already exist vsa.setTarget("", VestaSource::notFound, VestaSource::notFound, VestaSource::nameInUse); vsa.lookup(cnewver, vsi_newverpar, newverarc); vsa.setTarget(""); |
In the case where the new version doesn't exist, check that the new version parent is an appendable directory, that its master flag is set, and that the user has write permission to it. Also, check that the new version to be created doesn't already exist.
685 |
vsi_sessdirpar = vsa.decl(csessdirpar, vs_sessdirpar); |
Declare the session directory parent to the atomic action.
687 688 689 690 691 692 693 694 |
// sessdir already exists vsi_sessdir = vsa.decl(csessdir, vs_sessdir); vsa.typeCheck(csessdir, vsi_sessdir, VestaSourceAtomic:: typebit(VestaSource::appendableDirectory), VestaSource::inappropriateOp); vsa.testMaster(csessdir, vsi_sessdir, true, VestaSource::notMaster); vsa.accessCheck(csessdir, vsi_sessdir, AccessControl::write, true, VestaSource::noPermission); |
In the case where the session directory already exists (because it was created in a remote repository), declare it to the atomic action. Also, check that it is an appendable directory, that its master flag is set, and that the user has write access to it.
697 698 699 700 701 702 703 704 705 706 707 708 709 |
// sessdir will be created in sessdirpar vsa.typeCheck(csessdirpar, vsi_sessdirpar, VestaSourceAtomic:: typebit(VestaSource::appendableDirectory), VestaSource::inappropriateOp); vsa.testMaster(csessdirpar, vsi_sessdirpar, true, VestaSource::notMaster); vsa.accessCheck(csessdirpar, vsi_sessdirpar, AccessControl::write, true, VestaSource::noPermission); // Be sure sessdir doesn't already exist vsa.setTarget("", VestaSource::notFound, VestaSource::notFound, VestaSource::nameInUse); vsa.lookup(csessdir, vsi_sessdirpar, sessdirarc); vsa.setTarget("setTarget"); |
In the case where the session directory doesn't exist, check that the session directory parent is an appendable directory, that its master flag is set, and that the user has write permission to it. Also, check that the session directory to be created doesn't already exist.
712 713 714 715 716 717 718 719 720 721 722 723 724 |
if (!omit_work) { vsi_workdirpar = vsa.decl(cworkdirpar, vs_workdirpar); vsa.typeCheck(cworkdirpar, vsi_workdirpar, VestaSourceAtomic:: typebit(VestaSource::mutableDirectory), VestaSource::inappropriateOp); vsa.accessCheck(cworkdirpar, vsi_workdirpar, AccessControl::write, true, VestaSource::noPermission); // Be sure workdir doesn't already exist vsa.setTarget("", VestaSource::notFound, VestaSource::notFound, VestaSource::nameInUse); vsa.lookup(cworkdir, vsi_workdirpar, workdirarc); vsa.setTarget(""); } |
If a working directory is being created, declare the working directory parent to the atomic action. Then check that it is a mutable directory and that the user has write permission to it. Finally, check that the working directory to be created doesn't already exist.
729 730 731 732 733 734 |
if (!vs_newver) { // Insert reservation stub for newver vsi_newver = vsa.insertStub(cnewver, vsi_newverpar, newverarc, true, VestaSource::dontReplace); } |
In the case where the new version needs to be created locally, add a step to create it.
736 737 738 739 740 741 742 743 744 745 746 747 748 |
// Set attributes on newver stub if (!omit_old) { vsa.setAttrib(cnewver, vsi_newver, "old-version", coldver); } if (!omit_sess) { vsa.setAttrib(cnewver, vsi_newver, "session-dir", csessdir); } if (!omit_work) { vsa.setAttrib(cnewver, vsi_newver, "work-dir", cworkdir); } vsa.setAttrib(cnewver, vsi_newver, "checkout-time", timebuf); vsa.setAttrib(cnewver, vsi_newver, "checkout-by", cuser); vsa.setAttrib(cnewver, vsi_newver, "#mode", mode); |
Add steps to the action to set attributes on the new version.
752 753 754 755 756 757 |
if (!vs_sessdir) { // Insert session directory vsi_sessdir = vsa.insertAppendableDirectory(csessdir, vsi_sessdirpar, sessdirarc, true, VestaSource::dontReplace); } |
In the case where the session directory needs to be created locally, add a step to create it.
758 759 760 761 762 763 764 765 766 767 768 769 770 771 |
// Set attribs on session directory if (!omit_old) { vsa.setAttrib(csessdir, vsi_sessdir, "old-version", coldver); } if (!omit_new) { vsa.setAttrib(csessdir, vsi_sessdir, "new-version", cnewver); } if (!omit_work) { vsa.setAttrib(csessdir, vsi_sessdir, "work-dir", cworkdir); } vsa.setAttrib(csessdir, vsi_sessdir, "checkout-time", timebuf); vsa.setAttrib(csessdir, vsi_sessdir, "checkout-by", cuser); vsa.setAttrib(csessdir, vsi_sessdir, "type", "session"); vsa.setAttrib(csessdir, vsi_sessdir, "#mode", mode); |
Add steps to the action to set attributes on the session directory.
773 774 775 776 777 778 |
// Insert session version 0 Text csessver0 = csessdir + "/0"; if (!omit_old) { vsa.insertImmutableDirectory(csessver0, vsi_sessdir, "0", vsi_oldver, true, VestaSource::dontReplace); } |
If there's an old version, insert a copy of it in the session directory with the name "0".
780 781 782 783 784 785 |
// Insert "latest" link in session Text clatest = csessdir + "/latest"; VestaSourceAtomic::VSIndex vsi_latest = vsa.insertStub(clatest, vsi_sessdir, "latest", true, VestaSource::dontReplace); vsa.setAttrib(clatest, vsi_latest, "symlink-to", "$LAST"); |
Add steps to the action to insert the "latest" symbolic link in the session directory.
789 790 791 792 793 794 |
// Insert working directory // Note: okay for vsi_oldver to be -1 here; // this makes workdir empty. VestaSourceAtomic::VSIndex vsi_workdir = vsa.insertMutableDirectory(cworkdir, vsi_workdirpar, workdirarc, vsi_oldver, true, VestaSource::dontReplace); |
Add an action to create the working directory.
795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 |
// Set attribs on workdir if (!omit_old) { vsa.setAttrib(cworkdir, vsi_workdir, "old-version", coldver); } if (!omit_new) { vsa.setAttrib(cworkdir, vsi_workdir, "new-version", cnewver); } if (!omit_sess) { vsa.setAttrib(cworkdir, vsi_workdir, "session-dir", csessdir); if (!omit_old) { vsa.setAttrib(cworkdir, vsi_workdir, "session-ver-arc", "0"); } } vsa.setAttrib(cworkdir, vsi_workdir, "checkout-time", timebuf); vsa.setAttrib(cworkdir, vsi_workdir, "checkout-by", cuser); |
Add actions to set attributes on the working directory.
812 813 814 815 816 817 818 819 820 821 822 823 824 825 |
// // Run the local action // if (!vsa.run()) { VestaSource::errorCode err; if (vsa.lasterr == VestaSource::ok) { err = vsa.okreplace; } else { err = vsa.lasterr; } cerr << program_name << ": " << vsa.name(vsa.ndone) << ": " << ReposUI::errorCodeText(err) << endl; exit(2); } |
Execute the local repository atomic action. If it fails, print an error message and exit.
A class for holding ASCII text strings. Declared in Text.H (in /vesta/vestasys.org/basics/basics).
A template class used to hold a linear sequence of other objects. Declared in Sequence.H (in /vesta/vestasys.org/basics/generics).
A collection of functions making up the interface to the Vesta configuration file. Declared in VestaConfig.H (in /vesta/vestasys.org/vesta/config).
static member functions called by vcheckout:
The repository's access control API. Declared in AccessControl.H (in /vesta/vestasys.org/vesta/repos).
Member types used in vcheckout:
static member functions used in vcheckout:
A base class that represents objects in the repository. In client applications, instances of the derived class VDirSurrogate are used.
See VestaSource.H (in /vesta/vestasys.org/vesta/repos) for the class declararion.
Member functions called in vcheckout:
A class derived from VestaSource used in clienat applications. Most member functions implementations are actually remote procedure calls to the repository server. Also has static member functions for other operations clients need.
See VDirSurrogate.H (in /vesta/vestasys.org/vesta/repos). for the class declaration.
static member functions called in vcheckout:
A helper class for performing replications between repositories. An instance of Replicator is used for performing replications from a particular source repository to a particular destination repository.
See Replicator.H (in /vesta/vestasys.org/vesta/repltools) for the class declaration.
Member types used in vcheckout:
Member functions called in vcheckout:
A collection of helper functions to make writing repository tools easier. Declared in ReposUI.H (in /vesta/vestasys.org/vesta/repos_ui).
static member functions called in vcheckout:
An alternative interface to the repository which allows a sequence of operations to be performed in a single atomic step. Most steps that can be sued in an atomic action correspond to VestaSource member functions. Declared in VestaSourceAtomic.H (in /vesta/vestasys.org/vesta/repos).
Member types used in vcheckout:
Member functions called in vcheckout: