Fedora 8 is redhat-style too.
[util-vserver.git] / src / vhashify-init.hc
1 // $Id$    --*- c -*--
2
3 // Copyright (C) 2005 Enrico Scholz <enrico.scholz@informatik.tu-chemnitz.de>
4 //  
5 // This program is free software; you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation; version 2 of the License.
8 //  
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 // GNU General Public License for more details.
13 //  
14 // You should have received a copy of the GNU General Public License
15 // along with this program; if not, write to the Free Software
16 // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17
18 #include "pathconfig.h"
19 #include "lib_internal/util-dotfile.h"
20
21 #include <sys/param.h>
22
23 static UNUSED void
24 freeHashList(HashDirCollection *hash_vec)
25 {
26   for (struct HashDirInfo *itm = Vector_begin(hash_vec);
27        itm!=Vector_end(hash_vec);
28        ++itm) {
29     free(const_cast(char *)(itm->path.d));
30   }
31
32   Vector_free(hash_vec);
33 }
34
35 static size_t
36 initHashList(HashDirCollection *hash_vec, char const *hashdir)
37 {
38   int           cur_dir = Eopen(".", O_RDONLY|O_DIRECTORY, 0);
39   Echdir(hashdir);
40   
41   DIR           *d      = Eopendir(".");
42   struct dirent *ep;
43   size_t        l   = strlen(hashdir);
44   size_t        res = 0;
45
46   while ((ep=readdir(d)) != 0) {
47     struct stat         st;
48
49     if (isDotfile(ep->d_name) ||
50         stat(ep->d_name, &st)==-1 || !S_ISDIR(st.st_mode))
51       continue;
52
53     if (HashDirInfo_findDevice(hash_vec, st.st_dev)!=0) {
54       WRITE_MSG(2, "Duplicate hash-dir entry '");
55       WRITE_STR(2, ep->d_name);
56       WRITE_MSG(2, "' found\n");
57       continue;
58     }
59
60     char                *full_path = Emalloc(l + strlen(ep->d_name) + 3);
61     char                *ptr       = full_path + l;
62
63     memcpy(full_path, hashdir, l);
64     while (ptr>full_path && ptr[-1]=='/') --ptr;
65     *ptr++ = '/';
66     strcpy(ptr, ep->d_name);
67     strcat(ptr, "/");   // append a trailing '/'
68
69
70     struct HashDirInfo  tmp = {
71       .device  = st.st_dev,
72       .path    = { full_path, strlen(full_path) },
73     };
74
75     res = MAX(res, tmp.path.l);
76     
77     memcpy(Vector_pushback(hash_vec), &tmp, sizeof tmp);
78   }
79
80   if (Vector_count(hash_vec)==0) {
81     WRITE_MSG(2, "Could not find a place for the hashified files at '");
82     WRITE_STR(2, hashdir);
83     WRITE_MSG(2, "'.\n");
84     exit(wrapper_exit_code);
85   }
86
87   Eclosedir(d);
88   Efchdir(cur_dir);
89   Eclose(cur_dir);
90
91   return res;
92 }
93
94 static bool
95 initHashMethod(struct HashDirConfiguration *conf, char const *filename)
96 {
97   int           fd = open(filename, O_RDONLY);
98   if (fd==-1 && conf->method==0)
99     conf->method = hashFunctionDefault();
100
101   if (fd==-1) {
102     assert(conf->method!=0);
103     if (conf->method==0)      return false;
104     if (global_args->dry_run) return true;      // do not create the file
105     
106     fd = Eopen(filename, O_WRONLY|O_CREAT|O_EXCL|O_NOFOLLOW, 0644);
107     TEMP_FAILURE_RETRY(write(fd, conf->method->name, strlen(conf->method->name)));
108     TEMP_FAILURE_RETRY(write(fd, "\n", 1));
109   }
110   else {
111     off_t       s  = Elseek(fd, 0, SEEK_END);
112     char        buf[s + 1];
113     Elseek(fd, 0, SEEK_SET);
114
115     conf->method=0;
116
117     if (s>0 && read(fd, buf, s+1)==s) {
118       while (s>0 && (buf[s-1]=='\0' || buf[s-1]=='\n'))
119         --s;
120       buf[s] = '\0';
121
122       conf->method = hashFunctionFind(buf);
123       if (conf->method==0) {
124         WRITE_MSG(2, "Can not find hash-function '");
125         WRITE_STR(2, buf);
126         WRITE_MSG(2, "'\n");
127       }
128     }
129     else
130       WRITE_MSG(2, "Can not read configuration file for hash-method\n");
131   }
132
133   if (conf->method!=0 && conf->method->digestsize*8>HASH_MAXBITS) {
134     WRITE_MSG(2, "Wow... what an huge hash-function. I can not handle so much bits; giving up...\n");
135     conf->method=0;
136   }
137   
138   Eclose(fd);
139   return conf->method!=0;
140 }
141
142 static bool
143 initHashBlocks(struct HashDirConfiguration *conf, char const *filename)
144 {
145   int           fd = open(filename, O_RDONLY);
146
147   if (fd==-1) {
148     char                str[sizeof("all,start,middle,end,")] = { [0] = '\0' };
149
150     if (global_args->dry_run) return true;      // do not create the file
151     
152     fd = Eopen(filename, O_WRONLY|O_CREAT|O_EXCL|O_NOFOLLOW, 0644);
153
154     if (conf->blocks== hshALL)    strcat(str, "all\n");
155     if (conf->blocks & hshSTART)  strcat(str, "start\n");
156     if (conf->blocks & hshMIDDLE) strcat(str, "middle\n");
157     if (conf->blocks & hshEND)    strcat(str, "end\n");
158
159     EwriteAll(fd, str, strlen(str));
160   }
161   else {
162     off_t       s  = Elseek(fd, 0, SEEK_END);
163     char        buf[s + 1];
164     Elseek(fd, 0, SEEK_SET);
165     
166     conf->blocks = hshINVALID;
167       
168     if (s>0 && read(fd, buf, s+1)==s) {
169       char      *tok = buf;
170       char      *sep = "\n,\t ";
171
172       buf[s]       = '\0';
173       conf->blocks = hshALL;
174
175       do {
176         char    *ptr = strsep(&tok, sep);
177
178         if (*ptr=='#') { sep = "\n"; continue; }
179         sep = "\n,\t ";
180         if (*ptr=='\0') continue;
181
182         if      (strcasecmp(ptr, "all")   ==0) conf->blocks  = hshALL;
183         else {
184           if (conf->blocks==hshINVALID) conf->blocks = 0;
185           
186           else if (strcasecmp(ptr, "start") ==0) conf->blocks |= hshSTART;
187           else if (strcasecmp(ptr, "middle")==0) conf->blocks |= hshMIDDLE;
188           else if (strcasecmp(ptr, "end")   ==0) conf->blocks |= hshEND;
189           else {
190             WRITE_MSG(2, "Invalid block descriptor '");
191             WRITE_STR(2, ptr);
192             WRITE_MSG(2, "'\n");
193             conf->blocks = hshINVALID;
194             tok = 0;
195           }
196         }
197       } while (tok!=0);
198     }
199     else
200       WRITE_MSG(2, "Can not read configuration file for hash-blocks\n");
201   }
202
203   Eclose(fd);
204   return conf->blocks!=hshINVALID;
205 }
206
207 static bool
208 initHashBlockSize(struct HashDirConfiguration *conf, char const *filename)
209 {
210   if (conf->blocks==hshALL) return true;
211   
212   int           fd = open(filename, O_RDONLY);
213   if (fd==-1) {
214     char                str[sizeof("0x") + sizeof(size_t)*3+2] = {
215       [0] = '0', [1] = 'x'
216     };
217     size_t              len = utilvserver_fmt_xuint(str+2, conf->blocksize);
218
219     if (global_args->dry_run) return true;      // do not create the file
220
221     fd = Eopen(filename, O_WRONLY|O_CREAT|O_EXCL|O_NOFOLLOW, 0644);
222     EwriteAll(fd, str, len+2);
223   }
224   else {
225     off_t       s  = Elseek(fd, 0, SEEK_END);
226     char        buf[s + 1];
227     Elseek(fd, 0, SEEK_SET);
228
229     conf->blocksize = (size_t)(-1);
230
231     if (s>0 && read(fd, buf, s+1)==s) {
232       char      *errptr;
233       
234       while (s>0 && (buf[s-1]=='\0' || buf[s-1]=='\n'))
235         --s;
236       buf[s] = '\0';
237
238       conf->blocksize = strtol(buf, &errptr, 0);
239       if (errptr==buf || (*errptr!='\0' && *errptr!='\n')) {
240         WRITE_MSG(2, "Failed to parse blocksize '");
241         WRITE_STR(2, buf);
242         WRITE_MSG(2, "'\n");
243         conf->blocksize = (size_t)(-1);
244       }
245     }
246     else
247       WRITE_MSG(2, "Can not read configuration file for hash-blocksize\n");
248   }
249
250   Eclose(fd);
251   return conf->blocksize!=(size_t)(-1);
252 }
253
254 static bool
255 initHashConf(struct HashDirConfiguration *conf, char const *hashdir)
256 {
257   size_t                l = strlen(hashdir);
258   char                  tmp[l + MAX(MAX(sizeof("/method"), sizeof("/blocks")),
259                                     sizeof("/blocksize"))];
260
261   memcpy(tmp, hashdir, l);
262
263   return ((strcpy(tmp+l, "/method"),    initHashMethod   (conf, tmp)) &&
264           (strcpy(tmp+l, "/blocks"),    initHashBlocks   (conf, tmp)) &&
265           (strcpy(tmp+l, "/blocksize"), initHashBlockSize(conf, tmp)));
266 }
267
268 static char *
269 searchHashdir(char const *lhs, char const *rhs)
270 {
271   size_t        l1  = strlen(lhs);
272   size_t        l2  = rhs ? strlen(rhs) : 0;
273   char *        res = Emalloc(l1 + l2 + 1);
274   struct stat   st;
275
276   strcpy(res, lhs);
277   if (rhs) strcat(res, rhs);
278
279   if (stat(res, &st)==-1 || !S_ISDIR(st.st_mode)) {
280     free(res);
281     res = 0;
282   }
283
284   return res;
285 }
286
287 static void
288 initModeManually(struct Arguments const UNUSED *args, int argc, char *argv[])
289 {
290   assert(args->hash_dir!=0);
291   
292   if (argc<2) {
293     WRITE_MSG(2, "No exclude list specified\n");
294     exit(1);
295   }
296
297   if (!initHashConf(&global_info.hash_conf, args->hash_dir)) {
298     WRITE_MSG(2, "failed to initialize hash-configuration\n");
299     exit(1);
300   }
301   
302   global_info.hash_dirs_max_size = initHashList(&global_info.hash_dirs,
303                                                 args->hash_dir);
304   MatchList_initManually(&global_info.dst_list, 0, strdup(argv[0]), argv[1]);
305 }
306
307 static void
308 initModeVserver(struct Arguments const UNUSED *args, int argc, char *argv[])
309 {
310   char const                            *hashdir   = args->hash_dir;
311   struct MatchVserverInfo               vserver = {
312     .name        = argv[0],
313     .use_pkgmgmt = true
314   };
315
316   if (!MatchVserverInfo_init(&vserver)) {
317     WRITE_MSG(2, "Failed to initialize unification for vserver\n");
318     exit(1);
319   }
320
321   if (argc!=1) {
322     WRITE_MSG(2, "More than one vserver is not supported\n");
323     exit(1);
324   }
325
326   if (!MatchList_initByVserver(&global_info.dst_list, &vserver)) {
327     WRITE_MSG(2, "unification not configured for this vserver\n");
328     exit(1);
329   }
330
331   if (hashdir==0) hashdir = searchHashdir(vserver.appdir.d, "/hash");
332   if (hashdir==0) hashdir = searchHashdir(CONFDIR "/.defaults/apps/vunify/hash", 0);
333
334   if (hashdir==0) {
335     WRITE_MSG(2, "no hash-directory configured for this vserver.\n");
336     exit(1);
337   }
338
339   if (!initHashConf(&global_info.hash_conf, hashdir)) {
340     WRITE_MSG(2, "failed to initialize hash-configuration\n");
341     exit(1);
342   }
343   
344   global_info.hash_dirs_max_size = initHashList(&global_info.hash_dirs, hashdir);
345
346   free(const_cast(char *)(hashdir));
347   MatchVserverInfo_free(&vserver);
348 }
349