00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026 #include <Basics.H>
00027 #include <Text.H>
00028 #include <VestaConfig.H>
00029 #include <VestaSource.H>
00030 #include <VDirSurrogate.H>
00031 #include <VestaSourceAtomic.H>
00032 #include <Replicator.H>
00033 #include <signal.h>
00034 #include "ReposUI.H"
00035
00036 #if !defined(__sun__)
00037 extern "C" {
00038 #include <getopt.h>
00039 }
00040 #endif
00041
00042 using std::cout;
00043 using std::cerr;
00044 using std::endl;
00045
00046 Text program_name;
00047
00048 void usage()
00049 {
00050 cerr << "Usage: " << program_name << endl <<
00051 " [-q] [-Q] [-a | -A] [-f] [-F]" << endl <<
00052 " [-o old-version | -O]" << endl <<
00053 " [-m message | -M]" << endl <<
00054 " [-h hints]" << endl <<
00055 " [-R repos]" << endl <<
00056 " package/branch" << endl << endl;
00057 exit(1);
00058 }
00059
00060 int
00061 main(int argc, char* argv[])
00062 {
00063 program_name = argv[0];
00064 try {
00065
00066
00067
00068 Text defpkgpar, timefmt, repos, defhints, foreignparentdir;
00069 bool default_repos = true;
00070
00071
00072
00073
00074 bool acquire_mastership = false;
00075
00076 defpkgpar = VestaConfig::get_Text("UserInterface",
00077 "DefaultPackageParent");
00078 timefmt = VestaConfig::get_Text("UserInterface", "TimeFormat");
00079 (void) VestaConfig::get("UserInterface", "DefaultHints", defhints);
00080
00081 bool prompt_msg = true, omit_msg = false;
00082 if(VestaConfig::is_set("UserInterface", "vbranch_message")) {
00083 if(!VestaConfig::get_bool("UserInterface", "vbranch_message")) {
00084 prompt_msg = false;
00085 omit_msg = true;
00086 }
00087 }
00088
00089
00090 if(VestaConfig::get("UserInterface", "ForeignParent", foreignparentdir))
00091 {
00092 foreignparentdir = ReposUI::canonicalize(foreignparentdir);
00093
00094 if(foreignparentdir[foreignparentdir.Length()-1] != '/')
00095 foreignparentdir += "/";
00096 }
00097
00098 time_t now = time(NULL);
00099 char timebuf[256];
00100 strftime(timebuf, sizeof(timebuf), timefmt.cchars(), localtime(&now));
00101 Text cuser(AccessControl::self()->user());
00102 Text user(cuser.Sub(0, cuser.FindChar('@')));
00103 Text lhost(VDirSurrogate::defaultHost());
00104 Text lport(VDirSurrogate::defaultPort());
00105 const int vlen = 7;
00106 const Replicator::Flags rflags = (Replicator::Flags)
00107 (Replicator::attrNew | Replicator::attrOld | Replicator::attrAccess |
00108 Replicator::revive | Replicator::inclStubs | Replicator::latest);
00109
00110 if(VestaConfig::is_set("UserInterface", "vbranch_acquires"))
00111 {
00112 acquire_mastership = VestaConfig::get_bool("UserInterface",
00113 "vbranch_acquires");
00114 }
00115
00116
00117
00118
00119 bool quiet = false;
00120 bool query = false;
00121 bool force = false;
00122 bool omit_old = false, default_old = true;
00123 bool foreign_branch = false;
00124
00125 Text pkg, oldver, hints, message;
00126
00127 opterr = 0;
00128 for (;;) {
00129 int c = getopt(argc, argv, "qQaAfFo:Om:Mh:R:");
00130 if (c == EOF) break;
00131 switch (c) {
00132 case 'q':
00133 quiet = true;
00134 break;
00135 case 'Q':
00136 query = true;
00137 break;
00138 case 'a':
00139 acquire_mastership = true;
00140 break;
00141 case 'A':
00142 acquire_mastership = false;
00143 break;
00144 case 'f':
00145 force = true;
00146 break;
00147 case 'F':
00148 foreign_branch = true;
00149 break;
00150 case 'o':
00151 oldver = optarg;
00152 omit_old = false;
00153 default_old = false;
00154 break;
00155 case 'O':
00156 omit_old = true;
00157 default_old = false;
00158 break;
00159 case 'm':
00160 message = optarg;
00161 omit_msg = false;
00162 if(message == "-") {
00163 message = "";
00164 prompt_msg = true;
00165 }
00166 else
00167 prompt_msg = false;
00168 break;
00169 case 'M':
00170 omit_msg = true;
00171 prompt_msg = false;
00172 break;
00173 case 'h':
00174 hints = optarg;
00175 break;
00176 case 'R':
00177 repos = optarg;
00178 default_repos = false;
00179 break;
00180 case '?':
00181 default:
00182 usage();
00183 }
00184 }
00185 if (argc != optind + 1) {
00186 usage();
00187 }
00188 pkg = Text(argv[optind]);
00189
00190
00191 if(foreign_branch && foreignparentdir.Empty()) {
00192 cerr << program_name
00193 << (": [UserInterface]ForeignParent not set; "
00194 "-F flag requires config setting") << endl;
00195 exit(1);
00196 }
00197
00198
00199
00200
00201
00202 if (!default_repos) {
00203 int colon = repos.FindCharR(':');
00204 if (colon == -1) {
00205 lhost = repos;
00206 repos = repos + ":" + lport;
00207 } else {
00208 lhost = repos.Sub(0, colon);
00209 lport = repos.Sub(colon+1);
00210 }
00211 hints = hints + " " + repos;
00212 }
00213 hints = hints + " " + defhints;
00214
00215
00216 if (!query && prompt_msg) {
00217 message = ReposUI::getMessage("branch description");
00218 }
00219
00220
00221
00222
00223 Text cpkg, cparent, newpart, coldver, ccheckout, clatest, cslatest, cver0;
00224 cpkg = ReposUI::canonicalize(pkg, defpkgpar);
00225
00226
00227
00228 if(foreign_branch &&
00229 (cpkg.Sub(0, foreignparentdir.Length()) == foreignparentdir)) {
00230 foreign_branch = false;
00231 }
00232 ReposUI::split(cpkg, cparent, newpart, "branch name");
00233
00234
00235 if (!omit_old) {
00236 if (default_old) {
00237 int endpos = 0;
00238 while (isdigit(newpart[endpos])) {
00239 endpos++;
00240 }
00241 if (endpos == 0) {
00242 cerr << program_name <<
00243 ": can't determine branch base: " <<
00244 "branch name does not begin with digits" << endl;
00245 exit(2);
00246 }
00247 coldver = cpkg.Sub(0, cparent.Length() + 1 + endpos);
00248 } else {
00249 coldver = ReposUI::canonicalize(oldver, cparent);
00250 }
00251 }
00252
00253 if(foreign_branch) {
00254
00255
00256
00257
00258
00259 cparent = foreignparentdir +
00260 ReposUI::stripSpecificRoot(cparent, ReposUI::VESTA, true);
00261 cpkg = cparent + "/" + newpart;
00262 }
00263
00264 ccheckout = cpkg + "/checkout";
00265 clatest = cpkg + "/latest";
00266 cslatest = cpkg + "/checkout/latest";
00267 cver0 = cpkg + "/0";
00268
00269
00270
00271
00272
00273 VestaSource* vs_mparent = NULL;
00274 try {
00275 vs_mparent = ReposUI::filenameToMasterVS(cparent, hints);
00276 }
00277 catch(ReposUI::failure f) {
00278 if(foreign_branch) {
00279 if(!query) {
00280 Text pathname = cparent.Sub(foreignparentdir.Length());
00281 vs_mparent =
00282 ReposUI::lookupCreatePath(foreignparentdir, pathname, hints);
00283 if(!vs_mparent->inAttribs("type", "package")) {
00284 vs_mparent->addAttrib("type", "package");
00285 }
00286 }
00287 }
00288 else {
00289 throw(f);
00290 }
00291 }
00292
00293 Text mhost, mport;
00294 if(vs_mparent) {
00295
00296
00297 if (!force && !vs_mparent->inAttribs("type", "package")) {
00298 cerr << program_name << ": " << cparent << " at "
00299 << vs_mparent->host() << ":" << vs_mparent->port()
00300 << " is not a package or branch" << endl;
00301 exit(2);
00302 }
00303 mhost = vs_mparent->host();
00304 mport = vs_mparent->port();
00305 }
00306 else {
00307
00308
00309
00310 VestaSource* vs_mforeignparent =
00311 ReposUI::filenameToMasterVS(foreignparentdir, hints);
00312 mhost = vs_mforeignparent->host();
00313 mport = vs_mforeignparent->port();
00314 delete vs_mforeignparent;
00315 }
00316
00317 bool remote = (mhost != lhost || mport != lport);
00318 Text hint_dir;
00319 if(remote && vs_mparent) {
00320
00321 hint_dir = ReposUI::getMasterHintDir(vs_mparent, cparent);
00322 }
00323
00324
00325
00326
00327
00328 VestaSource* vs_moldver = NULL;
00329 VestaSource* vs_roldver = NULL;
00330 if (!omit_old) {
00331 vs_roldver = ReposUI::filenameToRealVS(coldver, hints);
00332 if (vs_roldver->type == VestaSource::stub) {
00333 cerr << program_name << ": " << coldver
00334 << " is not checked in yet" << endl;
00335 exit(2);
00336 } else if (vs_roldver->type == VestaSource::ghost) {
00337 cerr << program_name << ": " << coldver
00338 << " has been deleted" << endl;
00339 exit(2);
00340 }
00341
00342
00343 if(vs_roldver->host() == mhost && vs_roldver->port() == mport) {
00344 vs_moldver = vs_roldver;
00345 }
00346 else {
00347 try {
00348 vs_moldver = ReposUI::filenameToVS(coldver, mhost, mport);
00349
00350
00351 if(vs_moldver->type != vs_roldver->type) {
00352 delete vs_moldver;
00353 vs_moldver = NULL;
00354 }
00355 }
00356 catch(...){
00357
00358 }
00359 if(!vs_moldver && !query){
00360
00361 if (!quiet) {
00362 cout << "Replicating " << coldver << " from "
00363 << vs_roldver->host() << ":" << vs_roldver->port()
00364 << " to " << mhost << ":" << mport << endl;
00365 }
00366 Replicator repl(vs_roldver->host(), vs_roldver->port(),
00367 mhost, mport);
00368 Replicator::DirectiveSeq direcs;
00369 Replicator::Directive d('+', coldver.Sub(vlen));
00370 direcs.addhi(d);
00371 repl.replicate(&direcs, rflags);
00372 vs_moldver = ReposUI::filenameToVS(coldver, mhost, mport);
00373 }
00374 }
00375 }
00376
00377 if (!quiet) {
00378 cout << "Creating branch " << cpkg;
00379 if (remote) {
00380 cout << " at " << mhost << ":" << mport;
00381 }
00382 cout << endl;
00383 }
00384
00385 if (query) {
00386 cerr << program_name << ": nothing done (-Q flag given)" << endl;
00387 exit(0);
00388 }
00389
00390
00391
00392
00393 VestaSourceAtomic vsa(mhost, mport);
00394
00395
00396 VestaSourceAtomic::VSIndex vsi_parent =
00397 vsa.decl(cparent, vs_mparent);
00398 vsa.testMaster(cparent, vsi_parent, true,
00399 VestaSource::notMaster);
00400
00401
00402 VestaSourceAtomic::VSIndex vsi_oldver = -1;
00403 if (!omit_old) {
00404 assert(vs_moldver != 0);
00405 vsi_oldver = vsa.decl(coldver, vs_moldver);
00406
00407 vsa.typeCheck(coldver, vsi_oldver, VestaSourceAtomic::
00408 typebit(VestaSource::immutableDirectory),
00409 VestaSource::inappropriateOp);
00410 }
00411
00412
00413
00414 VestaSourceAtomic::VSIndex vsi_pkg =
00415 vsa.insertAppendableDirectory(cpkg, vsi_parent,
00416 newpart, true,
00417 VestaSource::dontReplace);
00418
00419
00420 VestaSourceAtomic::VSIndex vsi_ver0 = -1;
00421 if (!omit_old) {
00422 vsi_ver0 =
00423 vsa.insertImmutableDirectory(cver0, vsi_pkg, "0", vsi_oldver, true,
00424 VestaSource::dontReplace);
00425 }
00426
00427
00428 VestaSourceAtomic::VSIndex vsi_checkout =
00429 vsa.insertAppendableDirectory(ccheckout, vsi_pkg, "checkout", true,
00430 VestaSource::dontReplace);
00431
00432
00433 VestaSourceAtomic::VSIndex vsi_latest =
00434 vsa.insertStub(clatest, vsi_pkg, "latest", true,
00435 VestaSource::dontReplace);
00436 VestaSourceAtomic::VSIndex vsi_latest2 =
00437 vsa.insertStub(cslatest, vsi_checkout, "latest", true,
00438 VestaSource::dontReplace);
00439
00440
00441 vsa.setAttrib(cpkg, vsi_pkg, "created-by", cuser);
00442 vsa.setAttrib(cpkg, vsi_pkg, "creation-time", timebuf);
00443
00444
00445
00446
00447 vsa.addAttrib(cpkg, vsi_pkg, "type", "package");
00448 vsa.addAttrib(cpkg, vsi_pkg, "type", "branch");
00449 if (!omit_old) {
00450 vsa.setAttrib(cpkg, vsi_pkg, "old-version", coldver);
00451 }
00452
00453 if(!omit_msg && !message.Empty()){
00454 vsa.setAttrib(cpkg, vsi_pkg, "message", message);
00455 }
00456 if (vsi_ver0 != -1) {
00457 vsa.setAttrib(cver0, vsi_ver0, "old-version", coldver);
00458 }
00459 vsa.setAttrib(ccheckout, vsi_checkout, "type", "checkout");
00460 vsa.setAttrib(clatest, vsi_latest, "symlink-to", "$LAST");
00461 vsa.setAttrib(cslatest, vsi_latest2, "symlink-to", "$LAST");
00462
00463 if(!omit_old) {
00464 vsa.setTarget("", VestaSource::ok, VestaSource::noPermission);
00465 vsa.addAttrib(coldver + ": adding to next-branches", vsi_oldver,
00466 "next-branches", cpkg);
00467 vsa.setTarget("");
00468 }
00469
00470
00471
00472
00473
00474
00475 signal(SIGINT, SIG_IGN);
00476
00477
00478 if (!vsa.run()) {
00479 VestaSource::errorCode err;
00480 if (vsa.lasterr == VestaSource::ok) {
00481 err = vsa.okreplace;
00482 } else {
00483 err = vsa.lasterr;
00484 }
00485 cerr << program_name << ": " << vsa.name(vsa.ndone) << ": "
00486 << ReposUI::errorCodeText(err) << endl;
00487 exit(2);
00488 }
00489
00490
00491
00492 if(!omit_old && !quiet) {
00493 if(!vs_moldver->inAttribs("next-branches", cpkg.cchars())) {
00494 cerr << program_name << ": warning: "
00495 "next-branches attribute couldn't be added to version: "
00496 << coldver << " at "<< mhost << ":" << mport << endl;
00497 }
00498 }
00499
00500
00501 if (!remote) exit(0);
00502
00503
00504
00505
00506 Replicator repl(mhost, mport, lhost, lport);
00507 Replicator::DirectiveSeq direcs;
00508 Replicator::Directive d('+', cpkg.Sub(vlen));
00509 direcs.addhi(d);
00510 repl.replicate(&direcs, rflags);
00511
00512
00513
00514
00515
00516
00517 if(acquire_mastership)
00518 {
00519 VestaSource::errorCode err;
00520 err = VDirSurrogate::acquireMastership(cpkg.Sub(vlen).cchars(),
00521 lhost, lport, mhost, mport);
00522 if (err != VestaSource::ok) {
00523 cerr << program_name << ": error acquiring mastership of "
00524 << cpkg << ": " << ReposUI::errorCodeText(err) << endl;
00525 exit(2);
00526 }
00527 err = VDirSurrogate::acquireMastership(ccheckout.Sub(vlen).cchars(),
00528 lhost, lport, mhost, mport);
00529 if (err != VestaSource::ok) {
00530 cerr << program_name << ": error acquiring mastership of "
00531 << ccheckout << ": " << ReposUI::errorCodeText(err) << endl;
00532 exit(2);
00533 }
00534 }
00535
00536
00537 if(!omit_old && !query) {
00538 VestaSource* vs_oldver = NULL;
00539 if(vs_roldver->host() == lhost && vs_roldver->port() == lport)
00540 {
00541 vs_oldver = vs_roldver;
00542 }
00543 else
00544 {
00545 try
00546 {
00547 vs_oldver = ReposUI::filenameToVS(coldver, lhost, lport);
00548 }
00549 catch(ReposUI::failure)
00550 {
00551
00552 vs_oldver = NULL;
00553 }
00554 }
00555
00556
00557 if(vs_oldver)
00558 {
00559 VestaSource::errorCode err =
00560 vs_oldver->addAttrib("next-branches", cpkg.cchars());
00561 if(!quiet && (err != VestaSource::ok))
00562 {
00563 cerr << program_name << ": warning: "
00564 "next-branches attribute couldn't be added to version: "
00565 << coldver << endl;
00566 }
00567 }
00568 }
00569
00570
00571 Text master_hint = mhost + ":" + mport;
00572 VestaSource* vs_hint = NULL;
00573 vs_hint = ReposUI::filenameToVS(hint_dir, lhost, lport);
00574 if(!vs_hint->inAttribs("master-repository", master_hint.cchars())) {
00575 VestaSource::errorCode err =
00576 vs_hint->setAttrib("master-repository", master_hint.cchars());
00577 if(!quiet && (err != VestaSource::ok)) {
00578 cerr << program_name
00579 << ": warning: incorrect master-repository attribute of "
00580 << hint_dir << " couldn't be updated (should be "
00581 << master_hint << ")" << endl;
00582 }
00583 }
00584
00585
00586 } catch (VestaConfig::failure f) {
00587 cerr << program_name << ": " << f.msg << endl;
00588 exit(2);
00589 } catch (SRPC::failure f) {
00590 cerr << program_name
00591 << ": SRPC failure; " << f.msg << " (" << f.r << ")" << endl;
00592 exit(2);
00593 } catch (ReposUI::failure f) {
00594 cerr << program_name << ": " << f.msg << endl;
00595 exit(2);
00596 } catch(FS::Failure f) {
00597 cerr << program_name << ": " << f << endl;
00598 exit(2);
00599 }
00600
00601 return 0;
00602 }