3 // Copyright (C) 2003 Enrico Scholz <enrico.scholz@informatik.tu-chemnitz.de>
4 // based on vunify.cc by Jacques Gelinas
6 // This program is free software; you can redistribute it and/or modify
7 // it under the terms of the GNU General Public License as published by
8 // the Free Software Foundation; either version 2, or (at your option)
11 // This program is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
16 // You should have received a copy of the GNU General Public License
17 // along with this program; if not, write to the Free Software
18 // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
22 This utility is used to unify (using hard links) two or more
24 It compares the each vserver with the first one and for every
25 common package (RPM, same version), it does a hard link on non
26 configuration file. It turns the file immutable after that.
37 #include <sys/ioctl.h>
51 static bool undo = false;
53 static int ext2flags = EXT2_IMMUTABLE_FILE_FL | EXT2_IMMUTABLE_LINK_FL;
57 EXCLDIR(const char *s)
65 #define OPTION_TEST 1024
66 #define OPTION_UNDO 1025
67 #define OPTION_DEBUG 1026
68 #define OPTION_NOFLAGS 1027
69 #define OPTION_IMMUTABLE 1028
70 #define OPTION_IMM_UNLINK 1029
71 #define OPTION_EXCLDIR 1030
72 #define OPTION_INCLDIR 1031
74 static struct option const
76 { "help", no_argument, 0, 'h' },
77 { "version", no_argument, 0, 'v' },
78 { "test", no_argument, 0, OPTION_TEST },
79 { "undo", no_argument, 0, OPTION_UNDO },
80 { "debug", no_argument, 0, OPTION_DEBUG },
81 { "noflags", no_argument, 0, OPTION_NOFLAGS },
82 { "immutable", no_argument, 0, OPTION_IMMUTABLE },
83 { "immutable-mayunlink", no_argument, 0, OPTION_IMM_UNLINK },
84 { "excldir", required_argument, 0, OPTION_EXCLDIR },
85 { "incldir", required_argument, 0, OPTION_INCLDIR },
90 static vector<EXCLDIR> excldirs;
91 static vector<EXCLDIR> incldirs;
94 showHelp(std::ostream &out, char const *, int exit_code)
97 "vunify [ options ] reference-server vservers ... -- packages\n"
98 "vunify [ options ] reference-server vservers ... -- ALL\n"
100 "--test: Show what will be done, do not do it.\n"
101 "--undo: Put back the file in place, using copies from the\n"
102 " reference server.\n"
103 "--debug: Prints some debugging messages.\n"
104 "--noflags: Do not put any immutable flags on the file\n"
105 "--immutable: Set the immutable_file bit on the files.\n"
106 "--immutable-mayunlink: Sets the immutable_link flag on files.\n"
108 "--excldir: None of the files under a given directory will be unified\n"
109 "\tThe directory is expressed in absolute/logical form (relative\n"
110 "\tto the vserver root (ex: /var/log)\n"
112 "--incldir: All the files under a given directory will be unified\n"
113 "\tThe directory is expressed in absolute/logical form (relative\n"
114 "\tto the vserver root (ex: /var/log)\n"
116 "By default, the immutable_file and immutable_link flags are\n"
117 "set on the files. So if you want no immutable flags, you must\n"
118 "use --noflags. If you want a single flag, you must use\n"
119 "--noflags first, then the --immutable or --immutable-mayunlink\n"
122 std::exit(exit_code);
128 std::cout << "vunify " VERSION " -- unifies files of vservers\n"
129 "This program is part of " PACKAGE_STRING "\n\n"
130 "Copyright (C) 2003 Enrico Scholz\n"
131 VERSION_COPYRIGHT_DISCLAIMER;
136 static bool vunify_inside (vector<EXCLDIR> &dirs, const char *path)
139 for (unsigned i=0; i<dirs.size(); i++){
140 if (strncmp(dirs[i].prefix.c_str(),path,dirs[i].len)==0){
148 class PACKAGE_UNI: public Package{
150 list<string> files; // Files to unify
151 // This is loaded on demand
152 PACKAGE_UNI(string &_name, string &_version)
153 : Package(_name,_version)
156 PACKAGE_UNI(const char *_name, const char *_version)
157 : Package (_name,_version)
160 PACKAGE_UNI(const string &line)
164 // Load the file member of the package, but exclude configuration file
165 void loadfiles(Vserver const &ref)
168 if (debug) cout << "Loading files for package " << name << endl;
170 namever = name + '-' + version;
171 FILE *fin = vutil_execdistcmd (K_UNIFILES,ref,namever.c_str());
174 while (fgets(tmp,sizeof(tmp)-1,fin)!=NULL){
175 int last = strlen(tmp)-1;
176 if (last >= 0 && tmp[last] == '\n') tmp[last] = '\0';
177 bool must_unify = false;
178 int type = 0; // K_UNIFILES only report unify-able files
179 if(type == 0 && !vunify_inside(excldirs,tmp)){
181 }else if(vunify_inside(incldirs,tmp)){
185 files.push_front (tmp);
187 cout << "Package " << name << " exclude " << tmp << endl;
191 if (debug) cout << "Done\n";
197 static ostream & operator << (ostream &c, const PACKAGE_UNI &p)
199 return c << p.name << "-" << p.version;
204 cout << "xx " << a << endl;
211 printer (const char *_title): title(_title){}
212 void operator()(T const &a){
213 cout << title << " " << a << endl;
219 Load the list of all packages in a vserver
221 static void vunify_loadallpkg (Vserver const &refserver, list<PACKAGE_UNI> &packages)
223 FILE *fin = vutil_execdistcmd (K_PKGVERSION,refserver,NULL);
226 while (fgets(line,sizeof(line)-1,fin)!=NULL){
227 // fprintf (stderr,"line: %s",line);
228 int last = strlen(line)-1;
229 if (last >= 0 && line[last] == '\n') line[last] = '\0';
230 packages.push_back (PACKAGE_UNI(line));
237 Object to unify a file
238 The file is first removed, then a hard link is made and then
239 the immutable flag is done
242 Vserver const &ref_server;
243 Vserver const &target_server;
247 file_unifier(Vserver const &_ref, Vserver const &_target, int &_ret)
248 : ref_server(_ref),target_server(_target), ret(_ret)
251 void operator()(const string &file)
253 std::string refpath = ref_server.getVdir();
256 std::string dstpath = target_server.getVdir();
259 if (debug) cout << "Unify " << refpath << " -> " << dstpath << endl;
261 if (stat(refpath.c_str(),&st)==-1){
262 if (debug) cout << "File " << refpath << " does not exist, ignored\n";
263 }else if (setext2flag(refpath.c_str(),false,ext2flags)==-1){
265 }else if (vbuild_unlink(dstpath.c_str())==-1){
267 cerr << "Can't delete file " << dstpath
268 << " (" << strerror(errno) << ")\n";
271 if (vbuild_file_copy(refpath.c_str(),dstpath.c_str(),st)==-1){
273 cerr << "Can't copy file " << refpath << " to " << dstpath
274 << " (" << strerror(errno) << ")\n";
277 if (vbuild_link(refpath.c_str(),dstpath.c_str())==-1){
279 cerr << "Can't link file " << refpath << " to " << dstpath
280 << " (" << strerror(errno) << ")\n";
283 // We put back the original immutable because other vservers
284 // may be unified on it.
285 if (setext2flag(refpath.c_str(),true,ext2flags)==-1){
293 // Check if two package have the same name (but potentially different version)
297 same_name(PACKAGE_UNI &_pkg) : pkg(_pkg) {}
298 bool operator()(const PACKAGE_UNI &p)
300 return pkg.name == p.name;
304 // Predicate to decide if a package must be unified
305 class package_unifier{
307 Vserver const &ref_server;
308 Vserver const &target_server;
309 list<PACKAGE_UNI> &target_packages;
312 package_unifier(Vserver const &_ref,
313 Vserver const &_target,
314 list<PACKAGE_UNI> &_target_packages,
316 ref_server(_ref),target_server(_target),
317 target_packages(_target_packages), ret(_ret)
319 void operator()(PACKAGE_UNI &pkg)
321 if (find(target_packages.begin(),target_packages.end(),pkg)
322 !=target_packages.end()){
323 // Ok, the package is also in the target vserver
324 cout << "Unify pkg " << pkg << " from " << ref_server << " to "
325 << target_server << endl;
327 if (!testmode || debug){
328 pkg.loadfiles(ref_server);
329 for_each (pkg.files.begin(),pkg.files.end()
330 ,file_unifier(ref_server,target_server,ret));
333 // The package is missing, in test mode we provide more information
334 if (find_if(target_packages.begin(),target_packages.end(),same_name(pkg))
335 !=target_packages.end()){
336 cout << pkg << " exist in server " << target_server << " not unified\n";
338 cout << pkg << " does not exist in server " << target_server << endl;
344 // For each vserver, find the common packages and unify them
345 class server_unifier{
347 list<PACKAGE_UNI> &ref_packages;
348 Vserver const &ref_server;
351 server_unifier(Vserver const &_ref_server, list<PACKAGE_UNI> &_packages, int &_ret)
352 : ref_packages(_packages),ref_server(_ref_server), ret(_ret)
355 void operator()(Vserver const &serv)
357 list<PACKAGE_UNI> pkgs;
358 vunify_loadallpkg (serv,pkgs);
359 for_each(ref_packages.begin(),ref_packages.end()
360 ,package_unifier(ref_server,serv,pkgs,ret));
366 char **argv0,**argvn;
367 deleteif(char **_argv0, char **_argvn): argv0(_argv0),argvn(_argvn){}
368 bool operator()(const PACKAGE_UNI &pkg)
371 for (char **pt = argv0; pt < argvn; pt++){
372 if (pkg.name == *pt){
381 int main (int argc, char *argv[])
384 int c = getopt_long(argc, argv, "hv", CMDLINE_OPTIONS, 0);
386 case 'h' : showHelp(std::cout, argv[0], 0); break;
387 case 'v' : showVersion(); break;
388 case OPTION_TEST : testmode = true; break;
389 case OPTION_UNDO : undo = true; break;
390 case OPTION_DEBUG : ++debug; break;
391 case OPTION_NOFLAGS : ext2flags = 0; break;
392 case OPTION_IMMUTABLE : ext2flags |= EXT2_IMMUTABLE_FILE_FL; break;
393 case OPTION_IMM_UNLINK : ext2flags |= EXT2_IMMUTABLE_LINK_FL; break;
394 case OPTION_EXCLDIR : excldirs.push_back(EXCLDIR(optarg)); break;
395 case OPTION_INCLDIR : incldirs.push_back(EXCLDIR(optarg)); break;
397 std::cerr << "Try '" << argv[0] << " --help' for more information." << std::endl;
402 if (optind+1>=argc) {
403 std::cerr << "No vserver specified" << std::endl;
407 Vserver refserv(argv[optind++]);
408 std::vector<Vserver> vservers;
409 while (optind<argc && strcmp(argv[optind], "--")!=0)
410 vservers.push_back(Vserver(argv[optind++]));
414 std::cerr << "No packages specified" << std::endl;
418 for_each (vservers.begin(),vservers.end(),printer<Vserver>("vservers"));
420 std::list<PACKAGE_UNI> packages;
421 vunify_loadallpkg(refserv, packages);
423 if (optind!=argc-1 || strcmp(argv[optind], "ALL")!=0)
424 packages.remove_if(deleteif(argv+optind, argv+argc));
426 int ret = EXIT_SUCCESS;
428 for_each(vservers.begin(), vservers.end(), server_unifier(refserv,packages,ret));