explicitly cast result of dlsym() to prevent compiler warnings
[util-vserver.git] / util-vserver / src / vunify.cc
1 // $Id$
2
3 // Copyright (C) 2003 Enrico Scholz <enrico.scholz@informatik.tu-chemnitz.de>
4 // based on vunify.cc by Jacques Gelinas
5 //  
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)
9 // any later version.
10 //  
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.
15 //  
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.
19
20
21 /*
22         This utility is used to unify (using hard links) two or more
23         virtual servers.
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.
27 */
28 #ifdef HAVE_CONFIG_H
29 #  include <config.h>
30 #endif
31
32 #include <stdio.h>
33 #include <fcntl.h>
34 #include <sys/ioctl.h>
35 #include <sys/stat.h>
36 #include <unistd.h>
37 #include <errno.h>
38
39 #include <string>
40 #include <vector>
41 #include <list>
42 #include <algorithm>
43 #include <iostream>
44 #include "vutil.h"
45
46 using namespace std;
47
48 static bool undo = false;
49
50 static int  ext2flags = EXT2_IMMUTABLE_FILE_FL | EXT2_IMMUTABLE_LINK_FL;
51 struct EXCLDIR{
52         string prefix;
53         int len;
54         EXCLDIR(const char *s)
55         {
56                 prefix = s;
57                 prefix += '/';
58                 len = prefix.size();
59         }
60 };
61 static vector<EXCLDIR> excldirs;
62 static vector<EXCLDIR> incldirs;
63
64
65 static void usage()
66 {
67         cerr <<
68                 "vunify version " << VERSION <<
69                 "\n\n"
70                 "vunify [ options ] reference-server vservers ... -- packages\n"
71                 "vunify [ options ] reference-server vservers ... -- ALL\n"
72                 "\n"
73                 "--test: Show what will be done, do not do it.\n"
74                 "--undo: Put back the file in place, using copies from the\n"
75                 "        reference server.\n"
76                 "--debug: Prints some debugging messages.\n"
77                 "--noflags: Do not put any immutable flags on the file\n"
78                 "--immutable: Set the immutable_file bit on the files.\n"
79                 "--immutable-mayunlink: Sets the immutable_link flag on files.\n"
80                 "\n"
81                 "--excldir: None of the files under a given directory will be unified\n"
82                 "\tThe directory is expressed in absolute/logical form (relative\n"
83                 "\tto the vserver root (ex: /var/log)\n"
84                 "\n"
85                 "--incldir: All the files under a given directory will be unified\n"
86                 "\tThe directory is expressed in absolute/logical form (relative\n"
87                 "\tto the vserver root (ex: /var/log)\n"
88                 "\n"
89                 "By default, the immutable_file and     immutable_link flags are\n"
90                 "set on the files. So if you want no immutable flags, you must\n"
91                 "use --noflags. If you want a single flag, you must use\n"
92                 "--noflags first, then the --immutable or --immutable-mayunlink\n"
93                 "flag.\n"
94                 ;
95 }
96
97 static bool vunify_inside (vector<EXCLDIR> &dirs, const char *path)
98 {
99         bool found = false;
100         for (unsigned i=0; i<dirs.size(); i++){
101                 if (strncmp(dirs[i].prefix.c_str(),path,dirs[i].len)==0){
102                         found = true;
103                         break;
104                 }
105         }
106         return found;
107 }
108
109 class PACKAGE_UNI: public Package{
110 public:
111         list<string> files;             // Files to unify
112                                                         // This is loaded on demand
113         PACKAGE_UNI(string &_name, string &_version)
114                 : Package(_name,_version)
115         {
116         }
117         PACKAGE_UNI(const char *_name, const char *_version)
118                 : Package (_name,_version)
119         {
120         }
121         PACKAGE_UNI(const string &line)
122                 : Package (line)
123         {
124         }
125         // Load the file member of the package, but exclude configuration file
126         void loadfiles(const string &ref)
127         {
128                 if (files.empty()){
129                         if (debug) cout << "Loading files for package " << name << endl;
130                         string namever;
131                         namever = name + '-' + version;
132                         FILE *fin = vutil_execdistcmd (K_UNIFILES,ref,namever.c_str());
133                         if (fin != NULL){
134                                 char tmp[1000];
135                                 while (fgets(tmp,sizeof(tmp)-1,fin)!=NULL){
136                                         int last = strlen(tmp)-1;
137                                         if (last >= 0 && tmp[last] == '\n') tmp[last] = '\0';
138                                         bool must_unify = false;
139                                         int type = 0;   // K_UNIFILES only report unify-able files
140                                         if(type == 0 && !vunify_inside(excldirs,tmp)){
141                                                 must_unify = true;
142                                         }else if(vunify_inside(incldirs,tmp)){
143                                                 must_unify = true;
144                                         }
145                                         if (must_unify){
146                                                 files.push_front (tmp);
147                                         }else if (debug){
148                                                 cout << "Package " << name << " exclude " << tmp << endl;
149                                         }
150                                 }
151                         }
152                         if (debug) cout << "Done\n";
153                 }
154         }
155 };
156
157
158 static ostream & operator << (ostream &c, const PACKAGE_UNI &p)
159 {
160         return c << p.name << "-" << p.version;
161 }
162
163 template<class T>
164         void printit(T a){
165                 cout << "xx " << a << endl;
166         }
167
168 template<class T>
169         class printer{
170                 string title;
171                 public:
172                 printer (const char *_title): title(_title){}
173                 void operator()(T a){
174                         cout << title << " " << a << endl;
175                 }
176         };
177
178
179 /*
180         Load the list of all packages in a vserver
181 */
182 static void vunify_loadallpkg (string &refserver, list<PACKAGE_UNI> &packages)
183 {
184         FILE *fin = vutil_execdistcmd (K_PKGVERSION,refserver,NULL);
185         if (fin != NULL){
186                 char line[1000];
187                 while (fgets(line,sizeof(line)-1,fin)!=NULL){
188                         // fprintf (stderr,"line: %s",line);
189                         int last = strlen(line)-1;
190                         if (last >= 0 && line[last] == '\n') line[last] = '\0';
191                         packages.push_back (PACKAGE_UNI(line));
192                 }
193                 pclose (fin);
194         }
195 }
196
197 /*
198         Object to unify a file
199         The file is first removed, then a hard link is made  and then
200         the immutable flag is done
201 */
202 class file_unifier{
203         string &ref_server,&target_server;
204         int &ret;
205         public:
206         file_unifier(string &_ref, string &_target, int &_ret)
207                 : ref_server(_ref),target_server(_target), ret(_ret)
208         {}
209         void operator()(const string &file)
210         {
211                 string refpath = "/vservers/" + ref_server + file;
212                 string dstpath = "/vservers/" + target_server + file;
213                 if (debug) cout << "Unify " << refpath << " -> " << dstpath << endl;
214                 struct stat st;
215                 if (stat(refpath.c_str(),&st)==-1){
216                         if (debug) cout << "File " << refpath << " does not exist, ignored\n";
217                 }else if (setext2flag(refpath.c_str(),false,ext2flags)==-1){
218                         ret = -1;
219                 }else if (vbuild_unlink(dstpath.c_str())==-1){
220                         ret = -1;
221                         cerr << "Can't delete file " << dstpath
222                                 << " (" << strerror(errno) << ")\n";
223                 }else{
224                         if (undo){
225                                 if (vbuild_file_copy(refpath.c_str(),dstpath.c_str(),st)==-1){
226                                         ret = -1;
227                                         cerr << "Can't copy file " << refpath << " to " << dstpath
228                                                 << " (" << strerror(errno) << ")\n";
229                                 }
230                         }else{
231                                 if (vbuild_link(refpath.c_str(),dstpath.c_str())==-1){
232                                         ret = -1;
233                                         cerr << "Can't link file " << refpath << " to " << dstpath
234                                                 << " (" << strerror(errno) << ")\n";
235                                 }
236                         }
237                         // We put back the original immutable because other vservers
238                         // may be unified on it.
239                         if (setext2flag(refpath.c_str(),true,ext2flags)==-1){
240                                 ret = -1;
241                         }
242                 }
243         }
244 };
245 #if 0
246 // Check if two package have the same name (but potentially different version)
247 class same_name{
248         PACKAGE_UNI &pkg;
249 public:
250         same_name(PACKAGE_UNI &_pkg) : pkg(_pkg) {}
251         bool operator()(const PACKAGE_UNI &p)
252         {
253                 return pkg.name == p.name;
254         }
255 };
256 #endif
257 // Predicate to decide if a package must be unified
258 class package_unifier{
259 public:
260         string &ref_server,&target_server;
261         list<PACKAGE_UNI> &target_packages;
262         int &ret;
263         package_unifier(string &_ref,
264                         string &_target,
265                         list<PACKAGE_UNI> &_target_packages,
266                         int &_ret)
267                 : ref_server(_ref),target_server(_target)
268                 , target_packages(_target_packages) , ret(_ret)
269         {}
270         void operator()(PACKAGE_UNI &pkg)
271         {
272                 if (find(target_packages.begin(),target_packages.end(),pkg)
273                         !=target_packages.end()){
274                         // Ok, the package is also in the target vserver
275                         cout << "Unify pkg " << pkg << " from " << ref_server << " to "
276                                 << target_server << endl;
277
278                         if (!testmode || debug){
279                                 pkg.loadfiles(ref_server);
280                                 for_each (pkg.files.begin(),pkg.files.end()
281                                         ,file_unifier(ref_server,target_server,ret));
282                         }
283                 }else if (testmode){
284                         // The package is missing, in test mode we provide more information
285                         if (find_if(target_packages.begin(),target_packages.end(),same_name(pkg))
286                                 !=target_packages.end()){
287                                 cout << pkg << " exist in server " << target_server << " not unified\n";
288                         }else{
289                                 cout << pkg << " does not exist in server " << target_server << endl;
290                         }
291                 }
292         }
293 };
294
295 // For each vserver, find the common packages and unify them
296 class server_unifier{
297 public:
298         list<PACKAGE_UNI> &ref_packages;
299         string &ref_server;
300         int &ret;
301         server_unifier(string _ref_server, list<PACKAGE_UNI> &_packages, int &_ret)
302                 : ref_packages(_packages),ref_server(_ref_server), ret(_ret)
303                 {}
304         void operator()(string serv)
305         {
306                 list<PACKAGE_UNI> pkgs;
307                 vunify_loadallpkg (serv,pkgs);
308                 for_each(ref_packages.begin(),ref_packages.end()
309                         ,package_unifier(ref_server,serv,pkgs,ret));
310         }
311 };
312 class deleteif{
313 public:
314         char **argv0,**argvn;
315         deleteif(char **_argv0, char **_argvn): argv0(_argv0),argvn(_argvn){}
316         bool operator()(const PACKAGE_UNI &pkg)
317         {
318                 bool found = false;
319                 for (char **pt = argv0; pt < argvn; pt++){
320                         if (pkg.name == *pt){
321                                 found = true;
322                                 break;
323                         }
324                 }
325                 return !found;
326         }
327 };
328
329 int main (int argc, char *argv[])
330 {
331         int ret = -1;
332         int i;
333         for (i=1; i<argc; i++){
334                 const char *arg = argv[i];
335                 //const char *opt = argv[i+1];
336                 if (strcmp(arg,"--test")==0){
337                         testmode = true;
338                 }else if (strcmp(arg,"--undo")==0){
339                         undo = true;
340                 }else if (strcmp(arg,"--debug")==0){
341                         debug++;
342                 }else if (strcmp(arg,"--noflags")==0){
343                         ext2flags = 0;
344                 }else if (strcmp(arg,"--immutable")==0){
345                         ext2flags |= EXT2_IMMUTABLE_FILE_FL;
346                 }else if (strcmp(arg,"--immutable-mayunlink")==0){
347                         ext2flags |= EXT2_IMMUTABLE_LINK_FL;
348                 }else if (strcmp(arg,"--excldir")==0){
349                         i++;
350                         //excldirs[excldirs.size()] = EXCLDIR(argv[i]);
351                         excldirs.push_back (EXCLDIR(argv[i]));
352                 }else if (strcmp(arg,"--incldir")==0){
353                         i++;
354                         incldirs.push_back (EXCLDIR(argv[i]));
355                 }else{
356                         break;
357                 }
358         }
359         if (i==argc){
360                 usage();
361         }else{
362                 string refserv = argv[i++];
363                 list<string> vservers;
364                 for (; i<argc && strcmp(argv[i],"--")!=0; i++){
365                         vservers.push_front (argv[i]);
366                 }
367                 for_each (vservers.begin(),vservers.end(),printer<string>("vservers"));
368                 if (i == argc || strcmp(argv[i],"--")!=0){
369                         usage();
370                 }else{
371                         i++;
372                         if (i < argc){
373                                 list<PACKAGE_UNI> packages;
374                                 vunify_loadallpkg (refserv,packages);
375                                 if (i != argc-1 || strcmp(argv[i],"ALL")!=0){
376                                         // We keep only the packages supplied on the command line
377                                         packages.remove_if(deleteif (argv+i,argv+argc));
378                                 }
379                                 ret = 0;
380                                 umask (0);
381                                 for_each (vservers.begin(),vservers.end(),server_unifier(refserv,packages,ret));
382                         }else{
383                                 usage();
384                         }
385                 }
386         }
387         return ret;
388 }
389
390
391
392