9dd6ac4601bdd3eb0f03830785332e7b31c1412b
[util-vserver.git] / src / vbuild.cc
1 // $Id: vbuild.cc,v 1.4 2003/10/21 13:55:35 ensc Exp $
2
3 // Copyright (C) 2003 Enrico Scholz <enrico.scholz@informatik.tu-chemnitz.de>
4 // based on vbuild.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         This utility is used to build a new vserver using a reference vserver.
22         It uses hard link whenever possible instead of duplicating files.
23         Once done, it sets the immutable bits.
24 */
25 #ifdef HAVE_CONFIG_H
26 #  include <config.h>
27 #endif
28
29 #include <stdio.h>
30 #include <fcntl.h>
31 #include <sys/ioctl.h>
32 #include <sys/stat.h>
33 #include <unistd.h>
34 #include <errno.h>
35 #include <dirent.h>
36
37 #include <string>
38 #include <vector>
39 #include <list>
40 #include <set>
41 #include <cassert>
42
43 #include "vutil.h"
44
45 using namespace std;
46
47 struct EXCLDIR{
48         string prefix;
49         int len;
50         EXCLDIR(const char *s)
51         {
52                 prefix = s;
53                 prefix += '/';
54                 len = prefix.size();
55         }
56 };
57 static vector<EXCLDIR> excldirs;
58
59
60 static int  ext2flags = EXT2_IMMUTABLE_FILE_FL | EXT2_IMMUTABLE_LINK_FL;
61 static struct {
62         int nblink;
63         int nbcopy;
64         long size_copy;
65         int nbdir;
66         int nbsymlink;
67         int nbspc;
68 } stats;
69
70
71 static void usage()
72 {
73         cerr <<
74                 "vbuild version " << VERSION <<
75                 "\n\n"
76                 "vbuild [ options ] reference-server new-vservers\n"
77                 "\n"
78                 "--test: Show what will be done, do not do it.\n"
79                 "--debug: Prints some debugging messages.\n"
80                 "\n"
81                 "--excldir: None of the files under a given directory will be copied\n"
82                 "\tThe directory is expressed in absolute/logical form (relative\n"
83                 "\tto the vserver root (ex: /var/log)\n"
84                 "\n"
85                 "\n"
86                 "--noflags: Do not put any immutable flags on the file\n"
87                 "--immutable: Set the immutable_file bit on the files.\n"
88                 "--immutable-mayunlink: Sets the immutable_link flag on files.\n"
89                 "--stats: Produce statistics on the number of file linked\n"
90                 "         copied and so on.\n"
91                 "\n"
92                 "By default, the immutable_file and     immutable_link flags are\n"
93                 "set on the files. So if you want no immutable flags, you must\n"
94                 "use --noflags. If you want a single flag, you must use\n"
95                 "--noflags first, then the --immutable or --immutable-mayunlink\n"
96                 "flag.\n"
97                 ;
98 }
99
100 /*
101         Return true if a directory lies inside a directory set
102 */
103 static bool vbuild_inside (vector<EXCLDIR> &dirs, const char *path)
104 {
105         bool found = false;
106         for (unsigned i=0; i<dirs.size(); i++){
107                 if (strncmp(dirs[i].prefix.c_str(),path,dirs[i].len)==0){
108                         found = true;
109                         break;
110                 }
111         }
112         return found;
113 }
114
115
116
117 static int vbuild_copy (
118         string refserv,
119         string newserv,
120         dev_t dev,                      // We stay on the same volume
121         string logical_dir,
122         set<string> &files)
123 {
124         int ret = -1;
125         if (debug > 0) printf ("Copying directory %s\n",logical_dir.c_str());
126         DIR *dir = opendir (refserv.c_str());
127         if (dir == NULL){
128                 fprintf (stderr,"Can't open directory %s (%s)\n",refserv.c_str()
129                         ,strerror(errno));
130         }else{
131                 logical_dir += "/";
132                 bool copy_files = !vbuild_inside(excldirs,logical_dir.c_str());
133                 struct dirent *ent;
134                 ret = 0;
135                 while (ret == 0 && (ent=readdir(dir))!=NULL){
136                         if (strcmp(ent->d_name,".")==0 || strcmp(ent->d_name,"..")==0){
137                                 continue;
138                         }
139                         string file = refserv + "/" + ent->d_name;
140                         struct stat st;
141                         if (vutil_lstat(file.c_str(),st) == -1){
142                                 ret = -1;
143                         }else if (st.st_dev != dev){
144                                 if (debug > 0) printf ("Ignore sub-directory %s\n",file.c_str());
145                         }else{
146                                 string newfile = newserv + "/" + ent->d_name;
147                                 if (S_ISDIR(st.st_mode)){
148                                         if (vbuild_mkdir (newfile.c_str(),st.st_mode)==-1){
149                                                 fprintf (stderr,"Can't mkdir %s (%s)\n"
150                                                         ,newfile.c_str(),strerror(errno));
151                                                 ret = -1;
152                                         }else{
153                                                 stats.nbdir++;
154                                                 if (vbuild_chown(newfile.c_str(),st.st_uid,st.st_gid)==-1){
155                                                         fprintf (stderr,"Can't chown %s (%s)\n"
156                                                                 ,newfile.c_str(),strerror(errno));
157                                                         ret = -1;
158                                                 }
159                                                 ret |= vbuild_copy (file,newfile,dev
160                                                         ,logical_dir + ent->d_name,files);
161                                         }
162                                 }else if (S_ISLNK(st.st_mode)){
163                                         char path[PATH_MAX];
164                                         int len = readlink(file.c_str(),path,sizeof(path)-1);
165                                         if (len < 0){
166                                                 fprintf (stderr,"Can't readlink %s (%s)\n"
167                                                         ,file.c_str(),strerror(errno));
168                                                 ret = -1;
169                                         }else{
170                                                 path[len] = '\0';
171                                                 stats.nbsymlink++;
172                                                 if (vbuild_symlink (path,newfile.c_str())==-1){
173                                                         fprintf (stderr,"Can't symlink %s to %s (%s)\n",
174                                                                 newfile.c_str(),path,strerror(errno));
175                                                 }
176                                         }
177                                 }else if (S_ISBLK(st.st_mode)
178                                         || S_ISCHR(st.st_mode)
179                                         || S_ISFIFO(st.st_mode)){
180                                         stats.nbspc++;
181                                         if (vbuild_mknod (newfile.c_str(),st.st_mode,st.st_rdev)==-1){
182                                                 fprintf (stderr,"Can't mknod %s (%s)\n"
183                                                         ,newfile.c_str(),strerror(errno));
184                                                 ret = -1;
185                                         }
186                                 }else if (S_ISSOCK(st.st_mode)){
187                                         // Do nothing
188                                 }else if (copy_files){
189                                         // Ok, this is a file. We either copy it or do a link
190                                         string logical_file = logical_dir + ent->d_name;
191                                         if (files.find (logical_file)==files.end()){
192                                                 if (debug > 1) printf ("Copying file %s\n",file.c_str());
193                                                 if (vbuild_file_copy (file.c_str(),newfile.c_str(),st)==-1){
194                                                         fprintf (stderr,"Can't copy %s to %s (%s)\n",
195                                                                 file.c_str(),newfile.c_str(),strerror(errno));
196                                                         ret = -1;
197                                                 }else{
198                                                         stats.size_copy += st.st_size;
199                                                         stats.nbcopy++;
200                                                 }
201                                         }else{
202                                                 if (debug > 2) printf ("Linking file %s\n",file.c_str());
203                                                 setext2flag (file.c_str(),false,ext2flags);
204                                                 stats.nblink++;
205                                                 if (vbuild_link (file.c_str(),newfile.c_str())==-1){
206                                                         fprintf (stderr,"Can't link %s to %s (%s)\n",
207                                                                 file.c_str(),newfile.c_str(),strerror(errno));
208                                                         ret = -1;
209                                                 }
210                                                 setext2flag (file.c_str(),true,ext2flags);
211                                         }
212                                 }
213                         }
214                 }
215                 closedir(dir);
216         }
217         return ret;
218 }
219
220 static void
221 prepareVserver(Vserver const UNUSED &src, char const UNUSED *dst)
222 {
223   assert(false);
224 }
225
226 int main (int argc, char *argv[])
227 {
228         int ret = -1;
229         bool statistics = false;
230         int i;
231         for (i=1; i<argc; i++){
232                 const char *arg = argv[i];
233                 //const char *opt = argv[i+1];
234                 if (strcmp(arg,"--test")==0){
235                         testmode = true;
236                 }else if (strcmp(arg,"--debug")==0){
237                         debug++;
238                 }else if (strcmp(arg,"--stats")==0){
239                         statistics = true;
240                 }else if (strcmp(arg,"--noflags")==0){
241                         ext2flags = 0;
242                 }else if (strcmp(arg,"--immutable")==0){
243                         ext2flags |= EXT2_IMMUTABLE_FILE_FL;
244                 }else if (strcmp(arg,"--immutable-mayunlink")==0){
245                         ext2flags |= EXT2_IMMUTABLE_LINK_FL;
246                 }else if (strcmp(arg,"--excldir")==0){
247                         i++;
248                         excldirs.push_back (EXCLDIR(argv[i]));
249                 }else{
250                         break;
251                 }
252         }
253         if (i!=argc-2){
254                 usage();
255         }else{
256                 Vserver         refserv(argv[i++]);
257
258                 prepareVserver(refserv, argv[i]);
259                 Vserver         newserv(argv[i]);
260                 list<Package> packages;
261                 // Load the files which are not configuration files from
262                 // the packages
263                 vutil_loadallpkg (refserv,packages);
264                 set<string> files;
265                 for (list<Package>::iterator it=packages.begin(); it!=packages.end(); it++){
266                         (*it).loadfiles(refserv,files);
267                 }
268                 // Now, we do a recursive copy of refserv into newserv
269                 umask (0);
270                 // Check if it is on the same volume
271                 struct stat refst,newst;
272                 if (vutil_lstat(refserv.getName().c_str(),refst)!=-1
273                     && vutil_lstat(newserv.getName().c_str(),newst)!=1){
274                         if (refst.st_dev != newst.st_dev){
275                           fprintf (stderr,"Can't vbuild %s because it is not on the same volume as %s\n",
276                                    newserv.getName().c_str(),refserv.getName().c_str());
277                         }else{
278                                 stats.nbdir = stats.nblink = stats.nbcopy = stats.nbsymlink = 0;
279                                 stats.nbspc = 0;
280                                 stats.size_copy = 0;
281                                 ret = vbuild_copy (refserv.getName(),newserv.getName(),refst.st_dev,"",files);
282                                 if (statistics){
283                                         printf ("Directory created: %d\n",stats.nbdir);
284                                         printf ("Files copied     : %d\n",stats.nbcopy);
285                                         printf ("Bytes copied     : %ld\n",stats.size_copy);
286                                         printf ("Files linked     : %d\n",stats.nblink);
287                                         printf ("Files symlinked  : %d\n",stats.nbsymlink);
288                                         printf ("Special files    : %d\n",stats.nbspc);
289                                 }
290                         }
291                 }
292         }
293         return ret;
294 }
295
296