util-vserver-0.30.208
[util-vserver.git] / src / vhashify-init.hc
diff --git a/src/vhashify-init.hc b/src/vhashify-init.hc
new file mode 100644 (file)
index 0000000..e774c35
--- /dev/null
@@ -0,0 +1,349 @@
+// $Id: vhashify-init.hc,v 1.3 2005/03/23 02:10:23 ensc Exp $    --*- c -*--
+
+// Copyright (C) 2005 Enrico Scholz <enrico.scholz@informatik.tu-chemnitz.de>
+//  
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; version 2 of the License.
+//  
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//  
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+#include "pathconfig.h"
+#include "lib_internal/util-dotfile.h"
+
+#include <sys/param.h>
+
+static UNUSED void
+freeHashList(HashDirCollection *hash_vec)
+{
+  for (struct HashDirInfo *itm = Vector_begin(hash_vec);
+       itm!=Vector_end(hash_vec);
+       ++itm) {
+    free(const_cast(char *)(itm->path.d));
+  }
+
+  Vector_free(hash_vec);
+}
+
+static size_t
+initHashList(HashDirCollection *hash_vec, char const *hashdir)
+{
+  int          cur_dir = Eopen(".", O_RDONLY|O_DIRECTORY, 0);
+  Echdir(hashdir);
+  
+  DIR          *d      = Eopendir(".");
+  struct dirent        *ep;
+  size_t       l   = strlen(hashdir);
+  size_t       res = 0;
+
+  while ((ep=readdir(d)) != 0) {
+    struct stat                st;
+
+    if (isDotfile(ep->d_name) ||
+       stat(ep->d_name, &st)==-1 || !S_ISDIR(st.st_mode))
+      continue;
+
+    if (HashDirInfo_findDevice(hash_vec, st.st_dev)!=0) {
+      WRITE_MSG(2, "Duplicate hash-dir entry '");
+      WRITE_STR(2, ep->d_name);
+      WRITE_MSG(2, "' found\n");
+      continue;
+    }
+
+    char               *full_path = Emalloc(l + strlen(ep->d_name) + 3);
+    char               *ptr       = full_path + l;
+
+    memcpy(full_path, hashdir, l);
+    while (ptr>full_path && ptr[-1]=='/') --ptr;
+    *ptr++ = '/';
+    strcpy(ptr, ep->d_name);
+    strcat(ptr, "/");  // append a trailing '/'
+
+
+    struct HashDirInfo tmp = {
+      .device  = st.st_dev,
+      .path    = { full_path, strlen(full_path) },
+    };
+
+    res = MAX(res, tmp.path.l);
+    
+    memcpy(Vector_pushback(hash_vec), &tmp, sizeof tmp);
+  }
+
+  if (Vector_count(hash_vec)==0) {
+    WRITE_MSG(2, "Could not find a place for the hashified files at '");
+    WRITE_STR(2, hashdir);
+    WRITE_MSG(2, "'.\n");
+    exit(wrapper_exit_code);
+  }
+
+  Eclosedir(d);
+  Efchdir(cur_dir);
+  Eclose(cur_dir);
+
+  return res;
+}
+
+static bool
+initHashMethod(struct HashDirConfiguration *conf, char const *filename)
+{
+  int          fd = open(filename, O_RDONLY);
+  if (fd==-1 && conf->method==0)
+    conf->method = hashFunctionDefault();
+
+  if (fd==-1) {
+    assert(conf->method!=0);
+    if (conf->method==0)      return false;
+    if (global_args->dry_run) return true;     // do not create the file
+    
+    fd = Eopen(filename, O_WRONLY|O_CREAT|O_EXCL|O_NOFOLLOW, 0644);
+    TEMP_FAILURE_RETRY(write(fd, conf->method->name, strlen(conf->method->name)));
+    TEMP_FAILURE_RETRY(write(fd, "\n", 1));
+  }
+  else {
+    off_t      s  = Elseek(fd, 0, SEEK_END);
+    char       buf[s + 1];
+    Elseek(fd, 0, SEEK_SET);
+
+    conf->method=0;
+
+    if (s>0 && read(fd, buf, s+1)==s) {
+      while (s>0 && (buf[s-1]=='\0' || buf[s-1]=='\n'))
+       --s;
+      buf[s] = '\0';
+
+      conf->method = hashFunctionFind(buf);
+      if (conf->method==0) {
+       WRITE_MSG(2, "Can not find hash-function '");
+       WRITE_STR(2, buf);
+       WRITE_MSG(2, "'\n");
+      }
+    }
+    else
+      WRITE_MSG(2, "Can not read configuration file for hash-method\n");
+  }
+
+  if (conf->method!=0 && conf->method->digestsize*8>HASH_MAXBITS) {
+    WRITE_MSG(2, "Wow... what an huge hash-function. I can not handle so much bits; giving up...\n");
+    conf->method=0;
+  }
+  
+  Eclose(fd);
+  return conf->method!=0;
+}
+
+static bool
+initHashBlocks(struct HashDirConfiguration *conf, char const *filename)
+{
+  int          fd = open(filename, O_RDONLY);
+
+  if (fd==-1) {
+    char               str[sizeof("all,start,middle,end,")] = { [0] = '\0' };
+
+    if (global_args->dry_run) return true;     // do not create the file
+    
+    fd = Eopen(filename, O_WRONLY|O_CREAT|O_EXCL|O_NOFOLLOW, 0644);
+
+    if (conf->blocks== hshALL)    strcat(str, "all\n");
+    if (conf->blocks & hshSTART)  strcat(str, "start\n");
+    if (conf->blocks & hshMIDDLE) strcat(str, "middle\n");
+    if (conf->blocks & hshEND)    strcat(str, "end\n");
+
+    EwriteAll(fd, str, strlen(str));
+  }
+  else {
+    off_t      s  = Elseek(fd, 0, SEEK_END);
+    char       buf[s + 1];
+    Elseek(fd, 0, SEEK_SET);
+    
+    conf->blocks = hshINVALID;
+      
+    if (s>0 && read(fd, buf, s+1)==s) {
+      char     *tok = buf;
+      char     *sep = "\n,\t ";
+
+      buf[s]       = '\0';
+      conf->blocks = hshALL;
+
+      do {
+       char    *ptr = strsep(&tok, sep);
+
+       if (*ptr=='#') { sep = "\n"; continue; }
+       sep = "\n,\t ";
+       if (*ptr=='\0') continue;
+
+       if      (strcasecmp(ptr, "all")   ==0) conf->blocks  = hshALL;
+       else {
+         if (conf->blocks==hshINVALID) conf->blocks = 0;
+         
+         else if (strcasecmp(ptr, "start") ==0) conf->blocks |= hshSTART;
+         else if (strcasecmp(ptr, "middle")==0) conf->blocks |= hshMIDDLE;
+         else if (strcasecmp(ptr, "end")   ==0) conf->blocks |= hshEND;
+         else {
+           WRITE_MSG(2, "Invalid block descriptor '");
+           WRITE_STR(2, ptr);
+           WRITE_MSG(2, "'\n");
+           conf->blocks = hshINVALID;
+           tok = 0;
+         }
+       }
+      } while (tok!=0);
+    }
+    else
+      WRITE_MSG(2, "Can not read configuration file for hash-blocks\n");
+  }
+
+  Eclose(fd);
+  return conf->blocks!=hshINVALID;
+}
+
+static bool
+initHashBlockSize(struct HashDirConfiguration *conf, char const *filename)
+{
+  if (conf->blocks==hshALL) return true;
+  
+  int          fd = open(filename, O_RDONLY);
+  if (fd==-1) {
+    char               str[sizeof("0x") + sizeof(size_t)*3+2] = {
+      [0] = '0', [1] = 'x'
+    };
+    size_t             len = utilvserver_fmt_xuint(str+2, conf->blocksize);
+
+    if (global_args->dry_run) return true;     // do not create the file
+
+    fd = Eopen(filename, O_WRONLY|O_CREAT|O_EXCL|O_NOFOLLOW, 0644);
+    EwriteAll(fd, str, len+2);
+  }
+  else {
+    off_t      s  = Elseek(fd, 0, SEEK_END);
+    char       buf[s + 1];
+    Elseek(fd, 0, SEEK_SET);
+
+    conf->blocksize = (size_t)(-1);
+
+    if (s>0 && read(fd, buf, s+1)==s) {
+      char     *errptr;
+      
+      while (s>0 && (buf[s-1]=='\0' || buf[s-1]=='\n'))
+       --s;
+      buf[s] = '\0';
+
+      conf->blocksize = strtol(buf, &errptr, 0);
+      if (errptr==buf || (*errptr!='\0' && *errptr!='\n')) {
+       WRITE_MSG(2, "Failed to parse blocksize '");
+       WRITE_STR(2, buf);
+       WRITE_MSG(2, "'\n");
+       conf->blocksize = (size_t)(-1);
+      }
+    }
+    else
+      WRITE_MSG(2, "Can not read configuration file for hash-blocksize\n");
+  }
+
+  Eclose(fd);
+  return conf->blocksize!=(size_t)(-1);
+}
+
+static bool
+initHashConf(struct HashDirConfiguration *conf, char const *hashdir)
+{
+  size_t               l = strlen(hashdir);
+  char                 tmp[l + MAX(MAX(sizeof("/method"), sizeof("/blocks")),
+                                   sizeof("/blocksize"))];
+
+  memcpy(tmp, hashdir, l);
+
+  return ((strcpy(tmp+l, "/method"),    initHashMethod   (conf, tmp)) &&
+         (strcpy(tmp+l, "/blocks"),    initHashBlocks   (conf, tmp)) &&
+         (strcpy(tmp+l, "/blocksize"), initHashBlockSize(conf, tmp)));
+}
+
+static char *
+searchHashdir(char const *lhs, char const *rhs)
+{
+  size_t       l1  = strlen(lhs);
+  size_t       l2  = rhs ? strlen(rhs) : 0;
+  char *       res = Emalloc(l1 + l2 + 1);
+  struct stat  st;
+
+  strcpy(res, lhs);
+  if (rhs) strcat(res, rhs);
+
+  if (stat(res, &st)==-1 || !S_ISDIR(st.st_mode)) {
+    free(res);
+    res = 0;
+  }
+
+  return res;
+}
+
+static void
+initModeManually(struct Arguments const UNUSED *args, int argc, char *argv[])
+{
+  assert(args->hash_dir!=0);
+  
+  if (argc<2) {
+    WRITE_MSG(2, "No exclude list specified\n");
+    exit(1);
+  }
+
+  if (!initHashConf(&global_info.hash_conf, args->hash_dir)) {
+    WRITE_MSG(2, "failed to initialize hash-configuration\n");
+    exit(1);
+  }
+  
+  global_info.hash_dirs_max_size = initHashList(&global_info.hash_dirs,
+                                               args->hash_dir);
+  MatchList_initManually(&global_info.dst_list, 0, strdup(argv[0]), argv[1]);
+}
+
+static void
+initModeVserver(struct Arguments const UNUSED *args, int argc, char *argv[])
+{
+  char const                           *hashdir   = args->hash_dir;
+  struct MatchVserverInfo              vserver = {
+    .name        = argv[0],
+    .use_pkgmgmt = true
+  };
+
+  if (!MatchVserverInfo_init(&vserver)) {
+    WRITE_MSG(2, "Failed to initialize unification for vserver\n");
+    exit(1);
+  }
+
+  if (argc!=1) {
+    WRITE_MSG(2, "More than one vserver is not supported\n");
+    exit(1);
+  }
+
+  if (!MatchList_initByVserver(&global_info.dst_list, &vserver)) {
+    WRITE_MSG(2, "unification not configured for this vserver\n");
+    exit(1);
+  }
+
+  if (hashdir==0) hashdir = searchHashdir(vserver.appdir.d, "/hash");
+  if (hashdir==0) hashdir = searchHashdir(CONFDIR "/.defaults/apps/vunify/hash", 0);
+
+  if (hashdir==0) {
+    WRITE_MSG(2, "no hash-directory configured for this vserver.\n");
+    exit(1);
+  }
+
+  if (!initHashConf(&global_info.hash_conf, hashdir)) {
+    WRITE_MSG(2, "failed to initialize hash-configuration\n");
+    exit(1);
+  }
+  
+  global_info.hash_dirs_max_size = initHashList(&global_info.hash_dirs, hashdir);
+
+  free(const_cast(char *)(hashdir));
+  MatchVserverInfo_free(&vserver);
+}
+