Main Page | Namespace List | Class Hierarchy | Class List | Directories | File List | Namespace Members | Class Members | File Members

vbranch.C

Go to the documentation of this file.
00001 // Copyright (C) 2001, Compaq Computer Corporation
00002 // 
00003 // This file is part of Vesta.
00004 // 
00005 // Vesta is free software; you can redistribute it and/or
00006 // modify it under the terms of the GNU Lesser General Public
00007 // License as published by the Free Software Foundation; either
00008 // version 2.1 of the License, or (at your option) any later version.
00009 // 
00010 // Vesta is distributed in the hope that it will be useful,
00011 // but WITHOUT ANY WARRANTY; without even the implied warranty of
00012 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00013 // Lesser General Public License for more details.
00014 // 
00015 // You should have received a copy of the GNU Lesser General Public
00016 // License along with Vesta; if not, write to the Free Software
00017 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00018 
00019 //
00020 // vbranch.C
00021 //
00022 
00023 // Create a branch on a package in the Vesta repository.
00024 // See documentation in vbranch.1.mtex
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     // Read config file
00067     //
00068     Text defpkgpar, timefmt, repos, defhints, foreignparentdir;
00069     bool default_repos = true;
00070 
00071     // If the parent directory is mastered remotely, should we have
00072     // the local repository acquire mastership of the created package?
00073     // Defaults to on.
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     // get foreign parent dir
00090     if(VestaConfig::get("UserInterface", "ForeignParent", foreignparentdir))
00091       {
00092         foreignparentdir = ReposUI::canonicalize(foreignparentdir);
00093         // add last "/" if it is not present
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; // ==strlen("/vesta/")
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     // Parse command line
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     // check that [UserInterface]ForeignParent configuration is set 
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     // Use a nondefault repository if specified, and automatically
00200     // add it to the hints if not already present.
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     // get change history message
00216     if (!query && prompt_msg) {  
00217       message = ReposUI::getMessage("branch description");
00218     }
00219 
00220     //
00221     // Qualify and canonicalize names
00222     //
00223     Text cpkg, cparent, newpart, coldver, ccheckout, clatest, cslatest, cver0;
00224     cpkg = ReposUI::canonicalize(pkg, defpkgpar);
00225     // in a case of making a branch under foreign branch the -F flag 
00226     // should not be set since it will cause creation of multiple 
00227     // foreign dirs  
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     // define old version
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       // Foreign branches should be placed under foreignparentdir that is 
00255       // defined in vesta.cfg (usually /vesta/<current site>/foreign/).
00256       // The following part of the foreign branch parent path is cparent 
00257       // without root, so foreign branch parent will conatain 
00258       // /vesta/<current site>/foreign/<foreign site>/...
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     // Look up master of parent and sanity check 
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       // master of branch parent should be found in previous step
00296       // unless -Q -F flags are set and branch parent does not exist 
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       // find master of foreignparentdir to know repository, 
00308       // where branch parent should have been created if it was not 
00309       // for -Q flag.
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       // find directory whose master-repository attribute needs to be updated
00321       hint_dir = ReposUI::getMasterHintDir(vs_mparent, cparent);
00322     }
00323 
00324     //
00325     // Look up oldver, first replicating it to the master repository
00326     // where we'll create the branch if needed.
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       // find old version in the master repository
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           // If the target has a different type (e.g. stub or
00350           // ghost), we won't use it.
00351           if(vs_moldver->type != vs_roldver->type) {
00352             delete vs_moldver;
00353             vs_moldver = NULL;
00354           }
00355         }
00356         catch(...){
00357           // There is no old version in master repository
00358         }
00359         if(!vs_moldver && !query){
00360           // get a master replica 
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     } // if(!omit_old)
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     // Construct atomic action to create the branch
00392     //
00393     VestaSourceAtomic vsa(mhost, mport);
00394 
00395     // Declare parent directory and make sure it is master
00396     VestaSourceAtomic::VSIndex vsi_parent =
00397       vsa.decl(cparent, vs_mparent);
00398     vsa.testMaster(cparent, vsi_parent, true,
00399                    VestaSource::notMaster);
00400 
00401     // We also insert the old version  in the same action.
00402     VestaSourceAtomic::VSIndex vsi_oldver = -1;
00403     if (!omit_old) {
00404       assert(vs_moldver != 0);
00405       vsi_oldver = vsa.decl(coldver, vs_moldver);
00406       // Check that vs_moldver is an immutable directory, halt if not
00407       vsa.typeCheck(coldver, vsi_oldver, VestaSourceAtomic::
00408                     typebit(VestaSource::immutableDirectory),
00409                     VestaSource::inappropriateOp);
00410     }
00411 
00412     // Make package directory.  This is the last step that should
00413     // be able to halt, so we just try it; no need to test first.
00414     VestaSourceAtomic::VSIndex vsi_pkg =
00415       vsa.insertAppendableDirectory(cpkg, vsi_parent,
00416                                     newpart, true,
00417                                     VestaSource::dontReplace);
00418 
00419     // Insert version 0 if applicable
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     // Insert "checkout" subdirectory.
00428     VestaSourceAtomic::VSIndex vsi_checkout =
00429       vsa.insertAppendableDirectory(ccheckout, vsi_pkg, "checkout", true,
00430                                     VestaSource::dontReplace);
00431 
00432     // Insert "latest" links
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     // Set attributes
00441     vsa.setAttrib(cpkg, vsi_pkg, "created-by", cuser);
00442     vsa.setAttrib(cpkg, vsi_pkg, "creation-time", timebuf);
00443     // (We use add for both of the following because they have the
00444     // same timestamp, and we don't want the tiebreak algorithm to
00445     // sort an set after an add.  Since vsi_pkg is new, we know
00446     // the attributes were initially empty.)
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     // add message attribute
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     // add next-branches attribute to the old version (master)
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     // Disable ^C signal to make it less likely that we will be
00472     // killed after having done the remote action but
00473     // before having finished the whole job.
00474     //
00475     signal(SIGINT, SIG_IGN);
00476 
00477     // Commit
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     // check if the next-branches attribute was added to the old
00491     // version (master)
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     // Done if master was local
00501     if (!remote) exit(0);
00502 
00503     //
00504     // Replicate the new package here
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     // Take mastership of the package and its checkout directory if
00514     // requested.  (We don't bother getting mastership of the "latest"
00515     // links.)
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       } // if(acquire_mastership)
00535 
00536     // add next-branches attribute to the old version in local repository
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             // old version not present in local repository: ignore
00552             vs_oldver = NULL;
00553           }
00554         }
00555       
00556       // if there is old version in local repository add the attribute
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     } // if(!omit_old && !query)
00569     
00570     // Update master-repository hint
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 }

Generated on Mon May 8 00:48:55 2006 for Vesta by  doxygen 1.4.2