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

vcheckin.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 // vcheckin.C
00021 //
00022 
00023 // Check in the current version from a Vesta checkout session.
00024 // See vcheckin.1.mtex for documentation.
00025 
00026 
00027 #include <Basics.H>
00028 #include <Text.H>
00029 #include <VestaConfig.H>
00030 #include <VestaSource.H>
00031 #include <VDirSurrogate.H>
00032 #include <VestaSourceAtomic.H>
00033 #include <Replicator.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::cin;
00044 using std::cerr;
00045 using std::endl;
00046 
00047 Text program_name;
00048 
00049 void
00050 usage()
00051 {
00052   cerr << "Usage: " << program_name << endl <<
00053     "        [-q] [-Q] [-f]" << endl <<
00054     "        [-m message | -M]" << endl <<
00055     "        [-s session-dir | -S]" << endl <<
00056     "        [-c content | -C]" << endl <<
00057     "        [-n new-version]" << endl <<
00058     "        [-R repos]" << endl <<
00059     "        [-d dst] | -D]" << endl <<
00060     "        [[-w] work-dir | -W]" << endl << endl;
00061   exit(1);
00062 }
00063 
00064 
00065 int
00066 main(int argc, char* argv[])
00067 {
00068   program_name = argv[0];
00069   try {
00070     //
00071     // Read config file
00072     //
00073     Text defpkgpar, defworkpar, timefmt;
00074     defpkgpar = VestaConfig::get_Text("UserInterface",
00075                                       "DefaultPackageParent");
00076     defworkpar = VestaConfig::get_Text("UserInterface",
00077                                        "DefaultWorkParent");
00078     timefmt = VestaConfig::get_Text("UserInterface", "TimeFormat");
00079 
00080     bool prompt_msg = true, omit_msg = false;
00081     if(VestaConfig::is_set("UserInterface", "vcheckin_message")) {
00082       if(!VestaConfig::get_bool("UserInterface", "vcheckin_message")) {
00083         prompt_msg = false;
00084         omit_msg = true;
00085       }
00086     }
00087 
00088     time_t now = time(NULL);
00089     char timebuf[256];
00090     strftime(timebuf, sizeof(timebuf), timefmt.cchars(), localtime(&now));
00091     Text cuser(AccessControl::self()->user());
00092     Text user(cuser.Sub(0, cuser.FindChar('@')));
00093     Text lhost(VDirSurrogate::defaultHost());
00094     Text lport(VDirSurrogate::defaultPort());
00095     const int vlen = 7; // ==strlen("/vesta/")
00096     const Replicator::Flags rflags = (Replicator::Flags) 
00097       (Replicator::attrNew | Replicator::attrOld | Replicator::attrAccess |
00098        Replicator::revive | Replicator::inclStubs | Replicator::latest);
00099         
00100     // 
00101     // Parse command line
00102     //
00103     Text sessdir, content, newver, workdir, message, dstrepos, repos;
00104     bool no_workdir = false, no_sessdir = false;
00105     bool default_sessdir = true, default_content = true;
00106     bool default_newver = true, default_workdir = true;
00107     bool quiet = false, query = false, force = false;
00108     bool omit_dstrepos = false;
00109     bool default_repos = true;
00110     opterr = 0;
00111     for (;;) {
00112       int c = getopt(argc, argv, "qQfm:Ms:Sc:Cn:w:Wd:DR:");
00113       if (c == EOF) break;
00114       switch (c) {
00115       case 'q':
00116         quiet = true;
00117         break;
00118       case 'Q':
00119         query = true;
00120         break;
00121       case 'f':
00122         force = true;
00123         break;
00124       case 'm':
00125         message = optarg;
00126         omit_msg = false;
00127         if(message == "-") {
00128           message = "";
00129           prompt_msg = true;
00130         }
00131         else 
00132           prompt_msg = false;
00133         break;
00134       case 'M':
00135         omit_msg = true;
00136         prompt_msg = false;
00137         break;
00138       case 's':
00139         sessdir = optarg;
00140         default_sessdir = false;
00141         no_sessdir = false;
00142         break;
00143       case 'S':
00144         sessdir = "NONE";
00145         default_sessdir = false;
00146         no_sessdir = true;
00147         break;
00148       case 'c':
00149         content = optarg;
00150         default_content = false;
00151         break;
00152       case 'C':
00153         content = "-1";
00154         default_content = false;
00155         break;
00156       case 'n':
00157         newver = optarg;
00158         default_newver = false;
00159         break;
00160       case 'w':
00161         workdir = optarg;
00162         default_workdir = false;
00163         no_workdir = false;
00164         break;
00165       case 'W':
00166         workdir = "NONE";
00167         default_workdir = false;
00168         no_workdir = true;
00169         break;
00170       case 'd':
00171         dstrepos = optarg;
00172         omit_dstrepos = false;
00173         break;
00174       case 'D':
00175         omit_dstrepos = true;
00176         break;
00177       case 'R':
00178         repos = optarg;
00179         default_repos = false;
00180         break;
00181       case '?':
00182       default:
00183         usage();
00184       }
00185     }
00186         
00187     switch (argc - optind) {
00188     case 1:
00189       if (default_workdir) {
00190         workdir = argv[optind];
00191         default_workdir = false;
00192       }
00193       break;
00194     case 0:
00195       break;
00196     default:
00197       usage();
00198       /* not reached */
00199     }
00200         
00201     //
00202     // Use a nondefault repository if specified
00203     //
00204     if (!default_repos) {
00205       int colon = repos.FindCharR(':');
00206       if (colon == -1) {
00207         lhost = repos;
00208         repos = repos + ":" + lport;
00209       } else {
00210         lhost = repos.Sub(0, colon);
00211         lport = repos.Sub(colon+1);
00212       }
00213     }
00214 
00215     //
00216     // Fill in defaults as needed and look things up.
00217     //
00218     if (default_workdir) {
00219       workdir = ".";
00220     }
00221     VestaSource* vs_workdir = NULL;
00222     VestaSource* vs_workpar = NULL;
00223     Text cworkdir, cworkpar, workarc;
00224     if (!no_workdir) {
00225       cworkdir = ReposUI::canonicalize(workdir, defworkpar + "/" + user);
00226       ReposUI::split(cworkdir, cworkpar, workarc, "work-dir");
00227       vs_workdir = ReposUI::filenameToVS(cworkdir, lhost, lport);
00228       vs_workpar = ReposUI::filenameToVS(cworkpar, lhost, lport);
00229     }
00230 
00231     if (default_sessdir) {
00232       if (no_workdir) {
00233         cerr << program_name <<
00234           ": can't default session-dir; " <<
00235           "no working directory" << endl;
00236         exit(2);
00237       }
00238       // Get session-dir attribute of workdir
00239       char* attribval = vs_workdir->getAttrib("session-dir");
00240       if (attribval == NULL) {
00241         cerr << program_name <<
00242           ": can't default session-dir; " <<
00243           "work-dir has no session-dir attribute" << endl;
00244         exit(2);
00245       }
00246       sessdir = attribval;
00247       delete attribval;
00248     }
00249     VestaSource* vs_sessdir = NULL;
00250     Text csessdir;
00251     if (!no_sessdir) {
00252       csessdir = ReposUI::canonicalize(sessdir, defpkgpar);
00253       vs_sessdir = ReposUI::filenameToVS(csessdir, lhost, lport);
00254     }
00255 
00256     if (default_content) {
00257       if (no_sessdir) {
00258         cerr << program_name <<
00259           ": can't default content; " <<
00260           "no session directory to search in" << endl;
00261         exit(2);
00262       }
00263       long high = ReposUI::highver(vs_sessdir);
00264       char buf[64];
00265       sprintf(buf, "%ld", high);
00266       content = buf;
00267     }
00268     VestaSource* vs_content = NULL;
00269     Text ccontent;
00270     if (content != "-1") {
00271       ccontent = ReposUI::canonicalize(content, csessdir);
00272       vs_content = ReposUI::filenameToVS(ccontent, lhost, lport);
00273     }   
00274 
00275     if (default_newver) {
00276       // Get new-version attribute of session-dir, if any, else
00277       //  of work-dir, if any.
00278       char* attribval;
00279       if (!no_sessdir) {
00280         attribval = vs_sessdir->getAttrib("new-version");
00281         if (attribval == NULL) {
00282           cerr << program_name <<
00283             ": can't default new-version; " <<
00284             "session-dir has no new-version attribute" << endl;
00285           exit(2);
00286         }
00287       } else if (vs_workdir != NULL) {
00288         attribval = vs_workdir->getAttrib("new-version");
00289         if (attribval == NULL) {
00290           cerr << program_name <<
00291             ": can't default new-version; " <<
00292             "work-dir has no new-version attribute" << endl;
00293           exit(2);
00294         }
00295       } else {
00296         cerr << program_name <<
00297           ": can't default new-version; " <<
00298           "no session-dir or work-dir to examine" << endl;
00299         exit(2);
00300       }
00301       newver = attribval;
00302       delete attribval;
00303     }
00304     Text cnewver = ReposUI::canonicalize(newver, defpkgpar);
00305     Text cpkg, newverarc;
00306     ReposUI::split(cnewver, cpkg, newverarc, "new-version");
00307     VestaSource* vs_pkg = ReposUI::filenameToVS(cpkg, lhost, lport);
00308         
00309     // Look up newver stub and remember key attributes
00310     VestaSource *vs_newverstub;
00311     VestaSource::errorCode err =
00312       vs_pkg->lookup(newverarc.cchars(), vs_newverstub);
00313     if (err != VestaSource::ok) {
00314       cerr << program_name << ": " << cnewver << ": "
00315            << ReposUI::errorCodeText(err) << endl;
00316       exit(2);
00317     }
00318 
00319     // get stub attributes
00320     const char* stub_dstrepos = vs_newverstub->getAttrib("checkout-from");
00321     char* stub_msg = vs_newverstub->getAttrib("message");
00322 
00323     // --------------------------------------------------------------
00324     // TBD: This could be deleted in a future version once everyone
00325     // has a version of the repository that preserves attributes when
00326     // replacing a stub with an immutable directory.
00327     const char* stub_oldver = vs_newverstub->getAttrib("old-version");
00328     const char* stub_mstrepos = vs_newverstub->getAttrib("master-repository");
00329     // --------------------------------------------------------------
00330 
00331     if (dstrepos == "" && stub_dstrepos != NULL) {
00332       dstrepos = stub_dstrepos;
00333     }
00334     
00335     // edit history message
00336     if(!query && prompt_msg) {
00337       message = ReposUI::getMessage("change history message",
00338                                     "Checkout description",
00339                                     stub_msg);
00340     }
00341 
00342     // Sanity checking
00343     if (!force) {
00344       // Check types
00345       if (!no_sessdir && !vs_sessdir->inAttribs("type", "session")) {
00346         cerr << program_name << ": " << csessdir
00347           << " is not a session directory" << endl;
00348         exit(2);
00349       }
00350       if (!no_workdir &&
00351           vs_workdir->getAttrib("checkout-time") == NULL) {
00352         cerr << program_name << ": " << cworkdir
00353           << " is not a working directory" << endl;
00354         exit(2);
00355       }
00356       // Check that workdir has not been modified since the
00357       // last vadvance.  Omit check if the content was specified
00358       // explicitly; in that case we assume the user knew what
00359       // he was doing.
00360       //
00361       if (default_content && vs_workdir != NULL
00362           && ReposUI::changed(vs_workdir, vs_sessdir->timestamp())) {
00363         cerr << program_name << ": " << cworkdir 
00364              << " has been modified since last advance" << endl;
00365         exit(2);
00366       }
00367       // Check that newver is really a reservation stub
00368       if (vs_newverstub->type != VestaSource::stub ||
00369           !vs_newverstub->master ||
00370           vs_newverstub->getAttrib("checkout-time") == NULL) {
00371         cerr << program_name << ": " << cnewver
00372              << " is not a reservation stub" << endl;
00373         exit(2);
00374       }
00375       delete vs_newverstub;
00376     }
00377 
00378     if (!quiet) {
00379       cout << "Checking in " << cnewver << endl;
00380       if (!no_workdir) {
00381         cout << "Deleting " << cworkdir << endl;
00382       }
00383     }
00384 
00385     if (query) {
00386       cerr << program_name << ": nothing done (-Q flag given)" << endl;
00387       exit(0);
00388     }
00389 
00390     //
00391     // Do it, with failure atomicity
00392     //
00393     VestaSourceAtomic vsa(lhost, lport);
00394     VestaSourceAtomic::VSIndex vsi_content = -1,
00395       vsi_pkg = -1, vsi_workpar = -1, vsi_newverstub = -1,
00396       vsi_sessdir = -1, vsi_workdir = -1;
00397 
00398     // Do all error checking first, because once the atomic program
00399     // starts making changes, it can't roll back unless the
00400     // repository crashes.
00401 
00402     vsi_pkg = vsa.decl(cpkg, vs_pkg);
00403     vsa.typeCheck(cpkg, vsi_pkg, VestaSourceAtomic::
00404                   typebit(VestaSource::appendableDirectory),
00405                   VestaSource::inappropriateOp);
00406     vsa.accessCheck(cpkg, vsi_pkg, AccessControl::write,
00407                     true, VestaSource::noPermission);
00408 
00409     vsi_newverstub = vsa.lookup(cnewver, vsi_pkg, newverarc);
00410     vsa.typeCheck(cnewver, vsi_newverstub, VestaSourceAtomic::
00411                   typebit(VestaSource::stub), VestaSource::nameInUse);
00412     vsa.testMaster(cnewver, vsi_newverstub, true,
00413                    VestaSource::notMaster);
00414     vsa.accessCheck(cnewver, vsi_newverstub, AccessControl::write,
00415                     true, VestaSource::noPermission);
00416 
00417     if (vs_content) {
00418       vsi_content = vsa.decl(ccontent, vs_content);
00419       vsa.typeCheck(ccontent, vsi_content, VestaSourceAtomic::
00420                     typebit(VestaSource::immutableDirectory),
00421                     VestaSource::inappropriateOp);
00422       vsa.accessCheck(ccontent, vsi_content, AccessControl::read,
00423                       true, VestaSource::noPermission);
00424     }
00425 
00426     if (vs_workpar) {
00427       vsi_workpar = vsa.decl(cworkpar, vs_workpar);
00428       vsa.typeCheck(cworkpar, vsi_workpar, VestaSourceAtomic::
00429                     typebit(VestaSource::mutableDirectory),
00430                     VestaSource::inappropriateOp);
00431       vsa.accessCheck(cworkpar, vsi_workpar, AccessControl::write,
00432                       true, VestaSource::noPermission);
00433       vsi_workdir = vsa.lookup(cworkdir, vsi_workpar, workarc);
00434     }
00435 
00436     if (!no_sessdir) {
00437       vsi_sessdir = vsa.decl(csessdir, vs_sessdir);
00438       vsa.accessCheck(csessdir, vsi_sessdir, AccessControl::write,
00439                       true, VestaSource::noPermission);
00440     }     
00441         
00442     // All is well if atomic program runs up to this step; start
00443     // making the changes.
00444 
00445     VestaSourceAtomic::VSIndex vsi_newver = 
00446       vsa.insertImmutableDirectory(cnewver, vsi_pkg, newverarc, vsi_content,
00447                                    true, VestaSource::replaceDiff);
00448 
00449     if (!no_workdir) {
00450       vsa.reallyDelete(cworkdir, vsi_workpar, workarc, true);
00451     }
00452 
00453     // Set attributes.
00454     // We must have write permission on the new version, because
00455     // we have write permission on the parent, and the new version
00456     // as yet has no access control attribs of its own.
00457 
00458     // session dir
00459     if (!no_sessdir) {
00460       vsa.setAttrib(csessdir, vsi_sessdir, "checkin-by", cuser);
00461       vsa.setAttrib(csessdir, vsi_sessdir, "checkin-time", timebuf);
00462       // set the session-dir attrib on the new version
00463       vsa.setAttrib(cnewver, vsi_newver, "session-dir", csessdir);
00464     }
00465 
00466     // new version
00467     vsa.setAttrib(cnewver, vsi_newver, "checkin-by", cuser);
00468     vsa.setAttrib(cnewver, vsi_newver, "checkin-time", timebuf);
00469     if(!omit_msg && !message.Empty()) {
00470       vsa.setAttrib(cnewver, vsi_newver, "message", message);
00471     }
00472     if (vs_content) {
00473       vsa.setAttrib(cnewver, vsi_newver, "content", ccontent);
00474     }
00475     // clear some attribs set at checkout
00476     vsa.clearAttrib(cnewver, vsi_newver, "work-dir");
00477     vsa.clearAttrib(cnewver, vsi_newver, "#mode");
00478 
00479     if (vs_content) {
00480       vsa.setAttrib(cnewver, vsi_newver, "old-version", ReposUI::prevVersion(vs_content,
00481                               omit_dstrepos ? "" : dstrepos));
00482     } else if (!no_sessdir) {
00483       vsa.mergeAttrib(cnewver, vsi_sessdir, vsi_newver, "old-version");
00484     } else if (stub_oldver) {
00485       vsa.setAttrib(cnewver, vsi_newver, "old-version", stub_oldver);
00486     }
00487     if (stub_mstrepos) {
00488       vsa.setAttrib(cnewver, vsi_newver, "master-repository", stub_mstrepos);
00489     }
00490     // --------------------------------------------------------------
00491 
00492     // Commit
00493     if (!vsa.run()) {
00494       VestaSource::errorCode err;
00495       if (vsa.lasterr == VestaSource::ok) {
00496         err = vsa.okreplace;
00497       } else {
00498         err = vsa.lasterr;
00499       }
00500       cerr << program_name << ": " << vsa.name(vsa.ndone) << ": "
00501            << ReposUI::errorCodeText(err) << endl;
00502       exit(2);
00503     }
00504 
00505     // Replicate if needed
00506     if (!omit_dstrepos && dstrepos != "") {
00507       Text dhost, dport;
00508       int colon = dstrepos.FindCharR(':');
00509       if (colon == -1) {
00510         dhost = dstrepos;
00511         dport = lport;
00512       } else {
00513         dhost = dstrepos.Sub(0, colon);
00514         dport = dstrepos.Sub(colon+1);
00515       }
00516       if (lhost != dhost || lport != dport) {
00517         if (!quiet) {
00518           cout << "Replicating " << cnewver
00519                << " to " << dhost << ":" << dport << endl;
00520         }
00521         Replicator repl(lhost, lport, dhost, dport);
00522         Replicator::DirectiveSeq direcs;
00523         Replicator::Directive d('+', cnewver.Sub(vlen));
00524         direcs.addhi(d);
00525         repl.replicate(&direcs, rflags);
00526       }
00527     }
00528 
00529   } catch (VestaConfig::failure f) {
00530     cerr << program_name << ": " << f.msg << endl;
00531     exit(2);
00532   } catch (SRPC::failure f) {
00533     cerr << program_name
00534          << ": SRPC failure; " << f.msg << " (" << f.r << ")" << endl;
00535     exit(2);
00536   } catch (ReposUI::failure f) {
00537     cerr << program_name << ": " << f.msg << endl;
00538     exit(2);
00539   } catch (Replicator::Failure f) {
00540     if (f.code == (VestaSource::errorCode) -1) {
00541       cerr << program_name << ": " << f.msg << endl;
00542     } else {
00543       cerr << program_name << ": " << ReposUI::errorCodeText(f.code)
00544            << ", " << f.msg << endl;
00545     }
00546     exit(2);
00547   } catch(FS::Failure f) {
00548     cerr << program_name << ": " << f << endl;
00549     exit(2);
00550   }
00551 
00552   return 0;
00553 }

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