summaryrefslogtreecommitdiff
path: root/draft
diff options
context:
space:
mode:
authorLudovic Pouzenc <ludovic@pouzenc.fr>2017-05-05 11:28:51 +0200
committerLudovic Pouzenc <ludovic@pouzenc.fr>2017-05-05 11:28:51 +0200
commit604f3d64764270c052cfb43081ec522237bbdb75 (patch)
treeb3db80e35399412693c7a986b3021435b2914fe4 /draft
parentf7f175cb29192682f3ece9479f24a40672a3d74d (diff)
downloadeficast-604f3d64764270c052cfb43081ec522237bbdb75.tar.gz
eficast-604f3d64764270c052cfb43081ec522237bbdb75.tar.bz2
eficast-604f3d64764270c052cfb43081ec522237bbdb75.zip
Massive add for all draft stuff to keep it in sync
Diffstat (limited to 'draft')
-rw-r--r--draft/fuse-examples/cusexmp.c294
-rw-r--r--draft/fuse-examples/fioc.c211
-rw-r--r--draft/fuse-examples/fioc.h32
-rw-r--r--draft/fuse-examples/fioclient.c122
-rw-r--r--draft/fuse-examples/fsel.c278
-rw-r--r--draft/fuse-examples/fselclient.c72
-rw-r--r--draft/fuse-examples/fusexmp.c412
-rw-r--r--draft/fuse-examples/fusexmp_fh.c567
-rw-r--r--draft/fuse-examples/hello.c96
-rw-r--r--draft/fuse-examples/hello_ll.c181
-rw-r--r--draft/fuse-examples/null.c95
-rw-r--r--draft/mcastseed/AUTHORS3
-rw-r--r--draft/mcastseed/COPYING340
-rw-r--r--draft/mcastseed/ChangeLog0
-rw-r--r--draft/mcastseed/Makefile.am5
-rw-r--r--draft/mcastseed/NEWS0
l---------draft/mcastseed/README1
-rw-r--r--draft/mcastseed/README.md9
-rw-r--r--draft/mcastseed/configure.ac69
-rw-r--r--draft/mcastseed/lib/Makefile.am93
-rw-r--r--draft/mcastseed/lib/gl_anyrbtree_list1.h76
-rw-r--r--draft/mcastseed/lib/gl_anyrbtree_list2.h1028
-rw-r--r--draft/mcastseed/lib/gl_anytree_list1.h41
-rw-r--r--draft/mcastseed/lib/gl_anytree_list2.h940
-rw-r--r--draft/mcastseed/lib/gl_list.c3
-rw-r--r--draft/mcastseed/lib/gl_list.h841
-rw-r--r--draft/mcastseed/lib/gl_rbtree_list.c102
-rw-r--r--draft/mcastseed/lib/gl_rbtree_list.h34
-rw-r--r--draft/mcastseed/lib/stdbool.in.h132
-rw-r--r--draft/mcastseed/m4/00gnulib.m446
-rw-r--r--draft/mcastseed/m4/extern-inline.m4102
-rw-r--r--draft/mcastseed/m4/gnulib-cache.m447
-rw-r--r--draft/mcastseed/m4/gnulib-common.m4462
-rw-r--r--draft/mcastseed/m4/gnulib-comp.m4221
-rw-r--r--draft/mcastseed/m4/gnulib-tool.m457
-rw-r--r--draft/mcastseed/m4/stdbool.m4104
-rw-r--r--draft/mcastseed/src/Makefile.am16
-rw-r--r--draft/mcastseed/src/dgrambuf.c583
-rw-r--r--draft/mcastseed/src/dgrambuf.h41
-rw-r--r--draft/mcastseed/src/dgrambuf_test.c50
-rw-r--r--draft/mcastseed/src/mcastleech.c408
-rw-r--r--draft/mcastseed/src/mcastseed.c472
-rw-r--r--draft/mcastseed/src/random_speed_dd.c36
-rw-r--r--draft/mcastseed/src/sockets.c303
-rw-r--r--draft/mcastseed/src/sockets.h27
-rw-r--r--draft/other-tools/clonezilla/drbl-ocs.conf414
-rwxr-xr-xdraft/other-tools/clonezilla/ocs-expand-gpt-pt386
-rwxr-xr-xdraft/other-tools/clonezilla/ocs-expand-mbr-pt369
-rwxr-xr-xdraft/other-tools/clonezilla/ocs-restore-ebr119
-rwxr-xr-xdraft/other-tools/clonezilla/ocs-restore-mbr131
-rwxr-xr-xdraft/other-tools/clonezilla/ocs-sr1465
-rw-r--r--draft/other-tools/fog/fog-notes.txt148
-rwxr-xr-xdraft/other-tools/fog/fog.download261
-rwxr-xr-xdraft/other-tools/fog/fog.upload216
-rw-r--r--draft/other-tools/fog/funcs.sh2196
-rw-r--r--draft/other-tools/fog/partition-funcs.sh811
-rw-r--r--draft/other-tools/fog/procsfdisk.awk361
-rw-r--r--draft/other-tools/to-see.txt14
-rwxr-xr-xdraft/uftp-push.sh8
-rw-r--r--draft/uftp/bug-ts.txt495
-rw-r--r--draft/uftp/notes.txt42
-rwxr-xr-xdraft/uftp/sinkcat.sh39
-rwxr-xr-xdraft/uftp/sinkdo.sh57
-rw-r--r--draft/uftp/trace-lost-buf.txt485
64 files changed, 17069 insertions, 0 deletions
diff --git a/draft/fuse-examples/cusexmp.c b/draft/fuse-examples/cusexmp.c
new file mode 100644
index 0000000..01fcdf7
--- /dev/null
+++ b/draft/fuse-examples/cusexmp.c
@@ -0,0 +1,294 @@
+/*
+ CUSE example: Character device in Userspace
+ Copyright (C) 2008-2009 SUSE Linux Products GmbH
+ Copyright (C) 2008-2009 Tejun Heo <tj@kernel.org>
+
+ This program can be distributed under the terms of the GNU GPL.
+ See the file COPYING.
+
+ gcc -Wall cusexmp.c `pkg-config fuse --cflags --libs` -o cusexmp
+*/
+
+#define FUSE_USE_VERSION 29
+
+#include <cuse_lowlevel.h>
+#include <fuse_opt.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include "fioc.h"
+
+static void *cusexmp_buf;
+static size_t cusexmp_size;
+
+static const char *usage =
+"usage: cusexmp [options]\n"
+"\n"
+"options:\n"
+" --help|-h print this help message\n"
+" --maj=MAJ|-M MAJ device major number\n"
+" --min=MIN|-m MIN device minor number\n"
+" --name=NAME|-n NAME device name (mandatory)\n"
+"\n";
+
+static int cusexmp_resize(size_t new_size)
+{
+ void *new_buf;
+
+ if (new_size == cusexmp_size)
+ return 0;
+
+ new_buf = realloc(cusexmp_buf, new_size);
+ if (!new_buf && new_size)
+ return -ENOMEM;
+
+ if (new_size > cusexmp_size)
+ memset(new_buf + cusexmp_size, 0, new_size - cusexmp_size);
+
+ cusexmp_buf = new_buf;
+ cusexmp_size = new_size;
+
+ return 0;
+}
+
+static int cusexmp_expand(size_t new_size)
+{
+ if (new_size > cusexmp_size)
+ return cusexmp_resize(new_size);
+ return 0;
+}
+
+static void cusexmp_open(fuse_req_t req, struct fuse_file_info *fi)
+{
+ fuse_reply_open(req, fi);
+}
+
+static void cusexmp_read(fuse_req_t req, size_t size, off_t off,
+ struct fuse_file_info *fi)
+{
+ (void)fi;
+
+ if (off >= cusexmp_size)
+ off = cusexmp_size;
+ if (size > cusexmp_size - off)
+ size = cusexmp_size - off;
+
+ fuse_reply_buf(req, cusexmp_buf + off, size);
+}
+
+static void cusexmp_write(fuse_req_t req, const char *buf, size_t size,
+ off_t off, struct fuse_file_info *fi)
+{
+ (void)fi;
+
+ if (cusexmp_expand(off + size)) {
+ fuse_reply_err(req, ENOMEM);
+ return;
+ }
+
+ memcpy(cusexmp_buf + off, buf, size);
+ fuse_reply_write(req, size);
+}
+
+static void fioc_do_rw(fuse_req_t req, void *addr, const void *in_buf,
+ size_t in_bufsz, size_t out_bufsz, int is_read)
+{
+ const struct fioc_rw_arg *arg;
+ struct iovec in_iov[2], out_iov[3], iov[3];
+ size_t cur_size;
+
+ /* read in arg */
+ in_iov[0].iov_base = addr;
+ in_iov[0].iov_len = sizeof(*arg);
+ if (!in_bufsz) {
+ fuse_reply_ioctl_retry(req, in_iov, 1, NULL, 0);
+ return;
+ }
+ arg = in_buf;
+ in_buf += sizeof(*arg);
+ in_bufsz -= sizeof(*arg);
+
+ /* prepare size outputs */
+ out_iov[0].iov_base =
+ addr + (unsigned long)&(((struct fioc_rw_arg *)0)->prev_size);
+ out_iov[0].iov_len = sizeof(arg->prev_size);
+
+ out_iov[1].iov_base =
+ addr + (unsigned long)&(((struct fioc_rw_arg *)0)->new_size);
+ out_iov[1].iov_len = sizeof(arg->new_size);
+
+ /* prepare client buf */
+ if (is_read) {
+ out_iov[2].iov_base = arg->buf;
+ out_iov[2].iov_len = arg->size;
+ if (!out_bufsz) {
+ fuse_reply_ioctl_retry(req, in_iov, 1, out_iov, 3);
+ return;
+ }
+ } else {
+ in_iov[1].iov_base = arg->buf;
+ in_iov[1].iov_len = arg->size;
+ if (arg->size && !in_bufsz) {
+ fuse_reply_ioctl_retry(req, in_iov, 2, out_iov, 2);
+ return;
+ }
+ }
+
+ /* we're all set */
+ cur_size = cusexmp_size;
+ iov[0].iov_base = &cur_size;
+ iov[0].iov_len = sizeof(cur_size);
+
+ iov[1].iov_base = &cusexmp_size;
+ iov[1].iov_len = sizeof(cusexmp_size);
+
+ if (is_read) {
+ size_t off = arg->offset;
+ size_t size = arg->size;
+
+ if (off >= cusexmp_size)
+ off = cusexmp_size;
+ if (size > cusexmp_size - off)
+ size = cusexmp_size - off;
+
+ iov[2].iov_base = cusexmp_buf + off;
+ iov[2].iov_len = size;
+ fuse_reply_ioctl_iov(req, size, iov, 3);
+ } else {
+ if (cusexmp_expand(arg->offset + in_bufsz)) {
+ fuse_reply_err(req, ENOMEM);
+ return;
+ }
+
+ memcpy(cusexmp_buf + arg->offset, in_buf, in_bufsz);
+ fuse_reply_ioctl_iov(req, in_bufsz, iov, 2);
+ }
+}
+
+static void cusexmp_ioctl(fuse_req_t req, int cmd, void *arg,
+ struct fuse_file_info *fi, unsigned flags,
+ const void *in_buf, size_t in_bufsz, size_t out_bufsz)
+{
+ int is_read = 0;
+
+ (void)fi;
+
+ if (flags & FUSE_IOCTL_COMPAT) {
+ fuse_reply_err(req, ENOSYS);
+ return;
+ }
+
+ switch (cmd) {
+ case FIOC_GET_SIZE:
+ if (!out_bufsz) {
+ struct iovec iov = { arg, sizeof(size_t) };
+
+ fuse_reply_ioctl_retry(req, NULL, 0, &iov, 1);
+ } else
+ fuse_reply_ioctl(req, 0, &cusexmp_size,
+ sizeof(cusexmp_size));
+ break;
+
+ case FIOC_SET_SIZE:
+ if (!in_bufsz) {
+ struct iovec iov = { arg, sizeof(size_t) };
+
+ fuse_reply_ioctl_retry(req, &iov, 1, NULL, 0);
+ } else {
+ cusexmp_resize(*(size_t *)in_buf);
+ fuse_reply_ioctl(req, 0, NULL, 0);
+ }
+ break;
+
+ case FIOC_READ:
+ is_read = 1;
+ case FIOC_WRITE:
+ fioc_do_rw(req, arg, in_buf, in_bufsz, out_bufsz, is_read);
+ break;
+
+ default:
+ fuse_reply_err(req, EINVAL);
+ }
+}
+
+struct cusexmp_param {
+ unsigned major;
+ unsigned minor;
+ char *dev_name;
+ int is_help;
+};
+
+#define CUSEXMP_OPT(t, p) { t, offsetof(struct cusexmp_param, p), 1 }
+
+static const struct fuse_opt cusexmp_opts[] = {
+ CUSEXMP_OPT("-M %u", major),
+ CUSEXMP_OPT("--maj=%u", major),
+ CUSEXMP_OPT("-m %u", minor),
+ CUSEXMP_OPT("--min=%u", minor),
+ CUSEXMP_OPT("-n %s", dev_name),
+ CUSEXMP_OPT("--name=%s", dev_name),
+ FUSE_OPT_KEY("-h", 0),
+ FUSE_OPT_KEY("--help", 0),
+ FUSE_OPT_END
+};
+
+static int cusexmp_process_arg(void *data, const char *arg, int key,
+ struct fuse_args *outargs)
+{
+ struct cusexmp_param *param = data;
+
+ (void)outargs;
+ (void)arg;
+
+ switch (key) {
+ case 0:
+ param->is_help = 1;
+ fprintf(stderr, "%s", usage);
+ return fuse_opt_add_arg(outargs, "-ho");
+ default:
+ return 1;
+ }
+}
+
+static const struct cuse_lowlevel_ops cusexmp_clop = {
+ .open = cusexmp_open,
+ .read = cusexmp_read,
+ .write = cusexmp_write,
+ .ioctl = cusexmp_ioctl,
+};
+
+int main(int argc, char **argv)
+{
+ struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
+ struct cusexmp_param param = { 0, 0, NULL, 0 };
+ char dev_name[128] = "DEVNAME=";
+ const char *dev_info_argv[] = { dev_name };
+ struct cuse_info ci;
+
+ if (fuse_opt_parse(&args, &param, cusexmp_opts, cusexmp_process_arg)) {
+ printf("failed to parse option\n");
+ return 1;
+ }
+
+ if (!param.is_help) {
+ if (!param.dev_name) {
+ fprintf(stderr, "Error: device name missing\n");
+ return 1;
+ }
+ strncat(dev_name, param.dev_name, sizeof(dev_name) - 9);
+ }
+
+ memset(&ci, 0, sizeof(ci));
+ ci.dev_major = param.major;
+ ci.dev_minor = param.minor;
+ ci.dev_info_argc = 1;
+ ci.dev_info_argv = dev_info_argv;
+ ci.flags = CUSE_UNRESTRICTED_IOCTL;
+
+ return cuse_lowlevel_main(args.argc, args.argv, &ci, &cusexmp_clop,
+ NULL);
+}
diff --git a/draft/fuse-examples/fioc.c b/draft/fuse-examples/fioc.c
new file mode 100644
index 0000000..bee40b9
--- /dev/null
+++ b/draft/fuse-examples/fioc.c
@@ -0,0 +1,211 @@
+/*
+ FUSE fioc: FUSE ioctl example
+ Copyright (C) 2008 SUSE Linux Products GmbH
+ Copyright (C) 2008 Tejun Heo <teheo@suse.de>
+
+ This program can be distributed under the terms of the GNU GPL.
+ See the file COPYING.
+
+ gcc -Wall fioc.c `pkg-config fuse --cflags --libs` -o fioc
+*/
+
+#define FUSE_USE_VERSION 26
+
+#include <fuse.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <time.h>
+#include <errno.h>
+
+#include "fioc.h"
+
+#define FIOC_NAME "fioc"
+
+enum {
+ FIOC_NONE,
+ FIOC_ROOT,
+ FIOC_FILE,
+};
+
+static void *fioc_buf;
+static size_t fioc_size;
+
+static int fioc_resize(size_t new_size)
+{
+ void *new_buf;
+
+ if (new_size == fioc_size)
+ return 0;
+
+ new_buf = realloc(fioc_buf, new_size);
+ if (!new_buf && new_size)
+ return -ENOMEM;
+
+ if (new_size > fioc_size)
+ memset(new_buf + fioc_size, 0, new_size - fioc_size);
+
+ fioc_buf = new_buf;
+ fioc_size = new_size;
+
+ return 0;
+}
+
+static int fioc_expand(size_t new_size)
+{
+ if (new_size > fioc_size)
+ return fioc_resize(new_size);
+ return 0;
+}
+
+static int fioc_file_type(const char *path)
+{
+ if (strcmp(path, "/") == 0)
+ return FIOC_ROOT;
+ if (strcmp(path, "/" FIOC_NAME) == 0)
+ return FIOC_FILE;
+ return FIOC_NONE;
+}
+
+static int fioc_getattr(const char *path, struct stat *stbuf)
+{
+ stbuf->st_uid = getuid();
+ stbuf->st_gid = getgid();
+ stbuf->st_atime = stbuf->st_mtime = time(NULL);
+
+ switch (fioc_file_type(path)) {
+ case FIOC_ROOT:
+ stbuf->st_mode = S_IFDIR | 0755;
+ stbuf->st_nlink = 2;
+ break;
+ case FIOC_FILE:
+ stbuf->st_mode = S_IFREG | 0644;
+ stbuf->st_nlink = 1;
+ stbuf->st_size = fioc_size;
+ break;
+ case FIOC_NONE:
+ return -ENOENT;
+ }
+
+ return 0;
+}
+
+static int fioc_open(const char *path, struct fuse_file_info *fi)
+{
+ (void) fi;
+
+ if (fioc_file_type(path) != FIOC_NONE)
+ return 0;
+ return -ENOENT;
+}
+
+static int fioc_do_read(char *buf, size_t size, off_t offset)
+{
+ if (offset >= fioc_size)
+ return 0;
+
+ if (size > fioc_size - offset)
+ size = fioc_size - offset;
+
+ memcpy(buf, fioc_buf + offset, size);
+
+ return size;
+}
+
+static int fioc_read(const char *path, char *buf, size_t size,
+ off_t offset, struct fuse_file_info *fi)
+{
+ (void) fi;
+
+ if (fioc_file_type(path) != FIOC_FILE)
+ return -EINVAL;
+
+ return fioc_do_read(buf, size, offset);
+}
+
+static int fioc_do_write(const char *buf, size_t size, off_t offset)
+{
+ if (fioc_expand(offset + size))
+ return -ENOMEM;
+
+ memcpy(fioc_buf + offset, buf, size);
+
+ return size;
+}
+
+static int fioc_write(const char *path, const char *buf, size_t size,
+ off_t offset, struct fuse_file_info *fi)
+{
+ (void) fi;
+
+ if (fioc_file_type(path) != FIOC_FILE)
+ return -EINVAL;
+
+ return fioc_do_write(buf, size, offset);
+}
+
+static int fioc_truncate(const char *path, off_t size)
+{
+ if (fioc_file_type(path) != FIOC_FILE)
+ return -EINVAL;
+
+ return fioc_resize(size);
+}
+
+static int fioc_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
+ off_t offset, struct fuse_file_info *fi)
+{
+ (void) fi;
+ (void) offset;
+
+ if (fioc_file_type(path) != FIOC_ROOT)
+ return -ENOENT;
+
+ filler(buf, ".", NULL, 0);
+ filler(buf, "..", NULL, 0);
+ filler(buf, FIOC_NAME, NULL, 0);
+
+ return 0;
+}
+
+static int fioc_ioctl(const char *path, int cmd, void *arg,
+ struct fuse_file_info *fi, unsigned int flags, void *data)
+{
+ (void) arg;
+ (void) fi;
+ (void) flags;
+
+ if (fioc_file_type(path) != FIOC_FILE)
+ return -EINVAL;
+
+ if (flags & FUSE_IOCTL_COMPAT)
+ return -ENOSYS;
+
+ switch (cmd) {
+ case FIOC_GET_SIZE:
+ *(size_t *)data = fioc_size;
+ return 0;
+
+ case FIOC_SET_SIZE:
+ fioc_resize(*(size_t *)data);
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+static struct fuse_operations fioc_oper = {
+ .getattr = fioc_getattr,
+ .readdir = fioc_readdir,
+ .truncate = fioc_truncate,
+ .open = fioc_open,
+ .read = fioc_read,
+ .write = fioc_write,
+ .ioctl = fioc_ioctl,
+};
+
+int main(int argc, char *argv[])
+{
+ return fuse_main(argc, argv, &fioc_oper, NULL);
+}
diff --git a/draft/fuse-examples/fioc.h b/draft/fuse-examples/fioc.h
new file mode 100644
index 0000000..ec1a39d
--- /dev/null
+++ b/draft/fuse-examples/fioc.h
@@ -0,0 +1,32 @@
+/*
+ FUSE-ioctl: ioctl support for FUSE
+ Copyright (C) 2008 SUSE Linux Products GmbH
+ Copyright (C) 2008 Tejun Heo <teheo@suse.de>
+
+ This program can be distributed under the terms of the GNU GPL.
+ See the file COPYING.
+*/
+
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <sys/ioctl.h>
+
+enum {
+ FIOC_GET_SIZE = _IOR('E', 0, size_t),
+ FIOC_SET_SIZE = _IOW('E', 1, size_t),
+
+ /*
+ * The following two ioctls don't follow usual encoding rules
+ * and transfer variable amount of data.
+ */
+ FIOC_READ = _IO('E', 2),
+ FIOC_WRITE = _IO('E', 3),
+};
+
+struct fioc_rw_arg {
+ off_t offset;
+ void *buf;
+ size_t size;
+ size_t prev_size; /* out param for previous total size */
+ size_t new_size; /* out param for new total size */
+};
diff --git a/draft/fuse-examples/fioclient.c b/draft/fuse-examples/fioclient.c
new file mode 100644
index 0000000..5f05525
--- /dev/null
+++ b/draft/fuse-examples/fioclient.c
@@ -0,0 +1,122 @@
+/*
+ FUSE fioclient: FUSE ioctl example client
+ Copyright (C) 2008 SUSE Linux Products GmbH
+ Copyright (C) 2008 Tejun Heo <teheo@suse.de>
+
+ This program can be distributed under the terms of the GNU GPL.
+ See the file COPYING.
+
+ gcc -Wall fioclient.c -o fioclient
+*/
+
+#include <sys/types.h>
+#include <sys/fcntl.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <errno.h>
+#include "fioc.h"
+
+const char *usage =
+"Usage: fioclient FIOC_FILE COMMAND\n"
+"\n"
+"COMMANDS\n"
+" s [SIZE] : get size if SIZE is omitted, set size otherwise\n"
+" r SIZE [OFF] : read SIZE bytes @ OFF (dfl 0) and output to stdout\n"
+" w SIZE [OFF] : write SIZE bytes @ OFF (dfl 0) from stdin\n"
+"\n";
+
+static int do_rw(int fd, int is_read, size_t size, off_t offset,
+ size_t *prev_size, size_t *new_size)
+{
+ struct fioc_rw_arg arg = { .offset = offset };
+ ssize_t ret;
+
+ arg.buf = calloc(1, size);
+ if (!arg.buf) {
+ fprintf(stderr, "failed to allocated %zu bytes\n", size);
+ return -1;
+ }
+
+ if (is_read) {
+ arg.size = size;
+ ret = ioctl(fd, FIOC_READ, &arg);
+ if (ret >= 0)
+ fwrite(arg.buf, 1, ret, stdout);
+ } else {
+ arg.size = fread(arg.buf, 1, size, stdin);
+ fprintf(stderr, "Writing %zu bytes\n", arg.size);
+ ret = ioctl(fd, FIOC_WRITE, &arg);
+ }
+
+ if (ret >= 0) {
+ *prev_size = arg.prev_size;
+ *new_size = arg.new_size;
+ } else
+ perror("ioctl");
+
+ free(arg.buf);
+ return ret;
+}
+
+int main(int argc, char **argv)
+{
+ size_t param[2] = { };
+ size_t size, prev_size = 0, new_size = 0;
+ char cmd;
+ int fd, i, rc;
+
+ if (argc < 3)
+ goto usage;
+
+ fd = open(argv[1], O_RDWR);
+ if (fd < 0) {
+ perror("open");
+ return 1;
+ }
+
+ cmd = tolower(argv[2][0]);
+ argc -= 3;
+ argv += 3;
+
+ for (i = 0; i < argc; i++) {
+ char *endp;
+ param[i] = strtoul(argv[i], &endp, 0);
+ if (endp == argv[i] || *endp != '\0')
+ goto usage;
+ }
+
+ switch (cmd) {
+ case 's':
+ if (!argc) {
+ if (ioctl(fd, FIOC_GET_SIZE, &size)) {
+ perror("ioctl");
+ return 1;
+ }
+ printf("%zu\n", size);
+ } else {
+ size = param[0];
+ if (ioctl(fd, FIOC_SET_SIZE, &size)) {
+ perror("ioctl");
+ return 1;
+ }
+ }
+ return 0;
+
+ case 'r':
+ case 'w':
+ rc = do_rw(fd, cmd == 'r', param[0], param[1],
+ &prev_size, &new_size);
+ if (rc < 0)
+ return 1;
+ fprintf(stderr, "transferred %d bytes (%zu -> %zu)\n",
+ rc, prev_size, new_size);
+ return 0;
+ }
+
+ usage:
+ fprintf(stderr, "%s", usage);
+ return 1;
+}
diff --git a/draft/fuse-examples/fsel.c b/draft/fuse-examples/fsel.c
new file mode 100644
index 0000000..9cf0221
--- /dev/null
+++ b/draft/fuse-examples/fsel.c
@@ -0,0 +1,278 @@
+/*
+ FUSE fsel: FUSE select example
+ Copyright (C) 2008 SUSE Linux Products GmbH
+ Copyright (C) 2008 Tejun Heo <teheo@suse.de>
+
+ This program can be distributed under the terms of the GNU GPL.
+ See the file COPYING.
+
+ gcc -Wall fsel.c `pkg-config fuse --cflags --libs` -o fsel
+*/
+
+#define FUSE_USE_VERSION 29
+
+#include <fuse.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <time.h>
+#include <pthread.h>
+#include <poll.h>
+
+/*
+ * fsel_open_mask is used to limit the number of opens to 1 per file.
+ * This is to use file index (0-F) as fh as poll support requires
+ * unique fh per open file. Lifting this would require proper open
+ * file management.
+ */
+static unsigned fsel_open_mask;
+static const char fsel_hex_map[] = "0123456789ABCDEF";
+static struct fuse *fsel_fuse; /* needed for poll notification */
+
+#define FSEL_CNT_MAX 10 /* each file can store upto 10 chars */
+#define FSEL_FILES 16
+
+static pthread_mutex_t fsel_mutex; /* protects notify_mask and cnt array */
+static unsigned fsel_poll_notify_mask; /* poll notification scheduled? */
+static struct fuse_pollhandle *fsel_poll_handle[FSEL_FILES]; /* poll notify handles */
+static unsigned fsel_cnt[FSEL_FILES]; /* nbytes stored in each file */
+
+static int fsel_path_index(const char *path)
+{
+ char ch = path[1];
+
+ if (strlen(path) != 2 || path[0] != '/' || !isxdigit(ch) || islower(ch))
+ return -1;
+ return ch <= '9' ? ch - '0' : ch - 'A' + 10;
+}
+
+static int fsel_getattr(const char *path, struct stat *stbuf)
+{
+ int idx;
+
+ memset(stbuf, 0, sizeof(struct stat));
+
+ if (strcmp(path, "/") == 0) {
+ stbuf->st_mode = S_IFDIR | 0555;
+ stbuf->st_nlink = 2;
+ return 0;
+ }
+
+ idx = fsel_path_index(path);
+ if (idx < 0)
+ return -ENOENT;
+
+ stbuf->st_mode = S_IFREG | 0444;
+ stbuf->st_nlink = 1;
+ stbuf->st_size = fsel_cnt[idx];
+ return 0;
+}
+
+static int fsel_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
+ off_t offset, struct fuse_file_info *fi)
+{
+ char name[2] = { };
+ int i;
+
+ (void) offset;
+ (void) fi;
+
+ if (strcmp(path, "/") != 0)
+ return -ENOENT;
+
+ for (i = 0; i < FSEL_FILES; i++) {
+ name[0] = fsel_hex_map[i];
+ filler(buf, name, NULL, 0);
+ }
+
+ return 0;
+}
+
+static int fsel_open(const char *path, struct fuse_file_info *fi)
+{
+ int idx = fsel_path_index(path);
+
+ if (idx < 0)
+ return -ENOENT;
+ if ((fi->flags & 3) != O_RDONLY)
+ return -EACCES;
+ if (fsel_open_mask & (1 << idx))
+ return -EBUSY;
+ fsel_open_mask |= (1 << idx);
+
+ /*
+ * fsel files are nonseekable somewhat pipe-like files which
+ * gets filled up periodically by producer thread and consumed
+ * on read. Tell FUSE as such.
+ */
+ fi->fh = idx;
+ fi->direct_io = 1;
+ fi->nonseekable = 1;
+
+ return 0;
+}
+
+static int fsel_release(const char *path, struct fuse_file_info *fi)
+{
+ int idx = fi->fh;
+
+ (void) path;
+
+ fsel_open_mask &= ~(1 << idx);
+ return 0;
+}
+
+static int fsel_read(const char *path, char *buf, size_t size, off_t offset,
+ struct fuse_file_info *fi)
+{
+ int idx = fi->fh;
+
+ (void) path;
+ (void) offset;
+
+ pthread_mutex_lock(&fsel_mutex);
+ if (fsel_cnt[idx] < size)
+ size = fsel_cnt[idx];
+ printf("READ %X transferred=%zu cnt=%u\n", idx, size, fsel_cnt[idx]);
+ fsel_cnt[idx] -= size;
+ pthread_mutex_unlock(&fsel_mutex);
+
+ memset(buf, fsel_hex_map[idx], size);
+ return size;
+}
+
+static int fsel_poll(const char *path, struct fuse_file_info *fi,
+ struct fuse_pollhandle *ph, unsigned *reventsp)
+{
+ static unsigned polled_zero;
+ int idx = fi->fh;
+
+ (void) path;
+
+ /*
+ * Poll notification requires pointer to struct fuse which
+ * can't be obtained when using fuse_main(). As notification
+ * happens only after poll is called, fill it here from
+ * fuse_context.
+ */
+ if (!fsel_fuse) {
+ struct fuse_context *cxt = fuse_get_context();
+ if (cxt)
+ fsel_fuse = cxt->fuse;
+ }
+
+ pthread_mutex_lock(&fsel_mutex);
+
+ if (ph != NULL) {
+ struct fuse_pollhandle *oldph = fsel_poll_handle[idx];
+
+ if (oldph)
+ fuse_pollhandle_destroy(oldph);
+
+ fsel_poll_notify_mask |= (1 << idx);
+ fsel_poll_handle[idx] = ph;
+ }
+
+ if (fsel_cnt[idx]) {
+ *reventsp |= POLLIN;
+ printf("POLL %X cnt=%u polled_zero=%u\n",
+ idx, fsel_cnt[idx], polled_zero);
+ polled_zero = 0;
+ } else
+ polled_zero++;
+
+ pthread_mutex_unlock(&fsel_mutex);
+ return 0;
+}
+
+static struct fuse_operations fsel_oper = {
+ .getattr = fsel_getattr,
+ .readdir = fsel_readdir,
+ .open = fsel_open,
+ .release = fsel_release,
+ .read = fsel_read,
+ .poll = fsel_poll,
+};
+
+static void *fsel_producer(void *data)
+{
+ const struct timespec interval = { 0, 250000000 };
+ unsigned idx = 0, nr = 1;
+
+ (void) data;
+
+ while (1) {
+ int i, t;
+
+ pthread_mutex_lock(&fsel_mutex);
+
+ /*
+ * This is the main producer loop which is executed
+ * ever 500ms. On each iteration, it fills one byte
+ * to 1, 2 or 4 files and sends poll notification if
+ * requested.
+ */
+ for (i = 0, t = idx; i < nr;
+ i++, t = (t + FSEL_FILES / nr) % FSEL_FILES) {
+ if (fsel_cnt[t] == FSEL_CNT_MAX)
+ continue;
+
+ fsel_cnt[t]++;
+ if (fsel_fuse && (fsel_poll_notify_mask & (1 << t))) {
+ struct fuse_pollhandle *ph;
+
+ printf("NOTIFY %X\n", t);
+ ph = fsel_poll_handle[t];
+ fuse_notify_poll(ph);
+ fuse_pollhandle_destroy(ph);
+ fsel_poll_notify_mask &= ~(1 << t);
+ fsel_poll_handle[t] = NULL;
+ }
+ }
+
+ idx = (idx + 1) % FSEL_FILES;
+ if (idx == 0)
+ nr = (nr * 2) % 7; /* cycle through 1, 2 and 4 */
+
+ pthread_mutex_unlock(&fsel_mutex);
+
+ nanosleep(&interval, NULL);
+ }
+
+ return NULL;
+}
+
+int main(int argc, char *argv[])
+{
+ pthread_t producer;
+ pthread_attr_t attr;
+ int ret;
+
+ errno = pthread_mutex_init(&fsel_mutex, NULL);
+ if (errno) {
+ perror("pthread_mutex_init");
+ return 1;
+ }
+
+ errno = pthread_attr_init(&attr);
+ if (errno) {
+ perror("pthread_attr_init");
+ return 1;
+ }
+
+ errno = pthread_create(&producer, &attr, fsel_producer, NULL);
+ if (errno) {
+ perror("pthread_create");
+ return 1;
+ }
+
+ ret = fuse_main(argc, argv, &fsel_oper, NULL);
+
+ pthread_cancel(producer);
+ pthread_join(producer, NULL);
+
+ return ret;
+}
diff --git a/draft/fuse-examples/fselclient.c b/draft/fuse-examples/fselclient.c
new file mode 100644
index 0000000..7c4b837
--- /dev/null
+++ b/draft/fuse-examples/fselclient.c
@@ -0,0 +1,72 @@
+/*
+ FUSE fselclient: FUSE select example client
+ Copyright (C) 2008 SUSE Linux Products GmbH
+ Copyright (C) 2008 Tejun Heo <teheo@suse.de>
+
+ This program can be distributed under the terms of the GNU GPL.
+ See the file COPYING.
+
+ gcc -Wall fselclient.c -o fselclient
+*/
+
+#include <sys/select.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#define FSEL_FILES 16
+
+int main(void)
+{
+ static const char hex_map[FSEL_FILES] = "0123456789ABCDEF";
+ int fds[FSEL_FILES];
+ int i, nfds;
+
+ for (i = 0; i < FSEL_FILES; i++) {
+ char name[] = { hex_map[i], '\0' };
+ fds[i] = open(name, O_RDONLY);
+ if (fds[i] < 0) {
+ perror("open");
+ return 1;
+ }
+ }
+ nfds = fds[FSEL_FILES - 1] + 1;
+
+ while (1) {
+ static char buf[4096];
+ fd_set rfds;
+ int rc;
+
+ FD_ZERO(&rfds);
+ for (i = 0; i < FSEL_FILES; i++)
+ FD_SET(fds[i], &rfds);
+
+ rc = select(nfds, &rfds, NULL, NULL, NULL);
+
+ if (rc < 0) {
+ perror("select");
+ return 1;
+ }
+
+ for (i = 0; i < FSEL_FILES; i++) {
+ if (!FD_ISSET(fds[i], &rfds)) {
+ printf("_: ");
+ continue;
+ }
+ printf("%X:", i);
+ rc = read(fds[i], buf, sizeof(buf));
+ if (rc < 0) {
+ perror("read");
+ return 1;
+ }
+ printf("%02d ", rc);
+ }
+ printf("\n");
+ }
+}
diff --git a/draft/fuse-examples/fusexmp.c b/draft/fuse-examples/fusexmp.c
new file mode 100644
index 0000000..dca8a46
--- /dev/null
+++ b/draft/fuse-examples/fusexmp.c
@@ -0,0 +1,412 @@
+/*
+ FUSE: Filesystem in Userspace
+ Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
+ Copyright (C) 2011 Sebastian Pipping <sebastian@pipping.org>
+
+ This program can be distributed under the terms of the GNU GPL.
+ See the file COPYING.
+
+ gcc -Wall fusexmp.c `pkg-config fuse --cflags --libs` -o fusexmp
+*/
+
+#define FUSE_USE_VERSION 26
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifdef linux
+/* For pread()/pwrite()/utimensat() */
+#define _XOPEN_SOURCE 700
+#endif
+
+#include <fuse.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <dirent.h>
+#include <errno.h>
+#include <sys/time.h>
+#ifdef HAVE_SETXATTR
+#include <sys/xattr.h>
+#endif
+
+static int xmp_getattr(const char *path, struct stat *stbuf)
+{
+ int res;
+
+ res = lstat(path, stbuf);
+ if (res == -1)
+ return -errno;
+
+ return 0;
+}
+
+static int xmp_access(const char *path, int mask)
+{
+ int res;
+
+ res = access(path, mask);
+ if (res == -1)
+ return -errno;
+
+ return 0;
+}
+
+static int xmp_readlink(const char *path, char *buf, size_t size)
+{
+ int res;
+
+ res = readlink(path, buf, size - 1);
+ if (res == -1)
+ return -errno;
+
+ buf[res] = '\0';
+ return 0;
+}
+
+
+static int xmp_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
+ off_t offset, struct fuse_file_info *fi)
+{
+ DIR *dp;
+ struct dirent *de;
+
+ (void) offset;
+ (void) fi;
+
+ dp = opendir(path);
+ if (dp == NULL)
+ return -errno;
+
+ while ((de = readdir(dp)) != NULL) {
+ struct stat st;
+ memset(&st, 0, sizeof(st));
+ st.st_ino = de->d_ino;
+ st.st_mode = de->d_type << 12;
+ if (filler(buf, de->d_name, &st, 0))
+ break;
+ }
+
+ closedir(dp);
+ return 0;
+}
+
+static int xmp_mknod(const char *path, mode_t mode, dev_t rdev)
+{
+ int res;
+
+ /* On Linux this could just be 'mknod(path, mode, rdev)' but this
+ is more portable */
+ if (S_ISREG(mode)) {
+ res = open(path, O_CREAT | O_EXCL | O_WRONLY, mode);
+ if (res >= 0)
+ res = close(res);
+ } else if (S_ISFIFO(mode))
+ res = mkfifo(path, mode);
+ else
+ res = mknod(path, mode, rdev);
+ if (res == -1)
+ return -errno;
+
+ return 0;
+}
+
+static int xmp_mkdir(const char *path, mode_t mode)
+{
+ int res;
+
+ res = mkdir(path, mode);
+ if (res == -1)
+ return -errno;
+
+ return 0;
+}
+
+static int xmp_unlink(const char *path)
+{
+ int res;
+
+ res = unlink(path);
+ if (res == -1)
+ return -errno;
+
+ return 0;
+}
+
+static int xmp_rmdir(const char *path)
+{
+ int res;
+
+ res = rmdir(path);
+ if (res == -1)
+ return -errno;
+
+ return 0;
+}
+
+static int xmp_symlink(const char *from, const char *to)
+{
+ int res;
+
+ res = symlink(from, to);
+ if (res == -1)
+ return -errno;
+
+ return 0;
+}
+
+static int xmp_rename(const char *from, const char *to)
+{
+ int res;
+
+ res = rename(from, to);
+ if (res == -1)
+ return -errno;
+
+ return 0;
+}
+
+static int xmp_link(const char *from, const char *to)
+{
+ int res;
+
+ res = link(from, to);
+ if (res == -1)
+ return -errno;
+
+ return 0;
+}
+
+static int xmp_chmod(const char *path, mode_t mode)
+{
+ int res;
+
+ res = chmod(path, mode);
+ if (res == -1)
+ return -errno;
+
+ return 0;
+}
+
+static int xmp_chown(const char *path, uid_t uid, gid_t gid)
+{
+ int res;
+
+ res = lchown(path, uid, gid);
+ if (res == -1)
+ return -errno;
+
+ return 0;
+}
+
+static int xmp_truncate(const char *path, off_t size)
+{
+ int res;
+
+ res = truncate(path, size);
+ if (res == -1)
+ return -errno;
+
+ return 0;
+}
+
+#ifdef HAVE_UTIMENSAT
+static int xmp_utimens(const char *path, const struct timespec ts[2])
+{
+ int res;
+
+ /* don't use utime/utimes since they follow symlinks */
+ res = utimensat(0, path, ts, AT_SYMLINK_NOFOLLOW);
+ if (res == -1)
+ return -errno;
+
+ return 0;
+}
+#endif
+
+static int xmp_open(const char *path, struct fuse_file_info *fi)
+{
+ int res;
+
+ res = open(path, fi->flags);
+ if (res == -1)
+ return -errno;
+
+ close(res);
+ return 0;
+}
+
+static int xmp_read(const char *path, char *buf, size_t size, off_t offset,
+ struct fuse_file_info *fi)
+{
+ int fd;
+ int res;
+
+ (void) fi;
+ fd = open(path, O_RDONLY);
+ if (fd == -1)
+ return -errno;
+
+ res = pread(fd, buf, size, offset);
+ if (res == -1)
+ res = -errno;
+
+ close(fd);
+ return res;
+}
+
+static int xmp_write(const char *path, const char *buf, size_t size,
+ off_t offset, struct fuse_file_info *fi)
+{
+ int fd;
+ int res;
+
+ (void) fi;
+ fd = open(path, O_WRONLY);
+ if (fd == -1)
+ return -errno;
+
+ res = pwrite(fd, buf, size, offset);
+ if (res == -1)
+ res = -errno;
+
+ close(fd);
+ return res;
+}
+
+static int xmp_statfs(const char *path, struct statvfs *stbuf)
+{
+ int res;
+
+ res = statvfs(path, stbuf);
+ if (res == -1)
+ return -errno;
+
+ return 0;
+}
+
+static int xmp_release(const char *path, struct fuse_file_info *fi)
+{
+ /* Just a stub. This method is optional and can safely be left
+ unimplemented */
+
+ (void) path;
+ (void) fi;
+ return 0;
+}
+
+static int xmp_fsync(const char *path, int isdatasync,
+ struct fuse_file_info *fi)
+{
+ /* Just a stub. This method is optional and can safely be left
+ unimplemented */
+
+ (void) path;
+ (void) isdatasync;
+ (void) fi;
+ return 0;
+}
+
+#ifdef HAVE_POSIX_FALLOCATE
+static int xmp_fallocate(const char *path, int mode,
+ off_t offset, off_t length, struct fuse_file_info *fi)
+{
+ int fd;
+ int res;
+
+ (void) fi;
+
+ if (mode)
+ return -EOPNOTSUPP;
+
+ fd = open(path, O_WRONLY);
+ if (fd == -1)
+ return -errno;
+
+ res = -posix_fallocate(fd, offset, length);
+
+ close(fd);
+ return res;
+}
+#endif
+
+#ifdef HAVE_SETXATTR
+/* xattr operations are optional and can safely be left unimplemented */
+static int xmp_setxattr(const char *path, const char *name, const char *value,
+ size_t size, int flags)
+{
+ int res = lsetxattr(path, name, value, size, flags);
+ if (res == -1)
+ return -errno;
+ return 0;
+}
+
+static int xmp_getxattr(const char *path, const char *name, char *value,
+ size_t size)
+{
+ int res = lgetxattr(path, name, value, size);
+ if (res == -1)
+ return -errno;
+ return res;
+}
+
+static int xmp_listxattr(const char *path, char *list, size_t size)
+{
+ int res = llistxattr(path, list, size);
+ if (res == -1)
+ return -errno;
+ return res;
+}
+
+static int xmp_removexattr(const char *path, const char *name)
+{
+ int res = lremovexattr(path, name);
+ if (res == -1)
+ return -errno;
+ return 0;
+}
+#endif /* HAVE_SETXATTR */
+
+static struct fuse_operations xmp_oper = {
+ .getattr = xmp_getattr,
+ .access = xmp_access,
+ .readlink = xmp_readlink,
+ .readdir = xmp_readdir,
+ .mknod = xmp_mknod,
+ .mkdir = xmp_mkdir,
+ .symlink = xmp_symlink,
+ .unlink = xmp_unlink,
+ .rmdir = xmp_rmdir,
+ .rename = xmp_rename,
+ .link = xmp_link,
+ .chmod = xmp_chmod,
+ .chown = xmp_chown,
+ .truncate = xmp_truncate,
+#ifdef HAVE_UTIMENSAT
+ .utimens = xmp_utimens,
+#endif
+ .open = xmp_open,
+ .read = xmp_read,
+ .write = xmp_write,
+ .statfs = xmp_statfs,
+ .release = xmp_release,
+ .fsync = xmp_fsync,
+#ifdef HAVE_POSIX_FALLOCATE
+ .fallocate = xmp_fallocate,
+#endif
+#ifdef HAVE_SETXATTR
+ .setxattr = xmp_setxattr,
+ .getxattr = xmp_getxattr,
+ .listxattr = xmp_listxattr,
+ .removexattr = xmp_removexattr,
+#endif
+};
+
+int main(int argc, char *argv[])
+{
+ umask(0);
+ return fuse_main(argc, argv, &xmp_oper, NULL);
+}
diff --git a/draft/fuse-examples/fusexmp_fh.c b/draft/fuse-examples/fusexmp_fh.c
new file mode 100644
index 0000000..1ba9dbc
--- /dev/null
+++ b/draft/fuse-examples/fusexmp_fh.c
@@ -0,0 +1,567 @@
+/*
+ FUSE: Filesystem in Userspace
+ Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
+ Copyright (C) 2011 Sebastian Pipping <sebastian@pipping.org>
+
+ This program can be distributed under the terms of the GNU GPL.
+ See the file COPYING.
+
+ gcc -Wall fusexmp_fh.c `pkg-config fuse --cflags --libs` -lulockmgr -o fusexmp_fh
+*/
+
+#define FUSE_USE_VERSION 26
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#define _GNU_SOURCE
+
+#include <fuse.h>
+#include <ulockmgr.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <dirent.h>
+#include <errno.h>
+#include <sys/time.h>
+#ifdef HAVE_SETXATTR
+#include <sys/xattr.h>
+#endif
+#include <sys/file.h> /* flock(2) */
+
+static int xmp_getattr(const char *path, struct stat *stbuf)
+{
+ int res;
+
+ res = lstat(path, stbuf);
+ if (res == -1)
+ return -errno;
+
+ return 0;
+}
+
+static int xmp_fgetattr(const char *path, struct stat *stbuf,
+ struct fuse_file_info *fi)
+{
+ int res;
+
+ (void) path;
+
+ res = fstat(fi->fh, stbuf);
+ if (res == -1)
+ return -errno;
+
+ return 0;
+}
+
+static int xmp_access(const char *path, int mask)
+{
+ int res;
+
+ res = access(path, mask);
+ if (res == -1)
+ return -errno;
+
+ return 0;
+}
+
+static int xmp_readlink(const char *path, char *buf, size_t size)
+{
+ int res;
+
+ res = readlink(path, buf, size - 1);
+ if (res == -1)
+ return -errno;
+
+ buf[res] = '\0';
+ return 0;
+}
+
+struct xmp_dirp {
+ DIR *dp;
+ struct dirent *entry;
+ off_t offset;
+};
+
+static int xmp_opendir(const char *path, struct fuse_file_info *fi)
+{
+ int res;
+ struct xmp_dirp *d = malloc(sizeof(struct xmp_dirp));
+ if (d == NULL)
+ return -ENOMEM;
+
+ d->dp = opendir(path);
+ if (d->dp == NULL) {
+ res = -errno;
+ free(d);
+ return res;
+ }
+ d->offset = 0;
+ d->entry = NULL;
+
+ fi->fh = (unsigned long) d;
+ return 0;
+}
+
+static inline struct xmp_dirp *get_dirp(struct fuse_file_info *fi)
+{
+ return (struct xmp_dirp *) (uintptr_t) fi->fh;
+}
+
+static int xmp_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
+ off_t offset, struct fuse_file_info *fi)
+{
+ struct xmp_dirp *d = get_dirp(fi);
+
+ (void) path;
+ if (offset != d->offset) {
+ seekdir(d->dp, offset);
+ d->entry = NULL;
+ d->offset = offset;
+ }
+ while (1) {
+ struct stat st;
+ off_t nextoff;
+
+ if (!d->entry) {
+ d->entry = readdir(d->dp);
+ if (!d->entry)
+ break;
+ }
+
+ memset(&st, 0, sizeof(st));
+ st.st_ino = d->entry->d_ino;
+ st.st_mode = d->entry->d_type << 12;
+ nextoff = telldir(d->dp);
+ if (filler(buf, d->entry->d_name, &st, nextoff))
+ break;
+
+ d->entry = NULL;
+ d->offset = nextoff;
+ }
+
+ return 0;
+}
+
+static int xmp_releasedir(const char *path, struct fuse_file_info *fi)
+{
+ struct xmp_dirp *d = get_dirp(fi);
+ (void) path;
+ closedir(d->dp);
+ free(d);
+ return 0;
+}
+
+static int xmp_mknod(const char *path, mode_t mode, dev_t rdev)
+{
+ int res;
+
+ if (S_ISFIFO(mode))
+ res = mkfifo(path, mode);
+ else
+ res = mknod(path, mode, rdev);
+ if (res == -1)
+ return -errno;
+
+ return 0;
+}
+
+static int xmp_mkdir(const char *path, mode_t mode)
+{
+ int res;
+
+ res = mkdir(path, mode);
+ if (res == -1)
+ return -errno;
+
+ return 0;
+}
+
+static int xmp_unlink(const char *path)
+{
+ int res;
+
+ res = unlink(path);
+ if (res == -1)
+ return -errno;
+
+ return 0;
+}
+
+static int xmp_rmdir(const char *path)
+{
+ int res;
+
+ res = rmdir(path);
+ if (res == -1)
+ return -errno;
+
+ return 0;
+}
+
+static int xmp_symlink(const char *from, const char *to)
+{
+ int res;
+
+ res = symlink(from, to);
+ if (res == -1)
+ return -errno;
+
+ return 0;
+}
+
+static int xmp_rename(const char *from, const char *to)
+{
+ int res;
+
+ res = rename(from, to);
+ if (res == -1)
+ return -errno;
+
+ return 0;
+}
+
+static int xmp_link(const char *from, const char *to)
+{
+ int res;
+
+ res = link(from, to);
+ if (res == -1)
+ return -errno;
+
+ return 0;
+}
+
+static int xmp_chmod(const char *path, mode_t mode)
+{
+ int res;
+
+ res = chmod(path, mode);
+ if (res == -1)
+ return -errno;
+
+ return 0;
+}
+
+static int xmp_chown(const char *path, uid_t uid, gid_t gid)
+{
+ int res;
+
+ res = lchown(path, uid, gid);
+ if (res == -1)
+ return -errno;
+
+ return 0;
+}
+
+static int xmp_truncate(const char *path, off_t size)
+{
+ int res;
+
+ res = truncate(path, size);
+ if (res == -1)
+ return -errno;
+
+ return 0;
+}
+
+static int xmp_ftruncate(const char *path, off_t size,
+ struct fuse_file_info *fi)
+{
+ int res;
+
+ (void) path;
+
+ res = ftruncate(fi->fh, size);
+ if (res == -1)
+ return -errno;
+
+ return 0;
+}
+
+#ifdef HAVE_UTIMENSAT
+static int xmp_utimens(const char *path, const struct timespec ts[2])
+{
+ int res;
+
+ /* don't use utime/utimes since they follow symlinks */
+ res = utimensat(0, path, ts, AT_SYMLINK_NOFOLLOW);
+ if (res == -1)
+ return -errno;
+
+ return 0;
+}
+#endif
+
+static int xmp_create(const char *path, mode_t mode, struct fuse_file_info *fi)
+{
+ int fd;
+
+ fd = open(path, fi->flags, mode);
+ if (fd == -1)
+ return -errno;
+
+ fi->fh = fd;
+ return 0;
+}
+
+static int xmp_open(const char *path, struct fuse_file_info *fi)
+{
+ int fd;
+
+ fd = open(path, fi->flags);
+ if (fd == -1)
+ return -errno;
+
+ fi->fh = fd;
+ return 0;
+}
+
+static int xmp_read(const char *path, char *buf, size_t size, off_t offset,
+ struct fuse_file_info *fi)
+{
+ int res;
+
+ (void) path;
+ res = pread(fi->fh, buf, size, offset);
+ if (res == -1)
+ res = -errno;
+
+ return res;
+}
+
+static int xmp_read_buf(const char *path, struct fuse_bufvec **bufp,
+ size_t size, off_t offset, struct fuse_file_info *fi)
+{
+ struct fuse_bufvec *src;
+
+ (void) path;
+
+ src = malloc(sizeof(struct fuse_bufvec));
+ if (src == NULL)
+ return -ENOMEM;
+
+ *src = FUSE_BUFVEC_INIT(size);
+
+ src->buf[0].flags = FUSE_BUF_IS_FD | FUSE_BUF_FD_SEEK;
+ src->buf[0].fd = fi->fh;
+ src->buf[0].pos = offset;
+
+ *bufp = src;
+
+ return 0;
+}
+
+static int xmp_write(const char *path, const char *buf, size_t size,
+ off_t offset, struct fuse_file_info *fi)
+{
+ int res;
+
+ (void) path;
+ res = pwrite(fi->fh, buf, size, offset);
+ if (res == -1)
+ res = -errno;
+
+ return res;
+}
+
+static int xmp_write_buf(const char *path, struct fuse_bufvec *buf,
+ off_t offset, struct fuse_file_info *fi)
+{
+ struct fuse_bufvec dst = FUSE_BUFVEC_INIT(fuse_buf_size(buf));
+
+ (void) path;
+
+ dst.buf[0].flags = FUSE_BUF_IS_FD | FUSE_BUF_FD_SEEK;
+ dst.buf[0].fd = fi->fh;
+ dst.buf[0].pos = offset;
+
+ return fuse_buf_copy(&dst, buf, FUSE_BUF_SPLICE_NONBLOCK);
+}
+
+static int xmp_statfs(const char *path, struct statvfs *stbuf)
+{
+ int res;
+
+ res = statvfs(path, stbuf);
+ if (res == -1)
+ return -errno;
+
+ return 0;
+}
+
+static int xmp_flush(const char *path, struct fuse_file_info *fi)
+{
+ int res;
+
+ (void) path;
+ /* This is called from every close on an open file, so call the
+ close on the underlying filesystem. But since flush may be
+ called multiple times for an open file, this must not really
+ close the file. This is important if used on a network
+ filesystem like NFS which flush the data/metadata on close() */
+ res = close(dup(fi->fh));
+ if (res == -1)
+ return -errno;
+
+ return 0;
+}
+
+static int xmp_release(const char *path, struct fuse_file_info *fi)
+{
+ (void) path;
+ close(fi->fh);
+
+ return 0;
+}
+
+static int xmp_fsync(const char *path, int isdatasync,
+ struct fuse_file_info *fi)
+{
+ int res;
+ (void) path;
+
+#ifndef HAVE_FDATASYNC
+ (void) isdatasync;
+#else
+ if (isdatasync)
+ res = fdatasync(fi->fh);
+ else
+#endif
+ res = fsync(fi->fh);
+ if (res == -1)
+ return -errno;
+
+ return 0;
+}
+
+#ifdef HAVE_POSIX_FALLOCATE
+static int xmp_fallocate(const char *path, int mode,
+ off_t offset, off_t length, struct fuse_file_info *fi)
+{
+ (void) path;
+
+ if (mode)
+ return -EOPNOTSUPP;
+
+ return -posix_fallocate(fi->fh, offset, length);
+}
+#endif
+
+#ifdef HAVE_SETXATTR
+/* xattr operations are optional and can safely be left unimplemented */
+static int xmp_setxattr(const char *path, const char *name, const char *value,
+ size_t size, int flags)
+{
+ int res = lsetxattr(path, name, value, size, flags);
+ if (res == -1)
+ return -errno;
+ return 0;
+}
+
+static int xmp_getxattr(const char *path, const char *name, char *value,
+ size_t size)
+{
+ int res = lgetxattr(path, name, value, size);
+ if (res == -1)
+ return -errno;
+ return res;
+}
+
+static int xmp_listxattr(const char *path, char *list, size_t size)
+{
+ int res = llistxattr(path, list, size);
+ if (res == -1)
+ return -errno;
+ return res;
+}
+
+static int xmp_removexattr(const char *path, const char *name)
+{
+ int res = lremovexattr(path, name);
+ if (res == -1)
+ return -errno;
+ return 0;
+}
+#endif /* HAVE_SETXATTR */
+
+static int xmp_lock(const char *path, struct fuse_file_info *fi, int cmd,
+ struct flock *lock)
+{
+ (void) path;
+
+ return ulockmgr_op(fi->fh, cmd, lock, &fi->lock_owner,
+ sizeof(fi->lock_owner));
+}
+
+static int xmp_flock(const char *path, struct fuse_file_info *fi, int op)
+{
+ int res;
+ (void) path;
+
+ res = flock(fi->fh, op);
+ if (res == -1)
+ return -errno;
+
+ return 0;
+}
+
+static struct fuse_operations xmp_oper = {
+ .getattr = xmp_getattr,
+ .fgetattr = xmp_fgetattr,
+ .access = xmp_access,
+ .readlink = xmp_readlink,
+ .opendir = xmp_opendir,
+ .readdir = xmp_readdir,
+ .releasedir = xmp_releasedir,
+ .mknod = xmp_mknod,
+ .mkdir = xmp_mkdir,
+ .symlink = xmp_symlink,
+ .unlink = xmp_unlink,
+ .rmdir = xmp_rmdir,
+ .rename = xmp_rename,
+ .link = xmp_link,
+ .chmod = xmp_chmod,
+ .chown = xmp_chown,
+ .truncate = xmp_truncate,
+ .ftruncate = xmp_ftruncate,
+#ifdef HAVE_UTIMENSAT
+ .utimens = xmp_utimens,
+#endif
+ .create = xmp_create,
+ .open = xmp_open,
+ .read = xmp_read,
+ .read_buf = xmp_read_buf,
+ .write = xmp_write,
+ .write_buf = xmp_write_buf,
+ .statfs = xmp_statfs,
+ .flush = xmp_flush,
+ .release = xmp_release,
+ .fsync = xmp_fsync,
+#ifdef HAVE_POSIX_FALLOCATE
+ .fallocate = xmp_fallocate,
+#endif
+#ifdef HAVE_SETXATTR
+ .setxattr = xmp_setxattr,
+ .getxattr = xmp_getxattr,
+ .listxattr = xmp_listxattr,
+ .removexattr = xmp_removexattr,
+#endif
+ .lock = xmp_lock,
+ .flock = xmp_flock,
+
+ .flag_nullpath_ok = 1,
+#if HAVE_UTIMENSAT
+ .flag_utime_omit_ok = 1,
+#endif
+};
+
+int main(int argc, char *argv[])
+{
+ umask(0);
+ return fuse_main(argc, argv, &xmp_oper, NULL);
+}
diff --git a/draft/fuse-examples/hello.c b/draft/fuse-examples/hello.c
new file mode 100644
index 0000000..bcb6b4c
--- /dev/null
+++ b/draft/fuse-examples/hello.c
@@ -0,0 +1,96 @@
+/*
+ FUSE: Filesystem in Userspace
+ Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
+
+ This program can be distributed under the terms of the GNU GPL.
+ See the file COPYING.
+
+ gcc -Wall hello.c `pkg-config fuse --cflags --libs` -o hello
+*/
+
+#define FUSE_USE_VERSION 26
+
+#include <fuse.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <fcntl.h>
+
+static const char *hello_str = "Hello World!\n";
+static const char *hello_path = "/hello";
+
+static int hello_getattr(const char *path, struct stat *stbuf)
+{
+ int res = 0;
+
+ memset(stbuf, 0, sizeof(struct stat));
+ if (strcmp(path, "/") == 0) {
+ stbuf->st_mode = S_IFDIR | 0755;
+ stbuf->st_nlink = 2;
+ } else if (strcmp(path, hello_path) == 0) {
+ stbuf->st_mode = S_IFREG | 0444;
+ stbuf->st_nlink = 1;
+ stbuf->st_size = strlen(hello_str);
+ } else
+ res = -ENOENT;
+
+ return res;
+}
+
+static int hello_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
+ off_t offset, struct fuse_file_info *fi)
+{
+ (void) offset;
+ (void) fi;
+
+ if (strcmp(path, "/") != 0)
+ return -ENOENT;
+
+ filler(buf, ".", NULL, 0);
+ filler(buf, "..", NULL, 0);
+ filler(buf, hello_path + 1, NULL, 0);
+
+ return 0;
+}
+
+static int hello_open(const char *path, struct fuse_file_info *fi)
+{
+ if (strcmp(path, hello_path) != 0)
+ return -ENOENT;
+
+ if ((fi->flags & 3) != O_RDONLY)
+ return -EACCES;
+
+ return 0;
+}
+
+static int hello_read(const char *path, char *buf, size_t size, off_t offset,
+ struct fuse_file_info *fi)
+{
+ size_t len;
+ (void) fi;
+ if(strcmp(path, hello_path) != 0)
+ return -ENOENT;
+
+ len = strlen(hello_str);
+ if (offset < len) {
+ if (offset + size > len)
+ size = len - offset;
+ memcpy(buf, hello_str + offset, size);
+ } else
+ size = 0;
+
+ return size;
+}
+
+static struct fuse_operations hello_oper = {
+ .getattr = hello_getattr,
+ .readdir = hello_readdir,
+ .open = hello_open,
+ .read = hello_read,
+};
+
+int main(int argc, char *argv[])
+{
+ return fuse_main(argc, argv, &hello_oper, NULL);
+}
diff --git a/draft/fuse-examples/hello_ll.c b/draft/fuse-examples/hello_ll.c
new file mode 100644
index 0000000..1405441
--- /dev/null
+++ b/draft/fuse-examples/hello_ll.c
@@ -0,0 +1,181 @@
+/*
+ FUSE: Filesystem in Userspace
+ Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
+
+ This program can be distributed under the terms of the GNU GPL.
+ See the file COPYING.
+
+ gcc -Wall hello_ll.c `pkg-config fuse --cflags --libs` -o hello_ll
+*/
+
+#define FUSE_USE_VERSION 26
+
+#include <fuse_lowlevel.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <assert.h>
+
+static const char *hello_str = "Hello World!\n";
+static const char *hello_name = "hello";
+
+static int hello_stat(fuse_ino_t ino, struct stat *stbuf)
+{
+ stbuf->st_ino = ino;
+ switch (ino) {
+ case 1:
+ stbuf->st_mode = S_IFDIR | 0755;
+ stbuf->st_nlink = 2;
+ break;
+
+ case 2:
+ stbuf->st_mode = S_IFREG | 0444;
+ stbuf->st_nlink = 1;
+ stbuf->st_size = strlen(hello_str);
+ break;
+
+ default:
+ return -1;
+ }
+ return 0;
+}
+
+static void hello_ll_getattr(fuse_req_t req, fuse_ino_t ino,
+ struct fuse_file_info *fi)
+{
+ struct stat stbuf;
+
+ (void) fi;
+
+ memset(&stbuf, 0, sizeof(stbuf));
+ if (hello_stat(ino, &stbuf) == -1)
+ fuse_reply_err(req, ENOENT);
+ else
+ fuse_reply_attr(req, &stbuf, 1.0);
+}
+
+static void hello_ll_lookup(fuse_req_t req, fuse_ino_t parent, const char *name)
+{
+ struct fuse_entry_param e;
+
+ if (parent != 1 || strcmp(name, hello_name) != 0)
+ fuse_reply_err(req, ENOENT);
+ else {
+ memset(&e, 0, sizeof(e));
+ e.ino = 2;
+ e.attr_timeout = 1.0;
+ e.entry_timeout = 1.0;
+ hello_stat(e.ino, &e.attr);
+
+ fuse_reply_entry(req, &e);
+ }
+}
+
+struct dirbuf {
+ char *p;
+ size_t size;
+};
+
+static void dirbuf_add(fuse_req_t req, struct dirbuf *b, const char *name,
+ fuse_ino_t ino)
+{
+ struct stat stbuf;
+ size_t oldsize = b->size;
+ b->size += fuse_add_direntry(req, NULL, 0, name, NULL, 0);
+ b->p = (char *) realloc(b->p, b->size);
+ memset(&stbuf, 0, sizeof(stbuf));
+ stbuf.st_ino = ino;
+ fuse_add_direntry(req, b->p + oldsize, b->size - oldsize, name, &stbuf,
+ b->size);
+}
+
+#define min(x, y) ((x) < (y) ? (x) : (y))
+
+static int reply_buf_limited(fuse_req_t req, const char *buf, size_t bufsize,
+ off_t off, size_t maxsize)
+{
+ if (off < bufsize)
+ return fuse_reply_buf(req, buf + off,
+ min(bufsize - off, maxsize));
+ else
+ return fuse_reply_buf(req, NULL, 0);
+}
+
+static void hello_ll_readdir(fuse_req_t req, fuse_ino_t ino, size_t size,
+ off_t off, struct fuse_file_info *fi)
+{
+ (void) fi;
+
+ if (ino != 1)
+ fuse_reply_err(req, ENOTDIR);
+ else {
+ struct dirbuf b;
+
+ memset(&b, 0, sizeof(b));
+ dirbuf_add(req, &b, ".", 1);
+ dirbuf_add(req, &b, "..", 1);
+ dirbuf_add(req, &b, hello_name, 2);
+ reply_buf_limited(req, b.p, b.size, off, size);
+ free(b.p);
+ }
+}
+
+static void hello_ll_open(fuse_req_t req, fuse_ino_t ino,
+ struct fuse_file_info *fi)
+{
+ if (ino != 2)
+ fuse_reply_err(req, EISDIR);
+ else if ((fi->flags & 3) != O_RDONLY)
+ fuse_reply_err(req, EACCES);
+ else
+ fuse_reply_open(req, fi);
+}
+
+static void hello_ll_read(fuse_req_t req, fuse_ino_t ino, size_t size,
+ off_t off, struct fuse_file_info *fi)
+{
+ (void) fi;
+
+ assert(ino == 2);
+ reply_buf_limited(req, hello_str, strlen(hello_str), off, size);
+}
+
+static struct fuse_lowlevel_ops hello_ll_oper = {
+ .lookup = hello_ll_lookup,
+ .getattr = hello_ll_getattr,
+ .readdir = hello_ll_readdir,
+ .open = hello_ll_open,
+ .read = hello_ll_read,
+};
+
+int main(int argc, char *argv[])
+{
+ struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
+ struct fuse_chan *ch;
+ char *mountpoint;
+ int err = -1;
+
+ if (fuse_parse_cmdline(&args, &mountpoint, NULL, NULL) != -1 &&
+ (ch = fuse_mount(mountpoint, &args)) != NULL) {
+ struct fuse_session *se;
+
+ se = fuse_lowlevel_new(&args, &hello_ll_oper,
+ sizeof(hello_ll_oper), NULL);
+ if (se != NULL) {
+ if (fuse_set_signal_handlers(se) != -1) {
+ fuse_session_add_chan(se, ch);
+ err = fuse_session_loop(se);
+ fuse_remove_signal_handlers(se);
+ fuse_session_remove_chan(ch);
+ }
+ fuse_session_destroy(se);
+ }
+ fuse_unmount(mountpoint, ch);
+ }
+ fuse_opt_free_args(&args);
+
+ return err ? 1 : 0;
+}
diff --git a/draft/fuse-examples/null.c b/draft/fuse-examples/null.c
new file mode 100644
index 0000000..b72cf4d
--- /dev/null
+++ b/draft/fuse-examples/null.c
@@ -0,0 +1,95 @@
+/*
+ FUSE: Filesystem in Userspace
+ Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
+
+ This program can be distributed under the terms of the GNU GPL.
+ See the file COPYING.
+
+ gcc -Wall null.c `pkg-config fuse --cflags --libs` -o null
+*/
+
+#define FUSE_USE_VERSION 26
+
+#include <fuse.h>
+#include <string.h>
+#include <unistd.h>
+#include <time.h>
+#include <errno.h>
+
+static int null_getattr(const char *path, struct stat *stbuf)
+{
+ if(strcmp(path, "/") != 0)
+ return -ENOENT;
+
+ stbuf->st_mode = S_IFREG | 0644;
+ stbuf->st_nlink = 1;
+ stbuf->st_uid = getuid();
+ stbuf->st_gid = getgid();
+ stbuf->st_size = (1ULL << 32); /* 4G */
+ stbuf->st_blocks = 0;
+ stbuf->st_atime = stbuf->st_mtime = stbuf->st_ctime = time(NULL);
+
+ return 0;
+}
+
+static int null_truncate(const char *path, off_t size)
+{
+ (void) size;
+
+ if(strcmp(path, "/") != 0)
+ return -ENOENT;
+
+ return 0;
+}
+
+static int null_open(const char *path, struct fuse_file_info *fi)
+{
+ (void) fi;
+
+ if(strcmp(path, "/") != 0)
+ return -ENOENT;
+
+ return 0;
+}
+
+static int null_read(const char *path, char *buf, size_t size,
+ off_t offset, struct fuse_file_info *fi)
+{
+ (void) buf;
+ (void) offset;
+ (void) fi;
+
+ if(strcmp(path, "/") != 0)
+ return -ENOENT;
+
+ if (offset >= (1ULL << 32))
+ return 0;
+
+ return size;
+}
+
+static int null_write(const char *path, const char *buf, size_t size,
+ off_t offset, struct fuse_file_info *fi)
+{
+ (void) buf;
+ (void) offset;
+ (void) fi;
+
+ if(strcmp(path, "/") != 0)
+ return -ENOENT;
+
+ return size;
+}
+
+static struct fuse_operations null_oper = {
+ .getattr = null_getattr,
+ .truncate = null_truncate,
+ .open = null_open,
+ .read = null_read,
+ .write = null_write,
+};
+
+int main(int argc, char *argv[])
+{
+ return fuse_main(argc, argv, &null_oper, NULL);
+}
diff --git a/draft/mcastseed/AUTHORS b/draft/mcastseed/AUTHORS
new file mode 100644
index 0000000..c338cf6
--- /dev/null
+++ b/draft/mcastseed/AUTHORS
@@ -0,0 +1,3 @@
+Ludovic Pouzenc <ludovic@pouzenc.fr>
+Christian Beier <dontmind@freeshell.org>
+tmouse, http://cboard.cprogramming.com/showthread.php?t=67469
diff --git a/draft/mcastseed/COPYING b/draft/mcastseed/COPYING
new file mode 100644
index 0000000..d60c31a
--- /dev/null
+++ b/draft/mcastseed/COPYING
@@ -0,0 +1,340 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/draft/mcastseed/ChangeLog b/draft/mcastseed/ChangeLog
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/draft/mcastseed/ChangeLog
diff --git a/draft/mcastseed/Makefile.am b/draft/mcastseed/Makefile.am
new file mode 100644
index 0000000..b07663c
--- /dev/null
+++ b/draft/mcastseed/Makefile.am
@@ -0,0 +1,5 @@
+## Process this file with automake to produce Makefile.in
+SUBDIRS = lib src
+# For Gnulib
+ACLOCAL_AMFLAGS = -I m4
+EXTRA_DIST = m4/gnulib-cache.m4
diff --git a/draft/mcastseed/NEWS b/draft/mcastseed/NEWS
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/draft/mcastseed/NEWS
diff --git a/draft/mcastseed/README b/draft/mcastseed/README
new file mode 120000
index 0000000..42061c0
--- /dev/null
+++ b/draft/mcastseed/README
@@ -0,0 +1 @@
+README.md \ No newline at end of file
diff --git a/draft/mcastseed/README.md b/draft/mcastseed/README.md
new file mode 100644
index 0000000..98ddff0
--- /dev/null
+++ b/draft/mcastseed/README.md
@@ -0,0 +1,9 @@
+
+This is a simple multicast file transfer with TCP back-chat.
+Greatly inspired from https://github.com/bk138/Multicast-Client-Server-Example
+
+Tested on GNU/Linux Debian 8, may usable under other OS without big efforts (patches appreciated).
+
+# Building
+To compile, use
+ autoreconf -fi && ./configure && make
diff --git a/draft/mcastseed/configure.ac b/draft/mcastseed/configure.ac
new file mode 100644
index 0000000..e9e292f
--- /dev/null
+++ b/draft/mcastseed/configure.ac
@@ -0,0 +1,69 @@
+# -*- Autoconf -*-
+# Process this file with autoconf to produce a configure script.
+
+AC_PREREQ([2.69])
+AC_INIT(mcastseeder, 0.1, ludovic@pouzenc.fr)
+
+# Args deprecated http://www.gnu.org/software/automake/manual/automake.html#Modernize-AM_005fINIT_005fAUTOMAKE-invocation
+AM_INIT_AUTOMAKE #(AC_PACKAGE_NAME, AC_PACKAGE_VERSION)
+AM_CONFIG_HEADER(config.h)
+AM_MAINTAINER_MODE
+
+AC_CANONICAL_HOST
+case $host_os in
+ darwin*)
+ HOST_IS_DARWIN="yes"
+ ;;
+ *mingw32*)
+ HOST_IS_WINDOWS="yes"
+ ;;
+ *)
+ HOST_IS_DARWIN="no"
+ HOST_IS_WINDOWS="no"
+ ;;
+esac
+
+if test "$HOST_IS_WINDOWS" = "yes"; then
+ WSOCKLIB="-lws2_32"
+ AC_SUBST(WSOCKLIB)
+fi
+
+
+AM_CONDITIONAL(MINGW, [ test "$HOST_IS_WINDOWS" = "yes" ])
+AM_CONDITIONAL(DARWIN, [ test "$HOST_IS_DARWIN" = "yes" ])
+
+# Checks for programs.
+AC_ISC_POSIX
+AC_HEADER_STDC
+AC_PROG_CPP
+AC_PROG_CC
+# Gnulib
+gl_EARLY
+AM_PROG_CC_STDC
+AM_PROG_CC_C_O
+
+# Checks for libraries.
+
+# Checks for header files.
+AC_CHECK_HEADERS([OS.h fcntl.h limits.h netdb.h stddef.h stdint.h stdlib.h string.h sys/socket.h unistd.h])
+
+# Checks for typedefs, structures, and compiler characteristics.
+AC_CHECK_HEADER_STDBOOL
+AC_TYPE_SIZE_T
+AC_TYPE_SSIZE_T
+AC_TYPE_UINT32_T
+AC_TYPE_UINT64_T
+AC_TYPE_UINT8_T
+
+# Checks for library functions.
+AC_FUNC_MALLOC
+AC_CHECK_FUNCS([alarm memset select socket])
+
+AC_CONFIG_FILES([Makefile
+ lib/Makefile
+ src/Makefile])
+
+# Gnulib
+gl_INIT
+
+AC_OUTPUT
diff --git a/draft/mcastseed/lib/Makefile.am b/draft/mcastseed/lib/Makefile.am
new file mode 100644
index 0000000..38faabe
--- /dev/null
+++ b/draft/mcastseed/lib/Makefile.am
@@ -0,0 +1,93 @@
+## DO NOT EDIT! GENERATED AUTOMATICALLY!
+## Process this file with automake to produce Makefile.in.
+# Copyright (C) 2002-2016 Free Software Foundation, Inc.
+#
+# This file 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; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This file 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 file. If not, see <http://www.gnu.org/licenses/>.
+#
+# As a special exception to the GNU General Public License,
+# this file may be distributed as part of a program that
+# contains a configuration script generated by Autoconf, under
+# the same distribution terms as the rest of that program.
+#
+# Generated by gnulib-tool.
+# Reproduce by: gnulib-tool --import --lib=libgnu --source-base=lib --m4-base=m4 --doc-base=doc --tests-base=tests --aux-dir=. --no-conditional-dependencies --no-libtool --macro-prefix=gl rbtree-list
+
+AUTOMAKE_OPTIONS = 1.9.6 gnits
+
+SUBDIRS =
+noinst_HEADERS =
+noinst_LIBRARIES =
+noinst_LTLIBRARIES =
+EXTRA_DIST =
+BUILT_SOURCES =
+SUFFIXES =
+MOSTLYCLEANFILES = core *.stackdump
+MOSTLYCLEANDIRS =
+CLEANFILES =
+DISTCLEANFILES =
+MAINTAINERCLEANFILES =
+
+AM_CPPFLAGS =
+AM_CFLAGS =
+
+noinst_LIBRARIES += libgnu.a
+
+libgnu_a_SOURCES =
+libgnu_a_LIBADD = $(gl_LIBOBJS)
+libgnu_a_DEPENDENCIES = $(gl_LIBOBJS)
+EXTRA_libgnu_a_SOURCES =
+
+## begin gnulib module list
+
+libgnu_a_SOURCES += gl_list.h gl_list.c
+
+## end gnulib module list
+
+## begin gnulib module rbtree-list
+
+libgnu_a_SOURCES += gl_rbtree_list.h gl_rbtree_list.c gl_anyrbtree_list1.h gl_anyrbtree_list2.h gl_anytree_list1.h gl_anytree_list2.h
+
+## end gnulib module rbtree-list
+
+## begin gnulib module stdbool
+
+BUILT_SOURCES += $(STDBOOL_H)
+
+# We need the following in order to create <stdbool.h> when the system
+# doesn't have one that works.
+if GL_GENERATE_STDBOOL_H
+stdbool.h: stdbool.in.h $(top_builddir)/config.status
+ $(AM_V_GEN)rm -f $@-t $@ && \
+ { echo '/* DO NOT EDIT! GENERATED AUTOMATICALLY! */'; \
+ sed -e 's/@''HAVE__BOOL''@/$(HAVE__BOOL)/g' < $(srcdir)/stdbool.in.h; \
+ } > $@-t && \
+ mv $@-t $@
+else
+stdbool.h: $(top_builddir)/config.status
+ rm -f $@
+endif
+MOSTLYCLEANFILES += stdbool.h stdbool.h-t
+
+EXTRA_DIST += stdbool.in.h
+
+## end gnulib module stdbool
+
+
+mostlyclean-local: mostlyclean-generic
+ @for dir in '' $(MOSTLYCLEANDIRS); do \
+ if test -n "$$dir" && test -d $$dir; then \
+ echo "rmdir $$dir"; rmdir $$dir; \
+ fi; \
+ done; \
+ :
diff --git a/draft/mcastseed/lib/gl_anyrbtree_list1.h b/draft/mcastseed/lib/gl_anyrbtree_list1.h
new file mode 100644
index 0000000..0ae0715
--- /dev/null
+++ b/draft/mcastseed/lib/gl_anyrbtree_list1.h
@@ -0,0 +1,76 @@
+/* Sequential list data type implemented by a binary tree.
+ Copyright (C) 2006, 2009-2016 Free Software Foundation, Inc.
+ Written by Bruno Haible <bruno@clisp.org>, 2006.
+
+ 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; either version 3 of the License, or
+ (at your option) any later version.
+
+ 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, see <http://www.gnu.org/licenses/>. */
+
+/* Common code of gl_rbtree_list.c and gl_rbtreehash_list.c. */
+
+/* A red-black tree is a binary tree where every node is colored black or
+ red such that
+ 1. The root is black.
+ 2. No red node has a red parent.
+ Or equivalently: No red node has a red child.
+ 3. All paths from the root down to any NULL endpoint contain the same
+ number of black nodes.
+ Let's call this the "black-height" bh of the tree. It follows that every
+ such path contains exactly bh black and between 0 and bh red nodes. (The
+ extreme cases are a path containing only black nodes, and a path colored
+ alternately black-red-black-red-...-black-red.) The height of the tree
+ therefore is >= bh, <= 2*bh.
+ */
+
+/* -------------------------- gl_list_t Data Type -------------------------- */
+
+/* Color of a node. */
+typedef enum color { BLACK, RED } color_t;
+
+/* Concrete list node implementation, valid for this file only. */
+struct gl_list_node_impl
+{
+#if WITH_HASHTABLE
+ struct gl_hash_entry h; /* hash table entry fields; must be first */
+#endif
+ struct gl_list_node_impl *left; /* left branch, or NULL */
+ struct gl_list_node_impl *right; /* right branch, or NULL */
+ /* Parent pointer, or NULL. The parent pointer is not needed for most
+ operations. It is needed so that a gl_list_node_t can be returned
+ without memory allocation, on which the functions gl_list_remove_node,
+ gl_list_add_before, gl_list_add_after can be implemented. */
+ struct gl_list_node_impl *parent;
+ color_t color; /* node's color */
+ size_t branch_size; /* number of nodes in this branch,
+ = branchsize(left)+branchsize(right)+1 */
+ const void *value;
+};
+
+/* Concrete gl_list_impl type, valid for this file only. */
+struct gl_list_impl
+{
+ struct gl_list_impl_base base;
+#if WITH_HASHTABLE
+ /* A hash table: managed as an array of collision lists. */
+ struct gl_hash_entry **table;
+ size_t table_size;
+#endif
+ struct gl_list_node_impl *root; /* root node or NULL */
+};
+
+/* A red-black tree of height h has a black-height bh >= ceil(h/2) and
+ therefore at least 2^ceil(h/2) - 1 elements. So, h <= 116 (because a tree
+ of height h >= 117 would have at least 2^59 - 1 elements, and because even
+ on 64-bit machines,
+ sizeof (gl_list_node_impl) * (2^59 - 1) > 2^64
+ this would exceed the address space of the machine. */
+#define MAXHEIGHT 116
diff --git a/draft/mcastseed/lib/gl_anyrbtree_list2.h b/draft/mcastseed/lib/gl_anyrbtree_list2.h
new file mode 100644
index 0000000..a0e2e43
--- /dev/null
+++ b/draft/mcastseed/lib/gl_anyrbtree_list2.h
@@ -0,0 +1,1028 @@
+/* Sequential list data type implemented by a binary tree.
+ Copyright (C) 2006-2007, 2009-2016 Free Software Foundation, Inc.
+ Written by Bruno Haible <bruno@clisp.org>, 2006.
+
+ 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; either version 3 of the License, or
+ (at your option) any later version.
+
+ 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, see <http://www.gnu.org/licenses/>. */
+
+/* Common code of gl_rbtree_list.c and gl_rbtreehash_list.c. */
+
+/* -------------------------- gl_list_t Data Type -------------------------- */
+
+/* Create a subtree for count >= 1 elements.
+ Its black-height bh is passed as argument, with
+ 2^bh - 1 <= count <= 2^(bh+1) - 1. bh == 0 implies count == 1.
+ Its height is h where 2^(h-1) <= count <= 2^h - 1.
+ Return NULL upon out-of-memory. */
+static gl_list_node_t
+create_subtree_with_contents (unsigned int bh,
+ size_t count, const void **contents)
+{
+ size_t half1 = (count - 1) / 2;
+ size_t half2 = count / 2;
+ /* Note: half1 + half2 = count - 1. */
+ gl_list_node_t node =
+ (struct gl_list_node_impl *) malloc (sizeof (struct gl_list_node_impl));
+ if (node == NULL)
+ return NULL;
+
+ if (half1 > 0)
+ {
+ /* half1 > 0 implies count > 1, implies bh >= 1, implies
+ 2^(bh-1) - 1 <= half1 <= 2^bh - 1. */
+ node->left =
+ create_subtree_with_contents (bh - 1, half1, contents);
+ if (node->left == NULL)
+ goto fail1;
+ node->left->parent = node;
+ }
+ else
+ node->left = NULL;
+
+ node->value = contents[half1];
+
+ if (half2 > 0)
+ {
+ /* half2 > 0 implies count > 1, implies bh >= 1, implies
+ 2^(bh-1) - 1 <= half2 <= 2^bh - 1. */
+ node->right =
+ create_subtree_with_contents (bh - 1, half2, contents + half1 + 1);
+ if (node->right == NULL)
+ goto fail2;
+ node->right->parent = node;
+ }
+ else
+ node->right = NULL;
+
+ node->color = (bh == 0 ? RED : BLACK);
+
+ node->branch_size = count;
+
+ return node;
+
+ fail2:
+ if (node->left != NULL)
+ free_subtree (node->left);
+ fail1:
+ free (node);
+ return NULL;
+}
+
+static gl_list_t
+gl_tree_nx_create (gl_list_implementation_t implementation,
+ gl_listelement_equals_fn equals_fn,
+ gl_listelement_hashcode_fn hashcode_fn,
+ gl_listelement_dispose_fn dispose_fn,
+ bool allow_duplicates,
+ size_t count, const void **contents)
+{
+ struct gl_list_impl *list =
+ (struct gl_list_impl *) malloc (sizeof (struct gl_list_impl));
+
+ if (list == NULL)
+ return NULL;
+
+ list->base.vtable = implementation;
+ list->base.equals_fn = equals_fn;
+ list->base.hashcode_fn = hashcode_fn;
+ list->base.dispose_fn = dispose_fn;
+ list->base.allow_duplicates = allow_duplicates;
+#if WITH_HASHTABLE
+ {
+ size_t estimate = xsum (count, count / 2); /* 1.5 * count */
+ if (estimate < 10)
+ estimate = 10;
+ list->table_size = next_prime (estimate);
+ if (size_overflow_p (xtimes (list->table_size, sizeof (gl_hash_entry_t))))
+ goto fail1;
+ list->table =
+ (gl_hash_entry_t *) calloc (list->table_size, sizeof (gl_hash_entry_t));
+ if (list->table == NULL)
+ goto fail1;
+ }
+#endif
+ if (count > 0)
+ {
+ /* Assuming 2^bh - 1 <= count <= 2^(bh+1) - 2, we create a tree whose
+ upper bh levels are black, and only the partially present lowest
+ level is red. */
+ unsigned int bh;
+ {
+ size_t n;
+ for (n = count + 1, bh = 0; n > 1; n = n >> 1)
+ bh++;
+ }
+
+ list->root = create_subtree_with_contents (bh, count, contents);
+ if (list->root == NULL)
+ goto fail2;
+ list->root->parent = NULL;
+
+#if WITH_HASHTABLE
+ /* Now that the tree is built, node_position() works. Now we can
+ add the nodes to the hash table. */
+ if (add_nodes_to_buckets (list) < 0)
+ goto fail3;
+#endif
+ }
+ else
+ list->root = NULL;
+
+ return list;
+
+#if WITH_HASHTABLE
+ fail3:
+ free_subtree (list->root);
+#endif
+ fail2:
+#if WITH_HASHTABLE
+ free (list->table);
+ fail1:
+#endif
+ free (list);
+ return NULL;
+}
+
+/* Rotate left a subtree.
+
+ B D
+ / \ / \
+ A D --> B E
+ / \ / \
+ C E A C
+
+ Change the tree structure, update the branch sizes.
+ The caller must update the colors and register D as child of its parent. */
+static gl_list_node_t
+rotate_left (gl_list_node_t b_node, gl_list_node_t d_node)
+{
+ gl_list_node_t a_node = b_node->left;
+ gl_list_node_t c_node = d_node->left;
+ gl_list_node_t e_node = d_node->right;
+
+ b_node->right = c_node;
+ d_node->left = b_node;
+
+ d_node->parent = b_node->parent;
+ b_node->parent = d_node;
+ if (c_node != NULL)
+ c_node->parent = b_node;
+
+ b_node->branch_size =
+ (a_node != NULL ? a_node->branch_size : 0)
+ + 1 + (c_node != NULL ? c_node->branch_size : 0);
+ d_node->branch_size =
+ b_node->branch_size + 1 + (e_node != NULL ? e_node->branch_size : 0);
+
+ return d_node;
+}
+
+/* Rotate right a subtree.
+
+ D B
+ / \ / \
+ B E --> A D
+ / \ / \
+ A C C E
+
+ Change the tree structure, update the branch sizes.
+ The caller must update the colors and register B as child of its parent. */
+static gl_list_node_t
+rotate_right (gl_list_node_t b_node, gl_list_node_t d_node)
+{
+ gl_list_node_t a_node = b_node->left;
+ gl_list_node_t c_node = b_node->right;
+ gl_list_node_t e_node = d_node->right;
+
+ d_node->left = c_node;
+ b_node->right = d_node;
+
+ b_node->parent = d_node->parent;
+ d_node->parent = b_node;
+ if (c_node != NULL)
+ c_node->parent = d_node;
+
+ d_node->branch_size =
+ (c_node != NULL ? c_node->branch_size : 0)
+ + 1 + (e_node != NULL ? e_node->branch_size : 0);
+ b_node->branch_size =
+ (a_node != NULL ? a_node->branch_size : 0) + 1 + d_node->branch_size;
+
+ return b_node;
+}
+
+/* Ensure the tree is balanced, after an insertion operation.
+ Also assigns node->color.
+ parent is the given node's parent, known to be non-NULL. */
+static void
+rebalance_after_add (gl_list_t list, gl_list_node_t node, gl_list_node_t parent)
+{
+ for (;;)
+ {
+ /* At this point, parent = node->parent != NULL.
+ Think of node->color being RED (although node->color is not yet
+ assigned.) */
+ gl_list_node_t grandparent;
+ gl_list_node_t uncle;
+
+ if (parent->color == BLACK)
+ {
+ /* A RED color for node is acceptable. */
+ node->color = RED;
+ return;
+ }
+
+ grandparent = parent->parent;
+ /* Since parent is RED, we know that
+ grandparent is != NULL and colored BLACK. */
+
+ if (grandparent->left == parent)
+ uncle = grandparent->right;
+ else if (grandparent->right == parent)
+ uncle = grandparent->left;
+ else
+ abort ();
+
+ if (uncle != NULL && uncle->color == RED)
+ {
+ /* Change grandparent from BLACK to RED, and
+ change parent and uncle from RED to BLACK.
+ This makes it acceptable for node to be RED. */
+ node->color = RED;
+ parent->color = uncle->color = BLACK;
+ node = grandparent;
+ }
+ else
+ {
+ /* grandparent and uncle are BLACK. parent is RED. node wants
+ to be RED too.
+ In this case, recoloring is not sufficient. Need to perform
+ one or two rotations. */
+ gl_list_node_t *grandparentp;
+
+ if (grandparent->parent == NULL)
+ grandparentp = &list->root;
+ else if (grandparent->parent->left == grandparent)
+ grandparentp = &grandparent->parent->left;
+ else if (grandparent->parent->right == grandparent)
+ grandparentp = &grandparent->parent->right;
+ else
+ abort ();
+
+ if (grandparent->left == parent)
+ {
+ if (parent->right == node)
+ {
+ /* Rotation between node and parent. */
+ grandparent->left = rotate_left (parent, node);
+ node = parent;
+ parent = grandparent->left;
+ }
+ /* grandparent and uncle are BLACK. parent and node want to be
+ RED. parent = grandparent->left. node = parent->left.
+
+ grandparent parent
+ bh+1 bh+1
+ / \ / \
+ parent uncle --> node grandparent
+ bh bh bh bh
+ / \ / \
+ node C C uncle
+ bh bh bh bh
+ */
+ *grandparentp = rotate_right (parent, grandparent);
+ parent->color = BLACK;
+ node->color = grandparent->color = RED;
+ }
+ else /* grandparent->right == parent */
+ {
+ if (parent->left == node)
+ {
+ /* Rotation between node and parent. */
+ grandparent->right = rotate_right (node, parent);
+ node = parent;
+ parent = grandparent->right;
+ }
+ /* grandparent and uncle are BLACK. parent and node want to be
+ RED. parent = grandparent->right. node = parent->right.
+
+ grandparent parent
+ bh+1 bh+1
+ / \ / \
+ uncle parent --> grandparent node
+ bh bh bh bh
+ / \ / \
+ C node uncle C
+ bh bh bh bh
+ */
+ *grandparentp = rotate_left (grandparent, parent);
+ parent->color = BLACK;
+ node->color = grandparent->color = RED;
+ }
+ return;
+ }
+
+ /* Start again with a new (node, parent) pair. */
+ parent = node->parent;
+
+ if (parent == NULL)
+ {
+ /* Change node's color from RED to BLACK. This increases the
+ tree's black-height. */
+ node->color = BLACK;
+ return;
+ }
+ }
+}
+
+/* Ensure the tree is balanced, after a deletion operation.
+ CHILD was a grandchild of PARENT and is now its child. Between them,
+ a black node was removed. CHILD is also black, or NULL.
+ (CHILD can also be NULL. But PARENT is non-NULL.) */
+static void
+rebalance_after_remove (gl_list_t list, gl_list_node_t child, gl_list_node_t parent)
+{
+ for (;;)
+ {
+ /* At this point, we reduced the black-height of the CHILD subtree by 1.
+ To make up, either look for a possibility to turn a RED to a BLACK
+ node, or try to reduce the black-height tree of CHILD's sibling
+ subtree as well. */
+ gl_list_node_t *parentp;
+
+ if (parent->parent == NULL)
+ parentp = &list->root;
+ else if (parent->parent->left == parent)
+ parentp = &parent->parent->left;
+ else if (parent->parent->right == parent)
+ parentp = &parent->parent->right;
+ else
+ abort ();
+
+ if (parent->left == child)
+ {
+ gl_list_node_t sibling = parent->right;
+ /* sibling's black-height is >= 1. In particular,
+ sibling != NULL.
+
+ parent
+ / \
+ child sibling
+ bh bh+1
+ */
+
+ if (sibling->color == RED)
+ {
+ /* sibling is RED, hence parent is BLACK and sibling's children
+ are non-NULL and BLACK.
+
+ parent sibling
+ bh+2 bh+2
+ / \ / \
+ child sibling --> parent SR
+ bh bh+1 bh+1 bh+1
+ / \ / \
+ SL SR child SL
+ bh+1 bh+1 bh bh+1
+ */
+ *parentp = rotate_left (parent, sibling);
+ parent->color = RED;
+ sibling->color = BLACK;
+
+ /* Concentrate on the subtree of parent. The new sibling is
+ one of the old sibling's children, and known to be BLACK. */
+ parentp = &sibling->left;
+ sibling = parent->right;
+ }
+ /* Now we know that sibling is BLACK.
+
+ parent
+ / \
+ child sibling
+ bh bh+1
+ */
+ if (sibling->right != NULL && sibling->right->color == RED)
+ {
+ /*
+ parent sibling
+ bh+1|bh+2 bh+1|bh+2
+ / \ / \
+ child sibling --> parent SR
+ bh bh+1 bh+1 bh+1
+ / \ / \
+ SL SR child SL
+ bh bh bh bh
+ */
+ *parentp = rotate_left (parent, sibling);
+ sibling->color = parent->color;
+ parent->color = BLACK;
+ sibling->right->color = BLACK;
+ return;
+ }
+ else if (sibling->left != NULL && sibling->left->color == RED)
+ {
+ /*
+ parent parent
+ bh+1|bh+2 bh+1|bh+2
+ / \ / \
+ child sibling --> child SL
+ bh bh+1 bh bh+1
+ / \ / \
+ SL SR SLL sibling
+ bh bh bh bh
+ / \ / \
+ SLL SLR SLR SR
+ bh bh bh bh
+
+ where SLL, SLR, SR are all black.
+ */
+ parent->right = rotate_right (sibling->left, sibling);
+ /* Change sibling from BLACK to RED and SL from RED to BLACK. */
+ sibling->color = RED;
+ sibling = parent->right;
+ sibling->color = BLACK;
+
+ /* Now do as in the previous case. */
+ *parentp = rotate_left (parent, sibling);
+ sibling->color = parent->color;
+ parent->color = BLACK;
+ sibling->right->color = BLACK;
+ return;
+ }
+ else
+ {
+ if (parent->color == BLACK)
+ {
+ /* Change sibling from BLACK to RED. Then the entire
+ subtree at parent has decreased its black-height.
+ parent parent
+ bh+2 bh+1
+ / \ / \
+ child sibling --> child sibling
+ bh bh+1 bh bh
+ */
+ sibling->color = RED;
+
+ child = parent;
+ }
+ else
+ {
+ /* Change parent from RED to BLACK, but compensate by
+ changing sibling from BLACK to RED.
+ parent parent
+ bh+1 bh+1
+ / \ / \
+ child sibling --> child sibling
+ bh bh+1 bh bh
+ */
+ parent->color = BLACK;
+ sibling->color = RED;
+ return;
+ }
+ }
+ }
+ else if (parent->right == child)
+ {
+ gl_list_node_t sibling = parent->left;
+ /* sibling's black-height is >= 1. In particular,
+ sibling != NULL.
+
+ parent
+ / \
+ sibling child
+ bh+1 bh
+ */
+
+ if (sibling->color == RED)
+ {
+ /* sibling is RED, hence parent is BLACK and sibling's children
+ are non-NULL and BLACK.
+
+ parent sibling
+ bh+2 bh+2
+ / \ / \
+ sibling child --> SR parent
+ bh+1 ch bh+1 bh+1
+ / \ / \
+ SL SR SL child
+ bh+1 bh+1 bh+1 bh
+ */
+ *parentp = rotate_right (sibling, parent);
+ parent->color = RED;
+ sibling->color = BLACK;
+
+ /* Concentrate on the subtree of parent. The new sibling is
+ one of the old sibling's children, and known to be BLACK. */
+ parentp = &sibling->right;
+ sibling = parent->left;
+ }
+ /* Now we know that sibling is BLACK.
+
+ parent
+ / \
+ sibling child
+ bh+1 bh
+ */
+ if (sibling->left != NULL && sibling->left->color == RED)
+ {
+ /*
+ parent sibling
+ bh+1|bh+2 bh+1|bh+2
+ / \ / \
+ sibling child --> SL parent
+ bh+1 bh bh+1 bh+1
+ / \ / \
+ SL SR SR child
+ bh bh bh bh
+ */
+ *parentp = rotate_right (sibling, parent);
+ sibling->color = parent->color;
+ parent->color = BLACK;
+ sibling->left->color = BLACK;
+ return;
+ }
+ else if (sibling->right != NULL && sibling->right->color == RED)
+ {
+ /*
+ parent parent
+ bh+1|bh+2 bh+1|bh+2
+ / \ / \
+ sibling child --> SR child
+ bh+1 bh bh+1 bh
+ / \ / \
+ SL SR sibling SRR
+ bh bh bh bh
+ / \ / \
+ SRL SRR SL SRL
+ bh bh bh bh
+
+ where SL, SRL, SRR are all black.
+ */
+ parent->left = rotate_left (sibling, sibling->right);
+ /* Change sibling from BLACK to RED and SL from RED to BLACK. */
+ sibling->color = RED;
+ sibling = parent->left;
+ sibling->color = BLACK;
+
+ /* Now do as in the previous case. */
+ *parentp = rotate_right (sibling, parent);
+ sibling->color = parent->color;
+ parent->color = BLACK;
+ sibling->left->color = BLACK;
+ return;
+ }
+ else
+ {
+ if (parent->color == BLACK)
+ {
+ /* Change sibling from BLACK to RED. Then the entire
+ subtree at parent has decreased its black-height.
+ parent parent
+ bh+2 bh+1
+ / \ / \
+ sibling child --> sibling child
+ bh+1 bh bh bh
+ */
+ sibling->color = RED;
+
+ child = parent;
+ }
+ else
+ {
+ /* Change parent from RED to BLACK, but compensate by
+ changing sibling from BLACK to RED.
+ parent parent
+ bh+1 bh+1
+ / \ / \
+ sibling child --> sibling child
+ bh+1 bh bh bh
+ */
+ parent->color = BLACK;
+ sibling->color = RED;
+ return;
+ }
+ }
+ }
+ else
+ abort ();
+
+ /* Start again with a new (child, parent) pair. */
+ parent = child->parent;
+
+#if 0 /* Already handled. */
+ if (child != NULL && child->color == RED)
+ {
+ child->color = BLACK;
+ return;
+ }
+#endif
+
+ if (parent == NULL)
+ return;
+ }
+}
+
+static void
+gl_tree_remove_node_from_tree (gl_list_t list, gl_list_node_t node)
+{
+ gl_list_node_t parent = node->parent;
+
+ if (node->left == NULL)
+ {
+ /* Replace node with node->right. */
+ gl_list_node_t child = node->right;
+
+ if (child != NULL)
+ {
+ child->parent = parent;
+ /* Since node->left == NULL, child must be RED and of height 1,
+ hence node must have been BLACK. Recolor the child. */
+ child->color = BLACK;
+ }
+ if (parent == NULL)
+ list->root = child;
+ else
+ {
+ if (parent->left == node)
+ parent->left = child;
+ else /* parent->right == node */
+ parent->right = child;
+
+ /* Update branch_size fields of the parent nodes. */
+ {
+ gl_list_node_t p;
+
+ for (p = parent; p != NULL; p = p->parent)
+ p->branch_size--;
+ }
+
+ if (child == NULL && node->color == BLACK)
+ rebalance_after_remove (list, child, parent);
+ }
+ }
+ else if (node->right == NULL)
+ {
+ /* It is not absolutely necessary to treat this case. But the more
+ general case below is more complicated, hence slower. */
+ /* Replace node with node->left. */
+ gl_list_node_t child = node->left;
+
+ child->parent = parent;
+ /* Since node->right == NULL, child must be RED and of height 1,
+ hence node must have been BLACK. Recolor the child. */
+ child->color = BLACK;
+ if (parent == NULL)
+ list->root = child;
+ else
+ {
+ if (parent->left == node)
+ parent->left = child;
+ else /* parent->right == node */
+ parent->right = child;
+
+ /* Update branch_size fields of the parent nodes. */
+ {
+ gl_list_node_t p;
+
+ for (p = parent; p != NULL; p = p->parent)
+ p->branch_size--;
+ }
+ }
+ }
+ else
+ {
+ /* Replace node with the rightmost element of the node->left subtree. */
+ gl_list_node_t subst;
+ gl_list_node_t subst_parent;
+ gl_list_node_t child;
+ color_t removed_color;
+
+ for (subst = node->left; subst->right != NULL; )
+ subst = subst->right;
+
+ subst_parent = subst->parent;
+
+ child = subst->left;
+
+ removed_color = subst->color;
+
+ /* The case subst_parent == node is special: If we do nothing special,
+ we get confusion about node->left, subst->left and child->parent.
+ subst_parent == node
+ <==> The 'for' loop above terminated immediately.
+ <==> subst == subst_parent->left
+ [otherwise subst == subst_parent->right]
+ In this case, we would need to first set
+ child->parent = node; node->left = child;
+ and later - when we copy subst into node's position - again
+ child->parent = subst; subst->left = child;
+ Altogether a no-op. */
+ if (subst_parent != node)
+ {
+ if (child != NULL)
+ child->parent = subst_parent;
+ subst_parent->right = child;
+ }
+
+ /* Update branch_size fields of the parent nodes. */
+ {
+ gl_list_node_t p;
+
+ for (p = subst_parent; p != NULL; p = p->parent)
+ p->branch_size--;
+ }
+
+ /* Copy subst into node's position.
+ (This is safer than to copy subst's value into node, keep node in
+ place, and free subst.) */
+ if (subst_parent != node)
+ {
+ subst->left = node->left;
+ subst->left->parent = subst;
+ }
+ subst->right = node->right;
+ subst->right->parent = subst;
+ subst->color = node->color;
+ subst->branch_size = node->branch_size;
+ subst->parent = parent;
+ if (parent == NULL)
+ list->root = subst;
+ else if (parent->left == node)
+ parent->left = subst;
+ else /* parent->right == node */
+ parent->right = subst;
+
+ if (removed_color == BLACK)
+ {
+ if (child != NULL && child->color == RED)
+ /* Recolor the child. */
+ child->color = BLACK;
+ else
+ /* Rebalancing starts at child's parent, that is subst_parent -
+ except when subst_parent == node. In this case, we need to use
+ its replacement, subst. */
+ rebalance_after_remove (list, child,
+ subst_parent != node ? subst_parent : subst);
+ }
+ }
+}
+
+static gl_list_node_t
+gl_tree_nx_add_first (gl_list_t list, const void *elt)
+{
+ /* Create new node. */
+ gl_list_node_t new_node =
+ (struct gl_list_node_impl *) malloc (sizeof (struct gl_list_node_impl));
+
+ if (new_node == NULL)
+ return NULL;
+
+ new_node->left = NULL;
+ new_node->right = NULL;
+ new_node->branch_size = 1;
+ new_node->value = elt;
+#if WITH_HASHTABLE
+ new_node->h.hashcode =
+ (list->base.hashcode_fn != NULL
+ ? list->base.hashcode_fn (new_node->value)
+ : (size_t)(uintptr_t) new_node->value);
+#endif
+
+ /* Add it to the tree. */
+ if (list->root == NULL)
+ {
+ new_node->color = BLACK;
+ list->root = new_node;
+ new_node->parent = NULL;
+ }
+ else
+ {
+ gl_list_node_t node;
+
+ for (node = list->root; node->left != NULL; )
+ node = node->left;
+
+ node->left = new_node;
+ new_node->parent = node;
+
+ /* Update branch_size fields of the parent nodes. */
+ {
+ gl_list_node_t p;
+
+ for (p = node; p != NULL; p = p->parent)
+ p->branch_size++;
+ }
+
+ /* Color and rebalance. */
+ rebalance_after_add (list, new_node, node);
+ }
+
+#if WITH_HASHTABLE
+ /* Add node to the hash table.
+ Note that this is only possible _after_ the node has been added to the
+ tree structure, because add_to_bucket() uses node_position(). */
+ if (add_to_bucket (list, new_node) < 0)
+ {
+ gl_tree_remove_node_from_tree (list, new_node);
+ free (new_node);
+ return NULL;
+ }
+ hash_resize_after_add (list);
+#endif
+
+ return new_node;
+}
+
+static gl_list_node_t
+gl_tree_nx_add_last (gl_list_t list, const void *elt)
+{
+ /* Create new node. */
+ gl_list_node_t new_node =
+ (struct gl_list_node_impl *) malloc (sizeof (struct gl_list_node_impl));
+
+ if (new_node == NULL)
+ return NULL;
+
+ new_node->left = NULL;
+ new_node->right = NULL;
+ new_node->branch_size = 1;
+ new_node->value = elt;
+#if WITH_HASHTABLE
+ new_node->h.hashcode =
+ (list->base.hashcode_fn != NULL
+ ? list->base.hashcode_fn (new_node->value)
+ : (size_t)(uintptr_t) new_node->value);
+#endif
+
+ /* Add it to the tree. */
+ if (list->root == NULL)
+ {
+ new_node->color = BLACK;
+ list->root = new_node;
+ new_node->parent = NULL;
+ }
+ else
+ {
+ gl_list_node_t node;
+
+ for (node = list->root; node->right != NULL; )
+ node = node->right;
+
+ node->right = new_node;
+ new_node->parent = node;
+
+ /* Update branch_size fields of the parent nodes. */
+ {
+ gl_list_node_t p;
+
+ for (p = node; p != NULL; p = p->parent)
+ p->branch_size++;
+ }
+
+ /* Color and rebalance. */
+ rebalance_after_add (list, new_node, node);
+ }
+
+#if WITH_HASHTABLE
+ /* Add node to the hash table.
+ Note that this is only possible _after_ the node has been added to the
+ tree structure, because add_to_bucket() uses node_position(). */
+ if (add_to_bucket (list, new_node) < 0)
+ {
+ gl_tree_remove_node_from_tree (list, new_node);
+ free (new_node);
+ return NULL;
+ }
+ hash_resize_after_add (list);
+#endif
+
+ return new_node;
+}
+
+static gl_list_node_t
+gl_tree_nx_add_before (gl_list_t list, gl_list_node_t node, const void *elt)
+{
+ /* Create new node. */
+ gl_list_node_t new_node =
+ (struct gl_list_node_impl *) malloc (sizeof (struct gl_list_node_impl));
+
+ if (new_node == NULL)
+ return NULL;
+
+ new_node->left = NULL;
+ new_node->right = NULL;
+ new_node->branch_size = 1;
+ new_node->value = elt;
+#if WITH_HASHTABLE
+ new_node->h.hashcode =
+ (list->base.hashcode_fn != NULL
+ ? list->base.hashcode_fn (new_node->value)
+ : (size_t)(uintptr_t) new_node->value);
+#endif
+
+ /* Add it to the tree. */
+ if (node->left == NULL)
+ node->left = new_node;
+ else
+ {
+ for (node = node->left; node->right != NULL; )
+ node = node->right;
+ node->right = new_node;
+ }
+ new_node->parent = node;
+
+ /* Update branch_size fields of the parent nodes. */
+ {
+ gl_list_node_t p;
+
+ for (p = node; p != NULL; p = p->parent)
+ p->branch_size++;
+ }
+
+ /* Color and rebalance. */
+ rebalance_after_add (list, new_node, node);
+
+#if WITH_HASHTABLE
+ /* Add node to the hash table.
+ Note that this is only possible _after_ the node has been added to the
+ tree structure, because add_to_bucket() uses node_position(). */
+ if (add_to_bucket (list, new_node) < 0)
+ {
+ gl_tree_remove_node_from_tree (list, new_node);
+ free (new_node);
+ return NULL;
+ }
+ hash_resize_after_add (list);
+#endif
+
+ return new_node;
+}
+
+static gl_list_node_t
+gl_tree_nx_add_after (gl_list_t list, gl_list_node_t node, const void *elt)
+{
+ /* Create new node. */
+ gl_list_node_t new_node =
+ (struct gl_list_node_impl *) malloc (sizeof (struct gl_list_node_impl));
+
+ if (new_node == NULL)
+ return NULL;
+
+ new_node->left = NULL;
+ new_node->right = NULL;
+ new_node->branch_size = 1;
+ new_node->value = elt;
+#if WITH_HASHTABLE
+ new_node->h.hashcode =
+ (list->base.hashcode_fn != NULL
+ ? list->base.hashcode_fn (new_node->value)
+ : (size_t)(uintptr_t) new_node->value);
+#endif
+
+ /* Add it to the tree. */
+ if (node->right == NULL)
+ node->right = new_node;
+ else
+ {
+ for (node = node->right; node->left != NULL; )
+ node = node->left;
+ node->left = new_node;
+ }
+ new_node->parent = node;
+
+ /* Update branch_size fields of the parent nodes. */
+ {
+ gl_list_node_t p;
+
+ for (p = node; p != NULL; p = p->parent)
+ p->branch_size++;
+ }
+
+ /* Color and rebalance. */
+ rebalance_after_add (list, new_node, node);
+
+#if WITH_HASHTABLE
+ /* Add node to the hash table.
+ Note that this is only possible _after_ the node has been added to the
+ tree structure, because add_to_bucket() uses node_position(). */
+ if (add_to_bucket (list, new_node) < 0)
+ {
+ gl_tree_remove_node_from_tree (list, new_node);
+ free (new_node);
+ return NULL;
+ }
+ hash_resize_after_add (list);
+#endif
+
+ return new_node;
+}
diff --git a/draft/mcastseed/lib/gl_anytree_list1.h b/draft/mcastseed/lib/gl_anytree_list1.h
new file mode 100644
index 0000000..675f107
--- /dev/null
+++ b/draft/mcastseed/lib/gl_anytree_list1.h
@@ -0,0 +1,41 @@
+/* Sequential list data type implemented by a binary tree.
+ Copyright (C) 2006, 2009-2016 Free Software Foundation, Inc.
+ Written by Bruno Haible <bruno@clisp.org>, 2006.
+
+ 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; either version 3 of the License, or
+ (at your option) any later version.
+
+ 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, see <http://www.gnu.org/licenses/>. */
+
+/* Common code of gl_avltree_list.c, gl_rbtree_list.c,
+ gl_avltreehash_list.c, gl_rbtreehash_list.c. */
+
+/* An item on the stack used for iterating across the elements. */
+typedef struct
+{
+ gl_list_node_t node;
+ size_t rightp;
+} iterstack_item_t;
+
+/* A stack used for iterating across the elements. */
+typedef iterstack_item_t iterstack_t[MAXHEIGHT];
+
+/* Free a non-empty subtree recursively.
+ This function is recursive and therefore not very fast. */
+static void
+free_subtree (gl_list_node_t node)
+{
+ if (node->left != NULL)
+ free_subtree (node->left);
+ if (node->right != NULL)
+ free_subtree (node->right);
+ free (node);
+}
diff --git a/draft/mcastseed/lib/gl_anytree_list2.h b/draft/mcastseed/lib/gl_anytree_list2.h
new file mode 100644
index 0000000..7e6fe45
--- /dev/null
+++ b/draft/mcastseed/lib/gl_anytree_list2.h
@@ -0,0 +1,940 @@
+/* Sequential list data type implemented by a binary tree.
+ Copyright (C) 2006-2016 Free Software Foundation, Inc.
+ Written by Bruno Haible <bruno@clisp.org>, 2006.
+
+ 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; either version 3 of the License, or
+ (at your option) any later version.
+
+ 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, see <http://www.gnu.org/licenses/>. */
+
+/* Common code of gl_avltree_list.c, gl_rbtree_list.c,
+ gl_avltreehash_list.c, gl_rbtreehash_list.c. */
+
+static gl_list_t
+gl_tree_nx_create_empty (gl_list_implementation_t implementation,
+ gl_listelement_equals_fn equals_fn,
+ gl_listelement_hashcode_fn hashcode_fn,
+ gl_listelement_dispose_fn dispose_fn,
+ bool allow_duplicates)
+{
+ struct gl_list_impl *list = (struct gl_list_impl *) malloc (sizeof (struct gl_list_impl));
+
+ if (list == NULL)
+ return NULL;
+
+ list->base.vtable = implementation;
+ list->base.equals_fn = equals_fn;
+ list->base.hashcode_fn = hashcode_fn;
+ list->base.dispose_fn = dispose_fn;
+ list->base.allow_duplicates = allow_duplicates;
+#if WITH_HASHTABLE
+ list->table_size = 11;
+ list->table =
+ (gl_hash_entry_t *) calloc (list->table_size, sizeof (gl_hash_entry_t));
+ if (list->table == NULL)
+ goto fail;
+#endif
+ list->root = NULL;
+
+ return list;
+
+#if WITH_HASHTABLE
+ fail:
+ free (list);
+ return NULL;
+#endif
+}
+
+static size_t
+gl_tree_size (gl_list_t list)
+{
+ return (list->root != NULL ? list->root->branch_size : 0);
+}
+
+static const void *
+gl_tree_node_value (gl_list_t list, gl_list_node_t node)
+{
+ return node->value;
+}
+
+static int
+gl_tree_node_nx_set_value (gl_list_t list, gl_list_node_t node, const void *elt)
+{
+#if WITH_HASHTABLE
+ if (elt != node->value)
+ {
+ size_t new_hashcode =
+ (list->base.hashcode_fn != NULL
+ ? list->base.hashcode_fn (elt)
+ : (size_t)(uintptr_t) elt);
+
+ if (new_hashcode != node->h.hashcode)
+ {
+ remove_from_bucket (list, node);
+ node->value = elt;
+ node->h.hashcode = new_hashcode;
+ if (add_to_bucket (list, node) < 0)
+ {
+ /* Out of memory. We removed node from a bucket but cannot add
+ it to another bucket. In order to avoid inconsistencies, we
+ must remove node entirely from the list. */
+ gl_tree_remove_node_from_tree (list, node);
+ free (node);
+ return -1;
+ }
+ }
+ else
+ node->value = elt;
+ }
+#else
+ node->value = elt;
+#endif
+ return 0;
+}
+
+static gl_list_node_t _GL_ATTRIBUTE_PURE
+gl_tree_next_node (gl_list_t list, gl_list_node_t node)
+{
+ if (node->right != NULL)
+ {
+ node = node->right;
+ while (node->left != NULL)
+ node = node->left;
+ }
+ else
+ {
+ while (node->parent != NULL && node->parent->right == node)
+ node = node->parent;
+ node = node->parent;
+ }
+ return node;
+}
+
+static gl_list_node_t _GL_ATTRIBUTE_PURE
+gl_tree_previous_node (gl_list_t list, gl_list_node_t node)
+{
+ if (node->left != NULL)
+ {
+ node = node->left;
+ while (node->right != NULL)
+ node = node->right;
+ }
+ else
+ {
+ while (node->parent != NULL && node->parent->left == node)
+ node = node->parent;
+ node = node->parent;
+ }
+ return node;
+}
+
+/* Return the node at the given position < gl_tree_size (list). */
+static gl_list_node_t _GL_ATTRIBUTE_PURE
+node_at (gl_list_node_t root, size_t position)
+{
+ /* Here we know that root != NULL. */
+ gl_list_node_t node = root;
+
+ for (;;)
+ {
+ if (node->left != NULL)
+ {
+ if (position < node->left->branch_size)
+ {
+ node = node->left;
+ continue;
+ }
+ position -= node->left->branch_size;
+ }
+ if (position == 0)
+ break;
+ position--;
+ node = node->right;
+ }
+ return node;
+}
+
+static const void * _GL_ATTRIBUTE_PURE
+gl_tree_get_at (gl_list_t list, size_t position)
+{
+ gl_list_node_t node = list->root;
+
+ if (!(node != NULL && position < node->branch_size))
+ /* Invalid argument. */
+ abort ();
+ node = node_at (node, position);
+ return node->value;
+}
+
+static gl_list_node_t
+gl_tree_nx_set_at (gl_list_t list, size_t position, const void *elt)
+{
+ gl_list_node_t node = list->root;
+
+ if (!(node != NULL && position < node->branch_size))
+ /* Invalid argument. */
+ abort ();
+ node = node_at (node, position);
+#if WITH_HASHTABLE
+ if (elt != node->value)
+ {
+ size_t new_hashcode =
+ (list->base.hashcode_fn != NULL
+ ? list->base.hashcode_fn (elt)
+ : (size_t)(uintptr_t) elt);
+
+ if (new_hashcode != node->h.hashcode)
+ {
+ remove_from_bucket (list, node);
+ node->value = elt;
+ node->h.hashcode = new_hashcode;
+ if (add_to_bucket (list, node) < 0)
+ {
+ /* Out of memory. We removed node from a bucket but cannot add
+ it to another bucket. In order to avoid inconsistencies, we
+ must remove node entirely from the list. */
+ gl_tree_remove_node_from_tree (list, node);
+ free (node);
+ return NULL;
+ }
+ }
+ else
+ node->value = elt;
+ }
+#else
+ node->value = elt;
+#endif
+ return node;
+}
+
+#if !WITH_HASHTABLE
+
+static gl_list_node_t
+gl_tree_search_from_to (gl_list_t list, size_t start_index, size_t end_index,
+ const void *elt)
+{
+ if (!(start_index <= end_index
+ && end_index <= (list->root != NULL ? list->root->branch_size : 0)))
+ /* Invalid arguments. */
+ abort ();
+ {
+ gl_listelement_equals_fn equals = list->base.equals_fn;
+ /* Iterate across all elements. */
+ gl_list_node_t node = list->root;
+ iterstack_t stack;
+ iterstack_item_t *stack_ptr = &stack[0];
+ size_t index = 0;
+
+ if (start_index == 0)
+ {
+ /* Consider all elements. */
+ for (;;)
+ {
+ /* Descend on left branch. */
+ for (;;)
+ {
+ if (node == NULL)
+ break;
+ stack_ptr->node = node;
+ stack_ptr->rightp = 0;
+ node = node->left;
+ stack_ptr++;
+ }
+ /* Climb up again. */
+ for (;;)
+ {
+ if (stack_ptr == &stack[0])
+ return NULL;
+ stack_ptr--;
+ if (!stack_ptr->rightp)
+ break;
+ }
+ node = stack_ptr->node;
+ /* Test against current element. */
+ if (equals != NULL ? equals (elt, node->value) : elt == node->value)
+ return node;
+ index++;
+ if (index >= end_index)
+ return NULL;
+ /* Descend on right branch. */
+ stack_ptr->rightp = 1;
+ node = node->right;
+ stack_ptr++;
+ }
+ }
+ else
+ {
+ /* Consider only elements at indices >= start_index.
+ In this case, rightp contains the difference between the start_index
+ for the parent node and the one for the child node (0 when the child
+ node is the parent's left child, > 0 when the child is the parent's
+ right child). */
+ for (;;)
+ {
+ /* Descend on left branch. */
+ for (;;)
+ {
+ if (node == NULL)
+ break;
+ if (node->branch_size <= start_index)
+ break;
+ stack_ptr->node = node;
+ stack_ptr->rightp = 0;
+ node = node->left;
+ stack_ptr++;
+ }
+ /* Climb up again. */
+ for (;;)
+ {
+ if (stack_ptr == &stack[0])
+ return NULL;
+ stack_ptr--;
+ if (!stack_ptr->rightp)
+ break;
+ start_index += stack_ptr->rightp;
+ }
+ node = stack_ptr->node;
+ {
+ size_t left_branch_size1 =
+ (node->left != NULL ? node->left->branch_size : 0) + 1;
+ if (start_index < left_branch_size1)
+ {
+ /* Test against current element. */
+ if (equals != NULL ? equals (elt, node->value) : elt == node->value)
+ return node;
+ /* Now that we have considered all indices < left_branch_size1,
+ we can increment start_index. */
+ start_index = left_branch_size1;
+ }
+ index++;
+ if (index >= end_index)
+ return NULL;
+ /* Descend on right branch. */
+ start_index -= left_branch_size1;
+ stack_ptr->rightp = left_branch_size1;
+ }
+ node = node->right;
+ stack_ptr++;
+ }
+ }
+ }
+}
+
+static size_t
+gl_tree_indexof_from_to (gl_list_t list, size_t start_index, size_t end_index,
+ const void *elt)
+{
+ if (!(start_index <= end_index
+ && end_index <= (list->root != NULL ? list->root->branch_size : 0)))
+ /* Invalid arguments. */
+ abort ();
+ {
+ gl_listelement_equals_fn equals = list->base.equals_fn;
+ /* Iterate across all elements. */
+ gl_list_node_t node = list->root;
+ iterstack_t stack;
+ iterstack_item_t *stack_ptr = &stack[0];
+ size_t index = 0;
+
+ if (start_index == 0)
+ {
+ /* Consider all elements. */
+ for (;;)
+ {
+ /* Descend on left branch. */
+ for (;;)
+ {
+ if (node == NULL)
+ break;
+ stack_ptr->node = node;
+ stack_ptr->rightp = 0;
+ node = node->left;
+ stack_ptr++;
+ }
+ /* Climb up again. */
+ for (;;)
+ {
+ if (stack_ptr == &stack[0])
+ return (size_t)(-1);
+ stack_ptr--;
+ if (!stack_ptr->rightp)
+ break;
+ }
+ node = stack_ptr->node;
+ /* Test against current element. */
+ if (equals != NULL ? equals (elt, node->value) : elt == node->value)
+ return index;
+ index++;
+ if (index >= end_index)
+ return (size_t)(-1);
+ /* Descend on right branch. */
+ stack_ptr->rightp = 1;
+ node = node->right;
+ stack_ptr++;
+ }
+ }
+ else
+ {
+ /* Consider only elements at indices >= start_index.
+ In this case, rightp contains the difference between the start_index
+ for the parent node and the one for the child node (0 when the child
+ node is the parent's left child, > 0 when the child is the parent's
+ right child). */
+ for (;;)
+ {
+ /* Descend on left branch. */
+ for (;;)
+ {
+ if (node == NULL)
+ break;
+ if (node->branch_size <= start_index)
+ break;
+ stack_ptr->node = node;
+ stack_ptr->rightp = 0;
+ node = node->left;
+ stack_ptr++;
+ }
+ /* Climb up again. */
+ for (;;)
+ {
+ if (stack_ptr == &stack[0])
+ return (size_t)(-1);
+ stack_ptr--;
+ if (!stack_ptr->rightp)
+ break;
+ start_index += stack_ptr->rightp;
+ }
+ node = stack_ptr->node;
+ {
+ size_t left_branch_size1 =
+ (node->left != NULL ? node->left->branch_size : 0) + 1;
+ if (start_index < left_branch_size1)
+ {
+ /* Test against current element. */
+ if (equals != NULL ? equals (elt, node->value) : elt == node->value)
+ return index;
+ /* Now that we have considered all indices < left_branch_size1,
+ we can increment start_index. */
+ start_index = left_branch_size1;
+ }
+ index++;
+ if (index >= end_index)
+ return (size_t)(-1);
+ /* Descend on right branch. */
+ start_index -= left_branch_size1;
+ stack_ptr->rightp = left_branch_size1;
+ }
+ node = node->right;
+ stack_ptr++;
+ }
+ }
+ }
+}
+
+#endif
+
+static gl_list_node_t
+gl_tree_nx_add_at (gl_list_t list, size_t position, const void *elt)
+{
+ size_t count = (list->root != NULL ? list->root->branch_size : 0);
+
+ if (!(position <= count))
+ /* Invalid argument. */
+ abort ();
+ if (position == count)
+ return gl_tree_nx_add_last (list, elt);
+ else
+ return gl_tree_nx_add_before (list, node_at (list->root, position), elt);
+}
+
+static bool
+gl_tree_remove_node (gl_list_t list, gl_list_node_t node)
+{
+#if WITH_HASHTABLE
+ /* Remove node from the hash table.
+ Note that this is only possible _before_ the node is removed from the
+ tree structure, because remove_from_bucket() uses node_position(). */
+ remove_from_bucket (list, node);
+#endif
+
+ gl_tree_remove_node_from_tree (list, node);
+
+ if (list->base.dispose_fn != NULL)
+ list->base.dispose_fn (node->value);
+ free (node);
+ return true;
+}
+
+static bool
+gl_tree_remove_at (gl_list_t list, size_t position)
+{
+ gl_list_node_t node = list->root;
+
+ if (!(node != NULL && position < node->branch_size))
+ /* Invalid argument. */
+ abort ();
+ node = node_at (node, position);
+ return gl_tree_remove_node (list, node);
+}
+
+static bool
+gl_tree_remove (gl_list_t list, const void *elt)
+{
+ if (list->root != NULL)
+ {
+ gl_list_node_t node =
+ gl_tree_search_from_to (list, 0, list->root->branch_size, elt);
+
+ if (node != NULL)
+ return gl_tree_remove_node (list, node);
+ }
+ return false;
+}
+
+#if !WITH_HASHTABLE
+
+static void
+gl_tree_list_free (gl_list_t list)
+{
+ /* Iterate across all elements in post-order. */
+ gl_list_node_t node = list->root;
+ iterstack_t stack;
+ iterstack_item_t *stack_ptr = &stack[0];
+
+ for (;;)
+ {
+ /* Descend on left branch. */
+ for (;;)
+ {
+ if (node == NULL)
+ break;
+ stack_ptr->node = node;
+ stack_ptr->rightp = false;
+ node = node->left;
+ stack_ptr++;
+ }
+ /* Climb up again. */
+ for (;;)
+ {
+ if (stack_ptr == &stack[0])
+ goto done_iterate;
+ stack_ptr--;
+ node = stack_ptr->node;
+ if (!stack_ptr->rightp)
+ break;
+ /* Free the current node. */
+ if (list->base.dispose_fn != NULL)
+ list->base.dispose_fn (node->value);
+ free (node);
+ }
+ /* Descend on right branch. */
+ stack_ptr->rightp = true;
+ node = node->right;
+ stack_ptr++;
+ }
+ done_iterate:
+ free (list);
+}
+
+#endif
+
+/* --------------------- gl_list_iterator_t Data Type --------------------- */
+
+static gl_list_iterator_t
+gl_tree_iterator (gl_list_t list)
+{
+ gl_list_iterator_t result;
+ gl_list_node_t node;
+
+ result.vtable = list->base.vtable;
+ result.list = list;
+ /* Start node is the leftmost node. */
+ node = list->root;
+ if (node != NULL)
+ while (node->left != NULL)
+ node = node->left;
+ result.p = node;
+ /* End point is past the rightmost node. */
+ result.q = NULL;
+#if defined GCC_LINT || defined lint
+ result.i = 0;
+ result.j = 0;
+ result.count = 0;
+#endif
+
+ return result;
+}
+
+static gl_list_iterator_t
+gl_tree_iterator_from_to (gl_list_t list, size_t start_index, size_t end_index)
+{
+ size_t count = (list->root != NULL ? list->root->branch_size : 0);
+ gl_list_iterator_t result;
+
+ if (!(start_index <= end_index && end_index <= count))
+ /* Invalid arguments. */
+ abort ();
+ result.vtable = list->base.vtable;
+ result.list = list;
+ /* Start node is the node at position start_index. */
+ result.p = (start_index < count ? node_at (list->root, start_index) : NULL);
+ /* End point is the node at position end_index. */
+ result.q = (end_index < count ? node_at (list->root, end_index) : NULL);
+#if defined GCC_LINT || defined lint
+ result.i = 0;
+ result.j = 0;
+ result.count = 0;
+#endif
+
+ return result;
+}
+
+static bool
+gl_tree_iterator_next (gl_list_iterator_t *iterator,
+ const void **eltp, gl_list_node_t *nodep)
+{
+ if (iterator->p != iterator->q)
+ {
+ gl_list_node_t node = (gl_list_node_t) iterator->p;
+ *eltp = node->value;
+ if (nodep != NULL)
+ *nodep = node;
+ /* Advance to the next node. */
+ if (node->right != NULL)
+ {
+ node = node->right;
+ while (node->left != NULL)
+ node = node->left;
+ }
+ else
+ {
+ while (node->parent != NULL && node->parent->right == node)
+ node = node->parent;
+ node = node->parent;
+ }
+ iterator->p = node;
+ return true;
+ }
+ else
+ return false;
+}
+
+static void
+gl_tree_iterator_free (gl_list_iterator_t *iterator)
+{
+}
+
+/* ---------------------- Sorted gl_list_t Data Type ---------------------- */
+
+static gl_list_node_t
+gl_tree_sortedlist_search (gl_list_t list, gl_listelement_compar_fn compar,
+ const void *elt)
+{
+ gl_list_node_t node;
+
+ for (node = list->root; node != NULL; )
+ {
+ int cmp = compar (node->value, elt);
+
+ if (cmp < 0)
+ node = node->right;
+ else if (cmp > 0)
+ node = node->left;
+ else /* cmp == 0 */
+ {
+ /* We have an element equal to ELT. But we need the leftmost such
+ element. */
+ gl_list_node_t found = node;
+ node = node->left;
+ for (; node != NULL; )
+ {
+ int cmp2 = compar (node->value, elt);
+
+ if (cmp2 < 0)
+ node = node->right;
+ else if (cmp2 > 0)
+ /* The list was not sorted. */
+ abort ();
+ else /* cmp2 == 0 */
+ {
+ found = node;
+ node = node->left;
+ }
+ }
+ return found;
+ }
+ }
+ return NULL;
+}
+
+static gl_list_node_t
+gl_tree_sortedlist_search_from_to (gl_list_t list,
+ gl_listelement_compar_fn compar,
+ size_t low, size_t high,
+ const void *elt)
+{
+ gl_list_node_t node;
+
+ if (!(low <= high
+ && high <= (list->root != NULL ? list->root->branch_size : 0)))
+ /* Invalid arguments. */
+ abort ();
+
+ for (node = list->root; node != NULL; )
+ {
+ size_t left_branch_size =
+ (node->left != NULL ? node->left->branch_size : 0);
+
+ if (low > left_branch_size)
+ {
+ low -= left_branch_size + 1;
+ high -= left_branch_size + 1;
+ node = node->right;
+ }
+ else if (high <= left_branch_size)
+ node = node->left;
+ else
+ {
+ /* Here low <= left_branch_size < high. */
+ int cmp = compar (node->value, elt);
+
+ if (cmp < 0)
+ {
+ low = 0;
+ high -= left_branch_size + 1;
+ node = node->right;
+ }
+ else if (cmp > 0)
+ node = node->left;
+ else /* cmp == 0 */
+ {
+ /* We have an element equal to ELT. But we need the leftmost
+ such element. */
+ gl_list_node_t found = node;
+ node = node->left;
+ for (; node != NULL; )
+ {
+ size_t left_branch_size2 =
+ (node->left != NULL ? node->left->branch_size : 0);
+
+ if (low > left_branch_size2)
+ {
+ low -= left_branch_size2 + 1;
+ node = node->right;
+ }
+ else
+ {
+ /* Here low <= left_branch_size2. */
+ int cmp2 = compar (node->value, elt);
+
+ if (cmp2 < 0)
+ {
+ low = 0;
+ node = node->right;
+ }
+ else if (cmp2 > 0)
+ /* The list was not sorted. */
+ abort ();
+ else /* cmp2 == 0 */
+ {
+ found = node;
+ node = node->left;
+ }
+ }
+ }
+ return found;
+ }
+ }
+ }
+ return NULL;
+}
+
+static size_t
+gl_tree_sortedlist_indexof (gl_list_t list, gl_listelement_compar_fn compar,
+ const void *elt)
+{
+ gl_list_node_t node;
+ size_t position;
+
+ for (node = list->root, position = 0; node != NULL; )
+ {
+ int cmp = compar (node->value, elt);
+
+ if (cmp < 0)
+ {
+ if (node->left != NULL)
+ position += node->left->branch_size;
+ position++;
+ node = node->right;
+ }
+ else if (cmp > 0)
+ node = node->left;
+ else /* cmp == 0 */
+ {
+ /* We have an element equal to ELT. But we need the leftmost such
+ element. */
+ size_t found_position =
+ position + (node->left != NULL ? node->left->branch_size : 0);
+ node = node->left;
+ for (; node != NULL; )
+ {
+ int cmp2 = compar (node->value, elt);
+
+ if (cmp2 < 0)
+ {
+ if (node->left != NULL)
+ position += node->left->branch_size;
+ position++;
+ node = node->right;
+ }
+ else if (cmp2 > 0)
+ /* The list was not sorted. */
+ abort ();
+ else /* cmp2 == 0 */
+ {
+ found_position =
+ position
+ + (node->left != NULL ? node->left->branch_size : 0);
+ node = node->left;
+ }
+ }
+ return found_position;
+ }
+ }
+ return (size_t)(-1);
+}
+
+static size_t
+gl_tree_sortedlist_indexof_from_to (gl_list_t list,
+ gl_listelement_compar_fn compar,
+ size_t low, size_t high,
+ const void *elt)
+{
+ gl_list_node_t node;
+ size_t position;
+
+ if (!(low <= high
+ && high <= (list->root != NULL ? list->root->branch_size : 0)))
+ /* Invalid arguments. */
+ abort ();
+
+ for (node = list->root, position = 0; node != NULL; )
+ {
+ size_t left_branch_size =
+ (node->left != NULL ? node->left->branch_size : 0);
+
+ if (low > left_branch_size)
+ {
+ low -= left_branch_size + 1;
+ high -= left_branch_size + 1;
+ position += left_branch_size + 1;
+ node = node->right;
+ }
+ else if (high <= left_branch_size)
+ node = node->left;
+ else
+ {
+ /* Here low <= left_branch_size < high. */
+ int cmp = compar (node->value, elt);
+
+ if (cmp < 0)
+ {
+ low = 0;
+ high -= left_branch_size + 1;
+ position += left_branch_size + 1;
+ node = node->right;
+ }
+ else if (cmp > 0)
+ node = node->left;
+ else /* cmp == 0 */
+ {
+ /* We have an element equal to ELT. But we need the leftmost
+ such element. */
+ size_t found_position =
+ position + (node->left != NULL ? node->left->branch_size : 0);
+ node = node->left;
+ for (; node != NULL; )
+ {
+ size_t left_branch_size2 =
+ (node->left != NULL ? node->left->branch_size : 0);
+
+ if (low > left_branch_size2)
+ {
+ low -= left_branch_size2 + 1;
+ node = node->right;
+ }
+ else
+ {
+ /* Here low <= left_branch_size2. */
+ int cmp2 = compar (node->value, elt);
+
+ if (cmp2 < 0)
+ {
+ position += left_branch_size2 + 1;
+ node = node->right;
+ }
+ else if (cmp2 > 0)
+ /* The list was not sorted. */
+ abort ();
+ else /* cmp2 == 0 */
+ {
+ found_position = position + left_branch_size2;
+ node = node->left;
+ }
+ }
+ }
+ return found_position;
+ }
+ }
+ }
+ return (size_t)(-1);
+}
+
+static gl_list_node_t
+gl_tree_sortedlist_nx_add (gl_list_t list, gl_listelement_compar_fn compar,
+ const void *elt)
+{
+ gl_list_node_t node = list->root;
+
+ if (node == NULL)
+ return gl_tree_nx_add_first (list, elt);
+
+ for (;;)
+ {
+ int cmp = compar (node->value, elt);
+
+ if (cmp < 0)
+ {
+ if (node->right == NULL)
+ return gl_tree_nx_add_after (list, node, elt);
+ node = node->right;
+ }
+ else if (cmp > 0)
+ {
+ if (node->left == NULL)
+ return gl_tree_nx_add_before (list, node, elt);
+ node = node->left;
+ }
+ else /* cmp == 0 */
+ return gl_tree_nx_add_before (list, node, elt);
+ }
+}
+
+static bool
+gl_tree_sortedlist_remove (gl_list_t list, gl_listelement_compar_fn compar,
+ const void *elt)
+{
+ gl_list_node_t node = gl_tree_sortedlist_search (list, compar, elt);
+ if (node != NULL)
+ return gl_tree_remove_node (list, node);
+ else
+ return false;
+}
diff --git a/draft/mcastseed/lib/gl_list.c b/draft/mcastseed/lib/gl_list.c
new file mode 100644
index 0000000..8793298
--- /dev/null
+++ b/draft/mcastseed/lib/gl_list.c
@@ -0,0 +1,3 @@
+#include <config.h>
+#define GL_LIST_INLINE _GL_EXTERN_INLINE
+#include "gl_list.h"
diff --git a/draft/mcastseed/lib/gl_list.h b/draft/mcastseed/lib/gl_list.h
new file mode 100644
index 0000000..c9d05b0
--- /dev/null
+++ b/draft/mcastseed/lib/gl_list.h
@@ -0,0 +1,841 @@
+/* Abstract sequential list data type. -*- coding: utf-8 -*-
+ Copyright (C) 2006-2016 Free Software Foundation, Inc.
+ Written by Bruno Haible <bruno@clisp.org>, 2006.
+
+ 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; either version 3 of the License, or
+ (at your option) any later version.
+
+ 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, see <http://www.gnu.org/licenses/>. */
+
+#ifndef _GL_LIST_H
+#define _GL_LIST_H
+
+#include <stdbool.h>
+#include <stddef.h>
+
+#ifndef _GL_INLINE_HEADER_BEGIN
+ #error "Please include config.h first."
+#endif
+_GL_INLINE_HEADER_BEGIN
+#ifndef GL_LIST_INLINE
+# define GL_LIST_INLINE _GL_INLINE
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/* gl_list is an abstract list data type. It can contain any number of
+ objects ('void *' or 'const void *' pointers) in any given order.
+ Duplicates are allowed, but can optionally be forbidden.
+
+ There are several implementations of this list datatype, optimized for
+ different operations or for memory. You can start using the simplest list
+ implementation, GL_ARRAY_LIST, and switch to a different implementation
+ later, when you realize which operations are performed the most frequently.
+ The API of the different implementations is exactly the same; when
+ switching to a different implementation, you only have to change the
+ gl_list_create call.
+
+ The implementations are:
+ GL_ARRAY_LIST a growable array
+ GL_CARRAY_LIST a growable circular array
+ GL_LINKED_LIST a linked list
+ GL_AVLTREE_LIST a binary tree (AVL tree)
+ GL_RBTREE_LIST a binary tree (red-black tree)
+ GL_LINKEDHASH_LIST a hash table with a linked list
+ GL_AVLTREEHASH_LIST a hash table with a binary tree (AVL tree)
+ GL_RBTREEHASH_LIST a hash table with a binary tree (red-black tree)
+
+ The memory consumption is asymptotically the same: O(1) for every object
+ in the list. When looking more closely at the average memory consumed
+ for an object, GL_ARRAY_LIST is the most compact representation, and
+ GL_LINKEDHASH_LIST and GL_TREEHASH_LIST need more memory.
+
+ The guaranteed average performance of the operations is, for a list of
+ n elements:
+
+ Operation ARRAY LINKED TREE LINKEDHASH TREEHASH
+ CARRAY with|without with|without
+ duplicates duplicates
+
+ gl_list_size O(1) O(1) O(1) O(1) O(1)
+ gl_list_node_value O(1) O(1) O(1) O(1) O(1)
+ gl_list_node_set_value O(1) O(1) O(1) O(1) O((log n)²)/O(1)
+ gl_list_next_node O(1) O(1) O(log n) O(1) O(log n)
+ gl_list_previous_node O(1) O(1) O(log n) O(1) O(log n)
+ gl_list_get_at O(1) O(n) O(log n) O(n) O(log n)
+ gl_list_set_at O(1) O(n) O(log n) O(n) O((log n)²)/O(log n)
+ gl_list_search O(n) O(n) O(n) O(n)/O(1) O(log n)/O(1)
+ gl_list_search_from O(n) O(n) O(n) O(n)/O(1) O((log n)²)/O(log n)
+ gl_list_search_from_to O(n) O(n) O(n) O(n)/O(1) O((log n)²)/O(log n)
+ gl_list_indexof O(n) O(n) O(n) O(n) O(log n)
+ gl_list_indexof_from O(n) O(n) O(n) O(n) O((log n)²)/O(log n)
+ gl_list_indexof_from_to O(n) O(n) O(n) O(n) O((log n)²)/O(log n)
+ gl_list_add_first O(n)/O(1) O(1) O(log n) O(1) O((log n)²)/O(log n)
+ gl_list_add_last O(1) O(1) O(log n) O(1) O((log n)²)/O(log n)
+ gl_list_add_before O(n) O(1) O(log n) O(1) O((log n)²)/O(log n)
+ gl_list_add_after O(n) O(1) O(log n) O(1) O((log n)²)/O(log n)
+ gl_list_add_at O(n) O(n) O(log n) O(n) O((log n)²)/O(log n)
+ gl_list_remove_node O(n) O(1) O(log n) O(n)/O(1) O((log n)²)/O(log n)
+ gl_list_remove_at O(n) O(n) O(log n) O(n) O((log n)²)/O(log n)
+ gl_list_remove O(n) O(n) O(n) O(n)/O(1) O((log n)²)/O(log n)
+ gl_list_iterator O(1) O(1) O(log n) O(1) O(log n)
+ gl_list_iterator_from_to O(1) O(n) O(log n) O(n) O(log n)
+ gl_list_iterator_next O(1) O(1) O(log n) O(1) O(log n)
+ gl_sortedlist_search O(log n) O(n) O(log n) O(n) O(log n)
+ gl_sortedlist_search_from O(log n) O(n) O(log n) O(n) O(log n)
+ gl_sortedlist_indexof O(log n) O(n) O(log n) O(n) O(log n)
+ gl_sortedlist_indexof_fro O(log n) O(n) O(log n) O(n) O(log n)
+ gl_sortedlist_add O(n) O(n) O(log n) O(n) O((log n)²)/O(log n)
+ gl_sortedlist_remove O(n) O(n) O(log n) O(n) O((log n)²)/O(log n)
+ */
+
+/* -------------------------- gl_list_t Data Type -------------------------- */
+
+/* Type of function used to compare two elements.
+ NULL denotes pointer comparison. */
+typedef bool (*gl_listelement_equals_fn) (const void *elt1, const void *elt2);
+
+/* Type of function used to compute a hash code.
+ NULL denotes a function that depends only on the pointer itself. */
+typedef size_t (*gl_listelement_hashcode_fn) (const void *elt);
+
+/* Type of function used to dispose an element once it's removed from a list.
+ NULL denotes a no-op. */
+typedef void (*gl_listelement_dispose_fn) (const void *elt);
+
+struct gl_list_impl;
+/* Type representing an entire list. */
+typedef struct gl_list_impl * gl_list_t;
+
+struct gl_list_node_impl;
+/* Type representing the position of an element in the list, in a way that
+ is more adapted to the list implementation than a plain index.
+ Note: It is invalidated by insertions and removals! */
+typedef struct gl_list_node_impl * gl_list_node_t;
+
+struct gl_list_implementation;
+/* Type representing a list datatype implementation. */
+typedef const struct gl_list_implementation * gl_list_implementation_t;
+
+#if 0 /* Unless otherwise specified, these are defined inline below. */
+
+/* Create an empty list.
+ IMPLEMENTATION is one of GL_ARRAY_LIST, GL_CARRAY_LIST, GL_LINKED_LIST,
+ GL_AVLTREE_LIST, GL_RBTREE_LIST, GL_LINKEDHASH_LIST, GL_AVLTREEHASH_LIST,
+ GL_RBTREEHASH_LIST.
+ EQUALS_FN is an element comparison function or NULL.
+ HASHCODE_FN is an element hash code function or NULL.
+ DISPOSE_FN is an element disposal function or NULL.
+ ALLOW_DUPLICATES is false if duplicate elements shall not be allowed in
+ the list. The implementation may verify this at runtime. */
+/* declared in gl_xlist.h */
+extern gl_list_t gl_list_create_empty (gl_list_implementation_t implementation,
+ gl_listelement_equals_fn equals_fn,
+ gl_listelement_hashcode_fn hashcode_fn,
+ gl_listelement_dispose_fn dispose_fn,
+ bool allow_duplicates);
+/* Likewise. Return NULL upon out-of-memory. */
+extern gl_list_t gl_list_nx_create_empty (gl_list_implementation_t implementation,
+ gl_listelement_equals_fn equals_fn,
+ gl_listelement_hashcode_fn hashcode_fn,
+ gl_listelement_dispose_fn dispose_fn,
+ bool allow_duplicates);
+
+/* Create a list with given contents.
+ IMPLEMENTATION is one of GL_ARRAY_LIST, GL_CARRAY_LIST, GL_LINKED_LIST,
+ GL_AVLTREE_LIST, GL_RBTREE_LIST, GL_LINKEDHASH_LIST, GL_AVLTREEHASH_LIST,
+ GL_RBTREEHASH_LIST.
+ EQUALS_FN is an element comparison function or NULL.
+ HASHCODE_FN is an element hash code function or NULL.
+ DISPOSE_FN is an element disposal function or NULL.
+ ALLOW_DUPLICATES is false if duplicate elements shall not be allowed in
+ the list. The implementation may verify this at runtime.
+ COUNT is the number of initial elements.
+ CONTENTS[0..COUNT-1] is the initial contents. */
+/* declared in gl_xlist.h */
+extern gl_list_t gl_list_create (gl_list_implementation_t implementation,
+ gl_listelement_equals_fn equals_fn,
+ gl_listelement_hashcode_fn hashcode_fn,
+ gl_listelement_dispose_fn dispose_fn,
+ bool allow_duplicates,
+ size_t count, const void **contents);
+/* Likewise. Return NULL upon out-of-memory. */
+extern gl_list_t gl_list_nx_create (gl_list_implementation_t implementation,
+ gl_listelement_equals_fn equals_fn,
+ gl_listelement_hashcode_fn hashcode_fn,
+ gl_listelement_dispose_fn dispose_fn,
+ bool allow_duplicates,
+ size_t count, const void **contents);
+
+/* Return the current number of elements in a list. */
+extern size_t gl_list_size (gl_list_t list);
+
+/* Return the element value represented by a list node. */
+extern const void * gl_list_node_value (gl_list_t list, gl_list_node_t node);
+
+/* Replace the element value represented by a list node. */
+/* declared in gl_xlist.h */
+extern void gl_list_node_set_value (gl_list_t list, gl_list_node_t node,
+ const void *elt);
+/* Likewise. Return 0 upon success, -1 upon out-of-memory. */
+extern int gl_list_node_nx_set_value (gl_list_t list, gl_list_node_t node,
+ const void *elt)
+#if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4)
+ __attribute__ ((__warn_unused_result__))
+#endif
+ ;
+
+/* Return the node immediately after the given node in the list, or NULL
+ if the given node is the last (rightmost) one in the list. */
+extern gl_list_node_t gl_list_next_node (gl_list_t list, gl_list_node_t node);
+
+/* Return the node immediately before the given node in the list, or NULL
+ if the given node is the first (leftmost) one in the list. */
+extern gl_list_node_t gl_list_previous_node (gl_list_t list, gl_list_node_t node);
+
+/* Return the element at a given position in the list.
+ POSITION must be >= 0 and < gl_list_size (list). */
+extern const void * gl_list_get_at (gl_list_t list, size_t position);
+
+/* Replace the element at a given position in the list.
+ POSITION must be >= 0 and < gl_list_size (list).
+ Return its node. */
+/* declared in gl_xlist.h */
+extern gl_list_node_t gl_list_set_at (gl_list_t list, size_t position,
+ const void *elt);
+/* Likewise. Return NULL upon out-of-memory. */
+extern gl_list_node_t gl_list_nx_set_at (gl_list_t list, size_t position,
+ const void *elt)
+#if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4)
+ __attribute__ ((__warn_unused_result__))
+#endif
+ ;
+
+/* Search whether an element is already in the list.
+ Return its node if found, or NULL if not present in the list. */
+extern gl_list_node_t gl_list_search (gl_list_t list, const void *elt);
+
+/* Search whether an element is already in the list,
+ at a position >= START_INDEX.
+ Return its node if found, or NULL if not present in the list. */
+extern gl_list_node_t gl_list_search_from (gl_list_t list, size_t start_index,
+ const void *elt);
+
+/* Search whether an element is already in the list,
+ at a position >= START_INDEX and < END_INDEX.
+ Return its node if found, or NULL if not present in the list. */
+extern gl_list_node_t gl_list_search_from_to (gl_list_t list,
+ size_t start_index,
+ size_t end_index,
+ const void *elt);
+
+/* Search whether an element is already in the list.
+ Return its position if found, or (size_t)(-1) if not present in the list. */
+extern size_t gl_list_indexof (gl_list_t list, const void *elt);
+
+/* Search whether an element is already in the list,
+ at a position >= START_INDEX.
+ Return its position if found, or (size_t)(-1) if not present in the list. */
+extern size_t gl_list_indexof_from (gl_list_t list, size_t start_index,
+ const void *elt);
+
+/* Search whether an element is already in the list,
+ at a position >= START_INDEX and < END_INDEX.
+ Return its position if found, or (size_t)(-1) if not present in the list. */
+extern size_t gl_list_indexof_from_to (gl_list_t list,
+ size_t start_index, size_t end_index,
+ const void *elt);
+
+/* Add an element as the first element of the list.
+ Return its node. */
+/* declared in gl_xlist.h */
+extern gl_list_node_t gl_list_add_first (gl_list_t list, const void *elt);
+/* Likewise. Return NULL upon out-of-memory. */
+extern gl_list_node_t gl_list_nx_add_first (gl_list_t list, const void *elt)
+#if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4)
+ __attribute__ ((__warn_unused_result__))
+#endif
+ ;
+
+/* Add an element as the last element of the list.
+ Return its node. */
+/* declared in gl_xlist.h */
+extern gl_list_node_t gl_list_add_last (gl_list_t list, const void *elt);
+/* Likewise. Return NULL upon out-of-memory. */
+extern gl_list_node_t gl_list_nx_add_last (gl_list_t list, const void *elt)
+#if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4)
+ __attribute__ ((__warn_unused_result__))
+#endif
+ ;
+
+/* Add an element before a given element node of the list.
+ Return its node. */
+/* declared in gl_xlist.h */
+extern gl_list_node_t gl_list_add_before (gl_list_t list, gl_list_node_t node,
+ const void *elt);
+/* Likewise. Return NULL upon out-of-memory. */
+extern gl_list_node_t gl_list_nx_add_before (gl_list_t list,
+ gl_list_node_t node,
+ const void *elt)
+#if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4)
+ __attribute__ ((__warn_unused_result__))
+#endif
+ ;
+
+/* Add an element after a given element node of the list.
+ Return its node. */
+/* declared in gl_xlist.h */
+extern gl_list_node_t gl_list_add_after (gl_list_t list, gl_list_node_t node,
+ const void *elt);
+/* Likewise. Return NULL upon out-of-memory. */
+extern gl_list_node_t gl_list_nx_add_after (gl_list_t list, gl_list_node_t node,
+ const void *elt)
+#if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4)
+ __attribute__ ((__warn_unused_result__))
+#endif
+ ;
+
+/* Add an element at a given position in the list.
+ POSITION must be >= 0 and <= gl_list_size (list). */
+/* declared in gl_xlist.h */
+extern gl_list_node_t gl_list_add_at (gl_list_t list, size_t position,
+ const void *elt);
+/* Likewise. Return NULL upon out-of-memory. */
+extern gl_list_node_t gl_list_nx_add_at (gl_list_t list, size_t position,
+ const void *elt)
+#if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4)
+ __attribute__ ((__warn_unused_result__))
+#endif
+ ;
+
+/* Remove an element from the list.
+ Return true. */
+extern bool gl_list_remove_node (gl_list_t list, gl_list_node_t node);
+
+/* Remove an element at a given position from the list.
+ POSITION must be >= 0 and < gl_list_size (list).
+ Return true. */
+extern bool gl_list_remove_at (gl_list_t list, size_t position);
+
+/* Search and remove an element from the list.
+ Return true if it was found and removed. */
+extern bool gl_list_remove (gl_list_t list, const void *elt);
+
+/* Free an entire list.
+ (But this call does not free the elements of the list.) */
+extern void gl_list_free (gl_list_t list);
+
+#endif /* End of inline and gl_xlist.h-defined functions. */
+
+/* --------------------- gl_list_iterator_t Data Type --------------------- */
+
+/* Functions for iterating through a list. */
+
+/* Type of an iterator that traverses a list.
+ This is a fixed-size struct, so that creation of an iterator doesn't need
+ memory allocation on the heap. */
+typedef struct
+{
+ /* For fast dispatch of gl_list_iterator_next. */
+ const struct gl_list_implementation *vtable;
+ /* For detecting whether the last returned element was removed. */
+ gl_list_t list;
+ size_t count;
+ /* Other, implementation-private fields. */
+ void *p; void *q;
+ size_t i; size_t j;
+} gl_list_iterator_t;
+
+#if 0 /* These are defined inline below. */
+
+/* Create an iterator traversing a list.
+ The list contents must not be modified while the iterator is in use,
+ except for replacing or removing the last returned element. */
+extern gl_list_iterator_t gl_list_iterator (gl_list_t list);
+
+/* Create an iterator traversing the element with indices i,
+ start_index <= i < end_index, of a list.
+ The list contents must not be modified while the iterator is in use,
+ except for replacing or removing the last returned element. */
+extern gl_list_iterator_t gl_list_iterator_from_to (gl_list_t list,
+ size_t start_index,
+ size_t end_index);
+
+/* If there is a next element, store the next element in *ELTP, store its
+ node in *NODEP if NODEP is non-NULL, advance the iterator and return true.
+ Otherwise, return false. */
+extern bool gl_list_iterator_next (gl_list_iterator_t *iterator,
+ const void **eltp, gl_list_node_t *nodep);
+
+/* Free an iterator. */
+extern void gl_list_iterator_free (gl_list_iterator_t *iterator);
+
+#endif /* End of inline functions. */
+
+/* ---------------------- Sorted gl_list_t Data Type ---------------------- */
+
+/* The following functions are for lists without duplicates where the
+ order is given by a sort criterion. */
+
+/* Type of function used to compare two elements. Same as for qsort().
+ NULL denotes pointer comparison. */
+typedef int (*gl_listelement_compar_fn) (const void *elt1, const void *elt2);
+
+#if 0 /* Unless otherwise specified, these are defined inline below. */
+
+/* Search whether an element is already in the list.
+ The list is assumed to be sorted with COMPAR.
+ Return its node if found, or NULL if not present in the list.
+ If the list contains several copies of ELT, the node of the leftmost one is
+ returned. */
+extern gl_list_node_t gl_sortedlist_search (gl_list_t list,
+ gl_listelement_compar_fn compar,
+ const void *elt);
+
+/* Search whether an element is already in the list.
+ The list is assumed to be sorted with COMPAR.
+ Only list elements with indices >= START_INDEX and < END_INDEX are
+ considered; the implementation uses these bounds to minimize the number
+ of COMPAR invocations.
+ Return its node if found, or NULL if not present in the list.
+ If the list contains several copies of ELT, the node of the leftmost one is
+ returned. */
+extern gl_list_node_t gl_sortedlist_search_from_to (gl_list_t list,
+ gl_listelement_compar_fn compar,
+ size_t start_index,
+ size_t end_index,
+ const void *elt);
+
+/* Search whether an element is already in the list.
+ The list is assumed to be sorted with COMPAR.
+ Return its position if found, or (size_t)(-1) if not present in the list.
+ If the list contains several copies of ELT, the position of the leftmost one
+ is returned. */
+extern size_t gl_sortedlist_indexof (gl_list_t list,
+ gl_listelement_compar_fn compar,
+ const void *elt);
+
+/* Search whether an element is already in the list.
+ The list is assumed to be sorted with COMPAR.
+ Only list elements with indices >= START_INDEX and < END_INDEX are
+ considered; the implementation uses these bounds to minimize the number
+ of COMPAR invocations.
+ Return its position if found, or (size_t)(-1) if not present in the list.
+ If the list contains several copies of ELT, the position of the leftmost one
+ is returned. */
+extern size_t gl_sortedlist_indexof_from_to (gl_list_t list,
+ gl_listelement_compar_fn compar,
+ size_t start_index,
+ size_t end_index,
+ const void *elt);
+
+/* Add an element at the appropriate position in the list.
+ The list is assumed to be sorted with COMPAR.
+ Return its node. */
+/* declared in gl_xlist.h */
+extern gl_list_node_t gl_sortedlist_add (gl_list_t list,
+ gl_listelement_compar_fn compar,
+ const void *elt);
+/* Likewise. Return NULL upon out-of-memory. */
+extern gl_list_node_t gl_sortedlist_nx_add (gl_list_t list,
+ gl_listelement_compar_fn compar,
+ const void *elt)
+#if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4)
+ __attribute__ ((__warn_unused_result__))
+#endif
+ ;
+
+/* Search and remove an element from the list.
+ The list is assumed to be sorted with COMPAR.
+ Return true if it was found and removed.
+ If the list contains several copies of ELT, only the leftmost one is
+ removed. */
+extern bool gl_sortedlist_remove (gl_list_t list,
+ gl_listelement_compar_fn compar,
+ const void *elt);
+
+#endif /* End of inline and gl_xlist.h-defined functions. */
+
+/* ------------------------ Implementation Details ------------------------ */
+
+struct gl_list_implementation
+{
+ /* gl_list_t functions. */
+ gl_list_t (*nx_create_empty) (gl_list_implementation_t implementation,
+ gl_listelement_equals_fn equals_fn,
+ gl_listelement_hashcode_fn hashcode_fn,
+ gl_listelement_dispose_fn dispose_fn,
+ bool allow_duplicates);
+ gl_list_t (*nx_create) (gl_list_implementation_t implementation,
+ gl_listelement_equals_fn equals_fn,
+ gl_listelement_hashcode_fn hashcode_fn,
+ gl_listelement_dispose_fn dispose_fn,
+ bool allow_duplicates,
+ size_t count, const void **contents);
+ size_t (*size) (gl_list_t list);
+ const void * (*node_value) (gl_list_t list, gl_list_node_t node);
+ int (*node_nx_set_value) (gl_list_t list, gl_list_node_t node,
+ const void *elt);
+ gl_list_node_t (*next_node) (gl_list_t list, gl_list_node_t node);
+ gl_list_node_t (*previous_node) (gl_list_t list, gl_list_node_t node);
+ const void * (*get_at) (gl_list_t list, size_t position);
+ gl_list_node_t (*nx_set_at) (gl_list_t list, size_t position,
+ const void *elt);
+ gl_list_node_t (*search_from_to) (gl_list_t list, size_t start_index,
+ size_t end_index, const void *elt);
+ size_t (*indexof_from_to) (gl_list_t list, size_t start_index,
+ size_t end_index, const void *elt);
+ gl_list_node_t (*nx_add_first) (gl_list_t list, const void *elt);
+ gl_list_node_t (*nx_add_last) (gl_list_t list, const void *elt);
+ gl_list_node_t (*nx_add_before) (gl_list_t list, gl_list_node_t node,
+ const void *elt);
+ gl_list_node_t (*nx_add_after) (gl_list_t list, gl_list_node_t node,
+ const void *elt);
+ gl_list_node_t (*nx_add_at) (gl_list_t list, size_t position,
+ const void *elt);
+ bool (*remove_node) (gl_list_t list, gl_list_node_t node);
+ bool (*remove_at) (gl_list_t list, size_t position);
+ bool (*remove_elt) (gl_list_t list, const void *elt);
+ void (*list_free) (gl_list_t list);
+ /* gl_list_iterator_t functions. */
+ gl_list_iterator_t (*iterator) (gl_list_t list);
+ gl_list_iterator_t (*iterator_from_to) (gl_list_t list,
+ size_t start_index,
+ size_t end_index);
+ bool (*iterator_next) (gl_list_iterator_t *iterator,
+ const void **eltp, gl_list_node_t *nodep);
+ void (*iterator_free) (gl_list_iterator_t *iterator);
+ /* Sorted gl_list_t functions. */
+ gl_list_node_t (*sortedlist_search) (gl_list_t list,
+ gl_listelement_compar_fn compar,
+ const void *elt);
+ gl_list_node_t (*sortedlist_search_from_to) (gl_list_t list,
+ gl_listelement_compar_fn compar,
+ size_t start_index,
+ size_t end_index,
+ const void *elt);
+ size_t (*sortedlist_indexof) (gl_list_t list,
+ gl_listelement_compar_fn compar,
+ const void *elt);
+ size_t (*sortedlist_indexof_from_to) (gl_list_t list,
+ gl_listelement_compar_fn compar,
+ size_t start_index, size_t end_index,
+ const void *elt);
+ gl_list_node_t (*sortedlist_nx_add) (gl_list_t list,
+ gl_listelement_compar_fn compar,
+ const void *elt);
+ bool (*sortedlist_remove) (gl_list_t list,
+ gl_listelement_compar_fn compar,
+ const void *elt);
+};
+
+struct gl_list_impl_base
+{
+ const struct gl_list_implementation *vtable;
+ gl_listelement_equals_fn equals_fn;
+ gl_listelement_hashcode_fn hashcode_fn;
+ gl_listelement_dispose_fn dispose_fn;
+ bool allow_duplicates;
+};
+
+/* Define all functions of this file as accesses to the
+ struct gl_list_implementation. */
+
+GL_LIST_INLINE gl_list_t
+gl_list_nx_create_empty (gl_list_implementation_t implementation,
+ gl_listelement_equals_fn equals_fn,
+ gl_listelement_hashcode_fn hashcode_fn,
+ gl_listelement_dispose_fn dispose_fn,
+ bool allow_duplicates)
+{
+ return implementation->nx_create_empty (implementation, equals_fn,
+ hashcode_fn, dispose_fn,
+ allow_duplicates);
+}
+
+GL_LIST_INLINE gl_list_t
+gl_list_nx_create (gl_list_implementation_t implementation,
+ gl_listelement_equals_fn equals_fn,
+ gl_listelement_hashcode_fn hashcode_fn,
+ gl_listelement_dispose_fn dispose_fn,
+ bool allow_duplicates,
+ size_t count, const void **contents)
+{
+ return implementation->nx_create (implementation, equals_fn, hashcode_fn,
+ dispose_fn, allow_duplicates, count,
+ contents);
+}
+
+GL_LIST_INLINE size_t
+gl_list_size (gl_list_t list)
+{
+ return ((const struct gl_list_impl_base *) list)->vtable
+ ->size (list);
+}
+
+GL_LIST_INLINE const void *
+gl_list_node_value (gl_list_t list, gl_list_node_t node)
+{
+ return ((const struct gl_list_impl_base *) list)->vtable
+ ->node_value (list, node);
+}
+
+GL_LIST_INLINE int
+#if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4)
+ __attribute__ ((__warn_unused_result__))
+#endif
+gl_list_node_nx_set_value (gl_list_t list, gl_list_node_t node,
+ const void *elt)
+{
+ return ((const struct gl_list_impl_base *) list)->vtable
+ ->node_nx_set_value (list, node, elt);
+}
+
+GL_LIST_INLINE gl_list_node_t
+gl_list_next_node (gl_list_t list, gl_list_node_t node)
+{
+ return ((const struct gl_list_impl_base *) list)->vtable
+ ->next_node (list, node);
+}
+
+GL_LIST_INLINE gl_list_node_t
+gl_list_previous_node (gl_list_t list, gl_list_node_t node)
+{
+ return ((const struct gl_list_impl_base *) list)->vtable
+ ->previous_node (list, node);
+}
+
+GL_LIST_INLINE const void *
+gl_list_get_at (gl_list_t list, size_t position)
+{
+ return ((const struct gl_list_impl_base *) list)->vtable
+ ->get_at (list, position);
+}
+
+GL_LIST_INLINE gl_list_node_t
+#if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4)
+ __attribute__ ((__warn_unused_result__))
+#endif
+gl_list_nx_set_at (gl_list_t list, size_t position, const void *elt)
+{
+ return ((const struct gl_list_impl_base *) list)->vtable
+ ->nx_set_at (list, position, elt);
+}
+
+GL_LIST_INLINE gl_list_node_t
+gl_list_search (gl_list_t list, const void *elt)
+{
+ size_t size = ((const struct gl_list_impl_base *) list)->vtable->size (list);
+ return ((const struct gl_list_impl_base *) list)->vtable
+ ->search_from_to (list, 0, size, elt);
+}
+
+GL_LIST_INLINE gl_list_node_t
+gl_list_search_from (gl_list_t list, size_t start_index, const void *elt)
+{
+ size_t size = ((const struct gl_list_impl_base *) list)->vtable->size (list);
+ return ((const struct gl_list_impl_base *) list)->vtable
+ ->search_from_to (list, start_index, size, elt);
+}
+
+GL_LIST_INLINE gl_list_node_t
+gl_list_search_from_to (gl_list_t list, size_t start_index, size_t end_index,
+ const void *elt)
+{
+ return ((const struct gl_list_impl_base *) list)->vtable
+ ->search_from_to (list, start_index, end_index, elt);
+}
+
+GL_LIST_INLINE size_t
+gl_list_indexof (gl_list_t list, const void *elt)
+{
+ size_t size = ((const struct gl_list_impl_base *) list)->vtable->size (list);
+ return ((const struct gl_list_impl_base *) list)->vtable
+ ->indexof_from_to (list, 0, size, elt);
+}
+
+GL_LIST_INLINE size_t
+gl_list_indexof_from (gl_list_t list, size_t start_index, const void *elt)
+{
+ size_t size = ((const struct gl_list_impl_base *) list)->vtable->size (list);
+ return ((const struct gl_list_impl_base *) list)->vtable
+ ->indexof_from_to (list, start_index, size, elt);
+}
+
+GL_LIST_INLINE size_t
+gl_list_indexof_from_to (gl_list_t list, size_t start_index, size_t end_index,
+ const void *elt)
+{
+ return ((const struct gl_list_impl_base *) list)->vtable
+ ->indexof_from_to (list, start_index, end_index, elt);
+}
+
+GL_LIST_INLINE gl_list_node_t
+#if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4)
+ __attribute__ ((__warn_unused_result__))
+#endif
+gl_list_nx_add_first (gl_list_t list, const void *elt)
+{
+ return ((const struct gl_list_impl_base *) list)->vtable
+ ->nx_add_first (list, elt);
+}
+
+GL_LIST_INLINE gl_list_node_t
+#if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4)
+ __attribute__ ((__warn_unused_result__))
+#endif
+gl_list_nx_add_last (gl_list_t list, const void *elt)
+{
+ return ((const struct gl_list_impl_base *) list)->vtable
+ ->nx_add_last (list, elt);
+}
+
+GL_LIST_INLINE gl_list_node_t
+#if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4)
+ __attribute__ ((__warn_unused_result__))
+#endif
+gl_list_nx_add_before (gl_list_t list, gl_list_node_t node, const void *elt)
+{
+ return ((const struct gl_list_impl_base *) list)->vtable
+ ->nx_add_before (list, node, elt);
+}
+
+GL_LIST_INLINE gl_list_node_t
+#if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4)
+ __attribute__ ((__warn_unused_result__))
+#endif
+gl_list_nx_add_after (gl_list_t list, gl_list_node_t node, const void *elt)
+{
+ return ((const struct gl_list_impl_base *) list)->vtable
+ ->nx_add_after (list, node, elt);
+}
+
+GL_LIST_INLINE gl_list_node_t
+#if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4)
+ __attribute__ ((__warn_unused_result__))
+#endif
+gl_list_nx_add_at (gl_list_t list, size_t position, const void *elt)
+{
+ return ((const struct gl_list_impl_base *) list)->vtable
+ ->nx_add_at (list, position, elt);
+}
+
+GL_LIST_INLINE bool
+gl_list_remove_node (gl_list_t list, gl_list_node_t node)
+{
+ return ((const struct gl_list_impl_base *) list)->vtable
+ ->remove_node (list, node);
+}
+
+GL_LIST_INLINE bool
+gl_list_remove_at (gl_list_t list, size_t position)
+{
+ return ((const struct gl_list_impl_base *) list)->vtable
+ ->remove_at (list, position);
+}
+
+GL_LIST_INLINE bool
+gl_list_remove (gl_list_t list, const void *elt)
+{
+ return ((const struct gl_list_impl_base *) list)->vtable
+ ->remove_elt (list, elt);
+}
+
+GL_LIST_INLINE void
+gl_list_free (gl_list_t list)
+{
+ ((const struct gl_list_impl_base *) list)->vtable->list_free (list);
+}
+
+GL_LIST_INLINE gl_list_iterator_t
+gl_list_iterator (gl_list_t list)
+{
+ return ((const struct gl_list_impl_base *) list)->vtable
+ ->iterator (list);
+}
+
+GL_LIST_INLINE gl_list_iterator_t
+gl_list_iterator_from_to (gl_list_t list, size_t start_index, size_t end_index)
+{
+ return ((const struct gl_list_impl_base *) list)->vtable
+ ->iterator_from_to (list, start_index, end_index);
+}
+
+GL_LIST_INLINE bool
+gl_list_iterator_next (gl_list_iterator_t *iterator,
+ const void **eltp, gl_list_node_t *nodep)
+{
+ return iterator->vtable->iterator_next (iterator, eltp, nodep);
+}
+
+GL_LIST_INLINE void
+gl_list_iterator_free (gl_list_iterator_t *iterator)
+{
+ iterator->vtable->iterator_free (iterator);
+}
+
+GL_LIST_INLINE gl_list_node_t
+gl_sortedlist_search (gl_list_t list, gl_listelement_compar_fn compar, const void *elt)
+{
+ return ((const struct gl_list_impl_base *) list)->vtable
+ ->sortedlist_search (list, compar, elt);
+}
+
+GL_LIST_INLINE gl_list_node_t
+gl_sortedlist_search_from_to (gl_list_t list, gl_listelement_compar_fn compar, size_t start_index, size_t end_index, const void *elt)
+{
+ return ((const struct gl_list_impl_base *) list)->vtable
+ ->sortedlist_search_from_to (list, compar, start_index, end_index,
+ elt);
+}
+
+GL_LIST_INLINE size_t
+gl_sortedlist_indexof (gl_list_t list, gl_listelement_compar_fn compar, const void *elt)
+{
+ return ((const struct gl_list_impl_base *) list)->vtable
+ ->sortedlist_indexof (list, compar, elt);
+}
+
+GL_LIST_INLINE size_t
+gl_sortedlist_indexof_from_to (gl_list_t list, gl_listelement_compar_fn compar, size_t start_index, size_t end_index, const void *elt)
+{
+ return ((const struct gl_list_impl_base *) list)->vtable
+ ->sortedlist_indexof_from_to (list, compar, start_index, end_index,
+ elt);
+}
+
+GL_LIST_INLINE gl_list_node_t
+#if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4)
+ __attribute__ ((__warn_unused_result__))
+#endif
+gl_sortedlist_nx_add (gl_list_t list, gl_listelement_compar_fn compar, const void *elt)
+{
+ return ((const struct gl_list_impl_base *) list)->vtable
+ ->sortedlist_nx_add (list, compar, elt);
+}
+
+GL_LIST_INLINE bool
+gl_sortedlist_remove (gl_list_t list, gl_listelement_compar_fn compar, const void *elt)
+{
+ return ((const struct gl_list_impl_base *) list)->vtable
+ ->sortedlist_remove (list, compar, elt);
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+_GL_INLINE_HEADER_END
+
+#endif /* _GL_LIST_H */
diff --git a/draft/mcastseed/lib/gl_rbtree_list.c b/draft/mcastseed/lib/gl_rbtree_list.c
new file mode 100644
index 0000000..5d621f1
--- /dev/null
+++ b/draft/mcastseed/lib/gl_rbtree_list.c
@@ -0,0 +1,102 @@
+/* Sequential list data type implemented by a binary tree.
+ Copyright (C) 2006, 2008-2016 Free Software Foundation, Inc.
+ Written by Bruno Haible <bruno@clisp.org>, 2006.
+
+ 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; either version 3 of the License, or
+ (at your option) any later version.
+
+ 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, see <http://www.gnu.org/licenses/>. */
+
+#include <config.h>
+
+/* Specification. */
+#include "gl_rbtree_list.h"
+
+#include <stdlib.h>
+
+/* -------------------------- gl_list_t Data Type -------------------------- */
+
+/* Generic red-black tree code. */
+#include "gl_anyrbtree_list1.h"
+
+/* Generic binary tree code. */
+#include "gl_anytree_list1.h"
+
+/* Generic red-black tree code. */
+#include "gl_anyrbtree_list2.h"
+
+/* Generic binary tree code. */
+#include "gl_anytree_list2.h"
+
+/* For debugging. */
+static unsigned int
+check_invariants (gl_list_node_t node, gl_list_node_t parent)
+{
+ unsigned int left_blackheight =
+ (node->left != NULL ? check_invariants (node->left, node) : 0);
+ unsigned int right_blackheight =
+ (node->right != NULL ? check_invariants (node->right, node) : 0);
+
+ if (!(node->parent == parent))
+ abort ();
+ if (!(node->branch_size
+ == (node->left != NULL ? node->left->branch_size : 0)
+ + 1 + (node->right != NULL ? node->right->branch_size : 0)))
+ abort ();
+ if (!(node->color == BLACK || node->color == RED))
+ abort ();
+ if (parent == NULL && !(node->color == BLACK))
+ abort ();
+ if (!(left_blackheight == right_blackheight))
+ abort ();
+
+ return left_blackheight + (node->color == BLACK ? 1 : 0);
+}
+void
+gl_rbtree_list_check_invariants (gl_list_t list)
+{
+ if (list->root != NULL)
+ check_invariants (list->root, NULL);
+}
+
+const struct gl_list_implementation gl_rbtree_list_implementation =
+ {
+ gl_tree_nx_create_empty,
+ gl_tree_nx_create,
+ gl_tree_size,
+ gl_tree_node_value,
+ gl_tree_node_nx_set_value,
+ gl_tree_next_node,
+ gl_tree_previous_node,
+ gl_tree_get_at,
+ gl_tree_nx_set_at,
+ gl_tree_search_from_to,
+ gl_tree_indexof_from_to,
+ gl_tree_nx_add_first,
+ gl_tree_nx_add_last,
+ gl_tree_nx_add_before,
+ gl_tree_nx_add_after,
+ gl_tree_nx_add_at,
+ gl_tree_remove_node,
+ gl_tree_remove_at,
+ gl_tree_remove,
+ gl_tree_list_free,
+ gl_tree_iterator,
+ gl_tree_iterator_from_to,
+ gl_tree_iterator_next,
+ gl_tree_iterator_free,
+ gl_tree_sortedlist_search,
+ gl_tree_sortedlist_search_from_to,
+ gl_tree_sortedlist_indexof,
+ gl_tree_sortedlist_indexof_from_to,
+ gl_tree_sortedlist_nx_add,
+ gl_tree_sortedlist_remove
+ };
diff --git a/draft/mcastseed/lib/gl_rbtree_list.h b/draft/mcastseed/lib/gl_rbtree_list.h
new file mode 100644
index 0000000..8cb9d11
--- /dev/null
+++ b/draft/mcastseed/lib/gl_rbtree_list.h
@@ -0,0 +1,34 @@
+/* Sequential list data type implemented by a binary tree.
+ Copyright (C) 2006, 2009-2016 Free Software Foundation, Inc.
+ Written by Bruno Haible <bruno@clisp.org>, 2006.
+
+ 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; either version 3 of the License, or
+ (at your option) any later version.
+
+ 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, see <http://www.gnu.org/licenses/>. */
+
+#ifndef _GL_RBTREE_LIST_H
+#define _GL_RBTREE_LIST_H
+
+#include "gl_list.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern const struct gl_list_implementation gl_rbtree_list_implementation;
+#define GL_RBTREE_LIST &gl_rbtree_list_implementation
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _GL_RBTREE_LIST_H */
diff --git a/draft/mcastseed/lib/stdbool.in.h b/draft/mcastseed/lib/stdbool.in.h
new file mode 100644
index 0000000..7ecf203
--- /dev/null
+++ b/draft/mcastseed/lib/stdbool.in.h
@@ -0,0 +1,132 @@
+/* Copyright (C) 2001-2003, 2006-2016 Free Software Foundation, Inc.
+ Written by Bruno Haible <haible@clisp.cons.org>, 2001.
+
+ 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; either version 3, or (at your option)
+ any later version.
+
+ 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, see <http://www.gnu.org/licenses/>. */
+
+#ifndef _GL_STDBOOL_H
+#define _GL_STDBOOL_H
+
+/* ISO C 99 <stdbool.h> for platforms that lack it. */
+
+/* Usage suggestions:
+
+ Programs that use <stdbool.h> should be aware of some limitations
+ and standards compliance issues.
+
+ Standards compliance:
+
+ - <stdbool.h> must be #included before 'bool', 'false', 'true'
+ can be used.
+
+ - You cannot assume that sizeof (bool) == 1.
+
+ - Programs should not undefine the macros bool, true, and false,
+ as C99 lists that as an "obsolescent feature".
+
+ Limitations of this substitute, when used in a C89 environment:
+
+ - <stdbool.h> must be #included before the '_Bool' type can be used.
+
+ - You cannot assume that _Bool is a typedef; it might be a macro.
+
+ - Bit-fields of type 'bool' are not supported. Portable code
+ should use 'unsigned int foo : 1;' rather than 'bool foo : 1;'.
+
+ - In C99, casts and automatic conversions to '_Bool' or 'bool' are
+ performed in such a way that every nonzero value gets converted
+ to 'true', and zero gets converted to 'false'. This doesn't work
+ with this substitute. With this substitute, only the values 0 and 1
+ give the expected result when converted to _Bool' or 'bool'.
+
+ - C99 allows the use of (_Bool)0.0 in constant expressions, but
+ this substitute cannot always provide this property.
+
+ Also, it is suggested that programs use 'bool' rather than '_Bool';
+ this isn't required, but 'bool' is more common. */
+
+
+/* 7.16. Boolean type and values */
+
+/* BeOS <sys/socket.h> already #defines false 0, true 1. We use the same
+ definitions below, but temporarily we have to #undef them. */
+#if defined __BEOS__ && !defined __HAIKU__
+# include <OS.h> /* defines bool but not _Bool */
+# undef false
+# undef true
+#endif
+
+#ifdef __cplusplus
+# define _Bool bool
+# define bool bool
+#else
+# if defined __BEOS__ && !defined __HAIKU__
+ /* A compiler known to have 'bool'. */
+ /* If the compiler already has both 'bool' and '_Bool', we can assume they
+ are the same types. */
+# if !@HAVE__BOOL@
+typedef bool _Bool;
+# endif
+# else
+# if !defined __GNUC__
+ /* If @HAVE__BOOL@:
+ Some HP-UX cc and AIX IBM C compiler versions have compiler bugs when
+ the built-in _Bool type is used. See
+ http://gcc.gnu.org/ml/gcc-patches/2003-12/msg02303.html
+ http://lists.gnu.org/archive/html/bug-coreutils/2005-11/msg00161.html
+ http://lists.gnu.org/archive/html/bug-coreutils/2005-10/msg00086.html
+ Similar bugs are likely with other compilers as well; this file
+ wouldn't be used if <stdbool.h> was working.
+ So we override the _Bool type.
+ If !@HAVE__BOOL@:
+ Need to define _Bool ourselves. As 'signed char' or as an enum type?
+ Use of a typedef, with SunPRO C, leads to a stupid
+ "warning: _Bool is a keyword in ISO C99".
+ Use of an enum type, with IRIX cc, leads to a stupid
+ "warning(1185): enumerated type mixed with another type".
+ Even the existence of an enum type, without a typedef,
+ "Invalid enumerator. (badenum)" with HP-UX cc on Tru64.
+ The only benefit of the enum, debuggability, is not important
+ with these compilers. So use 'signed char' and no enum. */
+# define _Bool signed char
+# else
+ /* With this compiler, trust the _Bool type if the compiler has it. */
+# if !@HAVE__BOOL@
+ /* For the sake of symbolic names in gdb, define true and false as
+ enum constants, not only as macros.
+ It is tempting to write
+ typedef enum { false = 0, true = 1 } _Bool;
+ so that gdb prints values of type 'bool' symbolically. But then
+ values of type '_Bool' might promote to 'int' or 'unsigned int'
+ (see ISO C 99 6.7.2.2.(4)); however, '_Bool' must promote to 'int'
+ (see ISO C 99 6.3.1.1.(2)). So add a negative value to the
+ enum; this ensures that '_Bool' promotes to 'int'. */
+typedef enum { _Bool_must_promote_to_int = -1, false = 0, true = 1 } _Bool;
+# endif
+# endif
+# endif
+# define bool _Bool
+#endif
+
+/* The other macros must be usable in preprocessor directives. */
+#ifdef __cplusplus
+# define false false
+# define true true
+#else
+# define false 0
+# define true 1
+#endif
+
+#define __bool_true_false_are_defined 1
+
+#endif /* _GL_STDBOOL_H */
diff --git a/draft/mcastseed/m4/00gnulib.m4 b/draft/mcastseed/m4/00gnulib.m4
new file mode 100644
index 0000000..bb37e32
--- /dev/null
+++ b/draft/mcastseed/m4/00gnulib.m4
@@ -0,0 +1,46 @@
+# 00gnulib.m4 serial 3
+dnl Copyright (C) 2009-2016 Free Software Foundation, Inc.
+dnl This file is free software; the Free Software Foundation
+dnl gives unlimited permission to copy and/or distribute it,
+dnl with or without modifications, as long as this notice is preserved.
+
+dnl This file must be named something that sorts before all other
+dnl gnulib-provided .m4 files. It is needed until such time as we can
+dnl assume Autoconf 2.64, with its improved AC_DEFUN_ONCE and
+dnl m4_divert semantics.
+
+# Until autoconf 2.63, handling of the diversion stack required m4_init
+# to be called first; but this does not happen with aclocal. Wrapping
+# the entire execution in another layer of the diversion stack fixes this.
+# Worse, prior to autoconf 2.62, m4_wrap depended on the underlying m4
+# for whether it was FIFO or LIFO; in order to properly balance with
+# m4_init, we need to undo our push just before anything wrapped within
+# the m4_init body. The way to ensure this is to wrap both sides of
+# m4_init with a one-shot macro that does the pop at the right time.
+m4_ifndef([_m4_divert_diversion],
+[m4_divert_push([KILL])
+m4_define([gl_divert_fixup], [m4_divert_pop()m4_define([$0])])
+m4_define([m4_init],
+ [gl_divert_fixup()]m4_defn([m4_init])[gl_divert_fixup()])])
+
+
+# AC_DEFUN_ONCE([NAME], VALUE)
+# ----------------------------
+# Define NAME to expand to VALUE on the first use (whether by direct
+# expansion, or by AC_REQUIRE), and to nothing on all subsequent uses.
+# Avoid bugs in AC_REQUIRE in Autoconf 2.63 and earlier. This
+# definition is slower than the version in Autoconf 2.64, because it
+# can only use interfaces that existed since 2.59; but it achieves the
+# same effect. Quoting is necessary to avoid confusing Automake.
+m4_version_prereq([2.63.263], [],
+[m4_define([AC][_DEFUN_ONCE],
+ [AC][_DEFUN([$1],
+ [AC_REQUIRE([_gl_DEFUN_ONCE([$1])],
+ [m4_indir([_gl_DEFUN_ONCE([$1])])])])]dnl
+[AC][_DEFUN([_gl_DEFUN_ONCE([$1])], [$2])])])
+
+# gl_00GNULIB
+# -----------
+# Witness macro that this file has been included. Needed to force
+# Automake to include this file prior to all other gnulib .m4 files.
+AC_DEFUN([gl_00GNULIB])
diff --git a/draft/mcastseed/m4/extern-inline.m4 b/draft/mcastseed/m4/extern-inline.m4
new file mode 100644
index 0000000..1e578f3
--- /dev/null
+++ b/draft/mcastseed/m4/extern-inline.m4
@@ -0,0 +1,102 @@
+dnl 'extern inline' a la ISO C99.
+
+dnl Copyright 2012-2016 Free Software Foundation, Inc.
+dnl This file is free software; the Free Software Foundation
+dnl gives unlimited permission to copy and/or distribute it,
+dnl with or without modifications, as long as this notice is preserved.
+
+AC_DEFUN([gl_EXTERN_INLINE],
+[
+ AH_VERBATIM([extern_inline],
+[/* Please see the Gnulib manual for how to use these macros.
+
+ Suppress extern inline with HP-UX cc, as it appears to be broken; see
+ <http://lists.gnu.org/archive/html/bug-texinfo/2013-02/msg00030.html>.
+
+ Suppress extern inline with Sun C in standards-conformance mode, as it
+ mishandles inline functions that call each other. E.g., for 'inline void f
+ (void) { } inline void g (void) { f (); }', c99 incorrectly complains
+ 'reference to static identifier "f" in extern inline function'.
+ This bug was observed with Sun C 5.12 SunOS_i386 2011/11/16.
+
+ Suppress extern inline (with or without __attribute__ ((__gnu_inline__)))
+ on configurations that mistakenly use 'static inline' to implement
+ functions or macros in standard C headers like <ctype.h>. For example,
+ if isdigit is mistakenly implemented via a static inline function,
+ a program containing an extern inline function that calls isdigit
+ may not work since the C standard prohibits extern inline functions
+ from calling static functions. This bug is known to occur on:
+
+ OS X 10.8 and earlier; see:
+ http://lists.gnu.org/archive/html/bug-gnulib/2012-12/msg00023.html
+
+ DragonFly; see
+ http://muscles.dragonflybsd.org/bulk/bleeding-edge-potential/latest-per-pkg/ah-tty-0.3.12.log
+
+ FreeBSD; see:
+ http://lists.gnu.org/archive/html/bug-gnulib/2014-07/msg00104.html
+
+ OS X 10.9 has a macro __header_inline indicating the bug is fixed for C and
+ for clang but remains for g++; see <http://trac.macports.org/ticket/41033>.
+ Assume DragonFly and FreeBSD will be similar. */
+#if (((defined __APPLE__ && defined __MACH__) \
+ || defined __DragonFly__ || defined __FreeBSD__) \
+ && (defined __header_inline \
+ ? (defined __cplusplus && defined __GNUC_STDC_INLINE__ \
+ && ! defined __clang__) \
+ : ((! defined _DONT_USE_CTYPE_INLINE_ \
+ && (defined __GNUC__ || defined __cplusplus)) \
+ || (defined _FORTIFY_SOURCE && 0 < _FORTIFY_SOURCE \
+ && defined __GNUC__ && ! defined __cplusplus))))
+# define _GL_EXTERN_INLINE_STDHEADER_BUG
+#endif
+#if ((__GNUC__ \
+ ? defined __GNUC_STDC_INLINE__ && __GNUC_STDC_INLINE__ \
+ : (199901L <= __STDC_VERSION__ \
+ && !defined __HP_cc \
+ && !defined __PGI \
+ && !(defined __SUNPRO_C && __STDC__))) \
+ && !defined _GL_EXTERN_INLINE_STDHEADER_BUG)
+# define _GL_INLINE inline
+# define _GL_EXTERN_INLINE extern inline
+# define _GL_EXTERN_INLINE_IN_USE
+#elif (2 < __GNUC__ + (7 <= __GNUC_MINOR__) && !defined __STRICT_ANSI__ \
+ && !defined _GL_EXTERN_INLINE_STDHEADER_BUG)
+# if defined __GNUC_GNU_INLINE__ && __GNUC_GNU_INLINE__
+ /* __gnu_inline__ suppresses a GCC 4.2 diagnostic. */
+# define _GL_INLINE extern inline __attribute__ ((__gnu_inline__))
+# else
+# define _GL_INLINE extern inline
+# endif
+# define _GL_EXTERN_INLINE extern
+# define _GL_EXTERN_INLINE_IN_USE
+#else
+# define _GL_INLINE static _GL_UNUSED
+# define _GL_EXTERN_INLINE static _GL_UNUSED
+#endif
+
+/* In GCC 4.6 (inclusive) to 5.1 (exclusive),
+ suppress bogus "no previous prototype for 'FOO'"
+ and "no previous declaration for 'FOO'" diagnostics,
+ when FOO is an inline function in the header; see
+ <https://gcc.gnu.org/bugzilla/show_bug.cgi?id=54113> and
+ <https://gcc.gnu.org/bugzilla/show_bug.cgi?id=63877>. */
+#if __GNUC__ == 4 && 6 <= __GNUC_MINOR__
+# if defined __GNUC_STDC_INLINE__ && __GNUC_STDC_INLINE__
+# define _GL_INLINE_HEADER_CONST_PRAGMA
+# else
+# define _GL_INLINE_HEADER_CONST_PRAGMA \
+ _Pragma ("GCC diagnostic ignored \"-Wsuggest-attribute=const\"")
+# endif
+# define _GL_INLINE_HEADER_BEGIN \
+ _Pragma ("GCC diagnostic push") \
+ _Pragma ("GCC diagnostic ignored \"-Wmissing-prototypes\"") \
+ _Pragma ("GCC diagnostic ignored \"-Wmissing-declarations\"") \
+ _GL_INLINE_HEADER_CONST_PRAGMA
+# define _GL_INLINE_HEADER_END \
+ _Pragma ("GCC diagnostic pop")
+#else
+# define _GL_INLINE_HEADER_BEGIN
+# define _GL_INLINE_HEADER_END
+#endif])
+])
diff --git a/draft/mcastseed/m4/gnulib-cache.m4 b/draft/mcastseed/m4/gnulib-cache.m4
new file mode 100644
index 0000000..d75ad4f
--- /dev/null
+++ b/draft/mcastseed/m4/gnulib-cache.m4
@@ -0,0 +1,47 @@
+# Copyright (C) 2002-2016 Free Software Foundation, Inc.
+#
+# This file 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; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This file 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 file. If not, see <http://www.gnu.org/licenses/>.
+#
+# As a special exception to the GNU General Public License,
+# this file may be distributed as part of a program that
+# contains a configuration script generated by Autoconf, under
+# the same distribution terms as the rest of that program.
+#
+# Generated by gnulib-tool.
+#
+# This file represents the specification of how gnulib-tool is used.
+# It acts as a cache: It is written and read by gnulib-tool.
+# In projects that use version control, this file is meant to be put under
+# version control, like the configure.ac and various Makefile.am files.
+
+
+# Specification in the form of a command-line invocation:
+# gnulib-tool --import --lib=libgnu --source-base=lib --m4-base=m4 --doc-base=doc --tests-base=tests --aux-dir=. --no-conditional-dependencies --no-libtool --macro-prefix=gl rbtree-list
+
+# Specification in the form of a few gnulib-tool.m4 macro invocations:
+gl_LOCAL_DIR([])
+gl_MODULES([
+ rbtree-list
+])
+gl_AVOID([])
+gl_SOURCE_BASE([lib])
+gl_M4_BASE([m4])
+gl_PO_BASE([])
+gl_DOC_BASE([doc])
+gl_TESTS_BASE([tests])
+gl_LIB([libgnu])
+gl_MAKEFILE_NAME([])
+gl_MACRO_PREFIX([gl])
+gl_PO_DOMAIN([])
+gl_WITNESS_C_MACRO([])
diff --git a/draft/mcastseed/m4/gnulib-common.m4 b/draft/mcastseed/m4/gnulib-common.m4
new file mode 100644
index 0000000..f8454c8
--- /dev/null
+++ b/draft/mcastseed/m4/gnulib-common.m4
@@ -0,0 +1,462 @@
+# gnulib-common.m4 serial 36
+dnl Copyright (C) 2007-2016 Free Software Foundation, Inc.
+dnl This file is free software; the Free Software Foundation
+dnl gives unlimited permission to copy and/or distribute it,
+dnl with or without modifications, as long as this notice is preserved.
+
+# gl_COMMON
+# is expanded unconditionally through gnulib-tool magic.
+AC_DEFUN([gl_COMMON], [
+ dnl Use AC_REQUIRE here, so that the code is expanded once only.
+ AC_REQUIRE([gl_00GNULIB])
+ AC_REQUIRE([gl_COMMON_BODY])
+])
+AC_DEFUN([gl_COMMON_BODY], [
+ AH_VERBATIM([_Noreturn],
+[/* The _Noreturn keyword of C11. */
+#if ! (defined _Noreturn \
+ || (defined __STDC_VERSION__ && 201112 <= __STDC_VERSION__))
+# if (3 <= __GNUC__ || (__GNUC__ == 2 && 8 <= __GNUC_MINOR__) \
+ || 0x5110 <= __SUNPRO_C)
+# define _Noreturn __attribute__ ((__noreturn__))
+# elif defined _MSC_VER && 1200 <= _MSC_VER
+# define _Noreturn __declspec (noreturn)
+# else
+# define _Noreturn
+# endif
+#endif
+])
+ AH_VERBATIM([isoc99_inline],
+[/* Work around a bug in Apple GCC 4.0.1 build 5465: In C99 mode, it supports
+ the ISO C 99 semantics of 'extern inline' (unlike the GNU C semantics of
+ earlier versions), but does not display it by setting __GNUC_STDC_INLINE__.
+ __APPLE__ && __MACH__ test for Mac OS X.
+ __APPLE_CC__ tests for the Apple compiler and its version.
+ __STDC_VERSION__ tests for the C99 mode. */
+#if defined __APPLE__ && defined __MACH__ && __APPLE_CC__ >= 5465 && !defined __cplusplus && __STDC_VERSION__ >= 199901L && !defined __GNUC_STDC_INLINE__
+# define __GNUC_STDC_INLINE__ 1
+#endif])
+ AH_VERBATIM([unused_parameter],
+[/* Define as a marker that can be attached to declarations that might not
+ be used. This helps to reduce warnings, such as from
+ GCC -Wunused-parameter. */
+#if __GNUC__ >= 3 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 7)
+# define _GL_UNUSED __attribute__ ((__unused__))
+#else
+# define _GL_UNUSED
+#endif
+/* The name _UNUSED_PARAMETER_ is an earlier spelling, although the name
+ is a misnomer outside of parameter lists. */
+#define _UNUSED_PARAMETER_ _GL_UNUSED
+
+/* gcc supports the "unused" attribute on possibly unused labels, and
+ g++ has since version 4.5. Note to support C++ as well as C,
+ _GL_UNUSED_LABEL should be used with a trailing ; */
+#if !defined __cplusplus || __GNUC__ > 4 \
+ || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5)
+# define _GL_UNUSED_LABEL _GL_UNUSED
+#else
+# define _GL_UNUSED_LABEL
+#endif
+
+/* The __pure__ attribute was added in gcc 2.96. */
+#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 96)
+# define _GL_ATTRIBUTE_PURE __attribute__ ((__pure__))
+#else
+# define _GL_ATTRIBUTE_PURE /* empty */
+#endif
+
+/* The __const__ attribute was added in gcc 2.95. */
+#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 95)
+# define _GL_ATTRIBUTE_CONST __attribute__ ((__const__))
+#else
+# define _GL_ATTRIBUTE_CONST /* empty */
+#endif
+])
+ dnl Preparation for running test programs:
+ dnl Tell glibc to write diagnostics from -D_FORTIFY_SOURCE=2 to stderr, not
+ dnl to /dev/tty, so they can be redirected to log files. Such diagnostics
+ dnl arise e.g., in the macros gl_PRINTF_DIRECTIVE_N, gl_SNPRINTF_DIRECTIVE_N.
+ LIBC_FATAL_STDERR_=1
+ export LIBC_FATAL_STDERR_
+])
+
+# gl_MODULE_INDICATOR_CONDITION
+# expands to a C preprocessor expression that evaluates to 1 or 0, depending
+# whether a gnulib module that has been requested shall be considered present
+# or not.
+m4_define([gl_MODULE_INDICATOR_CONDITION], [1])
+
+# gl_MODULE_INDICATOR_SET_VARIABLE([modulename])
+# sets the shell variable that indicates the presence of the given module to
+# a C preprocessor expression that will evaluate to 1.
+AC_DEFUN([gl_MODULE_INDICATOR_SET_VARIABLE],
+[
+ gl_MODULE_INDICATOR_SET_VARIABLE_AUX(
+ [GNULIB_[]m4_translit([[$1]],
+ [abcdefghijklmnopqrstuvwxyz./-],
+ [ABCDEFGHIJKLMNOPQRSTUVWXYZ___])],
+ [gl_MODULE_INDICATOR_CONDITION])
+])
+
+# gl_MODULE_INDICATOR_SET_VARIABLE_AUX([variable])
+# modifies the shell variable to include the gl_MODULE_INDICATOR_CONDITION.
+# The shell variable's value is a C preprocessor expression that evaluates
+# to 0 or 1.
+AC_DEFUN([gl_MODULE_INDICATOR_SET_VARIABLE_AUX],
+[
+ m4_if(m4_defn([gl_MODULE_INDICATOR_CONDITION]), [1],
+ [
+ dnl Simplify the expression VALUE || 1 to 1.
+ $1=1
+ ],
+ [gl_MODULE_INDICATOR_SET_VARIABLE_AUX_OR([$1],
+ [gl_MODULE_INDICATOR_CONDITION])])
+])
+
+# gl_MODULE_INDICATOR_SET_VARIABLE_AUX_OR([variable], [condition])
+# modifies the shell variable to include the given condition. The shell
+# variable's value is a C preprocessor expression that evaluates to 0 or 1.
+AC_DEFUN([gl_MODULE_INDICATOR_SET_VARIABLE_AUX_OR],
+[
+ dnl Simplify the expression 1 || CONDITION to 1.
+ if test "$[]$1" != 1; then
+ dnl Simplify the expression 0 || CONDITION to CONDITION.
+ if test "$[]$1" = 0; then
+ $1=$2
+ else
+ $1="($[]$1 || $2)"
+ fi
+ fi
+])
+
+# gl_MODULE_INDICATOR([modulename])
+# defines a C macro indicating the presence of the given module
+# in a location where it can be used.
+# | Value | Value |
+# | in lib/ | in tests/ |
+# --------------------------------------------+---------+-----------+
+# Module present among main modules: | 1 | 1 |
+# --------------------------------------------+---------+-----------+
+# Module present among tests-related modules: | 0 | 1 |
+# --------------------------------------------+---------+-----------+
+# Module not present at all: | 0 | 0 |
+# --------------------------------------------+---------+-----------+
+AC_DEFUN([gl_MODULE_INDICATOR],
+[
+ AC_DEFINE_UNQUOTED([GNULIB_]m4_translit([[$1]],
+ [abcdefghijklmnopqrstuvwxyz./-],
+ [ABCDEFGHIJKLMNOPQRSTUVWXYZ___]),
+ [gl_MODULE_INDICATOR_CONDITION],
+ [Define to a C preprocessor expression that evaluates to 1 or 0,
+ depending whether the gnulib module $1 shall be considered present.])
+])
+
+# gl_MODULE_INDICATOR_FOR_TESTS([modulename])
+# defines a C macro indicating the presence of the given module
+# in lib or tests. This is useful to determine whether the module
+# should be tested.
+# | Value | Value |
+# | in lib/ | in tests/ |
+# --------------------------------------------+---------+-----------+
+# Module present among main modules: | 1 | 1 |
+# --------------------------------------------+---------+-----------+
+# Module present among tests-related modules: | 1 | 1 |
+# --------------------------------------------+---------+-----------+
+# Module not present at all: | 0 | 0 |
+# --------------------------------------------+---------+-----------+
+AC_DEFUN([gl_MODULE_INDICATOR_FOR_TESTS],
+[
+ AC_DEFINE([GNULIB_TEST_]m4_translit([[$1]],
+ [abcdefghijklmnopqrstuvwxyz./-],
+ [ABCDEFGHIJKLMNOPQRSTUVWXYZ___]), [1],
+ [Define to 1 when the gnulib module $1 should be tested.])
+])
+
+# gl_ASSERT_NO_GNULIB_POSIXCHECK
+# asserts that there will never be a need to #define GNULIB_POSIXCHECK.
+# and thereby enables an optimization of configure and config.h.
+# Used by Emacs.
+AC_DEFUN([gl_ASSERT_NO_GNULIB_POSIXCHECK],
+[
+ dnl Override gl_WARN_ON_USE_PREPARE.
+ dnl But hide this definition from 'aclocal'.
+ AC_DEFUN([gl_W][ARN_ON_USE_PREPARE], [])
+])
+
+# gl_ASSERT_NO_GNULIB_TESTS
+# asserts that there will be no gnulib tests in the scope of the configure.ac
+# and thereby enables an optimization of config.h.
+# Used by Emacs.
+AC_DEFUN([gl_ASSERT_NO_GNULIB_TESTS],
+[
+ dnl Override gl_MODULE_INDICATOR_FOR_TESTS.
+ AC_DEFUN([gl_MODULE_INDICATOR_FOR_TESTS], [])
+])
+
+# Test whether <features.h> exists.
+# Set HAVE_FEATURES_H.
+AC_DEFUN([gl_FEATURES_H],
+[
+ AC_CHECK_HEADERS_ONCE([features.h])
+ if test $ac_cv_header_features_h = yes; then
+ HAVE_FEATURES_H=1
+ else
+ HAVE_FEATURES_H=0
+ fi
+ AC_SUBST([HAVE_FEATURES_H])
+])
+
+# m4_foreach_w
+# is a backport of autoconf-2.59c's m4_foreach_w.
+# Remove this macro when we can assume autoconf >= 2.60.
+m4_ifndef([m4_foreach_w],
+ [m4_define([m4_foreach_w],
+ [m4_foreach([$1], m4_split(m4_normalize([$2]), [ ]), [$3])])])
+
+# AS_VAR_IF(VAR, VALUE, [IF-MATCH], [IF-NOT-MATCH])
+# ----------------------------------------------------
+# Backport of autoconf-2.63b's macro.
+# Remove this macro when we can assume autoconf >= 2.64.
+m4_ifndef([AS_VAR_IF],
+[m4_define([AS_VAR_IF],
+[AS_IF([test x"AS_VAR_GET([$1])" = x""$2], [$3], [$4])])])
+
+# gl_PROG_CC_C99
+# Modifies the value of the shell variable CC in an attempt to make $CC
+# understand ISO C99 source code.
+# This is like AC_PROG_CC_C99, except that
+# - AC_PROG_CC_C99 did not exist in Autoconf versions < 2.60,
+# - AC_PROG_CC_C99 does not mix well with AC_PROG_CC_STDC
+# <http://lists.gnu.org/archive/html/bug-gnulib/2011-09/msg00367.html>,
+# but many more packages use AC_PROG_CC_STDC than AC_PROG_CC_C99
+# <http://lists.gnu.org/archive/html/bug-gnulib/2011-09/msg00441.html>.
+# Remaining problems:
+# - When AC_PROG_CC_STDC is invoked twice, it adds the C99 enabling options
+# to CC twice
+# <http://lists.gnu.org/archive/html/bug-gnulib/2011-09/msg00431.html>.
+# - AC_PROG_CC_STDC is likely to change now that C11 is an ISO standard.
+AC_DEFUN([gl_PROG_CC_C99],
+[
+ dnl Change that version number to the minimum Autoconf version that supports
+ dnl mixing AC_PROG_CC_C99 calls with AC_PROG_CC_STDC calls.
+ m4_version_prereq([9.0],
+ [AC_REQUIRE([AC_PROG_CC_C99])],
+ [AC_REQUIRE([AC_PROG_CC_STDC])])
+])
+
+# gl_PROG_AR_RANLIB
+# Determines the values for AR, ARFLAGS, RANLIB that fit with the compiler.
+# The user can set the variables AR, ARFLAGS, RANLIB if he wants to override
+# the values.
+AC_DEFUN([gl_PROG_AR_RANLIB],
+[
+ dnl Minix 3 comes with two toolchains: The Amsterdam Compiler Kit compiler
+ dnl as "cc", and GCC as "gcc". They have different object file formats and
+ dnl library formats. In particular, the GNU binutils programs ar and ranlib
+ dnl produce libraries that work only with gcc, not with cc.
+ AC_REQUIRE([AC_PROG_CC])
+ AC_BEFORE([$0], [AM_PROG_AR])
+ AC_CACHE_CHECK([for Minix Amsterdam compiler], [gl_cv_c_amsterdam_compiler],
+ [
+ AC_EGREP_CPP([Amsterdam],
+ [
+#ifdef __ACK__
+Amsterdam
+#endif
+ ],
+ [gl_cv_c_amsterdam_compiler=yes],
+ [gl_cv_c_amsterdam_compiler=no])
+ ])
+
+ dnl Don't compete with AM_PROG_AR's decision about AR/ARFLAGS if we are not
+ dnl building with __ACK__.
+ if test $gl_cv_c_amsterdam_compiler = yes; then
+ if test -z "$AR"; then
+ AR='cc -c.a'
+ fi
+ if test -z "$ARFLAGS"; then
+ ARFLAGS='-o'
+ fi
+ else
+ dnl AM_PROG_AR was added in automake v1.11.2. AM_PROG_AR does not AC_SUBST
+ dnl ARFLAGS variable (it is filed into Makefile.in directly by automake
+ dnl script on-demand, if not specified by ./configure of course).
+ dnl Don't AC_REQUIRE the AM_PROG_AR otherwise the code for __ACK__ above
+ dnl will be ignored. Also, pay attention to call AM_PROG_AR in else block
+ dnl because AM_PROG_AR is written so it could re-set AR variable even for
+ dnl __ACK__. It may seem like its easier to avoid calling the macro here,
+ dnl but we need to AC_SUBST both AR/ARFLAGS (thus those must have some good
+ dnl default value and automake should usually know them).
+ m4_ifdef([AM_PROG_AR], [AM_PROG_AR], [:])
+ fi
+
+ dnl In case the code above has not helped with setting AR/ARFLAGS, use
+ dnl Automake-documented default values for AR and ARFLAGS, but prefer
+ dnl ${host}-ar over ar (useful for cross-compiling).
+ AC_CHECK_TOOL([AR], [ar], [ar])
+ if test -z "$ARFLAGS"; then
+ ARFLAGS='cr'
+ fi
+
+ AC_SUBST([AR])
+ AC_SUBST([ARFLAGS])
+ if test -z "$RANLIB"; then
+ if test $gl_cv_c_amsterdam_compiler = yes; then
+ RANLIB=':'
+ else
+ dnl Use the ranlib program if it is available.
+ AC_PROG_RANLIB
+ fi
+ fi
+ AC_SUBST([RANLIB])
+])
+
+# AC_PROG_MKDIR_P
+# is a backport of autoconf-2.60's AC_PROG_MKDIR_P, with a fix
+# for interoperability with automake-1.9.6 from autoconf-2.62.
+# Remove this macro when we can assume autoconf >= 2.62 or
+# autoconf >= 2.60 && automake >= 1.10.
+# AC_AUTOCONF_VERSION was introduced in 2.62, so use that as the witness.
+m4_ifndef([AC_AUTOCONF_VERSION],[
+m4_ifdef([AC_PROG_MKDIR_P], [
+ dnl For automake-1.9.6 && autoconf < 2.62: Ensure MKDIR_P is AC_SUBSTed.
+ m4_define([AC_PROG_MKDIR_P],
+ m4_defn([AC_PROG_MKDIR_P])[
+ AC_SUBST([MKDIR_P])])], [
+ dnl For autoconf < 2.60: Backport of AC_PROG_MKDIR_P.
+ AC_DEFUN_ONCE([AC_PROG_MKDIR_P],
+ [AC_REQUIRE([AM_PROG_MKDIR_P])dnl defined by automake
+ MKDIR_P='$(mkdir_p)'
+ AC_SUBST([MKDIR_P])])])
+])
+
+# AC_C_RESTRICT
+# This definition is copied from post-2.69 Autoconf and overrides the
+# AC_C_RESTRICT macro from autoconf 2.60..2.69. It can be removed
+# once autoconf >= 2.70 can be assumed. It's painful to check version
+# numbers, and in practice this macro is more up-to-date than Autoconf
+# is, so override Autoconf unconditionally.
+AC_DEFUN([AC_C_RESTRICT],
+[AC_CACHE_CHECK([for C/C++ restrict keyword], [ac_cv_c_restrict],
+ [ac_cv_c_restrict=no
+ # The order here caters to the fact that C++ does not require restrict.
+ for ac_kw in __restrict __restrict__ _Restrict restrict; do
+ AC_COMPILE_IFELSE(
+ [AC_LANG_PROGRAM(
+ [[typedef int *int_ptr;
+ int foo (int_ptr $ac_kw ip) { return ip[0]; }
+ int bar (int [$ac_kw]); /* Catch GCC bug 14050. */
+ int bar (int ip[$ac_kw]) { return ip[0]; }
+ ]],
+ [[int s[1];
+ int *$ac_kw t = s;
+ t[0] = 0;
+ return foo (t) + bar (t);
+ ]])],
+ [ac_cv_c_restrict=$ac_kw])
+ test "$ac_cv_c_restrict" != no && break
+ done
+ ])
+ AH_VERBATIM([restrict],
+[/* Define to the equivalent of the C99 'restrict' keyword, or to
+ nothing if this is not supported. Do not define if restrict is
+ supported directly. */
+#undef restrict
+/* Work around a bug in Sun C++: it does not support _Restrict or
+ __restrict__, even though the corresponding Sun C compiler ends up with
+ "#define restrict _Restrict" or "#define restrict __restrict__" in the
+ previous line. Perhaps some future version of Sun C++ will work with
+ restrict; if so, hopefully it defines __RESTRICT like Sun C does. */
+#if defined __SUNPRO_CC && !defined __RESTRICT
+# define _Restrict
+# define __restrict__
+#endif])
+ case $ac_cv_c_restrict in
+ restrict) ;;
+ no) AC_DEFINE([restrict], []) ;;
+ *) AC_DEFINE_UNQUOTED([restrict], [$ac_cv_c_restrict]) ;;
+ esac
+])# AC_C_RESTRICT
+
+# gl_BIGENDIAN
+# is like AC_C_BIGENDIAN, except that it can be AC_REQUIREd.
+# Note that AC_REQUIRE([AC_C_BIGENDIAN]) does not work reliably because some
+# macros invoke AC_C_BIGENDIAN with arguments.
+AC_DEFUN([gl_BIGENDIAN],
+[
+ AC_C_BIGENDIAN
+])
+
+# gl_CACHE_VAL_SILENT(cache-id, command-to-set-it)
+# is like AC_CACHE_VAL(cache-id, command-to-set-it), except that it does not
+# output a spurious "(cached)" mark in the midst of other configure output.
+# This macro should be used instead of AC_CACHE_VAL when it is not surrounded
+# by an AC_MSG_CHECKING/AC_MSG_RESULT pair.
+AC_DEFUN([gl_CACHE_VAL_SILENT],
+[
+ saved_as_echo_n="$as_echo_n"
+ as_echo_n=':'
+ AC_CACHE_VAL([$1], [$2])
+ as_echo_n="$saved_as_echo_n"
+])
+
+# AS_VAR_COPY was added in autoconf 2.63b
+m4_define_default([AS_VAR_COPY],
+[AS_LITERAL_IF([$1[]$2], [$1=$$2], [eval $1=\$$2])])
+
+# AC_PROG_SED was added in autoconf 2.59b
+m4_ifndef([AC_PROG_SED],
+[AC_DEFUN([AC_PROG_SED],
+[AC_CACHE_CHECK([for a sed that does not truncate output], ac_cv_path_SED,
+ [dnl ac_script should not contain more than 99 commands (for HP-UX sed),
+ dnl but more than about 7000 bytes, to catch a limit in Solaris 8 /usr/ucb/sed.
+ ac_script=s/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb/
+ for ac_i in 1 2 3 4 5 6 7; do
+ ac_script="$ac_script$as_nl$ac_script"
+ done
+ echo "$ac_script" 2>/dev/null | sed 99q >conftest.sed
+ AS_UNSET([ac_script])
+ if test -z "$SED"; then
+ ac_path_SED_found=false
+ _AS_PATH_WALK([], [
+ for ac_prog in sed gsed; do
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ ac_path_SED="$as_dir/$ac_prog$ac_exec_ext"
+ AS_EXECUTABLE_P(["$ac_path_SED"]) || continue
+ case `"$ac_path_SED" --version 2>&1` in
+ *GNU*) ac_cv_path_SED=$ac_path_SED ac_path_SED_found=:;;
+ *)
+ ac_count=0
+ _AS_ECHO_N([0123456789]) >conftest.in
+ while :
+ do
+ cat conftest.in conftest.in >conftest.tmp
+ mv conftest.tmp conftest.in
+ cp conftest.in conftest.nl
+ echo >> conftest.nl
+ "$ac_path_SED" -f conftest.sed <conftest.nl >conftest.out 2>/dev/null || break
+ diff conftest.out conftest.nl >/dev/null 2>&1 || break
+ ac_count=`expr $ac_count + 1`
+ if test $ac_count -gt ${ac_path_SED_max-0}; then
+ # Best so far, but keep looking for better
+ ac_cv_path_SED=$ac_path_SED
+ ac_path_SED_max=$ac_count
+ fi
+ test $ac_count -gt 10 && break
+ done
+ rm -f conftest.in conftest.tmp conftest.nl conftest.out;;
+ esac
+ $ac_path_SED_found && break 3
+ done
+ done])
+ if test -z "$ac_cv_path_SED"; then
+ AC_ERROR([no acceptable sed could be found in \$PATH])
+ fi
+ else
+ ac_cv_path_SED=$SED
+ fi
+ SED="$ac_cv_path_SED"
+ AC_SUBST([SED])dnl
+ rm -f conftest.sed
+])])])
diff --git a/draft/mcastseed/m4/gnulib-comp.m4 b/draft/mcastseed/m4/gnulib-comp.m4
new file mode 100644
index 0000000..b809eea
--- /dev/null
+++ b/draft/mcastseed/m4/gnulib-comp.m4
@@ -0,0 +1,221 @@
+# DO NOT EDIT! GENERATED AUTOMATICALLY!
+# Copyright (C) 2002-2016 Free Software Foundation, Inc.
+#
+# This file 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; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This file 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 file. If not, see <http://www.gnu.org/licenses/>.
+#
+# As a special exception to the GNU General Public License,
+# this file may be distributed as part of a program that
+# contains a configuration script generated by Autoconf, under
+# the same distribution terms as the rest of that program.
+#
+# Generated by gnulib-tool.
+#
+# This file represents the compiled summary of the specification in
+# gnulib-cache.m4. It lists the computed macro invocations that need
+# to be invoked from configure.ac.
+# In projects that use version control, this file can be treated like
+# other built files.
+
+
+# This macro should be invoked from ./configure.ac, in the section
+# "Checks for programs", right after AC_PROG_CC, and certainly before
+# any checks for libraries, header files, types and library functions.
+AC_DEFUN([gl_EARLY],
+[
+ m4_pattern_forbid([^gl_[A-Z]])dnl the gnulib macro namespace
+ m4_pattern_allow([^gl_ES$])dnl a valid locale name
+ m4_pattern_allow([^gl_LIBOBJS$])dnl a variable
+ m4_pattern_allow([^gl_LTLIBOBJS$])dnl a variable
+
+ # Pre-early section.
+ AC_REQUIRE([gl_PROG_AR_RANLIB])
+
+ # Code from module extern-inline:
+ # Code from module list:
+ # Code from module rbtree-list:
+ # Code from module stdbool:
+])
+
+# This macro should be invoked from ./configure.ac, in the section
+# "Check for header files, types and library functions".
+AC_DEFUN([gl_INIT],
+[
+ AM_CONDITIONAL([GL_COND_LIBTOOL], [false])
+ gl_cond_libtool=false
+ gl_libdeps=
+ gl_ltlibdeps=
+ gl_m4_base='m4'
+ m4_pushdef([AC_LIBOBJ], m4_defn([gl_LIBOBJ]))
+ m4_pushdef([AC_REPLACE_FUNCS], m4_defn([gl_REPLACE_FUNCS]))
+ m4_pushdef([AC_LIBSOURCES], m4_defn([gl_LIBSOURCES]))
+ m4_pushdef([gl_LIBSOURCES_LIST], [])
+ m4_pushdef([gl_LIBSOURCES_DIR], [])
+ gl_COMMON
+ gl_source_base='lib'
+ AC_REQUIRE([gl_EXTERN_INLINE])
+ AM_STDBOOL_H
+ # End of code from modules
+ m4_ifval(gl_LIBSOURCES_LIST, [
+ m4_syscmd([test ! -d ]m4_defn([gl_LIBSOURCES_DIR])[ ||
+ for gl_file in ]gl_LIBSOURCES_LIST[ ; do
+ if test ! -r ]m4_defn([gl_LIBSOURCES_DIR])[/$gl_file ; then
+ echo "missing file ]m4_defn([gl_LIBSOURCES_DIR])[/$gl_file" >&2
+ exit 1
+ fi
+ done])dnl
+ m4_if(m4_sysval, [0], [],
+ [AC_FATAL([expected source file, required through AC_LIBSOURCES, not found])])
+ ])
+ m4_popdef([gl_LIBSOURCES_DIR])
+ m4_popdef([gl_LIBSOURCES_LIST])
+ m4_popdef([AC_LIBSOURCES])
+ m4_popdef([AC_REPLACE_FUNCS])
+ m4_popdef([AC_LIBOBJ])
+ AC_CONFIG_COMMANDS_PRE([
+ gl_libobjs=
+ gl_ltlibobjs=
+ if test -n "$gl_LIBOBJS"; then
+ # Remove the extension.
+ sed_drop_objext='s/\.o$//;s/\.obj$//'
+ for i in `for i in $gl_LIBOBJS; do echo "$i"; done | sed -e "$sed_drop_objext" | sort | uniq`; do
+ gl_libobjs="$gl_libobjs $i.$ac_objext"
+ gl_ltlibobjs="$gl_ltlibobjs $i.lo"
+ done
+ fi
+ AC_SUBST([gl_LIBOBJS], [$gl_libobjs])
+ AC_SUBST([gl_LTLIBOBJS], [$gl_ltlibobjs])
+ ])
+ gltests_libdeps=
+ gltests_ltlibdeps=
+ m4_pushdef([AC_LIBOBJ], m4_defn([gltests_LIBOBJ]))
+ m4_pushdef([AC_REPLACE_FUNCS], m4_defn([gltests_REPLACE_FUNCS]))
+ m4_pushdef([AC_LIBSOURCES], m4_defn([gltests_LIBSOURCES]))
+ m4_pushdef([gltests_LIBSOURCES_LIST], [])
+ m4_pushdef([gltests_LIBSOURCES_DIR], [])
+ gl_COMMON
+ gl_source_base='tests'
+changequote(,)dnl
+ gltests_WITNESS=IN_`echo "${PACKAGE-$PACKAGE_TARNAME}" | LC_ALL=C tr abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ | LC_ALL=C sed -e 's/[^A-Z0-9_]/_/g'`_GNULIB_TESTS
+changequote([, ])dnl
+ AC_SUBST([gltests_WITNESS])
+ gl_module_indicator_condition=$gltests_WITNESS
+ m4_pushdef([gl_MODULE_INDICATOR_CONDITION], [$gl_module_indicator_condition])
+ m4_popdef([gl_MODULE_INDICATOR_CONDITION])
+ m4_ifval(gltests_LIBSOURCES_LIST, [
+ m4_syscmd([test ! -d ]m4_defn([gltests_LIBSOURCES_DIR])[ ||
+ for gl_file in ]gltests_LIBSOURCES_LIST[ ; do
+ if test ! -r ]m4_defn([gltests_LIBSOURCES_DIR])[/$gl_file ; then
+ echo "missing file ]m4_defn([gltests_LIBSOURCES_DIR])[/$gl_file" >&2
+ exit 1
+ fi
+ done])dnl
+ m4_if(m4_sysval, [0], [],
+ [AC_FATAL([expected source file, required through AC_LIBSOURCES, not found])])
+ ])
+ m4_popdef([gltests_LIBSOURCES_DIR])
+ m4_popdef([gltests_LIBSOURCES_LIST])
+ m4_popdef([AC_LIBSOURCES])
+ m4_popdef([AC_REPLACE_FUNCS])
+ m4_popdef([AC_LIBOBJ])
+ AC_CONFIG_COMMANDS_PRE([
+ gltests_libobjs=
+ gltests_ltlibobjs=
+ if test -n "$gltests_LIBOBJS"; then
+ # Remove the extension.
+ sed_drop_objext='s/\.o$//;s/\.obj$//'
+ for i in `for i in $gltests_LIBOBJS; do echo "$i"; done | sed -e "$sed_drop_objext" | sort | uniq`; do
+ gltests_libobjs="$gltests_libobjs $i.$ac_objext"
+ gltests_ltlibobjs="$gltests_ltlibobjs $i.lo"
+ done
+ fi
+ AC_SUBST([gltests_LIBOBJS], [$gltests_libobjs])
+ AC_SUBST([gltests_LTLIBOBJS], [$gltests_ltlibobjs])
+ ])
+ LIBGNU_LIBDEPS="$gl_libdeps"
+ AC_SUBST([LIBGNU_LIBDEPS])
+ LIBGNU_LTLIBDEPS="$gl_ltlibdeps"
+ AC_SUBST([LIBGNU_LTLIBDEPS])
+])
+
+# Like AC_LIBOBJ, except that the module name goes
+# into gl_LIBOBJS instead of into LIBOBJS.
+AC_DEFUN([gl_LIBOBJ], [
+ AS_LITERAL_IF([$1], [gl_LIBSOURCES([$1.c])])dnl
+ gl_LIBOBJS="$gl_LIBOBJS $1.$ac_objext"
+])
+
+# Like AC_REPLACE_FUNCS, except that the module name goes
+# into gl_LIBOBJS instead of into LIBOBJS.
+AC_DEFUN([gl_REPLACE_FUNCS], [
+ m4_foreach_w([gl_NAME], [$1], [AC_LIBSOURCES(gl_NAME[.c])])dnl
+ AC_CHECK_FUNCS([$1], , [gl_LIBOBJ($ac_func)])
+])
+
+# Like AC_LIBSOURCES, except the directory where the source file is
+# expected is derived from the gnulib-tool parameterization,
+# and alloca is special cased (for the alloca-opt module).
+# We could also entirely rely on EXTRA_lib..._SOURCES.
+AC_DEFUN([gl_LIBSOURCES], [
+ m4_foreach([_gl_NAME], [$1], [
+ m4_if(_gl_NAME, [alloca.c], [], [
+ m4_define([gl_LIBSOURCES_DIR], [lib])
+ m4_append([gl_LIBSOURCES_LIST], _gl_NAME, [ ])
+ ])
+ ])
+])
+
+# Like AC_LIBOBJ, except that the module name goes
+# into gltests_LIBOBJS instead of into LIBOBJS.
+AC_DEFUN([gltests_LIBOBJ], [
+ AS_LITERAL_IF([$1], [gltests_LIBSOURCES([$1.c])])dnl
+ gltests_LIBOBJS="$gltests_LIBOBJS $1.$ac_objext"
+])
+
+# Like AC_REPLACE_FUNCS, except that the module name goes
+# into gltests_LIBOBJS instead of into LIBOBJS.
+AC_DEFUN([gltests_REPLACE_FUNCS], [
+ m4_foreach_w([gl_NAME], [$1], [AC_LIBSOURCES(gl_NAME[.c])])dnl
+ AC_CHECK_FUNCS([$1], , [gltests_LIBOBJ($ac_func)])
+])
+
+# Like AC_LIBSOURCES, except the directory where the source file is
+# expected is derived from the gnulib-tool parameterization,
+# and alloca is special cased (for the alloca-opt module).
+# We could also entirely rely on EXTRA_lib..._SOURCES.
+AC_DEFUN([gltests_LIBSOURCES], [
+ m4_foreach([_gl_NAME], [$1], [
+ m4_if(_gl_NAME, [alloca.c], [], [
+ m4_define([gltests_LIBSOURCES_DIR], [tests])
+ m4_append([gltests_LIBSOURCES_LIST], _gl_NAME, [ ])
+ ])
+ ])
+])
+
+# This macro records the list of files which have been installed by
+# gnulib-tool and may be removed by future gnulib-tool invocations.
+AC_DEFUN([gl_FILE_LIST], [
+ lib/gl_anyrbtree_list1.h
+ lib/gl_anyrbtree_list2.h
+ lib/gl_anytree_list1.h
+ lib/gl_anytree_list2.h
+ lib/gl_list.c
+ lib/gl_list.h
+ lib/gl_rbtree_list.c
+ lib/gl_rbtree_list.h
+ lib/stdbool.in.h
+ m4/00gnulib.m4
+ m4/extern-inline.m4
+ m4/gnulib-common.m4
+ m4/stdbool.m4
+])
diff --git a/draft/mcastseed/m4/gnulib-tool.m4 b/draft/mcastseed/m4/gnulib-tool.m4
new file mode 100644
index 0000000..0d2ee44
--- /dev/null
+++ b/draft/mcastseed/m4/gnulib-tool.m4
@@ -0,0 +1,57 @@
+# gnulib-tool.m4 serial 2
+dnl Copyright (C) 2004-2005, 2009-2016 Free Software Foundation, Inc.
+dnl This file is free software; the Free Software Foundation
+dnl gives unlimited permission to copy and/or distribute it,
+dnl with or without modifications, as long as this notice is preserved.
+
+dnl The following macros need not be invoked explicitly.
+dnl Invoking them does nothing except to declare default arguments
+dnl for "gnulib-tool --import".
+
+dnl Usage: gl_LOCAL_DIR([DIR])
+AC_DEFUN([gl_LOCAL_DIR], [])
+
+dnl Usage: gl_MODULES([module1 module2 ...])
+AC_DEFUN([gl_MODULES], [])
+
+dnl Usage: gl_AVOID([module1 module2 ...])
+AC_DEFUN([gl_AVOID], [])
+
+dnl Usage: gl_SOURCE_BASE([DIR])
+AC_DEFUN([gl_SOURCE_BASE], [])
+
+dnl Usage: gl_M4_BASE([DIR])
+AC_DEFUN([gl_M4_BASE], [])
+
+dnl Usage: gl_PO_BASE([DIR])
+AC_DEFUN([gl_PO_BASE], [])
+
+dnl Usage: gl_DOC_BASE([DIR])
+AC_DEFUN([gl_DOC_BASE], [])
+
+dnl Usage: gl_TESTS_BASE([DIR])
+AC_DEFUN([gl_TESTS_BASE], [])
+
+dnl Usage: gl_WITH_TESTS
+AC_DEFUN([gl_WITH_TESTS], [])
+
+dnl Usage: gl_LIB([LIBNAME])
+AC_DEFUN([gl_LIB], [])
+
+dnl Usage: gl_LGPL or gl_LGPL([VERSION])
+AC_DEFUN([gl_LGPL], [])
+
+dnl Usage: gl_MAKEFILE_NAME([FILENAME])
+AC_DEFUN([gl_MAKEFILE_NAME], [])
+
+dnl Usage: gl_LIBTOOL
+AC_DEFUN([gl_LIBTOOL], [])
+
+dnl Usage: gl_MACRO_PREFIX([PREFIX])
+AC_DEFUN([gl_MACRO_PREFIX], [])
+
+dnl Usage: gl_PO_DOMAIN([DOMAIN])
+AC_DEFUN([gl_PO_DOMAIN], [])
+
+dnl Usage: gl_VC_FILES([BOOLEAN])
+AC_DEFUN([gl_VC_FILES], [])
diff --git a/draft/mcastseed/m4/stdbool.m4 b/draft/mcastseed/m4/stdbool.m4
new file mode 100644
index 0000000..a556153
--- /dev/null
+++ b/draft/mcastseed/m4/stdbool.m4
@@ -0,0 +1,104 @@
+# Check for stdbool.h that conforms to C99.
+
+dnl Copyright (C) 2002-2006, 2009-2016 Free Software Foundation, Inc.
+dnl This file is free software; the Free Software Foundation
+dnl gives unlimited permission to copy and/or distribute it,
+dnl with or without modifications, as long as this notice is preserved.
+
+#serial 6
+
+# Prepare for substituting <stdbool.h> if it is not supported.
+
+AC_DEFUN([AM_STDBOOL_H],
+[
+ AC_REQUIRE([AC_CHECK_HEADER_STDBOOL])
+
+ # Define two additional variables used in the Makefile substitution.
+
+ if test "$ac_cv_header_stdbool_h" = yes; then
+ STDBOOL_H=''
+ else
+ STDBOOL_H='stdbool.h'
+ fi
+ AC_SUBST([STDBOOL_H])
+ AM_CONDITIONAL([GL_GENERATE_STDBOOL_H], [test -n "$STDBOOL_H"])
+
+ if test "$ac_cv_type__Bool" = yes; then
+ HAVE__BOOL=1
+ else
+ HAVE__BOOL=0
+ fi
+ AC_SUBST([HAVE__BOOL])
+])
+
+# AM_STDBOOL_H will be renamed to gl_STDBOOL_H in the future.
+AC_DEFUN([gl_STDBOOL_H], [AM_STDBOOL_H])
+
+# This version of the macro is needed in autoconf <= 2.68.
+
+AC_DEFUN([AC_CHECK_HEADER_STDBOOL],
+ [AC_CACHE_CHECK([for stdbool.h that conforms to C99],
+ [ac_cv_header_stdbool_h],
+ [AC_COMPILE_IFELSE(
+ [AC_LANG_PROGRAM(
+ [[
+ #include <stdbool.h>
+
+ #if __cplusplus < 201103
+ #ifndef bool
+ "error: bool is not defined"
+ #endif
+ #ifndef false
+ "error: false is not defined"
+ #endif
+ #if false
+ "error: false is not 0"
+ #endif
+ #ifndef true
+ "error: true is not defined"
+ #endif
+ #if true != 1
+ "error: true is not 1"
+ #endif
+ #endif
+
+ #ifndef __bool_true_false_are_defined
+ "error: __bool_true_false_are_defined is not defined"
+ #endif
+
+ struct s { _Bool s: 1; _Bool t; } s;
+
+ char a[true == 1 ? 1 : -1];
+ char b[false == 0 ? 1 : -1];
+ char c[__bool_true_false_are_defined == 1 ? 1 : -1];
+ char d[(bool) 0.5 == true ? 1 : -1];
+ /* See body of main program for 'e'. */
+ char f[(_Bool) 0.0 == false ? 1 : -1];
+ char g[true];
+ char h[sizeof (_Bool)];
+ char i[sizeof s.t];
+ enum { j = false, k = true, l = false * true, m = true * 256 };
+ /* The following fails for
+ HP aC++/ANSI C B3910B A.05.55 [Dec 04 2003]. */
+ _Bool n[m];
+ char o[sizeof n == m * sizeof n[0] ? 1 : -1];
+ char p[-1 - (_Bool) 0 < 0 && -1 - (bool) 0 < 0 ? 1 : -1];
+ /* Catch a bug in an HP-UX C compiler. See
+ http://gcc.gnu.org/ml/gcc-patches/2003-12/msg02303.html
+ http://lists.gnu.org/archive/html/bug-coreutils/2005-11/msg00161.html
+ */
+ _Bool q = true;
+ _Bool *pq = &q;
+ ]],
+ [[
+ bool e = &s;
+ *pq |= q;
+ *pq |= ! q;
+ /* Refer to every declared value, to avoid compiler optimizations. */
+ return (!a + !b + !c + !d + !e + !f + !g + !h + !i + !!j + !k + !!l
+ + !m + !n + !o + !p + !q + !pq);
+ ]])],
+ [ac_cv_header_stdbool_h=yes],
+ [ac_cv_header_stdbool_h=no])])
+ AC_CHECK_TYPES([_Bool])
+])
diff --git a/draft/mcastseed/src/Makefile.am b/draft/mcastseed/src/Makefile.am
new file mode 100644
index 0000000..2f2a735
--- /dev/null
+++ b/draft/mcastseed/src/Makefile.am
@@ -0,0 +1,16 @@
+## Process this file with automake to produce Makefile.in
+
+AM_CPPFLAGS = -I $(top_srcdir)/lib
+AM_CFLAGS =\
+ -Wall \
+ -Wextra \
+ -pedantic \
+ -Wno-format
+
+bin_PROGRAMS = mcastseed mcastleech random_speed_dd
+
+mcastseed_SOURCES = mcastseed.c sockets.c
+mcastleech_SOURCES = mcastleech.c sockets.c dgrambuf.c
+mcastleech_LDADD = $(top_builddir)/lib/libgnu.a
+random_speed_dd_SOURCES = random_speed_dd.c
+
diff --git a/draft/mcastseed/src/dgrambuf.c b/draft/mcastseed/src/dgrambuf.c
new file mode 100644
index 0000000..5d4c302
--- /dev/null
+++ b/draft/mcastseed/src/dgrambuf.c
@@ -0,0 +1,583 @@
+/*
+ * dgrambuf.c - C datagrams buffer.
+ *
+ * Copyright 2016 by Ludovic Pouzenc <ludovic@pouzenc.fr>
+ */
+#define _GNU_SOURCE /* See feature_test_macros(7) */
+#include "dgrambuf.h"
+
+#include "config.h"
+
+#include <sys/socket.h> /* recvmmsg() _GNU_SOURCE */
+#include <stdlib.h> /* calloc(), free() */
+#include <stdio.h> /* perror() */
+#include <errno.h> /* errno */
+#include <string.h> /* memset() */
+#include <sys/uio.h> /* writev() */
+#include <stdint.h> /* uint8_t, uint64_t */
+#include <signal.h> /* sigaction() */
+#include <unistd.h> /* alarm() */
+#include <limits.h> /* SSIZE_MAX */
+#include "gl_rbtree_list.h" /* Red-Black Tree backed Sorted list, gnulib-tool --import rbtree-list */
+
+struct indexed_uint {
+ size_t index;
+ unsigned int value;
+};
+
+struct dgrambuf_stats_t {
+ uint64_t dgrambuf_read_on_full;
+ uint64_t recvmmsg_calls, recv_dgrams, recv_byte;
+ uint64_t dgram_invalid, dgram_past, dgram_future, dgram_dup, dgram_end_marker;
+ uint64_t writev_calls, write_partial, write_byte;
+};
+
+struct dgrambuf_t {
+ /* dgram validation after receive, takes dgram len and a pointer to the start of dgram data
+ Must returns dgram seq number or 0 if invalid dgram */
+ int (*validate_func)(unsigned int, void *, unsigned int*);
+
+ struct dgrambuf_stats_t stats;
+ struct sigaction sa_sigalrm;
+
+ size_t dgram_slots;
+ size_t dgram_max_size;
+ size_t dgram_header_size;
+
+ size_t iovec_slots;
+ struct mmsghdr *msgs;
+ struct iovec *iov_recv;
+ struct iovec *iov_write; /* malloc'ed array */
+
+ struct iovec *partial_write_iov; /* Pointer to an item of iov_write[] */
+ size_t partial_write_remaining_iovcnt;
+ size_t partial_write_remaining_bytes;
+
+ unsigned int dgram_seq_last;
+ unsigned int dgram_seq_base;
+ unsigned int *dgram_len;
+
+ struct indexed_uint *dgram_slot_seq; /* malloc'ed array */
+ struct indexed_uint **dgram_read_active_slots; /* malloc'd array of pointers to items of dgram_slot_seq[] */
+ size_t dgram_read_active_slots_count;
+ struct indexed_uint **dgram_write_active_slots; /* malloc'd array of pointers to items of dgram_slot_seq[] */
+ size_t dgram_write_active_slots_count;
+
+ gl_list_t dgram_empty_slots;
+ gl_list_t dgram_used_slots;
+
+ uint8_t *buf; /* malloc-ed 2d byte array : buf[dgram_slots][dgram_max_size] */
+};
+
+void _sigalrm_handler(int signum);
+int _compare_indexed_uint(const void *pa, const void *pb);
+bool _equals_indexed_uint(const void *pa, const void *pb);
+void _update_ordered_seq_numbers(dgrambuf_t dbuf);
+
+#ifndef HAVE_MIN_SIZE_T
+size_t min_size_t(size_t a, size_t b) { return (a<b)?a:b; }
+#endif /*HAVE_MIN_SIZE_T*/
+
+void dgrambuf_set_validate_func(dgrambuf_t dbuf, int (*validate_func)(unsigned int, void *, unsigned int *)) {
+ dbuf->validate_func = validate_func;
+}
+
+size_t dgrambuf_get_free_count(const dgrambuf_t dbuf) {
+ return gl_list_size(dbuf->dgram_empty_slots);
+}
+
+size_t dgrambuf_get_used_count(const dgrambuf_t dbuf) {
+ return gl_list_size(dbuf->dgram_used_slots);
+}
+
+ssize_t dgrambuf_recvmmsg(dgrambuf_t dbuf, int sockfd, int timeout, int *info) {
+ uint8_t *dgram_base;
+ ssize_t recv_byte;
+ size_t i, dgram_index, recv_msg_count, free_count;
+ int res;
+ unsigned int seq, dgram_len;
+ struct sigaction sa_old;
+ struct indexed_uint *active_slot;
+ gl_list_node_t pos;
+
+
+ /* Info ptr is mandatory */
+ *info = 0;
+
+ /* Validate function is mandatory */
+ if ( !dbuf->validate_func ) {
+ return -3;
+ }
+
+ /* Buffer is full, can't receive */
+ free_count = dgrambuf_get_free_count(dbuf);
+ if ( free_count == 0 ) {
+ dbuf->stats.dgrambuf_read_on_full++;
+ *info |= DGRAMBUF_RECV_OVERWRITE;
+ /*FIXME : this locks everything if buf full + next seq missing*/
+ return 0;
+ }
+
+ /* Initialize recvmmsg() syscall arguments and keep track of active slots */
+ for (i=0; i < dbuf->iovec_slots && i < free_count; i++) {
+ /* Pop a free slot, ignoring const modifier from gl_list_get_at() */
+ dbuf->dgram_read_active_slots[i] = (struct indexed_uint *) gl_list_get_at(dbuf->dgram_empty_slots, 0);
+ gl_sortedlist_remove(dbuf->dgram_empty_slots, _compare_indexed_uint, dbuf->dgram_read_active_slots[i]);
+
+ dgram_index = dbuf->dgram_read_active_slots[i]->index;
+ dbuf->iov_recv[i].iov_base = dbuf->buf + dgram_index * dbuf->dgram_max_size;
+ dbuf->iov_recv[i].iov_len = dbuf->dgram_max_size;
+
+ memset(dbuf->msgs + i, 0, sizeof(struct mmsghdr));
+ dbuf->msgs[i].msg_hdr.msg_iov = dbuf->iov_recv + i;
+ dbuf->msgs[i].msg_hdr.msg_iovlen = 1;
+ }
+ dbuf->dgram_read_active_slots_count = i;
+
+ /* Do the syscall with alarm() to circumvent bad behavior in recvmmsg(2) timeout */
+ if (timeout) {
+ sigaction(SIGALRM, &(dbuf->sa_sigalrm), &sa_old);
+ alarm(timeout);
+ }
+ res = recvmmsg(sockfd, dbuf->msgs, dbuf->dgram_read_active_slots_count, MSG_WAITFORONE, NULL);
+ if (timeout) {
+ alarm(0);
+ sigaction(SIGALRM, &sa_old, NULL);
+ }
+ dbuf->stats.recvmmsg_calls++;
+
+ if (res < 0) {
+ if ( errno == EINTR ) {
+ recv_msg_count = 0;
+ *info |= DGRAMBUF_RECV_EINTR;
+ } else {
+ perror("recvmmsg()");
+ return -1;
+ }
+ } else {
+ recv_msg_count = res;
+ }
+
+ if (recv_msg_count > 0) {
+ dbuf->stats.recv_dgrams += recv_msg_count;
+ if ( recv_msg_count == dbuf->dgram_read_active_slots_count ) {
+ *info |= DGRAMBUF_RECV_IOVEC_FULL;
+ }
+ }
+
+ /* Check all received messages */
+ for (i=0, recv_byte=0; i<recv_msg_count; i++) {
+ active_slot = dbuf->dgram_read_active_slots[i];
+ dgram_base = dbuf->iov_recv[i].iov_base;
+ dgram_len = dbuf->msgs[i].msg_len;
+
+ /* dgrambuf_new() adjust iovec_len to prevent overflows on ssize_t*/
+ recv_byte += dgram_len;
+
+ res = dbuf->validate_func(dgram_len, dgram_base, &seq);
+ switch (res) {
+ case 1:
+ if ( seq < dbuf->dgram_seq_base ) {
+ fprintf(stderr, "dgrambuf_recvmmsg(): #%zu past (%u)\n", i, seq);
+ dbuf->stats.dgram_past++;
+ } else if ( seq >= dbuf->dgram_seq_base + dbuf->dgram_slots ) {
+ fprintf(stderr, "dgrambuf_recvmmsg(): #%zu future (%u)\n", i, seq);
+ dbuf->stats.dgram_future++;
+ *info |= DGRAMBUF_RECV_FUTURE_DGRAM;
+ } else {
+ active_slot->value = seq;
+ pos = gl_sortedlist_search(dbuf->dgram_used_slots, _compare_indexed_uint, active_slot);
+ if ( pos != NULL ) {
+ fprintf(stderr, "dgrambuf_recvmmsg(): #%zu duplicate (%u)\n", i, seq);
+ dbuf->stats.dgram_dup++;
+ *info |= DGRAMBUF_RECV_DUPLICATE_DGRAM;
+ } else {
+ /*fprintf(stderr, "dgrambuf_recvmmsg(): #%zu valid (%u)\n", i, seq);*/
+ pos = gl_sortedlist_nx_add(dbuf->dgram_used_slots, _compare_indexed_uint, active_slot);
+ if ( pos == NULL ) /*TODO: better oom handling */
+ return -4;
+ dbuf->dgram_len[active_slot->index] = dgram_len;
+ *info |= DGRAMBUF_RECV_VALID_DGRAM;
+ continue;
+ }
+ }
+ break;
+ case 2:
+ fprintf(stderr, "dgrambuf_recvmmsg(): #%zu finalize (%u)\n", i, seq);
+ dbuf->stats.dgram_end_marker++;
+ dbuf->dgram_seq_last = seq;
+ *info |= DGRAMBUF_RECV_FINALIZE;
+ break;
+ default:
+ fprintf(stderr, "dgrambuf_recvmmsg(): #%zu invalid\n", i);
+ dbuf->stats.dgram_invalid++;
+ break;
+ }
+ /* In all invalid dgram cases, put back active_slot in dgram_free_slots */
+ pos = gl_sortedlist_nx_add(dbuf->dgram_empty_slots, _compare_indexed_uint, active_slot);
+ if ( !pos ) /*TODO: better oom handling */
+ return -4;
+ }
+
+ /* Push remaining active slots in dgram_empty_slots */
+ for (/*next i*/; i < dbuf->dgram_read_active_slots_count; i++) {
+ active_slot = dbuf->dgram_read_active_slots[i];
+ pos = gl_sortedlist_nx_add(dbuf->dgram_empty_slots, _compare_indexed_uint, active_slot);
+ if ( !pos ) /*TODO: better oom handling */
+ return -4;
+ }
+
+ dbuf->dgram_read_active_slots_count = 0;
+ dbuf->stats.recv_byte += recv_byte;
+
+ return recv_byte;
+}
+
+int dgrambuf_have_data_ready_to_write(dgrambuf_t dbuf) {
+ unsigned int next_dgram_seq;
+
+ /* Last write was partial, so there is more to write */
+ if ( dbuf->partial_write_remaining_bytes ) {
+ return 1;
+ }
+
+ /* dgram_used_slots is empty, nothing to write */
+ if ( dgrambuf_get_used_count(dbuf) == 0 ) {
+ return 0;
+ }
+
+ /* Nothing to write if next dgram is not in buffer at all */
+ next_dgram_seq = ((struct indexed_uint *) gl_list_get_at(dbuf->dgram_used_slots, 0))->value;
+ /*fprintf(stderr, "DEBUG : dgram_seq_base==%u next_dgram_seq == %u\n", dbuf->dgram_seq_base, next_dgram_seq);*/
+ if ( next_dgram_seq != dbuf->dgram_seq_base ) {
+ return 0;
+ }
+ /* At least some data of one dgram is availble for writing out */
+ return 1;
+}
+
+int dgrambuf_have_received_everything(dgrambuf_t dbuf) {
+ /*FIXME : Really implement this */
+ return dbuf->dgram_seq_last && ( dbuf->dgram_seq_base - 1 == dbuf->dgram_seq_last );
+}
+
+ssize_t dgrambuf_write(dgrambuf_t dbuf, int fd, int *info) {
+ size_t dgram_index, i, vlen, total, len, remain, used_count;
+ unsigned int curr_seq, prev_seq, dgram_len;
+ ssize_t nwrite;
+ struct iovec *iov;
+ struct indexed_uint *active_slot;
+ bool pos;
+
+ /* FIXME Info ptr is mandatory */
+ *info = 0;
+
+ if ( dbuf->partial_write_remaining_bytes ) {
+ /* Previous writev() was partial, continue it */
+ iov = dbuf->partial_write_iov;
+ vlen = dbuf->partial_write_remaining_iovcnt;
+ total = dbuf->partial_write_remaining_bytes;
+ } else if ( ! dgrambuf_have_data_ready_to_write(dbuf) ) {
+ return 0; /* XXX Inline code ? */
+ } else {
+ /* Prepare a write batch, buffer state is in dgram_seq_numbers */
+ iov = dbuf->iov_write;
+ total = 0;
+
+ /* Initialize iovecs for writev, take dgram payloads following the sequence numbers */
+ prev_seq = 0;
+ used_count = dgrambuf_get_used_count(dbuf);
+ for (i = 0; i < dbuf->iovec_slots && i < used_count; i++) {
+ /* Pop a used slot */
+ dbuf->dgram_write_active_slots[i] = (struct indexed_uint *) gl_list_get_at(dbuf->dgram_used_slots, 0);
+ gl_sortedlist_remove(dbuf->dgram_used_slots, _compare_indexed_uint, dbuf->dgram_write_active_slots[i]);
+ dbuf->dgram_write_active_slots_count++;
+
+ curr_seq = dbuf->dgram_write_active_slots[i]->value;
+
+ /* Skip empty dgram slot */
+ if ( curr_seq == 0 ) {
+ fprintf(stderr, "Oops : found empty slot (i==%zu)\n", i);
+ continue;
+ }
+ /* Skip if current dgram is a dup of the previous */
+ if ( curr_seq == prev_seq ) {
+ fprintf(stderr, "Oops : found duplicated dgram in buffer (%u)\n", curr_seq);
+ continue;
+ }
+ /* Skip dgram comming from the past */
+ if ( curr_seq < dbuf->dgram_seq_base ) {
+ fprintf(stderr, "Oops : found dgram from past in buffer (%u)\n", curr_seq);
+ continue;
+ }
+ /* Stop if current seq dgram is missing */
+ if ( ( i > 0 ) && (curr_seq > prev_seq+1 ) ) {
+ break;
+ }
+ /* Stop if first dgram to write is not in buffer at all */
+ if ( ( i == 0 ) && (curr_seq != dbuf->dgram_seq_base) ) {
+ fprintf(stderr, "Oops : nothing to write, missing %u seq\n", dbuf->dgram_seq_base);
+ break;
+ }
+
+ /* Normal case : curr_seq is the next dgram to write */
+ dgram_index = dbuf->dgram_write_active_slots[i]->index;
+ dgram_len = dbuf->dgram_len[dgram_index] - dbuf->dgram_header_size;
+
+ /* Setup iovecs */
+ dbuf->iov_write[i].iov_len = dgram_len;
+ dbuf->iov_write[i].iov_base = dbuf->buf
+ + dgram_index*dbuf->dgram_max_size + dbuf->dgram_header_size;
+
+ /* Update counters */
+ total += dgram_len;
+ prev_seq = curr_seq;
+ dbuf->dgram_seq_base = curr_seq + 1;
+ }
+ vlen = i;
+
+ /* Nothing valid to write out (but buffer not empty, missing the next dgram) */
+ if ( vlen == 0 ) {
+ fprintf(stderr, "Oops : nothing to write at all\n");
+ return -2;
+ }
+
+ if ( vlen == dbuf->iovec_slots ) {
+ *info |= DGRAMBUF_WRITE_IOVEC_FULL;
+ }
+ }
+
+ nwrite = writev(fd, iov, vlen);
+ dbuf->stats.writev_calls++;
+ if ( nwrite < 0 ) {
+ /* Treat non fatal errors */
+ if ( errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) {
+ /* Keeps some state informations for retry */
+ dbuf->partial_write_remaining_bytes = total;
+ dbuf->partial_write_remaining_iovcnt = vlen;
+ dbuf->partial_write_iov = iov;
+ *info |= DGRAMBUF_WRITE_EWOULDBLOCK_OR_EINTR;
+ return 0;
+ }
+ /* Print fatal errors and bail out */
+ perror("writev()");
+ return -1;
+ }
+
+ dbuf->partial_write_remaining_bytes = total - nwrite;
+ if ( nwrite > 0 ) {
+ dbuf->stats.write_byte += nwrite;
+ *info |= DGRAMBUF_WRITE_SUCCESS;
+
+ if ( dbuf->partial_write_remaining_bytes ) {
+ /* If the write was partially done */
+ *info |= DGRAMBUF_WRITE_PARTIAL;
+ dbuf->stats.write_partial++;
+ /* Find the partially written iov and update it */
+ remain = nwrite;
+ for (i=0; i<vlen; i++) {
+ len = dbuf->iov_write[i].iov_len;
+ if ( remain < len ) {
+ dbuf->partial_write_remaining_iovcnt = vlen - i;
+ if ( dbuf->partial_write_iov ) {
+ dbuf->partial_write_iov += i;
+ } else {
+ dbuf->partial_write_iov = dbuf->iov_write + i;
+ }
+
+ dbuf->iov_write[i].iov_base =
+ (uint8_t *) dbuf->iov_write[i].iov_base + remain;
+ dbuf->iov_write[i].iov_len -= remain;
+ break;
+ }
+ remain -= len;
+ }
+ if ( i == vlen ) {
+ fprintf(stderr, "Fatal : failed to find partial iov after partial write\n");
+ return -3;
+ }
+
+ } else {
+ /* Full write has happened */
+ for (i=0; i<dbuf->dgram_write_active_slots_count; i++) {
+ active_slot = (struct indexed_uint *) dbuf->dgram_write_active_slots[i];
+ active_slot->value = 0;
+ pos = gl_sortedlist_nx_add(dbuf->dgram_empty_slots, _compare_indexed_uint, active_slot);
+ if ( !pos ) /*TODO: better oom handling ? */
+ return -4;
+ }
+ dbuf->dgram_write_active_slots_count = 0;
+ /* Wipe outdated partial_* values */
+ dbuf->partial_write_iov = NULL;
+ dbuf->partial_write_remaining_iovcnt = 0;
+ }
+ }
+
+ return nwrite;
+}
+
+int dgrambuf_stats(dgrambuf_t dbuf, char **allocated_string) {
+ uint64_t dgram_pending = dgrambuf_get_used_count(dbuf);
+ uint64_t dgram_missing = 0;
+ if ( dbuf->dgram_seq_last ) {
+ dgram_missing = dbuf->dgram_seq_last - (dbuf->dgram_seq_base - 1) - dgram_pending;
+ }
+
+ return asprintf(allocated_string,
+ "dgrambuf_read_on_full==%d "
+ "recvmmsg_calls==%d, recv_dgrams==%d, recv_byte==%d, "
+ "dgram_invalid==%d, dgram_past==%d, dgram_future==%d, dgram_dup==%d, dgram_end_marker==%d, "
+ "writev_calls==%d, write_partial==%d, write_byte==%d "
+ "dgram_pending==%d, dgram_missing==%d",
+ dbuf->stats.dgrambuf_read_on_full,
+ dbuf->stats.recvmmsg_calls, dbuf->stats.recv_dgrams, dbuf->stats.recv_byte,
+ dbuf->stats.dgram_invalid, dbuf->stats.dgram_past, dbuf->stats.dgram_future, dbuf->stats.dgram_dup, dbuf->stats.dgram_end_marker,
+ dbuf->stats.writev_calls, dbuf->stats.write_partial, dbuf->stats.write_byte,
+ dgram_pending, dgram_missing
+ );
+}
+
+dgrambuf_t dgrambuf_new(size_t dgram_slots, size_t dgram_max_size, size_t dgram_header_size, size_t iovec_slots) {
+
+ const void **dgram_slot_seq_ptrs = NULL;
+ dgrambuf_t dbuf;
+ size_t i;
+
+ dbuf = calloc(1, sizeof(struct dgrambuf_t));
+ if (!dbuf) goto fail0;
+
+ dbuf->validate_func = NULL;
+ /* Implicit with dbuf = calloc(...)
+ memset(&(dbuf->stats), 0, sizeof(struct dgrambuf_stats_t));
+ memset(&(dbuf->sa_sigalrm), 0, sizeof(struct sigaction));
+ */
+ dbuf->sa_sigalrm.sa_handler = _sigalrm_handler;
+
+ dbuf->dgram_slots = dgram_slots;
+ dbuf->dgram_max_size = dgram_max_size;
+ dbuf->dgram_header_size = dgram_header_size;
+
+ /* writev() and dgrambuf_recvmmsg accumulates read/write bytes in ssize_t */
+ iovec_slots = min_size_t(iovec_slots, SSIZE_MAX/dgram_max_size);
+ dbuf->iovec_slots = iovec_slots;
+
+ dbuf->msgs = calloc(iovec_slots, sizeof(struct mmsghdr));
+ if (!dbuf->msgs) goto fail1;
+
+ dbuf->iov_recv = calloc(iovec_slots, sizeof(struct iovec));
+ if (!dbuf->iov_recv) goto fail2;
+
+ dbuf->iov_write = calloc(iovec_slots, sizeof(struct iovec));
+ if (!dbuf->iov_write) goto fail3;
+
+ /* Implicit with dbuf = calloc(...)
+ dbuf->partial_write_iov = NULL;
+ dbuf->partial_write_remaining_iovcnt = 0;
+ dbuf->partial_write_remaining_bytes = 0;
+
+ dbuf->dgram_seq_last = 0;
+ */
+ dbuf->dgram_seq_base = 1;
+ dbuf->dgram_len = calloc(dgram_slots, sizeof(unsigned int));
+ if (!dbuf->dgram_len) goto fail4;
+
+ dbuf->dgram_slot_seq = calloc(dgram_slots, sizeof(struct indexed_uint));
+ if (!dbuf->dgram_slot_seq) goto fail5;
+ for (i=0; i<dgram_slots; i++) {
+ dbuf->dgram_slot_seq[i].index = i;
+ }
+
+ /* Implicit with dbuf = calloc(...)
+ dbuf->dgram_read_active_slots_count = 0;
+ */
+ dbuf->dgram_read_active_slots = calloc(iovec_slots, sizeof(struct indexed_uint *));
+ if (!dbuf->dgram_read_active_slots) goto fail6;
+
+ /* Implicit with dbuf = calloc(...)
+ dbuf->dgram_write_active_slots_count = 0;
+ */
+ dbuf->dgram_write_active_slots = calloc(iovec_slots, sizeof(struct indexed_uint *));
+ if (!dbuf->dgram_write_active_slots) goto fail7;
+
+ dgram_slot_seq_ptrs = calloc(dgram_slots, sizeof(void *));
+ for (i=0; i<dgram_slots; i++) {
+ dbuf->dgram_slot_seq[i].index = i;
+ dgram_slot_seq_ptrs[i] = &(dbuf->dgram_slot_seq[i]);
+ }
+ if (!dgram_slot_seq_ptrs) goto fail7;
+
+ dbuf->dgram_empty_slots = gl_list_nx_create(GL_RBTREE_LIST, _equals_indexed_uint,
+ NULL, NULL, false, dgram_slots, dgram_slot_seq_ptrs);
+ if (!dbuf->dgram_empty_slots) goto fail8;
+
+ free(dgram_slot_seq_ptrs);
+ dgram_slot_seq_ptrs=NULL;
+
+ dbuf->dgram_used_slots = gl_list_nx_create_empty(GL_RBTREE_LIST, _equals_indexed_uint,
+ NULL, NULL, false);
+ if (!dbuf->dgram_used_slots) goto fail9;
+
+ dbuf->buf = calloc(dgram_slots, dgram_max_size);
+ if (!dbuf->buf) goto fail10;
+
+ return dbuf;
+
+fail10: gl_list_free(dbuf->dgram_used_slots);
+fail9: gl_list_free(dbuf->dgram_empty_slots);
+fail8: free(dbuf->dgram_write_active_slots);
+fail7: free(dbuf->dgram_read_active_slots);
+fail6: free(dbuf->dgram_slot_seq);
+fail5: free(dbuf->dgram_len);
+fail4: free(dbuf->iov_write);
+fail3: free(dbuf->iov_recv);
+fail2: free(dbuf->msgs);
+fail1: free(dbuf);
+fail0: return NULL;
+}
+
+void dgrambuf_free(dgrambuf_t *dbuf) {
+ if (dbuf && *dbuf) {
+ free((*dbuf)->buf);
+ gl_list_free((*dbuf)->dgram_used_slots);
+ gl_list_free((*dbuf)->dgram_empty_slots);
+ free((*dbuf)->dgram_write_active_slots);
+ free((*dbuf)->dgram_read_active_slots);
+ free((*dbuf)->dgram_slot_seq);
+ free((*dbuf)->dgram_len);
+ free((*dbuf)->iov_write);
+ free((*dbuf)->iov_recv);
+ free((*dbuf)->msgs);
+ free(*dbuf);
+ *dbuf = NULL;
+ }
+}
+
+void _sigalrm_handler(int signum) {
+ /* Nothing to do except interrupting the pending syscall */
+ if (signum) {} /* Avoid compiler warning */
+}
+
+int _compare_indexed_uint(const void *pa, const void *pb) {
+ const struct indexed_uint *a = pa;
+ const struct indexed_uint *b = pb;
+ if (a->value < b->value)
+ return -1;
+ else if ( a->value > b->value )
+ return 1;
+ else
+ return 0;
+/*
+ if (a->index < b->index)
+ return -1;
+ else if (a->index > b->index)
+ return 1;
+ else
+ return 0;
+*/
+}
+
+bool _equals_indexed_uint(const void *pa, const void *pb) {
+ const struct indexed_uint *a = pa;
+ const struct indexed_uint *b = pb;
+ return (a->value == b->value) /*&& (a->index == b->index)*/;
+}
diff --git a/draft/mcastseed/src/dgrambuf.h b/draft/mcastseed/src/dgrambuf.h
new file mode 100644
index 0000000..a83647b
--- /dev/null
+++ b/draft/mcastseed/src/dgrambuf.h
@@ -0,0 +1,41 @@
+#ifndef DGRAMBUF_H
+#define DGRAMBUF_H
+/*
+ * dgrambuf.c - C datagrams buffer.
+ *
+ * Copyright 2016 by Ludovic Pouzenc <ludovic@pouzenc.fr>
+ */
+#include <stdlib.h> /* size_t */
+
+#define DGRAMBUF_RECV_OVERWRITE 1 << 1
+#define DGRAMBUF_RECV_EINTR 1 << 2
+#define DGRAMBUF_RECV_IOVEC_FULL 1 << 3
+#define DGRAMBUF_RECV_FINALIZE 1 << 4
+#define DGRAMBUF_RECV_FUTURE_DGRAM 1 << 5
+#define DGRAMBUF_RECV_DUPLICATE_DGRAM 1 << 6
+#define DGRAMBUF_RECV_VALID_DGRAM 1 << 6
+
+#define DGRAMBUF_WRITE_PARTIAL 1 << 1
+#define DGRAMBUF_WRITE_EWOULDBLOCK_OR_EINTR 1 << 2
+#define DGRAMBUF_WRITE_IOVEC_FULL 1 << 3
+#define DGRAMBUF_WRITE_SUCCESS 1 << 4
+
+typedef struct dgrambuf_t *dgrambuf_t;
+
+dgrambuf_t dgrambuf_new(size_t dgram_slots, size_t dgram_max_size, size_t dgram_header_size, size_t iovec_slots);
+void dgrambuf_free(dgrambuf_t *);
+
+void dgrambuf_set_validate_func(dgrambuf_t dbuf, int (*validate_func)(unsigned int, void *, unsigned int *));
+
+size_t dgrambuf_get_free_count(const dgrambuf_t);
+size_t dgrambuf_get_used_count(const dgrambuf_t);
+int dgrambuf_have_data_ready_to_write(const dgrambuf_t);
+int dgrambuf_have_received_everything(const dgrambuf_t);
+
+int dgrambuf_stats(dgrambuf_t dbuf, char **allocated_string);
+
+/* Warning : dgrambuf_recvmmsg sets and restore SIGALRM handler if timeout != 0 */
+ssize_t dgrambuf_recvmmsg(dgrambuf_t dbuf, int sockfd, int timeout, int *info);
+ssize_t dgrambuf_write(dgrambuf_t dbuf, int fd, int *info);
+
+#endif /* DGRAMBUF_H */
diff --git a/draft/mcastseed/src/dgrambuf_test.c b/draft/mcastseed/src/dgrambuf_test.c
new file mode 100644
index 0000000..6f9ef22
--- /dev/null
+++ b/draft/mcastseed/src/dgrambuf_test.c
@@ -0,0 +1,50 @@
+#include "dgrambuf.h"
+
+#define _GNU_SOURCE
+#include <netinet/ip.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
+
+int open_test_socket();
+
+/*
+ * Quick'n'dirty bash udp sender
+ * while true; do echo $RANDOM > /dev/udp/127.0.0.1/1234; sleep 0.25; done
+ */
+
+int main() {
+ int res, sockfd, info;
+ dgrambuf_t dgb;
+
+ sockfd = open_test_socket();
+ dgb = dgrambuf_new(3, 50, 8, 8);
+
+ do {
+ res = dgrambuf_recvmmsg(dgb, sockfd, 1, &info);
+ printf("dgrambuf_recvmmsg() => %i\n", res);
+ printf("dgrambuf_free_count => %zu\n", dgrambuf_get_free_count(dgb));
+ } while ( res > 0 );
+ return 0;
+}
+
+int open_test_socket() {
+ int sockfd;
+ struct sockaddr_in sa;
+ sockfd = socket(AF_INET, SOCK_DGRAM, 0);
+ if (sockfd == -1) {
+ perror("socket()");
+ exit(EXIT_FAILURE);
+ }
+
+ sa.sin_family = AF_INET;
+ sa.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+ sa.sin_port = htons(1234);
+ if (bind(sockfd, (struct sockaddr *) &sa, sizeof(sa)) == -1) {
+ perror("bind()");
+ exit(EXIT_FAILURE);
+ }
+
+ return sockfd;
+}
diff --git a/draft/mcastseed/src/mcastleech.c b/draft/mcastseed/src/mcastleech.c
new file mode 100644
index 0000000..3345665
--- /dev/null
+++ b/draft/mcastseed/src/mcastleech.c
@@ -0,0 +1,408 @@
+/*
+ * mcastleech.c - Multicast client for huge streams to be piped to other programs (partitions cloning)
+ *
+ * Copyright 2016 by Ludovic Pouzenc <ludovic@pouzenc.fr>
+ *
+ * Greatly inspired from examples written by tmouse, July 2005
+ * http://cboard.cprogramming.com/showthread.php?t=67469
+ */
+#define _GNU_SOURCE /* See feature_test_macros(7) */
+#include "config.h"
+
+#include <unistd.h> /* close() */
+#include <stdio.h> /* fprintf(), stderr */
+#include <stdlib.h> /* EXIT_SUCCESS */
+#include <string.h> /* strncmp() */
+#include <fcntl.h> /* fcntl() */
+#include "sockets.h"
+#include "dgrambuf.h"
+
+#define MTU 1500
+#define MULTICAST_RECV_BUF (MTU-20-8)
+#define MULTICAST_SO_RCVBUF_WANTED 425984
+#define MAX_IOVEC (MULTICAST_SO_RCVBUF_WANTED / MULTICAST_RECV_BUF)
+#define DGRAM_HEADER_SIZE 8
+
+#define DEFAULT_MCAST_IP_STR "ff02::114"
+#define DEFAULT_PORT_STR "9000"
+
+/* Cmdline Arguments */
+char *prog_name = NULL;
+char *mcast_ip = NULL;
+char *port = NULL;
+
+/* Sockets as global, used everywhere, even in die() */
+int mcast_sock = -1; /* Multicast socket for receiving data */
+int ucast_sock = -1; /* Unicast socket for give feedback to server */
+
+/* Buffer used for earch recvfrom() */
+char recvbuf[MULTICAST_RECV_BUF];
+/* Huge ring buffer to absorb consumer speed variations without loosing datagrams */
+dgrambuf_t dgrambuf;
+
+/* Strings to print out representation of various states of the program */
+const char * const state_str[] = {
+ "start",
+ "wait_hello_and_connect_back",
+ "wait_start_and_start_job",
+ "receive_data",
+ "finalize_job",
+ "is_there_more_job"
+};
+
+/* Some boring funcs you didn't want to read now */
+void die(char* msg);
+void usage(char *msg);
+void arg_parse(int argc, char* argv[]);
+void fsm_trace(int state);
+int get_available_mem_kb();
+void set_O_NONBLOCK(int fd, int set);
+void dgrambuf_init();
+int validate_data_dgram(unsigned int nread, void *recvbuf, unsigned int *seq);
+int send_status(int state, int info_r, int info_w);
+
+/* Parts of the "protocol", definitions are after main() */
+int wait_hello_and_connect_back();
+int wait_start_and_start_job();
+int receive_data();
+int finalize_job();
+int is_there_more_job();
+
+int main(int argc, char* argv[]) {
+ int state = 1; /* state of the "protocol" state machine */
+ int res;
+
+ arg_parse(argc, argv);
+ dgrambuf_init();
+
+ /*XXX Maybe elsewhere, when popen'ing target program */
+ set_O_NONBLOCK(1, 1);
+
+/* XXX Dummy */
+ fcntl(1, F_SETPIPE_SZ, 4096);
+ fprintf(stderr, "pipe_size==%i\n", fcntl(1, F_GETPIPE_SZ));
+ /* Finite state machine */
+ while ( state > 0 ) {
+ fsm_trace(state);
+ switch ( state ) {
+ case 1: state = (wait_hello_and_connect_back() == 0)?2:1; break;
+ case 2: state = (wait_start_and_start_job() == 0)?2:3; break;
+ case 3:
+ res = receive_data();
+ if (res==0) state = 4;
+ else if (res==1) state=3;
+ else state = -1;
+ break;
+ case 4: state = (finalize_job() == 0)?5:-2; break;
+ case 5: state = (is_there_more_job() == 0)?2:0; break; /* XXX Should retry recv ? */
+ }
+ }
+ fsm_trace(state);
+
+ if ( mcast_sock > 0 ) {
+ close(mcast_sock);
+ mcast_sock = -1;
+ }
+
+ dgrambuf_free(&dgrambuf);
+
+ if ( state < 0 ) {
+ return -state;
+ }
+
+ return EXIT_SUCCESS;
+}
+
+
+int wait_hello_and_connect_back() {
+ /* Buffers for host and service strings after resolve */
+ char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV];
+ /* Server address, filled by system after first recvfrom */
+ struct sockaddr_storage peer_addr;
+ socklen_t peer_addr_len;
+ /* Various needed variables */
+ ssize_t nread;
+ int res;
+
+ /* Setup mcast_sock */
+ if ( mcast_sock > 0 ) {
+ close(mcast_sock);
+ mcast_sock = -1;
+ }
+ mcast_sock = mcast_recv_socket(mcast_ip, port, MULTICAST_SO_RCVBUF_WANTED);
+ if(mcast_sock < 0) {
+ usage("Could not setup multicast socket. Wrong args given ?");
+ }
+
+ /* Wait for a single datagram from the server (for sync, no check on contain) */
+ peer_addr_len = sizeof(struct sockaddr_storage);
+ nread = recvfrom(mcast_sock, recvbuf, MULTICAST_RECV_BUF, 0, (struct sockaddr *) &peer_addr, &peer_addr_len);
+ if (nread < 0 ) {
+ perror("recvfrom() failed");
+ return -1;
+ }
+ /* Get peer informations as strings from peer_addr */
+ res = getnameinfo((struct sockaddr *) &peer_addr, peer_addr_len,
+ hbuf, NI_MAXHOST, sbuf, NI_MAXSERV, NI_NUMERICSERV);
+ if ( res != 0 ) {
+ fprintf(stderr, "getnameinfo: %s\n", gai_strerror(res));
+ return -2;
+ }
+ /* Connect back to the server, with reliable unicast */
+ if ( ucast_sock > 0 ) {
+ close(ucast_sock);
+ }
+ /* FIXME : ucast_client_socket() use DNS resolver and could block */
+ ucast_sock = ucast_client_socket(hbuf,port);
+ if(ucast_sock < 0) {
+ fprintf(stderr, "Could not setup unicast socket or connect to %s:%s\n", hbuf, port);
+ return -3;
+ }
+
+ return 0;
+}
+
+int wait_start_and_start_job() {
+ ssize_t nread, nwrite;
+
+ /* Wait for a "start" datagram from the server */
+ nread = recvfrom(mcast_sock, recvbuf, MULTICAST_RECV_BUF, 0, NULL, 0);
+ if (nread < 0 ) {
+ perror("recvfrom() failed");
+ return -1;
+ }
+ if ( nread >= 5 && strncmp("start", recvbuf, 5) == 0 ) {
+ /* Reply "ready" through unicast stream socket */
+ nwrite = write(ucast_sock, "ready", 5);
+ if ( nwrite < 0 ) {
+ fprintf(stderr, "write() failed\n");
+ return -2;
+ }
+ if (nwrite != 5) {
+ fprintf(stderr, "write() short\n");
+ return -3;
+ }
+
+ return 1;
+ }
+
+ return 0;
+}
+
+/*
+#define DGRAMBUF_RECV_OVERWRITE 1 << 1
+#define DGRAMBUF_RECV_EINTR 1 << 2
+#define DGRAMBUF_RECV_IOVEC_FULL 1 << 3
+#define DGRAMBUF_RECV_FINALIZE 1 << 4
+#define DGRAMBUF_RECV_VALID_DGRAM 1 << 5
+
+#define DGRAMBUF_WRITE_PARTIAL 1 << 1
+#define DGRAMBUF_WRITE_EWOULDBLOCK_OR_EINTR 1 << 2
+#define DGRAMBUF_WRITE_IOVEC_FULL 1 << 3
+#define DGRAMBUF_WRITE_SUCCESS 1 << 4
+*/
+int receive_data() {
+ int info_r, info_w, res;
+ ssize_t nread, nwrite;
+ static int noop_calls_count = 0;
+
+ /* Read (blocking, timeout = 1 sec) */
+ nread = dgrambuf_recvmmsg(dgrambuf, mcast_sock, 1, &info_r);
+ if ( nread < 0 ) {
+ return nread;
+ }
+
+ /* Write (non-blocking) */
+ nwrite = dgrambuf_write(dgrambuf, 1, &info_w);
+ if ( nwrite < 0 ) {
+ return nwrite;
+ }
+
+ /*fprintf(stderr, "receive_data(): nread == %zi, nwrite == %zi\n", nread, nwrite);*/
+
+ /* XXX Crapy dead state detection */
+ if ( nread == 0 /* TEST && nwrite == 0 */ ) {
+ if ( noop_calls_count > 10 ) {
+ return 0;
+ }
+ noop_calls_count++;
+ } else {
+ noop_calls_count = 0;
+ }
+
+ /* Consider sending status back to seeder */
+ res = send_status(1, info_r, info_w);
+ if ( res < 0 ) {
+ return res;
+ }
+
+ if ( dgrambuf_have_received_everything(dgrambuf) ) {
+ return 0;
+ }
+ return 1;
+}
+
+
+int finalize_job() {
+ ssize_t nwrite;
+ int info_w, res;
+ char *stats;
+
+ /* Don't eat reources in a pooling fashion, blocking IO is fine when no more recv to do */
+ set_O_NONBLOCK(1, 0);
+
+ /* Flush the whole buffer */
+ do {
+ nwrite = dgrambuf_write(dgrambuf, 1, &info_w);
+ if ( nwrite < 0 ) {
+ return nwrite;
+ }
+ fprintf(stderr, "finalize_job(): nwrite == %zi\n", nwrite);
+ } while ( nwrite > 0);
+
+ /* Inform the seeder that have have finished */
+ res = send_status(2, 0, info_w);
+ if ( res < 0 ) {
+ return res;
+ }
+
+ res = dgrambuf_stats(dgrambuf, &stats);
+ if ( res != - 1 ) {
+ fprintf(stderr, "finalize_job(): dgrambuf_stats : %s\n",stats);
+ free(stats);
+ }
+ return 0;
+}
+
+int is_there_more_job() {
+ return 1;
+}
+
+
+
+
+void die(char* msg) {
+ fprintf(stderr, "%s\n", msg);
+ if (mcast_sock > 0)
+ close(mcast_sock);
+ if (ucast_sock > 0)
+ close(ucast_sock);
+ exit(EXIT_FAILURE);
+}
+
+void usage(char *msg) {
+ char ubuf[256];
+ if ( msg != NULL )
+ fprintf(stderr, "%s\n", msg);
+ ubuf[0] = '\0';
+ snprintf(ubuf, 255, "Usage: %s [port] [mcast_ip]\n", prog_name);
+ die(ubuf);
+}
+
+void arg_parse(int argc, char* argv[]) {
+ prog_name = argv[0];
+ if ( argc > 3 )
+ usage("Too many arguments");
+ port = (argc >= 2)?argv[1]:DEFAULT_PORT_STR;
+ mcast_ip = (argc >= 3)?argv[2]:DEFAULT_MCAST_IP_STR;
+}
+
+void fsm_trace(int state) {
+ static int prev_state = 0;
+
+ if ( state < 0 ) {
+ fprintf(stderr, "Abnormal exit condition %i (from %s)\n", state, state_str[prev_state]);
+ } else if ( prev_state != state) {
+ if ( state == 0 ) {
+ fprintf(stderr, "Normal exit (from %s)\n", state_str[prev_state]);
+ } else {
+ fprintf(stderr, "Now in %s (from %s)\n", state_str[state], state_str[prev_state]);
+ }
+ prev_state = state;
+ }
+}
+
+int get_available_mem_kb() {
+ char key[64];
+ int res, value, found=0;
+ FILE * fh = fopen("/proc/meminfo", "r");
+ if ( fh ) {
+ while (!found && !feof(fh)) {
+ res = fscanf(fh, "%63s %i kB\n", key, &value);
+ if ( res < 0 )
+ break;
+ found = ( strncmp("MemAvailable:", key, 12) == 0 );
+ }
+ fclose(fh);
+ }
+
+ if ( found && value > 0 ) {
+ return value;
+ }
+
+ return 0;
+}
+
+void set_O_NONBLOCK(int fd, int set) {
+ int res, flags;
+
+ flags = fcntl(fd, F_GETFL);
+ if ( flags == -1 ) {
+ perror("fcntl(1, F_GETFL)");
+ }
+ if ( set ) {
+ res = fcntl(fd, F_SETFL, flags | O_NONBLOCK);
+ } else {
+ res = fcntl(fd, F_SETFL, flags & !O_NONBLOCK);
+ }
+ if ( res == -1 ) {
+ perror("fcntl(1, F_SETFL)");
+ }
+}
+
+void dgrambuf_init() {
+ /* Guess dgrambuf size from global free memory */
+ size_t dgram_count;
+ int avail_mem = get_available_mem_kb();
+
+ if ( avail_mem < MULTICAST_SO_RCVBUF_WANTED ) {
+ dgram_count = MULTICAST_SO_RCVBUF_WANTED / MULTICAST_RECV_BUF;
+ } else {
+ dgram_count = avail_mem / MULTICAST_RECV_BUF / 2 * 1024;
+ }
+ /* XXX Dummy
+ dgram_count = 5;
+ */
+
+ /* Allocate dgrambuf */
+ dgrambuf = dgrambuf_new(dgram_count, MULTICAST_RECV_BUF, DGRAM_HEADER_SIZE, MAX_IOVEC);
+ if ( dgrambuf == NULL ) {
+ perror("dgrambuf_new/malloc");
+ exit(EXIT_FAILURE);
+ }
+
+ fprintf(stderr, "dgrambuf_get_free_count() => %zu\n", dgrambuf_get_free_count(dgrambuf));
+ dgrambuf_set_validate_func(dgrambuf, validate_data_dgram);
+}
+
+int validate_data_dgram(unsigned int nread, void *recvbuf, unsigned int *seq) {
+
+ if ( nread < DGRAM_HEADER_SIZE ) {
+ return 0;
+ }
+ if ( strncmp("data", recvbuf, 4) == 0 ) {
+ *seq = ntohl( *( (uint32_t *) recvbuf+1 ) );
+ return 1;
+ }
+ if ( strncmp("end:", recvbuf, 4) == 0 ) {
+ *seq = ntohl( *( (uint32_t *) recvbuf+1 ) );
+ return 2;
+ }
+ return 0;
+}
+
+int send_status(int state, int info_r, int info_w) {
+ if ( state && info_r && info_w ) {}
+ /* TODO Implement it */
+ return 0;
+}
diff --git a/draft/mcastseed/src/mcastseed.c b/draft/mcastseed/src/mcastseed.c
new file mode 100644
index 0000000..48f8869
--- /dev/null
+++ b/draft/mcastseed/src/mcastseed.c
@@ -0,0 +1,472 @@
+/*
+ * mcastseed.c - Multicast sender for huge streams to be piped to other programs (partitions cloning)
+ *
+ * Copyright 2016 by Ludovic Pouzenc <ludovic@pouzenc.fr>
+ *
+ * Greatly inspired from examples written by tmouse, July 2005
+ * http://cboard.cprogramming.com/showthread.php?t=67469
+ */
+#define _GNU_SOURCE /* See feature_test_macros(7) */
+#include "config.h"
+
+#include <unistd.h> /* close() */
+#include <stdio.h> /* fprintf(), stderr */
+#include <stdlib.h> /* atoi(), EXIT_SUCCESS */
+#include <string.h> /* strlen() */
+#include <sys/select.h> /* select(), FD_ZERO(), FD_SET() */
+#include "sockets.h"
+
+#define READ_BUF_LEN 256
+#define MAX_PENDING_CONNECTIONS 256
+#define MAX_CLIENTS 256
+#define MTU 1500
+/* Linux IPv6 fragmentation don't output ethernet frames larger than 1470 when MTU==1500 */
+#define MULTICAST_MAX_PAYLOAD_SIZE (MTU-40-8-(14+30))
+
+#define DEFAULT_MCAST_IP_STR "ff02::114"
+#define DEFAULT_PORT_STR "9000"
+#define DEFAULT_MCAST_TTL 1
+
+/* Cmdline Arguments */
+char *prog_name = NULL;
+char *mcast_ip = NULL;
+char *port = NULL;
+int mcast_ttl = 0;
+
+/* Sockets as global, used everywhere, even in die() */
+int mcast_sock = -1; /* Multicast socket for sending data */
+int ucast_sock = -1; /* Unicast socket for havee feedback from clients */
+
+/* Socket related data */
+struct addrinfo *mcast_addr = NULL;
+struct client {
+ int sock;
+ struct sockaddr addr;
+ int state;
+} clients[MAX_CLIENTS];
+int clients_next = 0;
+
+/* Buffer used for earch read() */
+char readbuf[READ_BUF_LEN];
+
+/* Strings to print out representation of various states of the program */
+const char * const state_str[] = {
+ "start",
+ "send_hello",
+ "accept_pending_clients_or_wait_a_bit",
+ "start_job",
+ "send_data",
+ "wait_all_finalize_job",
+ "is_there_more_job"
+};
+
+/* Some boring funcs you didn't want to read now */
+void die(char* msg);
+void usage(char *msg);
+void arg_parse(int argc, char* argv[]);
+void fsm_trace(int state);
+void setup_sockets();
+void unsetup_sockets();
+
+/* Parts of the "protocol", definitions are after main() */
+int send_hello();
+int accept_pending_clients_or_wait_a_bit();
+int start_job();
+int send_data();
+int wait_all_finalize_job();
+int is_there_more_job();
+
+int main(int argc, char *argv[]) {
+ int state = 1; /* state of the "protocol" state machine */
+ int res;
+ arg_parse(argc, argv);
+ setup_sockets();
+
+ /* Finite state machine */
+ while ( state > 0 ) {
+ fsm_trace(state);
+ switch ( state ) {
+ case 1: res = send_hello(); state = (res==0)?2:-1; break;
+ case 2: res = accept_pending_clients_or_wait_a_bit();
+ if (res==0) state = 2; /* Some clients has just come in, try to get more */
+ else if (res==1) state = 1; /* Nothing new. Keep accepting clients after another hello */
+ else if (res==2) state = 3; /* Wanted clients are accepted */
+ else state = -2;
+ break;
+ case 3: res = start_job();
+ if (res==0) state = 3; /* Keep trying to convince every client to start */
+ else if (res==1) state = 4; /* All clients have started the job pipe */
+ else if (res==2) state = 4; /* There is dead clients but all alive are ready to go */
+ else state = -3;
+ break;
+ case 4: res = send_data();
+ if (res==0) state = 4;
+ else if (res==1) state = 5; /* All data sent */
+ else state = -4;
+ break;
+ case 5: res = wait_all_finalize_job();
+ if (res==0) state = 5;
+ else if (res==1) state = 6;
+ else state = -5;
+ case 6: res = is_there_more_job();
+ if (res==0) state = 0;
+ else if (res==1) state = 3;
+ else state = -6;
+ break;
+ }
+ }
+ fsm_trace(state);
+
+ unsetup_sockets();
+
+ if ( state < 0 )
+ return -state;
+
+ return EXIT_SUCCESS;
+}
+
+int send_hello() {
+ ssize_t nwrite;
+ const char *payload = "hello";
+ int paylen = strlen(payload);
+
+ nwrite = sendto(mcast_sock, payload, paylen, 0, mcast_addr->ai_addr, mcast_addr->ai_addrlen);
+ if ( nwrite < 0 ) {
+ perror("sendto() failed");
+ return -1;
+ }
+ if ( nwrite < paylen ) {
+ fprintf(stderr, "%s", "Short packet sent");
+ }
+
+ return 0;
+}
+
+int accept_pending_clients_or_wait_a_bit() {
+ struct timeval timeout;
+ fd_set readfds, exceptfds;
+ ssize_t nread;
+ int res;
+
+ FD_ZERO(&readfds);
+ FD_ZERO(&exceptfds);
+ FD_SET(0,&readfds);
+ FD_SET(ucast_sock,&readfds);
+ FD_SET(ucast_sock,&exceptfds);
+ timeout.tv_sec = 2;
+ timeout.tv_usec = 0;
+
+ res = select(ucast_sock+1, &readfds, NULL, &exceptfds, &timeout);
+ if ( res < 0 ) {
+ perror("select() failed");
+ return -1;
+ }
+
+ if ( res > 0 ) {
+ if (FD_ISSET(ucast_sock, &readfds)) {
+ /*TODO : this assumes that the event is an accept() while ones could be send data there */
+ if ( clients_next >= MAX_CLIENTS ) {
+ fprintf(stderr, "%s\n", "Bouncing client, MAX_CLIENTS reached");
+ close(accept(ucast_sock, NULL, 0));
+ } else {
+ socklen_t addrlen = sizeof(struct sockaddr);
+ clients[clients_next].sock = accept(ucast_sock, &(clients[clients_next].addr), &addrlen);
+ clients[clients_next].state = 0;
+ printf("Connected client on fd %i\n", clients[clients_next].sock);
+ clients_next++;
+ }
+ }
+ /*TODO : drop this keybord read with accept(), this is not portable */
+ if ( FD_ISSET(0, &readfds)) {
+ nread = read(0, readbuf, READ_BUF_LEN);
+ if ( nread <= 0 ) {
+ fprintf(stderr, "%s\n", "lost stdin");
+ }
+ /* User wants to go now */
+ return 2;
+ }
+ if (FD_ISSET(ucast_sock, &exceptfds)) {
+ fprintf(stderr, "%s\n", "unhandled except on ucast_sock");
+ return -2;
+ }
+ }
+ if (res == 0 ) {
+ /* Nothing happened before timeout */
+ return 1;
+ }
+ return 0;
+}
+
+int start_job() {
+ struct timeval timeout;
+ fd_set readfds, exceptfds;
+ ssize_t nread, nwrite;
+ int all_ready, all_non_dead_ready;
+ int i, res;
+ int client_sock;
+ const char *payload = "start";
+ int paylen = strlen(payload);
+
+ nwrite = sendto(mcast_sock, payload, paylen, 0, mcast_addr->ai_addr, mcast_addr->ai_addrlen);
+ if ( nwrite < 0 ) {
+ perror("sendto() failed");
+ return -1;
+ }
+ if ( nwrite < paylen ) {
+ fprintf(stderr, "%s", "Short packet sent");
+ }
+
+ all_ready = 1;
+ all_non_dead_ready = 1;
+
+ FD_ZERO(&readfds);
+ FD_ZERO(&exceptfds);
+ for ( i=0; i<clients_next; i++) {
+ FD_SET(clients[i].sock,&readfds);
+ FD_SET(clients[i].sock,&exceptfds);
+ }
+ timeout.tv_sec = 2;
+ timeout.tv_usec = 0;
+ res = select(clients_next, &readfds, NULL, &exceptfds, &timeout);
+ if ( res < 0 ) {
+ perror("select() failed");
+ return -1;
+ }
+
+ if ( res > 0 ) {
+ for ( i=0; i<clients_next; i++) {
+ client_sock = clients[i].sock;
+ if (FD_ISSET(client_sock, &readfds)) {
+ printf("todo info from client %i\n", i);
+ nread = read(client_sock, readbuf, 5);
+ if ( nread <= 0 ) {
+ fprintf(stderr, "lost client %i\n", i);
+ clients[i].state = 2;
+ } else if ( nread < 5 ) {
+ fprintf(stderr, "short data from %i\n", i);
+ clients[i].state = 2;
+ } else if ( strncmp("ready", readbuf, 5) != 0 ) {
+ fprintf(stderr, "unexpected data from %i\n", i);
+ clients[i].state = 2;
+ } else {
+ /* Received "ready" ack from client */
+ clients[i].state = 1;
+ }
+ }
+ if (FD_ISSET(clients[i].sock, &exceptfds)) {
+ fprintf(stderr, "unhandled except on client %i\n", i);
+ clients[i].state = 2;
+ }
+ all_ready &= (clients[i].state == 1);
+ if ( clients[i].state != 2)
+ all_non_dead_ready &= (clients[i].state == 1);
+ }
+ }
+ /* (res == 0 ) nothing happened before timeout */
+
+ if ( all_ready )
+ return 1;
+ if ( all_non_dead_ready )
+ return 2;
+
+ return 0;
+}
+
+void send_fake(char buf[], int paylen, int i) {
+ *( (uint32_t *) buf+1 ) = htonl(i);
+ snprintf(buf+28, 6, "%05i", i);
+ *( (char *) buf+33 ) = ')';
+ sendto(mcast_sock, buf, paylen, 0, mcast_addr->ai_addr, mcast_addr->ai_addrlen);
+}
+
+int send_data() {
+ ssize_t nwrite;
+ char buf[MULTICAST_MAX_PAYLOAD_SIZE];
+ int paylen = MULTICAST_MAX_PAYLOAD_SIZE;
+ int i;
+
+ /* XXX Dummy */
+ memset(buf, '.', MULTICAST_MAX_PAYLOAD_SIZE-1);
+ buf[MULTICAST_MAX_PAYLOAD_SIZE-1]='\n';
+ strcpy(buf, "dataXXXXJe suis a la plage (XXXXX)");
+
+ send_fake(buf, paylen, 5);
+ send_fake(buf, paylen, 4);
+ send_fake(buf, paylen, 3);
+
+ for (i=6; i<=100000; i+=2) {
+ send_fake(buf, paylen, i);
+ }
+ for (i=7; i<=100000; i+=2) {
+ send_fake(buf, paylen, i);
+ }
+
+ send_fake(buf, paylen, 1);
+ send_fake(buf, paylen, 1);
+ send_fake(buf, paylen, 2);
+
+ *( (uint32_t *) buf+1 ) = htonl(3);
+ buf[21]='m', buf[22]='e', buf[23]='r'; buf[24]='.'; buf[25]='\n'; paylen = 26;
+ nwrite = sendto(mcast_sock, buf, paylen, 0, mcast_addr->ai_addr, mcast_addr->ai_addrlen);
+ if ( nwrite < 0 ) {
+ perror("sendto() failed");
+ return -1;
+ }
+ if ( nwrite < paylen ) {
+ fprintf(stderr, "%s", "Short packet sent");
+ }
+
+ return 1;
+}
+
+
+int wait_all_finalize_job() {
+ struct timeval timeout;
+ fd_set readfds, exceptfds;
+ ssize_t nread, nwrite;
+ int all_non_dead_done;
+ int i, res;
+ int client_sock;
+ char buf[] = "end:XXXX";
+ int paylen = strlen(buf);
+
+ *( (uint32_t *) buf+1 ) = htonl(100000);
+ nwrite = sendto(mcast_sock, buf, paylen, 0, mcast_addr->ai_addr, mcast_addr->ai_addrlen);
+ if ( nwrite < 0 ) {
+ perror("sendto() failed");
+ return -1;
+ }
+ if ( nwrite < paylen ) {
+ fprintf(stderr, "%s", "Short packet sent");
+ }
+
+ all_non_dead_done = 1;
+
+ FD_ZERO(&readfds);
+ FD_ZERO(&exceptfds);
+ for ( i=0; i<clients_next; i++) {
+ FD_SET(clients[i].sock,&readfds);
+ FD_SET(clients[i].sock,&exceptfds);
+ }
+ timeout.tv_sec = 2;
+ timeout.tv_usec = 0;
+ res = select(clients_next, &readfds, NULL, &exceptfds, &timeout);
+ if ( res < 0 ) {
+ perror("select() failed");
+ return -1;
+ }
+
+ if ( res > 0 ) {
+ for ( i=0; i<clients_next; i++) {
+ client_sock = clients[i].sock;
+ if (FD_ISSET(client_sock, &readfds)) {
+ printf("todo info from client %i\n", i);
+ nread = read(client_sock, readbuf, 5);
+ if ( nread <= 0 ) {
+ fprintf(stderr, "lost client %i\n", i);
+ clients[i].state = 2;
+ } else if ( nread < 5 ) {
+ fprintf(stderr, "short data from %i\n", i);
+ clients[i].state = 2;
+ } else if ( strncmp("done.", readbuf, 5) != 0 ) {
+ fprintf(stderr, "unexpected data from %i\n", i);
+ clients[i].state = 2;
+ } else {
+ /* Received "done." ack from client */
+ clients[i].state = 3;
+ }
+ }
+ if (FD_ISSET(clients[i].sock, &exceptfds)) {
+ fprintf(stderr, "unhandled except on client %i\n", i);
+ clients[i].state = 2;
+ }
+ if ( clients[i].state != 2)
+ all_non_dead_done &= (clients[i].state == 3);
+ }
+ }
+ /* (res == 0 ) nothing happened before timeout */
+
+ if ( all_non_dead_done )
+ return 1;
+
+ return 0;
+}
+
+
+int is_there_more_job() {
+ return 0;
+}
+
+
+void die(char* msg) {
+ fprintf(stderr, "%s\n", msg);
+ if (mcast_sock > 0)
+ close(mcast_sock);
+ if (ucast_sock > 0)
+ close(ucast_sock);
+ exit(EXIT_FAILURE);
+}
+
+void usage(char *msg) {
+ char ubuf[256];
+ if ( msg != NULL )
+ fprintf(stderr, "%s\n", msg);
+ ubuf[0] = '\0';
+ snprintf(ubuf, 255, "Usage: %s [port] [mcast_ip] [mcast_ttl]\n", prog_name);
+ die(ubuf);
+}
+
+void arg_parse(int argc, char* argv[]) {
+ prog_name = argv[0];
+ if ( argc > 3 )
+ usage("Too many arguments");
+ port = (argc >= 2)?argv[1]:DEFAULT_PORT_STR;
+ mcast_ip = (argc >= 3)?argv[2]:DEFAULT_MCAST_IP_STR;
+ mcast_ttl = (argc >= 4)?atoi(argv[3]):DEFAULT_MCAST_TTL;
+ if ( mcast_ttl < 1 || mcast_ttl > 64 )
+ mcast_ttl = 1;
+}
+
+void fsm_trace(int state) {
+ static int prev_state = 0;
+
+ if ( state < 0 ) {
+ fprintf(stderr, "Abnormal exit condition %i (from %s)\n", state, state_str[prev_state]);
+ } else if ( prev_state != state) {
+ if ( state == 0 ) {
+ fprintf(stderr, "Normal exit (from %s)\n", state_str[prev_state]);
+ } else {
+ fprintf(stderr, "Now in %s (from %s)\n", state_str[state], state_str[prev_state]);
+ }
+ prev_state = state;
+ }
+}
+
+void setup_sockets() {
+ /* Setup ucast_sock */
+ ucast_sock = ucast_server_socket(port, MAX_PENDING_CONNECTIONS);
+ if(ucast_sock < 0)
+ usage("Could not setup unicast socket. Wrong args given ?");
+
+ /* Setup mcast_sock */
+ mcast_sock = mcast_send_socket(mcast_ip, port, mcast_ttl, &mcast_addr);
+ if(mcast_sock < 0)
+ usage("Could not setup multicast socket. Wrong args given ?");
+}
+
+void unsetup_sockets() {
+ if ( ucast_sock > 0 ) {
+ close(ucast_sock);
+ ucast_sock = 0;
+ }
+
+ if ( mcast_sock > 0 ) {
+ close(mcast_sock);
+ mcast_sock = 0;
+ if ( mcast_addr ) {
+ freeaddrinfo(mcast_addr);
+ mcast_addr = 0;
+ }
+ }
+}
+
diff --git a/draft/mcastseed/src/random_speed_dd.c b/draft/mcastseed/src/random_speed_dd.c
new file mode 100644
index 0000000..4d94bc0
--- /dev/null
+++ b/draft/mcastseed/src/random_speed_dd.c
@@ -0,0 +1,36 @@
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+
+char buf[0xffff];
+
+int main() {
+ ssize_t nread, nwrite, remains;
+
+ srandom(1); /* Always the same pseudo-random sequence */
+
+ while ( (nread=read(0, buf, 0xfff & rand())) > 0 ) {
+ remains = nread;
+ while ( remains ) {
+ nwrite=write(1, buf, nread);
+ if ( nwrite < 0 ) {
+ if ( !(errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) ) {
+ perror("write");
+ return nwrite;
+ }
+ } else {
+ remains -= nwrite;
+ }
+ }
+ /*fprintf(stderr, "nread==%zu, nwrite==%zu\n", nread, nwrite);*/
+ usleep( 0xffff & rand() );
+ }
+ if ( nread < 0 ) {
+ perror("read");
+ return nread;
+ }
+
+ return 0;
+}
+
diff --git a/draft/mcastseed/src/sockets.c b/draft/mcastseed/src/sockets.c
new file mode 100644
index 0000000..6aea016
--- /dev/null
+++ b/draft/mcastseed/src/sockets.c
@@ -0,0 +1,303 @@
+/*
+ * Copyright 2016 by Ludovic Pouzenc <ludovic@pouzenc.fr>
+ *
+ * Greatly inspired from msock.h written by Christian Beier <dontmind@sdf.org>
+ */
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include "sockets.h"
+
+int mcast_recv_socket(char *mcast_ip, char *port, int wanted_so_rcvbuf) {
+
+ int sock;
+ struct addrinfo hints = { 0 }; /* Hints for name lookup */
+ struct addrinfo *ai_local = NULL; /* Local address to bind to */
+ struct addrinfo *mcast_ai = NULL; /* Multicast Address */
+ int yes=1;
+ int status, optval;
+ socklen_t optval_len;
+ int dfltrcvbuf;
+
+ /* Resolve the multicast group address */
+ hints.ai_family = PF_UNSPEC;
+ hints.ai_flags = AI_NUMERICHOST;
+ if ((status = getaddrinfo(mcast_ip, NULL, &hints, &mcast_ai)) != 0) {
+ fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(status));
+ goto error;
+ }
+
+ /*
+ * Get a local address with the same family (IPv4 or IPv6) as our multicast group
+ * This is for receiving on a certain port.
+ */
+ hints.ai_family = mcast_ai->ai_family;
+ hints.ai_socktype = SOCK_DGRAM;
+ hints.ai_flags = AI_PASSIVE; /* Return an address we can bind to */
+ if ( getaddrinfo(NULL, port, &hints, &ai_local) != 0 ) {
+ fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(status));
+ goto error;
+ }
+
+ /* Create socket for receiving datagrams */
+ if ( (sock = socket(ai_local->ai_family, ai_local->ai_socktype, 0)) < 0 ) {
+ perror("socket() failed");
+ goto error;
+ }
+
+ /*
+ * Enable SO_REUSEADDR to allow multiple instances of this
+ * application to receive copies of the multicast datagrams.
+ */
+ if (setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,(char*)&yes,sizeof(int)) == -1) {
+ perror("setsockopt");
+ goto error;
+ }
+
+ /* Bind the local address to the multicast port */
+ if ( bind(sock, ai_local->ai_addr, ai_local->ai_addrlen) != 0 ) {
+ perror("bind() failed");
+ goto error;
+ }
+
+ /* get/set socket receive buffer */
+ optval=0;
+ optval_len = sizeof(optval);
+ if(getsockopt(sock, SOL_SOCKET, SO_RCVBUF,(char*)&optval, &optval_len) !=0) {
+ perror("getsockopt");
+ goto error;
+ }
+ dfltrcvbuf = optval;
+ optval = wanted_so_rcvbuf;
+ if(setsockopt(sock,SOL_SOCKET,SO_RCVBUF,(char*)&optval,sizeof(optval)) != 0) {
+ perror("setsockopt");
+ goto error;
+ }
+ if(getsockopt(sock, SOL_SOCKET, SO_RCVBUF,(char*)&optval, &optval_len) != 0) {
+ perror("getsockopt");
+ goto error;
+ }
+ fprintf(stderr, "tried to set socket receive buffer from %d to %d, got %d\n",
+ dfltrcvbuf, wanted_so_rcvbuf, optval);
+
+
+ /* Join the multicast group. We do this seperately depending on whether we
+ * are using IPv4 or IPv6.
+ */
+ if ( mcast_ai->ai_family == PF_INET &&
+ mcast_ai->ai_addrlen == sizeof(struct sockaddr_in) ) /* IPv4 */
+ {
+ struct ip_mreq multicastRequest; /* Multicast address join structure */
+
+ /* Specify the multicast group */
+ memcpy(&multicastRequest.imr_multiaddr,
+ &((struct sockaddr_in*)(mcast_ai->ai_addr))->sin_addr,
+ sizeof(multicastRequest.imr_multiaddr));
+
+ /* Accept multicast from any interface */
+ multicastRequest.imr_interface.s_addr = htonl(INADDR_ANY);
+
+ /* Join the multicast address */
+ if ( setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char*) &multicastRequest, sizeof(multicastRequest)) != 0 ) {
+ perror("setsockopt() failed");
+ goto error;
+ }
+ }
+ else if ( mcast_ai->ai_family == PF_INET6 &&
+ mcast_ai->ai_addrlen == sizeof(struct sockaddr_in6) ) /* IPv6 */
+ {
+ struct ipv6_mreq multicastRequest; /* Multicast address join structure */
+
+ /* Specify the multicast group */
+ memcpy(&multicastRequest.ipv6mr_multiaddr,
+ &((struct sockaddr_in6*)(mcast_ai->ai_addr))->sin6_addr,
+ sizeof(multicastRequest.ipv6mr_multiaddr));
+
+ /* Accept multicast from any interface */
+ multicastRequest.ipv6mr_interface = 0;
+
+ /* Join the multicast address */
+ if ( setsockopt(sock, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, (char*) &multicastRequest, sizeof(multicastRequest)) != 0 ) {
+ perror("setsockopt() failed");
+ goto error;
+ }
+ }
+ else {
+ perror("Neither IPv4 or IPv6");
+ goto error;
+ }
+
+
+ if(ai_local)
+ freeaddrinfo(ai_local);
+ if(mcast_ai)
+ freeaddrinfo(mcast_ai);
+
+ return sock;
+
+error:
+ if(ai_local)
+ freeaddrinfo(ai_local);
+ if(mcast_ai)
+ freeaddrinfo(mcast_ai);
+
+ return -1;
+}
+
+
+int mcast_send_socket(char* mcast_ip, char* port, int multicastTTL, struct addrinfo **mcast_ai) {
+
+ int sock;
+ struct addrinfo hints = { 0 }; /* Hints for name lookup */
+ int status;
+
+
+ /* Resolve destination address for multicast datagrams */
+ hints.ai_family = PF_UNSPEC;
+ hints.ai_socktype = SOCK_DGRAM;
+ hints.ai_flags = AI_NUMERICHOST;
+ if ((status = getaddrinfo(mcast_ip, port, &hints, mcast_ai)) != 0 )
+ {
+ fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(status));
+ return -1;
+ }
+
+ /* Create socket for sending multicast datagrams */
+ if ( (sock = socket((*mcast_ai)->ai_family, (*mcast_ai)->ai_socktype, 0)) < 0 ) {
+ perror("socket() failed");
+ freeaddrinfo(*mcast_ai);
+ return -1;
+ }
+
+ /* Set TTL of multicast packet */
+ if ( setsockopt(sock,
+ (*mcast_ai)->ai_family == PF_INET6 ? IPPROTO_IPV6 : IPPROTO_IP,
+ (*mcast_ai)->ai_family == PF_INET6 ? IPV6_MULTICAST_HOPS : IP_MULTICAST_TTL,
+ (char*) &multicastTTL, sizeof(multicastTTL)) != 0 ) {
+ perror("setsockopt() failed");
+ freeaddrinfo(*mcast_ai);
+ return -1;
+ }
+
+ /* set the sending interface */
+ if((*mcast_ai)->ai_family == PF_INET) {
+ in_addr_t iface = INADDR_ANY; /* well, yeah, any */
+ if(setsockopt (sock,
+ IPPROTO_IP,
+ IP_MULTICAST_IF,
+ (char*)&iface, sizeof(iface)) != 0) {
+ perror("interface setsockopt() sending interface");
+ freeaddrinfo(*mcast_ai);
+ return -1;
+ }
+
+ }
+ if((*mcast_ai)->ai_family == PF_INET6) {
+ unsigned int ifindex = 0; /* 0 means 'default interface'*/
+ if(setsockopt (sock,
+ IPPROTO_IPV6,
+ IPV6_MULTICAST_IF,
+ (char*)&ifindex, sizeof(ifindex)) != 0) {
+ perror("interface setsockopt() sending interface");
+ freeaddrinfo(*mcast_ai);
+ return -1;
+ }
+
+ }
+
+ return sock;
+}
+
+
+int ucast_server_socket(char* port, int max_pending_conn) {
+
+ int sock;
+ struct addrinfo *serverAddr;
+ struct addrinfo hints = { 0 }; /* Hints for name lookup */
+ int status;
+
+
+ /* Prepare an addrinfo struct for a local socket */
+ hints.ai_family = PF_INET6;
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_flags = AI_PASSIVE;
+ if ((status = getaddrinfo(NULL, port, &hints, &serverAddr)) != 0 )
+ {
+ fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(status));
+ return -1;
+ }
+ /* Create socket */
+ if ( (sock = socket(serverAddr->ai_family, serverAddr->ai_socktype, serverAddr->ai_protocol)) < 0 ) {
+ perror("socket() failed");
+ freeaddrinfo(serverAddr);
+ return -1;
+ }
+
+ /* Accepts also IPv4 traffic if the socket is INET6 */
+ if(serverAddr->ai_family == PF_INET6) {
+ unsigned int no = 0;
+ if(setsockopt (sock,
+ IPPROTO_IPV6,
+ IPV6_V6ONLY,
+ (char*)&no, sizeof(no)) != 0) {
+ perror("setsockopt() !IPV6_V6ONLY failed");
+ freeaddrinfo(serverAddr);
+ return -1;
+ }
+ }
+
+ /* Bind socket to local address/port */
+ if ( bind(sock, serverAddr->ai_addr, serverAddr->ai_addrlen) < 0 ) {
+ perror("bind() failed");
+ close(sock);
+ freeaddrinfo(serverAddr);
+ return -1;
+ }
+
+ freeaddrinfo(serverAddr);
+
+ /* Start listening incoming connections */
+ if ( listen(sock, max_pending_conn) < 0 ) {
+ perror("listen() failed");
+ close(sock);
+ }
+
+ return sock;
+}
+
+int ucast_client_socket(char* server_ip, char* port) {
+
+ int sock;
+ struct addrinfo *serverAddr;
+ struct addrinfo hints = { 0 }; /* Hints for name lookup */
+ int status;
+
+ /* Resolve destination address */
+ hints.ai_family = PF_UNSPEC;
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_flags = AI_NUMERICHOST;
+ if ((status = getaddrinfo(server_ip, port, &hints, &serverAddr)) != 0 )
+ {
+ fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(status));
+ return -1;
+ }
+
+ /* Create socket */
+ if ( (sock = socket(serverAddr->ai_family, serverAddr->ai_socktype, 0)) < 0 ) {
+ perror("socket() failed");
+ freeaddrinfo(serverAddr);
+ return -1;
+ }
+
+ /* Connect it to the remote server */
+ if ( connect(sock, serverAddr->ai_addr, serverAddr->ai_addrlen) < 0 ) {
+ perror("connect() failed");
+ close(sock);
+ freeaddrinfo(serverAddr);
+ return -1;
+ }
+
+ freeaddrinfo(serverAddr);
+ return sock;
+}
+
diff --git a/draft/mcastseed/src/sockets.h b/draft/mcastseed/src/sockets.h
new file mode 100644
index 0000000..86f7c5b
--- /dev/null
+++ b/draft/mcastseed/src/sockets.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2016 by Ludovic Pouzenc <ludovic@pouzenc.fr>
+ *
+ * Greatly inspired from msock.h written by Christian Beier <dontmind@sdf.org>
+ */
+
+#ifndef SOCKETS_H
+#define SOCKETS_H
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netdb.h>
+/*
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <sys/un.h>
+#include <netinet/tcp.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+*/
+int mcast_recv_socket(char *mcast_ip, char *port, int wanted_so_rcvbuf);
+int mcast_send_socket(char *mcast_ip, char *port, int mcast_ttl, struct addrinfo **mcast_ai);
+
+int ucast_server_socket(char *port, int max_pending_conn);
+int ucast_client_socket(char *server_ip, char *port);
+
+#endif /*SOCKETS_H*/
diff --git a/draft/other-tools/clonezilla/drbl-ocs.conf b/draft/other-tools/clonezilla/drbl-ocs.conf
new file mode 100644
index 0000000..f204029
--- /dev/null
+++ b/draft/other-tools/clonezilla/drbl-ocs.conf
@@ -0,0 +1,414 @@
+# drbl-ocs (Clonezilla) setting
+# Do NOT modify if you are not sure what you are doing!
+OCSMGRD_PORT="6461"
+
+# Create image volumes with a size of X,
+# Annoying...
+# in the past, its unit is KB (for partimage 0.6.2).
+# MB (for partimage 0.6.4)
+# Since we use partimage 0.6.4 now, it's MB.
+# NOTE:
+# If you are using partimage 0.6.5 beta 3, the maximum is 2000 MB, you can not set it larger than 2000. Otherwise it will fails to get the correct set.
+# For other version of partimage, no such problem.
+# Ref:http://www.partimage.org/forums/viewtopic.php?t=481
+# http://sourceforge.net/forum/message.php?msg_id=3761573
+# "0" is also "no split" for clonezilla. Before clonezilla 2.1, if split, the image name will like: hda1.000, hda1.000... which is done by partimage.
+# Without split, the image is like: hda1 (not hda1.000, i.e. without ".000")
+# From clonezilla 2.1 or later, we do not let partimage built function to split it, we use program split to do that.
+# Unit for clonezilla split: MB, by default we set it as 100 GB, no more use unlimite (0) because the reason:
+# 2009/11/05, on Ubuntu 9.10 with upstart 0.6.3 or later, the "/sbin/init" won't accept the boot parameters as an option, it will always read /proc/cmdline, therefore if we use "0", the option will be shown in /proc/cmdline as "ocs_opt=... -i 0" and in /tftpboot/node_root/sbin/init we can not make it by removing "ocs_opt=... -i 0" anymore.
+# The volume limit to split an partition image file.
+# This is for those file systems could support large file, like ext4 a single file size limit is 16 TiB. Here we set it as 1TB
+# Unit: MB (1000*1000)
+VOL_LIMIT_DEFAULT="1000000"
+# Assign VOL_LIMIT_IN_INTERACTIVE as 4096 MB, since some cases when
+# using ocs-sr -x, it's in FAT16B/FAT32, the limitation is
+# 4,294,967,295 bytes (4 GiB - 1)
+# Unit: MB (1000*1000)
+# Ref: http://en.wikipedia.org/wiki/File_Allocation_Table
+# http://en.wikipedia.org/wiki/Megabyte
+VOL_LIMIT_IN_INTERACTIVE="4096"
+
+# Specify the suffix length for spliting the image file of a partition or LV. By default it's 2, i.e. aa, ab, ac...
+# Integer must >= 2.
+split_suf_len="2"
+
+# multicast setting
+# From udpcast menu:
+# --mcast-all-addr address
+# Uses a non-standard multicast address for the control
+# connection (which is used by the sender and receivers to
+# "find" each other). This is not the address that is used to
+# transfer the data.
+# By default mcast-all-addr is the Ethernet broadcast address
+# if ttl is 1, and 224.0.0.1 otherwise. This setting should not
+# be used except in very special situations, such as when
+# 224.0.0.1 cannot be used for policy reasons.
+# Note! It's better to keep 224.0.0.1, For example, if $eth_for_multicast is eth1. In Debian sarge, for different clients in eth2 or eth3, will not be able to connect eth1 to get the multicast packets
+MULTICAST_ALL_ADDR="224.0.0.1"
+MULTICAST_PORT="2232"
+TIME_TO_LIVE_OPT="--ttl 1"
+
+# You can assign extra option for udp-sender, e.g.
+# (1) "--max-bitrate 150m" (150 megabits/sec). See "man udp-sender" for more info.
+# Thanks to Mike Taylor for this idea. Ref: https://sourceforge.net/forum/message.php?msg_id=5281039
+# (2) "--fec 8x16/64"
+# Thanks to Pretzel for this idea. Ref: https://sourceforge.net/p/clonezilla/discussion/Clonezilla_server_edition/thread/6e1e87d4/
+# (3) "--retries-until-drop 50", for example, if you want to reduce the waiting time if some clients fail to continue then udp-sender drops them. By default it will take about 3 to 4 mins, with "--retries-until-drop 50", the time is reduced to about 40 secs. Thanks to Yitzon Belandria <yitzon _at_ gmail com> for sharing this result. Ref: https://sourceforge.net/p/drbl/discussion/250176/thread/70d6fd5e/?limit=25#4496
+udp_sender_extra_opt_default=""
+
+# You can assign extra option for udp-receiver here (very seldom)
+udp_receiver_extra_opt_default=""
+
+# The time to wait when multicast restoring more then 2 partitions.
+# For the first partition, we will ask user. This PART_PREPARE_TIME is for the wait time before next partition (2nd, 3rd... to start)
+# Not! It can not be too short!!! 30 secs is a good number.
+PART_PREPARE_TIME="30"
+# Time to prepare the next partition, like hda1 -> hda2
+SLEEP_TIME_AFTER_PART_CLONED="15"
+
+# time to wait default: sec
+TIME_TO_WAIT_DEFAULT="120"
+MAX_TIME_TO_WAIT_DEFAULT="300"
+#
+# ignore the MBR by using -M, we will ignore the error caused by devfs style
+# in /proc/partitions
+# now use -z0 and --volume=0 in DEFAULT_PARTIMAGE_SAVE_OPT, we will use partimage stdout and pipe it to use gzip/bzip2/lzop to compress it, not the compression function in partimge. --volume=0 is a must for stdout.
+DEFAULT_PARTIMAGE_SAVE_OPT="-M -f3 -o -d -b -c -z0 --volume=0"
+DEFAULT_PARTIMAGE_RESTORE_OPT="-M -f3 -b"
+
+# Extra gzip/lzma/xz option. This is used when using gzip/lzma/xz/lzip/lrzip (non-parallel) to save an image.
+# Adding "--rsyncable" to be friendly to rsync (https://sourceforge.net/p/clonezilla/discussion/Open_discussion/thread/8d5f80a6)
+# //NOTE// Option "--rsyncable" is not an official option for gzip, it's patched by some distributions. (https://sourceforge.net/p/clonezilla/discussion/Open_discussion/thread/8d5f80a6/?limit=25#89e2)
+rsyncable_opt_gzip=""
+if [ -n "$(LC_ALL=C gzip --help 2>&1 | grep -Ew -- "--rsyncable")" ]; then
+ rsyncable_opt_gzip="--rsyncable"
+fi
+extra_gzip_opt="--fast $rsyncable_opt_gzip"
+# -1 (fatest), -2, ... -9 (best compression)
+extra_bzip2_opt="-3"
+extra_lzop_opt="-3"
+extra_lzma_opt="-3"
+extra_xz_opt="-3"
+extra_lzip_opt="-3"
+extra_lrzip_opt="-L 3"
+
+# Use parallel gzip/pbzip2 when restoring
+use_parallel_decompression="yes"
+
+# Available programs for parallel bzip2: pbzip2, lbzip2
+# Default to use lbzip2 since pbzip2 1.0.5 has an issue about memory usage
+# https://sourceforge.net/projects/clonezilla/forums/forum/663168/topic/3517181
+parallel_bzip2_prog="lbzip2"
+
+# Extra pigz option. This is used when using pigz to save an image. For pigz, normally "-b 1024" (1024=1024K) is enough, although you can assign more like "-p 16" (16 threads).
+# Adding "-R" (--rsyncable) to be friendly to rsync (https://sourceforge.net/p/clonezilla/discussion/Open_discussion/thread/8d5f80a6)
+# //NOTE// Option "--rsyncable" is only for newer pigz, it's patched by some distributions. (https://sourceforge.net/p/clonezilla/discussion/Open_discussion/thread/8d5f80a6/?limit=25#89e2)
+rsyncable_opt_pigz=""
+if [ -n "$(LC_ALL=C pigz --help 2>&1 | grep -Ew -- "--rsyncable")" ]; then
+ rsyncable_opt_pigz="--rsyncable"
+fi
+extra_pigz_opt="--fast -b 1024 -p 16 $rsyncable_opt_pigz"
+# Extra pbzip2 option. This is used when using pbzip2 to save an image. For pbzip2, normally "-b9" (9=900K, the default value. It's different from pigz) is enough, although you can assign more like "-p16" (16 processors). Ref: https://sourceforge.net/tracker/?func=detail&atid=671650&aid=3324096&group_id=115473
+extra_pbzip2_opt="-3"
+extra_lbzip2_opt="-3"
+#
+extra_pixz_opt="-3"
+extra_plzip_opt="-3"
+
+# ntfsclone options, for saving and restoring, respectively. These variables will be appended when running ntfsclone.
+ntfsclone_save_extra_opt_def=""
+ntfsclone_restore_extra_opt_def=""
+
+# supported filesystem in partimage
+# ///NOTE/// FAT12 is not supported by partimage.
+partimage_support_fs="ext2 ext3 reiserfs xfs jfs fat16 fat32 vfat hpfs ufs hfs ntfs"
+
+# Supported filesystems in partclone
+# Although now partclone has the following programs:
+# clone.extfs clone.reiser4 clone.xfs clone.hfsp clone.reiserfs
+# This file system name is the same as we get from "parted -s /dev/[hsv]dx? print"
+# 2009/07/15 Since there is an issue when restoring vfat, remove fat*/vfat in the following list
+# partclone_support_fs="hfs+ hfsplus reiser4 xfs reiserfs ext2 ext3 ext4 ext4dev ntfs"
+# partclone 0.1.1-16 has fixed the issue about FAT. Turn it on:
+#partclone_support_fs="hfs+ hfsplus reiser4 xfs reiserfs ext2 ext3 ext4 ext4dev fat12 fat16 fat32 vfat ntfs"
+# 2009/09/12 partclone.xfs v0.1.1 (Rev:304M) is buggy. remove xfs in the following list.
+# 2009/09/18 partclone.xfs v0.1.9 was fixed. No more xfs problem. Add xfs in the following.
+# 2012/01/18 partclone.fstype from version 0.2.45 will tell vmfs3 and vmfs5. Therefore no more VMFS_volume_member and vmfs, we use vmfs3 and vmfs5. Since vmfs5 support in partclone 0.2.45 is still buggy due to libvmfs not supported well yet, we won't enable it until it's fully supported.
+# 2012/04/05 Enable vmfs5 since partclone 0.2.46 now works for vmfs5.
+# 2013/03/01 Enable minix since partclone 0.2.59 now works for minix.
+# 2014/12/01 Temporarily disabled vmfs3 and vmfs5 due to this issue:
+# https://github.com/glandium/vmfs-tools/issues/12
+# 2015/07/06 Enabled vmfs3 and vmfs5 since partclone >= 0.2.79 fixed this issue.
+partclone_support_fs="hfs+ hfsplus reiser4 reiserfs xfs jfs ext2 ext3 ext4 ext4dev fat12 fat16 fat32 vfat ntfs ufs btrfs exfat minix vmfs3 vmfs5 f2fs nilfs2"
+# Options to be appended in the partclone command, for save and restore.
+# For example, for restoring, if you want to ignore device size checking, "-C" can be put, like:
+# PARTCLONE_RESTORE_OPT_INIT="-C"
+# From Partclone 0.2.61, we switch to use "-z" instead of "-m":
+PARTCLONE_SAVE_OPT_INIT="-z 10485760"
+PARTCLONE_RESTORE_OPT_INIT="-z 10485760"
+PARTCLONE_RESTORE_ONTHEFLY_OPT_INIT="-z 10485760"
+
+# The debug_level for partimage
+# it's better we turn off the debug mode by default, this will reduce the server's loading.
+# It seems in partiamge 0.6.5-beta2, when using --debug=0 to save NTFS partition
+# partimage will crash. However, 0.6.5-beta1 works without this problem.
+# To avoid this, we set debug_level=1 as default value.
+debug_level="1"
+#
+MAX_DIALOG_HEIGHT="10"
+# dialog width for showing images
+RESTORE_DIA_WIDTH="70"
+#
+POSTACTION_DEF="reboot"
+# The poweroff/reboot options, which are used when clone finishes.
+# If you are impatient, you can set it as "-f -n" so it will force halt or reboot, don't call shutdown sequence, and do not sync. i.e. poweroff/reboot immediately. Note! This might fail your wake-on-LAN since it's not a normal soft-shutdown.
+# In some case (clonezilla live), we might overwrite this option to let it do normal poweroff/reboot.
+HALT_REBOOT_OPT=""
+# To avoid all the clients notify at almost same time, we use random sleep before send info. This is the maximun time limit (in secs).
+NOTIFY_OCS_SERVER_TIME_LIMIT="20"
+
+# OCS_PRERUN_DIR & OCS_POSTRUN_DIR
+# Run the script in the direcoty $OCS_POSTRUN_DIR before clone starts and finishes. The command will be run before that assigned in -p or --postaction."
+OCS_PRERUN_DIR="$DRBL_SCRIPT_PATH/prerun/ocs/"
+OCS_POSTRUN_DIR="$DRBL_SCRIPT_PATH/postrun/ocs/"
+
+# PREFIX for M$ windows hostname
+WIN_HOSTNAME_PREFIX="PC"
+
+# The way to trigger clonezilla client to start ocs-sr, 3 options:
+# (1) ocs-run.param: the ocs_opt is put in client's /etc/ocs/ocs-run.param
+# (2) proc-cmdline: the ocs_opt is put in client's /proc/cmdline, which is actually from server's pxelinux config /tftpboot/nbi_img/pxelinux.cfg/default or others.
+# (3) both: ocs_opt will be put both in /etc/ocs/ocs-run.param and server's pxelinux config /tftpboot/nbi_img/pxelinux.cfg/default or others. However, when ocs-run is run in client, it will use the ocs_opt from /tftpboot/nbi_img/pxelinux.cfg/default (i.e. /proc/cmdline)
+# We choose to use ocs_opt from proc-cmdline if it's both so that we can always put "ocs_client_trig_type=both" in this file. If we want to interactively debug clonezilla in client, just use "/etc/init.d/ocs-run -p start".
+# Note: SET IT AS "both" ONLY, other modes are only for backward compatibility testing.
+ocs_client_trig_type="both"
+
+# modify client's /etc/inittab when start clonezilla service.
+# If yes, both client's inittab and pxelinux config will be modified.
+# If no, just put "1" in pxelinux config so it will show in client's /proc/cmdline.
+# Available value: yes/no
+# Note: Do NOT set it as "yes", it's only for backward compatibility testing.
+modify_client_etc_inittab="no"
+
+# When use use drbl-ocs stop to stop clonezilla, do we want to clean the ocs related services (ssh, arm-wol...).
+# Available value: yes/no
+# Note: Do NOT set it as "yes", it's only for backward compatibility testing.
+clean_client_all_ocs_related_srv="no"
+
+# clean dhcpd lease in the server when it's clonezilla box mode.
+# Note: Do NOT set it as "yes", it's only for backward compatibility testing.
+clean_dhcpd_lease_in_clone_box_mode="no"
+
+# flag to regenerate the DRBL SSI template tarball
+# Note: Do NOT set it as "yes", it's only for backward compatibility testing.
+regen_drbl_ssi_template_tarball="no"
+
+# flag to re-put ocs related files in client's rc1.d (ocs-functions)
+# Note: Do NOT set it as "yes", it's only for backward compatibility testing.
+re_put_ocs_related_srv_in_client_rc1d="no"
+
+# flag to re-put S19ocs-run and all other services from template node's
+# client's rc1.d to /tftpboot/node_root/etc/drbl_ssi/rc1.d/ (drbl-ocs)
+# Note: Do NOT set it as "yes", it's only for backward compatibility testing.
+copy_rc1d_for_drbl_ssi_clone_box="no"
+
+# Flag to use dd or partclone in ocs (not ocs-onthefly)
+# Sector to sector copy program in ocs (not ocs-onthefly). Available progs are: dd, partclone
+S2S_IMAGE_PROG_IN_OCS="partclone"
+
+# Since dd does not report status with any option, we have to send SIGUSR1 to tell dd to report every some secs when using dd to save or restore an image. This will only take effect when S2S_IMAGE_PROG_IN_OCS = dd
+# dd report interval (secs)
+dd_report_interval="20"
+
+# Flag to use ntfsclone, dd, or partclone in ocs-onthefly
+# ///NOTE/// 2008/May/11 by Steven Shiau: local part to remote part still has a problem with partclone: e.g. local hda1 to remote hda3, due to the filesystem of source partition can not be informed to target machine in the current mechanism, it will fail if the filesystem is not the same with the list on $src_pt_info.
+# Something like: partclone.reiserfs can't restore from the image which filesystem is XFS not REISERFS
+USE_NTFSCLONE_IN_OCS_ONTHEFLY="no"
+USE_PARTCLONE_IN_OCS_ONTHEFLY="yes"
+# Sector to sector copy program. Available progs are: dd, partclone
+S2S_CLONE_PROG_IN_OCS_ONTHEFLY="partclone"
+
+# Hidden data space size limit. This is the max hidden data space size limit, if it's larger than
+# this, we won't create the file (e.g. sda-hidden-data-after-mbr).
+# Unit: MB
+hidden_data_after_mbr_limit="1024"
+
+# some presettings about clonezilla when running dcs
+# The prompt for user to input the disk when saving
+savedisk_preset="sda"
+# The prompt for user to input the partitions when saving
+saveparts_preset="sda1 sda2"
+
+# Options for encryption of image dir. We use ecryptfs to mount that. Therefore the options are from ecryptfs.
+# Run "man ecryptfs" for more details.
+ocs_ecryptfs_cipher="aes"
+ocs_ecryptfs_key_bytes="16"
+
+# checksum algorithm for files in the partition.
+# This is used to create the checksum for all the regular files in a partition or LV.
+# Available command: md5sum, sha1sum, sha256sum, sha512sum
+chksum_cmd_for_files_in_dev="md5sum"
+
+# Option for mounting file system for image repository ($ocsroot)
+# Ref: https://sourceforge.net/tracker/?func=detail&atid=671653&aid=2949613&group_id=115473
+# For cifs, noatime and nodiratime won't be honored, but cifs won't exit with error. It just ignores nodiratime.
+# For sshfs, nodiratime is not honored, and sshfs will exit with error.
+# For nfs, both noatime and nodiratime are honored.
+# Therefore we put noatime only, and add "nodiratime" for nfs, hfsplus and other fs.
+ocsroot_def_mnt_opt="noatime"
+
+# For davfs2 config. Do not modify unless you know what you are doing.
+davfs2_buf_size="10240" #KiByte maybe more
+davfs2_use_locks="0"
+davfs2_use_expect100="1"
+davfs2_use_compression="1"
+davfs2_cache_size_def="64" # MiByte. This is default value if appropriate free memory size can not be decided.
+davfs2_delay_upload="0"
+# The ratio for the cache size to free disk space (RAM disk actually) in Clonezilla live env.
+# Value must between 0 and 1. Default:0.2. Better not to be larger than 0.5
+# ///NOTE/// The cache mechanism of davfs2 is a problem (ref: https://sourceforge.net/forum/forum.php?thread_id=2248597&forum_id=82589). Although we use ratio_davfs2_cache_2_free_disk and free ramdisk space to set the cache size of davfs2, according to "man davfs2.conf":
+# cache_size: The amount of disk space in MiByte that may be used. mount.davfs will always take enough space to cache open files, ignoring this value if necessary.
+# Therefore the cache_size get in ocs-tune-conf-for-webdav is actually applied to VOL_LIMIT_DEFAULT and VOL_LIMIT_IN_INTERACTIVE, too, so that single file won't be over size.
+ratio_davfs2_cache_2_free_disk="0.2"
+# When webdav is the image repository, we need longer suffix length for "split" command, i.e. the option "-a" of split.
+# When "3" is used, Clonezilla live in a machine with 1 GB RAM could have 0.8 GB ramdisk size, so:
+# 26*26*26*0.8/5=2812 GB
+davfs2_split_suf_len_def="3"
+
+# This part is for DRBL/Clonezilla/GParted live
+# Settings
+# Debian Repository when creating debian live. It's better to use http://httpredir.debian.org/debian. However, some mirror site seems to be not so stable, and we might encounter the error like:
+# W: Failed to fetch http://httpredir.debian.org/debian/dists/sid/main/binary-i386/Packages Hash Sum mismatch
+# Therefore we use http://free.nchc.org.tw which can be fixed by Clonezilla team if something goes wrong.
+debian_mirror_url_def="http://free.nchc.org.tw/debian"
+debian_mirror_security_url_def="http://security.debian.org/debian-security"
+# Ubuntu repository when creating ubuntu live
+ubuntu_mirror_url_def="http://free.nchc.org.tw/ubuntu"
+ubuntu_mirror_security_url_def="http://free.nchc.org.tw/ubuntu"
+# DRBL repository
+DRBL_REPOSITORY_URL_def="http://free.nchc.org.tw/drbl-core"
+# URL to the repository key
+DRBL_GPG_KEY_URL="http://drbl.org/GPG-KEY-DRBL"
+
+# Clonezilla live iso template file name. By default we use debian-live-for-ocs.iso as the template file name in ocs-iso and ocs-live-dev.
+DEBIAN_ISO_DEF="debian-live-for-ocs.iso"
+# http://free.nchc.org.tw/drbl-core/iso/MD5SUMS
+md5_file="MD5SUMS"
+uni_font_file="unifont.bgf"
+syslinux_exe_file="syslinux.exe"
+makeboot_exe_file="MakeBoot.exe"
+wget_opt="-r -l1 --no-parent --passive-ftp -e robots=off -q -nd --retr-symlinks"
+CD_FILE_LIMIT="650" # unit: MB
+FILE_LIMIT="4500" # unit: MB
+# The kernel and initrd files are used just in case, normally ocs-iso or ocs-live-dev will search them.
+kernel_file="vmlinuz1"
+initrd_file="initrd1.img"
+# Client framebuffer mode
+VGA_MODE_DEF="788"
+BG_MODE_DEF="graphic"
+# Default console columns for adjust font and its size in live cd. This only will affect KMS mode, not other framebuffer mode.
+console_prefer_font="TerminusBold"
+# We want to show 100 characters in the screen (width)
+console_prefer_cols="100"
+# //NOTE// Generally this variable BOOT_PARAM_DEFAULT will be overwritten by the function get_live_boot_param in ocs-functions.
+BOOT_PARAM_DEFAULT="boot=live union=overlay"
+# Common boot parameters that Clonezilla live (iso/zip) need.
+# //NOTE// "nomodeset" is required for framebuffer mode (vga=788... or video=uvesafb:....). If no "nomodeset", it will enter kernel mode setting (KMS) mode. Then framebuffer mode will only take effect in the early booting. It won't work when we use Clonezilla. Therefore here we keep "nomodeset"
+supp_boot_param_ocs_live_iso="noswap edd=on nomodeset"
+# For Clonezilla live USB, we add "noeject" so by default we do not ask user to remove the USB flash drive when rebooting/halting.
+supp_boot_param_ocs_live_dev="$supp_boot_param_ocs_live_iso noeject"
+# Possible live media path (when live CD/USB-disk boots, the reall live media is mounted as...)
+# casper 1.77+debian-7 uses /live_media, 1.110 uses /cdrom
+# live-initramfs uses /live/image.
+# For Debian Live 3.x, rootfs is in /lib/live/mount/medium/live/filesystem.squashfs
+live_media_path_chklist="/lib/live/mount/medium /lib/live/image /live/image /live_media /cdrom"
+# Possible kernel/initrd paths in live CD/USB-disk are /casper (created by casper), /live (created by live-initramfs) or /isolinux (created by live pacakge with casper), or "/" (when using toram="filesystem.squashfs", filesystem.squashfs will be copied to / in the ram disk).
+live_sys_files_dir_list="live / isolinux casper"
+# For live-package, it's "/usr/share/make-live/hooks", for live-helper...
+live_pkg_hook_dir="/usr/share/make-live/hooks"
+# Some locales (especially CJK) need bterm to show
+locale_need_bterm="zh_TW zh_CN zh_HK zh_SG ja_JP ko_KR"
+# unifont.bgf path is required by bterm in clonezilla live.
+uni_font="$DRBL_SCRIPT_PATH/lib/unifont.bgf"
+# fb_term is either bterm or jfbterm. If not assigned, clonezilla live will try to use jfbterm first, then bterm. However, due to a bug of jfbterm in Ubuntu intrepid, bterm will be used in intrepid anyway.
+fb_term=""
+# Live build and debootstrap minimal version to create debian/gparted/drbl live, i.e. >= this
+lh_ver_required="3.0.5-1"
+debootstrap_ver_required="1.0.52"
+
+# Use RawCHS from EDD (sysfs interface to BIOS EDD information) for partclone.ntfsfixboot
+use_RawCHS_from_EDD_for_ntfsreloc="yes"
+
+# Use RawCHS from sfdisk for partclone.ntfsfixboot
+use_RawCHS_from_sfdisk_for_ntfsreloc="yes"
+
+# The run_grub2_from_restored_os_mode is for us to decide to run grub2 from the restored OS. Possible values are "yes", "no" or "". If "yes", Clonezilla will chroot and run grub-install. If "no", use the grub in the running system to run grub-install --root-directory=.... If "", then we will test in ocs-functions to use "yes" mode first, if fails, then "no" mode.
+# //NOTE// The OS arch in the running environment must be the same as that in the restored OS, otherwise, e.g. running OS is i386, and restored OS is x86-64, if this option is "yes", you will get error messages when run grub2 install:
+# "cannot run command" "Exec format error"
+run_grub2_from_restored_os_mode=""
+
+# A mounting point for temporary (middle) use.
+pre_ocsroot_path="/tmp/ocsroot_bind_root"
+
+# Log files for the output of clonezilla and related files.
+OCS_LOGFILE="/var/log/clonezilla.log" ; export OCS_LOGFILE
+OCS_NETCFG_LOG="/var/log/ocs-netcfg.log" ; export OCS_NETCFG_LOG
+OCS_MOUNT_LOG="/var/log/ocs-mount.log" ; export OCS_MOUNT_LOG
+
+
+###################################################
+### Settings about Clonezilla-live based client ###
+###################################################
+# The flag to use NFSRoot-based or Clonezilla-live-based system in the client
+# Available name: nfsroot, clonezilla-live
+diskless_client_os="nfsroot"
+
+# If diskless_client_os is clonezilla-live, the URL to download the clonezilla live. e.g.
+# http://downloads.sourceforge.net/clonezilla/clonezilla-live-1.2.5-6-i486.iso
+# http://sourceforge.net/projects/clonezilla/files/clonezilla_live_testing/clonezilla-live-1.2.5-6-i486.iso
+# Or
+# http://free.nchc.org.tw/clonezilla-live/testing/clonezilla-live-1.2.5-6-i486.iso
+# If you want to use a local copy, you can make it like:
+# iso_url_for_pxe_ocs_live_default="file://tmp/clonezilla-live-20130902-saucy-amd64.iso"
+iso_url_for_pxe_ocs_live_default=""
+
+# Flags when running drbl-ocs, shall we remove some boot parameters from pxelinux config file for Clonezilla live. (only takes effect when diskless_client_os="clonezilla-live"
+# Available flag for remove_some_boot_param_for_pxe_ocs_live: yes, no
+remove_some_boot_param_for_pxe_ocs_live="yes"
+
+# If remove_some_boot_param_for_pxe_ocs_live is yes, what shall we remove:
+# By default we remove quiet, splash, vga and video, we want it to be more verbose when booting clients
+boot_param_2_be_removed_in_pxe_ocs_live="quiet splash vga video"
+
+# Boot parameters to preseed: ocs_live_keymap, ocs_lang, ocs_daemonon, ocs_prerun,
+# Available keymaps: "NONE" (won't change the default layout), "/usr/share/keymaps/i386/azerty/fr-latin9.kmap.gz" (French keyboard)...
+ocs_live_keymap_for_pxe_ocs_live="NONE"
+
+# Available languages: en_US.UTF-8 es_ES.UTF-8 fr_FR.UTF-8 it_IT.UTF-8 ja_JP.UTF-8 zh_CN.UTF-8 zh_TW.UTF-8
+# However, the CJK might have problem to be shown on the screen because in the PXE client we do not always enter jfbterm unless it's Clonezilla live mode. Therefore it's better to keep it as "en_US.UTF-8"
+ocs_lang_for_pxe_ocs_live="en_US.UTF-8"
+
+# The daemon to be started, e.g. ssh (the file name in /etc/init.d/)
+ocs_daemonon_for_pxe_ocs_live="ssh"
+
+# The account and password for the Clonezilla live clients of Clonezilla SE.
+# //NOTE// These variables are not used to assign the default username and password of Clonezilla live when creating it by create-debian-live or create-ubuntu-live. They are only used for ssh login by drbl-doit. Therefore if someday Debian live changes its default username and password, we have to change here, too.
+ocs_live_username="user"
+ocs_live_passwd="live"
+
+# Prompt messages preference. Could be "tui" or "cmd". If not assigned, cmd is the default.
+# //NOTE// Not all the messages are supported to be shown in dialog/whiptail
+ocs_prompt_mode=""
+
+# The command to run before ocs_live_run command is executed. E.g. "mount -t nfs 192.168.120.254:/home/partimag/ /home/partimag". If not assigned, by default we will use:
+# mount -t nfs $IP:/home/partimag/ /home/partimag
+# where $IP is the 1st IP address on DRBL server.
+ocs_prerun_for_pxe_ocs_live=""
+
+### EFI network booting on Clonezilla SE ###
+# The option to decide if the EFI network booting is set as 1st priority
+# in the boot order in EFI NVRAM after the image is restored by Clonezilla SE
+# Values: yes, no
+efi_netboot_1st_in_nvram="yes"
+###################################################
diff --git a/draft/other-tools/clonezilla/ocs-expand-gpt-pt b/draft/other-tools/clonezilla/ocs-expand-gpt-pt
new file mode 100755
index 0000000..191bc06
--- /dev/null
+++ b/draft/other-tools/clonezilla/ocs-expand-gpt-pt
@@ -0,0 +1,386 @@
+#!/bin/bash
+# Author: Steven Shiau <steven _at_ nchc org tw>
+# License: GPL
+# A script to expand the GPT partition table by disk size ratio.
+# ///NOTE/// This program only works for GPT partition table, not for MBR.
+
+# Load DRBL setting and functions
+DRBL_SCRIPT_PATH="${DRBL_SCRIPT_PATH:-/usr/share/drbl}"
+
+. $DRBL_SCRIPT_PATH/sbin/drbl-conf-functions
+. /etc/drbl/drbl-ocs.conf
+. $DRBL_SCRIPT_PATH/sbin/ocs-functions
+
+# Settings
+# Flag to check target disk size before creating partition table
+chk_tgt_disk_size_bf_mk_pt="yes"
+
+#
+cmd_name="$(basename $0)"
+#
+USAGE() {
+ echo "$cmd_name: To create a proportional MBR partition table (not GPT) in a disk based on a existing partition table (sfdisk format)"
+ echo "Usage:"
+ echo " $cmd_name [OPTION] PARTITION_TABLE_FILE TARGET_DEVICE"
+ echo
+ echo "OPTION:"
+ echo " -b, --batch Run $cmd_name in batch mode, i.e. without any prompt or wait to press enter. VERY DANGEROUS!"
+ echo "$cmd_name will honor experimental variable EXTRA_SFDISK_OPT and use it as the option for sfdisk."
+ echo " -icds, --ignore-chk-dsk-size-pt Skip checking destination disk size before creating the partition table on it. By default it will be checked and if the size is smaller than the source disk, quit."
+ echo "Example:"
+ echo " To create a proportional partition table on disk /dev/sda based on /home/partimag/IMAGE/sda-pt.sf, use:"
+ echo "$cmd_name /home/partimag/IMAGE/sda-pt.sf /dev/sda"
+}
+
+# Parse command-line options
+while [ $# -gt 0 ]; do
+ case "$1" in
+ -b|--batch)
+ batch_mode="yes"
+ shift;;
+ -icds|--ignore-chk-dsk-size-pt)
+ chk_tgt_disk_size_bf_mk_pt="no"
+ shift;;
+ -*) echo "${0}: ${1}: invalid option" >&2
+ USAGE >& 2
+ exit 2 ;;
+ *) break ;;
+ esac
+done
+
+# Original sfdisk format file
+orig_sf=$1 # orig_sf is like: /home/partimag/IMAGE/sda-pt.sf
+target_disk=$2 # target_disk is like: /dev/sda
+
+#
+ask_and_load_lang_set $specified_lang
+
+#
+if [ -z "$orig_sf" ]; then
+ [ "$BOOTUP" = "color" ] && $SETCOLOR_FAILURE
+ echo "No source partition table file was assigned!"
+ [ "$BOOTUP" = "color" ] && $SETCOLOR_NORMAL
+ USAGE
+ echo "$msg_program_stop!"
+ exit 1
+fi
+if [ ! -f "$orig_sf" ]; then
+ [ "$BOOTUP" = "color" ] && $SETCOLOR_FAILURE
+ echo "Source partition table file \"$orig_sf\" was _NOT_ found"
+ [ "$BOOTUP" = "color" ] && $SETCOLOR_NORMAL
+ echo "$msg_program_stop!"
+ exit 1
+fi
+
+if [ -z "$target_disk" ]; then
+ [ "$BOOTUP" = "color" ] && $SETCOLOR_FAILURE
+ echo "No target disk was assigned!"
+ [ "$BOOTUP" = "color" ] && $SETCOLOR_NORMAL
+ echo "$msg_program_stop!"
+ USAGE
+ exit 1
+fi
+
+#
+if [ "$batch_mode" != "yes" ]; then
+ [ "$BOOTUP" = "color" ] && $SETCOLOR_WARNING
+ echo "This program will create a partition table in $target_disk"
+ echo "ALL THE DATA IN THE TARGET DEVICE WILL BE ERASED!!!"
+ [ "$BOOTUP" = "color" ] && $SETCOLOR_NORMAL
+ echo -n "$msg_are_u_sure_u_want_to_continue ? (y/N) "
+ read continue_confirm_ans
+ case "$continue_confirm_ans" in
+ y|Y|[yY][eE][sS])
+ echo "$msg_ok_let_do_it!"
+ ;;
+ *)
+ echo "$msg_program_stop!"
+ exit 1
+ esac
+fi
+
+# Check if GPT disk, if yes, exit
+if `is_mbr_partitition_table_disk $target_disk`; then
+ [ "$BOOTUP" = "color" ] && $SETCOLOR_FAILURE
+ echo "Disk $target_disk is MBR format. This program only works for GPT format."
+ [ "$BOOTUP" = "color" ] && $SETCOLOR_NORMAL
+ echo "$msg_program_stop!"
+ exit 1
+fi
+
+# Check if the format of sfdisk is created by sfdisk >= 2.26
+if [ -z "$(LC_ALL=C grep -E "^label: gpt" $orig_sf)" ]; then
+ [ "$BOOTUP" = "color" ] && $SETCOLOR_FAILURE
+ echo "Partition table file \"$orig_sf\" does not contain GPT info."
+ echo "Make sure it's GPT format and outputted by sfdisk >= 2.26."
+ [ "$BOOTUP" = "color" ] && $SETCOLOR_NORMAL
+ echo "$msg_program_stop!"
+ exit 1
+fi
+
+# No size info in sda-pt.sf, we have to use sda-pt.parted.
+orig_parted_tab="${orig_sf/.sf/.parted}"
+orig_parted_dir="$(dirname $orig_sf)"
+if [ ! -e "$orig_parted_tab" ]; then
+ [ "$BOOTUP" = "color" ] && $SETCOLOR_FAILURE
+ echo "$orig_parted_tab was not found! It is required so that we know the original disk size! Maybe your Clonezilla image is too old ? You can try to create such an file in your source machine by: \"parted -s /dev/$SOURCE_DEV unit s print > $orig_parted_tab\" (Replace $SOURCE_DEV with your device name)"
+ [ "$BOOTUP" = "color" ] && $SETCOLOR_NORMAL
+ echo "$msg_program_stop!"
+ exit 1
+fi
+
+# parted output format example:
+# Disk /dev/sda: 16777215s
+# Sector size (logical/physical): 512B/512B
+# Partition Table: msdos
+#
+# Number Start End Size Type File system Flags
+# 1 63s 586844s 586782s primary boot
+# 2 586845s 978074s 391230s primary
+# 3 978075s 1955204s 977130s primary
+# 4 1955205s 16776584s 14821380s extended
+# 5 1955268s 2151764s 196497s logical
+# 6 2151828s 2542994s 391167s logical
+# 7 2543058s 16776584s 14233527s logical
+
+ori_disk_size="$(LC_ALL=C grep -E "^Disk /dev" $orig_parted_tab | awk -F":" '{print $2}' | sed -e "s/s$//g")"
+# If nothing in target disk, parted will show like this:
+# sudo parted -s /dev/sda unit s print
+# Error: Unable to open /dev/sda - unrecognised disk label.
+
+if ! LC_ALL=C parted -s $target_disk unit s print &>/dev/null; then
+ # Try to create a partition table so that we can read the size via parted -s $dev unit s print
+ echo "No initial GPT table on disk $target_disk. Create one now by:" | tee --append $OCS_LOGFILE
+ echo "parted -s $target_disk mklabel gpt" | tee --append $OCS_LOGFILE
+ LC_ALL=C parted -s $target_disk mklabel gpt | tee --append $OCS_LOGFILE
+fi
+tgt_disk_size="$(LC_ALL=C parted -s $target_disk unit s print | grep -E "^Disk /dev" | awk -F":" '{print $2}' | sed -e "s/s$//g")"
+
+#
+ratio=$(echo "scale=10; $tgt_disk_size / $ori_disk_size" | bc -l) || exit 1
+echo "The ratio for target disk size to original disk size is $ratio."
+if [ "$chk_tgt_disk_size_bf_mk_pt" = "yes" ]; then
+ if [ "$(LC_ALL=C echo "$ratio < 1" | bc -l)" = "1" ]; then
+ [ "$BOOTUP" = "color" ] && $SETCOLOR_FAILURE
+ echo "The target disk (size=$tgt_disk_size sectors) is smaller than the source disk (size=$ori_disk_size sectors)!"
+ echo "Clonezilla won't be able to restore a partition image to smaller partition!"
+ [ "$BOOTUP" = "color" ] && $SETCOLOR_NORMAL
+ echo "Program terminated!"
+ exit 1
+ fi
+fi
+
+new_sf="$(mktemp /tmp/new_sf.XXXXXX)" || exit 1
+new_sf_tmp="$(mktemp /tmp/new_sf_tmp.XXXXXX)" || exit 1
+# Increase. Example for sfdisk format:
+# =====================================
+# label: gpt
+# label-id: ADC40CC4-77D7-4858-8F0E-8BC2447052AE
+# device: /dev/sda
+# unit: sectors
+# first-lba: 34
+# last-lba: 125829086
+#
+# /dev/sda1 : start= 2048, size= 409600, type=C12A7328-F81F-11D2-BA4B-00A0C93EC93B, uuid=E8D785ED-A65F-4EA1-ADF0-CC61E75677EB
+# /dev/sda2 : start= 411648, size= 1024000, type=EBD0A0A2-B9E5-4433-87C0-68B6B72699C7, uuid=AB72A495-4141-4500-AE48-B7AC492160B4
+# /dev/sda3 : start= 1435648, size= 40505344, type=E6D6D379-F507-44C2-A23C-238F2A3DF928, uuid=C7A87C41-72FD-41DB-9B10-DF3568A4089D
+# =====================================
+# label: gpt
+# label-id: D4165706-2FAD-491E-82CA-7866982D196B
+# device: /dev/sda
+# unit: sectors
+# first-lba: 34
+# last-lba: 125829086
+#
+# /dev/sda1 : start= 2048, size= 614400, type=DE94BBA4-06D1-4D40-A16A-BFD50179D6AC, uuid=0802BFF4-2549-4CD4-A1CA-93D54AEE26F0, name="Basic data partition", attrs="RequiredPartiton GUID:63"
+# /dev/sda2 : start= 616448, size= 202752, type=C12A7328-F81F-11D2-BA4B-00A0C93EC93B, uuid=C9C164D5-5E0F-476D-B2EA-A4D818A266CF, name="EFI system partition", attrs="GUID:63"
+# /dev/sda3 : start= 819200, size= 262144, type=E3C9E316-0B5C-4DB8-817D-F92DF00215AE, uuid=15D5A869-5C6E-4F2C-B234-7D6B91B58555, name="Microsoft reserved partition", attrs="GUID:63"
+# /dev/sda4 : start= 1081344, size= 124745728, type=EBD0A0A2-B9E5-4433-87C0-68B6B72699C7, uuid=E53DEA7E-2639-4AE9-9DFD-DDD2406C3EC0, name="Basic data partition"
+# =====================================
+
+# We only need those required info
+grep -E "^/dev" $orig_sf > $new_sf_tmp
+# start=291579750 or size=291563622 is not a good format for us to parse, we need a space between "=" and "number"
+# i.e.
+# No good:
+# /dev/sda1 : start= 63, size=291563622, Id= 7, bootable
+# /dev/sda2 : start=291579750, size= 20980890, Id= 7
+# /dev/sda3 : start= 0, size= 0, Id= 0
+# /dev/sda4 : start= 0, size= 0, Id= 0
+#
+# Good:
+# /dev/sda1 : start= 63, size= 291563622, Id= 7, bootable
+# /dev/sda2 : start= 291579750, size= 20980890, Id= 7
+# /dev/sda3 : start= 0, size= 0, Id= 0
+# /dev/sda4 : start= 0, size= 0, Id= 0
+
+# //NOTE// For partitions number >=10, it looks like:
+# label: gpt
+# label-id: 56C0DC13-942C-493F-B141-F9903658453B
+# device: /dev/sda
+# unit: sectors
+# first-lba: 34
+# last-lba: 125829086
+#
+# /dev/sda1 : start= 2048, size= 2097152, type=0FC63DAF-8483-4772-8E79-3D69D8477DE4, uuid=4F5FF221-D350-4CE7-BADA-20022DA453CC, name="Linux filesystem"
+# /dev/sda2 : start= 2099200, size= 2097152, type=0FC63DAF-8483-4772-8E79-3D69D8477DE4, uuid=975A9722-E2ED-46CC-B0E1-995095A6BB66, name="Linux filesystem"
+# /dev/sda3 : start= 4196352, size= 2097152, type=0FC63DAF-8483-4772-8E79-3D69D8477DE4, uuid=A27C9E16-70B1-48D0-9A3F-915AFBA8FCC7, name="Linux filesystem"
+# /dev/sda4 : start= 6293504, size= 2097152, type=0FC63DAF-8483-4772-8E79-3D69D8477DE4, uuid=3E134963-5B14-49C1-9F83-9BAF9C5F61C9, name="Linux filesystem"
+# /dev/sda5 : start= 8390656, size= 2097152, type=0FC63DAF-8483-4772-8E79-3D69D8477DE4, uuid=FB1FB744-E6DC-48FA-9891-256AD0F3CF7C, name="Linux filesystem"
+# /dev/sda6 : start= 10487808, size= 2097152, type=0FC63DAF-8483-4772-8E79-3D69D8477DE4, uuid=BB764A64-35BF-4415-A8D6-84642339FB3F, name="Linux filesystem"
+# /dev/sda7 : start= 12584960, size= 2097152, type=0FC63DAF-8483-4772-8E79-3D69D8477DE4, uuid=B0D77252-85AB-4AFB-8866-38C21605DB7B, name="Linux filesystem"
+# /dev/sda8 : start= 14682112, size= 2097152, type=0FC63DAF-8483-4772-8E79-3D69D8477DE4, uuid=79E8897A-B821-4C5D-A481-5F8628C57D2E, name="Linux filesystem"
+# /dev/sda9 : start= 16779264, size= 2097152, type=0FC63DAF-8483-4772-8E79-3D69D8477DE4, uuid=2CA8A44E-9A2B-475A-BFC8-1E6295847F6A, name="Linux filesystem"
+# /dev/sda10 : start= 18876416, size= 2097152, type=0FC63DAF-8483-4772-8E79-3D69D8477DE4, uuid=B454EC36-562F-4844-B8D9-28C5C1C111C9, name="Linux filesystem"
+# /dev/sda11 : start= 20973568, size= 104855519, type=0FC63DAF-8483-4772-8E79-3D69D8477DE4, uuid=FA55D210-62EE-4948-A853-FDD4360CDCB5, name="Linux filesystem"
+
+# Therefore we force to put a space no matter there is already space or not
+perl -pi -e "s/start=/start= /g" $new_sf_tmp
+perl -pi -e "s/size=/size= /g" $new_sf_tmp
+# Remove ":" in the temp file, since it's only for parsing, won't be used for sfdisk /dev/... < $new_sf_tmp ...
+perl -pi -e "s/://g" $new_sf_tmp
+
+# Get the line number of "last-lba:"
+header_line="$(LC_ALL=C grep -E -n "^last-lba:" $orig_sf | awk -F":" '{print $1}')"
+if [ -z "$header_line" ]; then
+ [ "$BOOTUP" = "color" ] && $SETCOLOR_FAILURE
+ echo "Failed to find the line \"last-lba:\" in $orig_sf!"
+ [ "$BOOTUP" = "color" ] && $SETCOLOR_NORMAL
+ echo "$msg_program_stop!"
+ exit 1
+fi
+# We do not output the line "last-lba:" in the generated line. Otherwise we have to calculate the last-lba number.
+LC_ALL=C head -n "$((header_line -1))" $orig_sf > $new_sf
+echo "" >> $new_sf
+start_no_keep=""
+size_no_keep=""
+append_to_next="0"
+# Initial gap for the 1st partition. After this, it should be 0.
+logical_part_gap="2"
+while read dev start start_no size size_no type uuid name attrs; do
+ pt_no="$(LC_ALL=C get_part_number $dev | sed -r -e "s|^[^[:digit:]]*||g")"
+ if `is_partition $dev` ; then
+ [ -z "$start_no_keep" ] && start_no_keep=${start_no/,/} # The 1st one
+ [ -z "$size_no_keep" ] && size_no_keep=0
+ if [ "${size_no/,/}" -eq 0 ]; then
+ start_no=0
+ size_no=0
+ else
+ # If we found the partition is EFI System partition, keep the size.
+ # EFI System partition: C12A7328-F81F-11D2-BA4B-00A0C93EC93B,
+ # Ref: https://en.wikipedia.org/wiki/GUID_Partition_Table
+ expand="true"
+ if [ -n "$(echo $type | grep -iE "type=C12A7328-F81F-11D2-BA4B-00A0C93EC93B")" ]; then
+ expand="false"
+ [ "$BOOTUP" = "color" ] && $SETCOLOR_WARNING
+ echo "\"EFI System partition\" found. Not to expand this partition."
+ [ "$BOOTUP" = "color" ] && $SETCOLOR_NORMAL
+ fi
+ # If we found the partition is MS Windows "system reserved partition", keep the size.
+ # Microsoft Reserved Partition (MSR): E3C9E316-0B5C-4DB8-817D-F92DF00215AE
+ # Ref: https://en.wikipedia.org/wiki/GUID_Partition_Table
+ if [ -n "$(echo $type | grep -iE "type=E3C9E316-0B5C-4DB8-817D-F92DF00215AE")" ]; then
+ expand="false"
+ [ "$BOOTUP" = "color" ] && $SETCOLOR_WARNING
+ echo "\"Microsoft Reserved Partition (MSR)\" found. Not to expand this partition."
+ [ "$BOOTUP" = "color" ] && $SETCOLOR_NORMAL
+ fi
+ # If we found the partition is MS Windows "system reserved partition", keep the size.
+ # Windows Recovery Environment DE94BBA4-06D1-4D40-A16A-BFD50179D6AC
+ # Ref: https://en.wikipedia.org/wiki/GUID_Partition_Table
+ if [ -n "$(echo $type | grep -iE "type=DE94BBA4-06D1-4D40-A16A-BFD50179D6AC")" ]; then
+ expand="false"
+ [ "$BOOTUP" = "color" ] && $SETCOLOR_WARNING
+ echo "\"Windows Recovery Environment\" found. Not to expand this partition."
+ [ "$BOOTUP" = "color" ] && $SETCOLOR_NORMAL
+ fi
+ # For newer Clonezilla (>=2.4.36-1drbl), a tag file (sda1.info) might exist
+ if [ -e "$orig_parted_dir/$(to_filename ${dev}).info" ]; then
+ . $orig_parted_dir/$(to_filename ${dev}).info
+ if [ "$PARTITION_TYPE" = "Win_boot_reserved" ]; then
+ expand="false"
+ fi
+ fi
+ # For Linux Swap partition
+ if [ -n "$(echo $type | grep -iE "type=0657FD6D-A4AB-43C4-84E5-0933C84B4F4F")" ]; then
+ [ "$BOOTUP" = "color" ] && $SETCOLOR_WARNING
+ echo "Linux swap partition \"$type\" found. Not to expand this partition."
+ [ "$BOOTUP" = "color" ] && $SETCOLOR_NORMAL
+ expand="false"
+ fi
+
+ if [ "$expand" = "true" ]; then
+ start_no="$(LC_ALL=C printf "%.0f" "$(echo "($start_no_keep + $size_no_keep)" | bc -l)")"
+ size_no="$(LC_ALL=C printf "%.0f" "$(echo "${size_no/,/}*$ratio + $append_to_next" | bc -l)")"
+ # Reset the space append_to_next.
+ append_to_next="0"
+ else
+ start_no="$(LC_ALL=C printf "%.0f" "$(echo "($start_no_keep + $size_no_keep)" | bc -l)")"
+ size_no="${size_no/,/}"
+ # Since we do not expand this "system reserved partition" or "swap partition",
+ # we append the space to the next partition.
+ # It's possible that there are more than one partition which will be
+ # kept as original size, e.g.
+ # Number Start End Size File system Name Flags
+ # 1 1049kB 106MB 105MB EFI system partition boot, esp
+ # 2 106MB 240MB 134MB Microsoft reserved partition msftres
+ # 3 240MB 128GB 128GB Basic data partition msftdata
+ # In the above, partition 1 and 2 will not be expanded. Therefore we
+ # might need to add the previous $append_to_next
+ append_to_next="$(LC_ALL=C printf "%.0f" "$(echo "$append_to_next + ${size_no}*$ratio - ${size_no}" | bc -l)")"
+ fi
+ fi
+ if [ "$size_no" -lt 0 ]; then
+ # append_to_next might be nagtive when "-icds" is enabled and ration is < 1, therefore
+ # we have to check the size_no.
+ [ "$BOOTUP" = "color" ] && $SETCOLOR_FAILURE
+ echo "The calculated size of $dev is < 0!"
+ echo "Unable to create a smaller partitions layout."
+ [ "$BOOTUP" = "color" ] && $SETCOLOR_NORMAL
+ echo "$msg_program_stop!"
+ exit 1
+ fi
+ echo $dev : $start $start_no, $size $size_no, $type $uuid $name $attrs >> $new_sf
+ [ "$start_no" -ne 0 ] && start_no_keep="${start_no/,/}"
+ [ "$size_no" -ne 0 ] && size_no_keep="${size_no/,/}"
+ fi
+done < $new_sf_tmp
+
+echo "The partition table to write in $target_disk:"
+echo "*****************************************"
+cat $new_sf
+echo "*****************************************"
+
+# EXTRA_SFDISK_OPT is environment variable
+# If not --force, we force to append it since by default if sfdisk find some CHS it does not like, it won't do it without --force
+if [ -z "$(echo $EXTRA_SFDISK_OPT | grep -Ew -- "--force")" ]; then
+ EXTRA_SFDISK_OPT="$EXTRA_SFDISK_OPT --force"
+fi
+echo "Running: sfdisk $EXTRA_SFDISK_OPT $target_disk < $new_sf"
+LC_ALL=C sfdisk $EXTRA_SFDISK_OPT $target_disk < $new_sf
+echo "Partition table was created by: sfdisk $EXTRA_SFDISK_OPT $target_disk < $new_sf"
+
+# Use parted to fill the last partitition to the end of disk because
+# there might be some resudial in the calculation.
+# Thanks to Conan for this suggestion.
+# Ref: https://sourceforge.net/p/clonezilla/discussion/
+# Clonezilla_live/thread/c5e92d87/?limit=25#080c
+if [ -n "$(LC_ALL=C parted -h | grep -w resizepart)" ]; then
+ # Find the last partition number.
+ # Firstly we get the line number for "Number Start End Size
+ # File system Name Flags"
+ line_after="$(LC_ALL=C parted -s $target_disk print | \
+ grep -n -Ew "^Number[[:space:]]+Start[[:space:]]+End.*" | \
+ awk -F":" '{print $1}')"
+ line_after="$((line_after +1))"
+ last_part_no="$(LC_ALL=C parted -s $target_disk print | \
+ tail -n +${line_after} | awk -F" " '{print $1}' | sort -V | tail -n 1)"
+ if [ -n "$last_part_no" ]; then
+ echo "Trying to extend the last partition to the end of disk by:"
+ echo "parted -s $target_disk resizepart $last_part_no 100%"
+ LC_ALL=C parted -s $target_disk resizepart $last_part_no 100%
+ fi
+fi
+
+[ -e "$new_sf" ] && rm -f $new_sf
+[ -e "$new_sf_tmp" ] && rm -f $new_sf_tmp
diff --git a/draft/other-tools/clonezilla/ocs-expand-mbr-pt b/draft/other-tools/clonezilla/ocs-expand-mbr-pt
new file mode 100755
index 0000000..eeb9759
--- /dev/null
+++ b/draft/other-tools/clonezilla/ocs-expand-mbr-pt
@@ -0,0 +1,369 @@
+#!/bin/bash
+# Author: Steven Shiau <steven _at_ nchc org tw>
+# License: GPL
+# A script to expand the partition table by disk size ratio.
+# ///NOTE/// This program only works for MBR partition table, not for GPT.
+
+# Load DRBL setting and functions
+DRBL_SCRIPT_PATH="${DRBL_SCRIPT_PATH:-/usr/share/drbl}"
+
+. $DRBL_SCRIPT_PATH/sbin/drbl-conf-functions
+. /etc/drbl/drbl-ocs.conf
+. $DRBL_SCRIPT_PATH/sbin/ocs-functions
+
+# Settings
+# Flag to check target disk size before creating partition table
+chk_tgt_disk_size_bf_mk_pt="yes"
+
+#
+cmd_name="$(basename $0)"
+#
+USAGE() {
+ echo "$cmd_name: To create a proportional MBR partition table (not GPT) in a disk based on a existing partition table (sfdisk format)"
+ echo "Usage:"
+ echo " $cmd_name [OPTION] PARTITION_TABLE_FILE TARGET_DEVICE"
+ echo
+ echo "OPTION:"
+ echo " -b, --batch Run $cmd_name in batch mode, i.e. without any prompt or wait to press enter. VERY DANGEROUS!"
+ echo "$cmd_name will honor experimental variable EXTRA_SFDISK_OPT and use it as the option for sfdisk."
+ echo " -icds, --ignore-chk-dsk-size-pt Skip checking destination disk size before creating the partition table on it. By default it will be checked and if the size is smaller than the source disk, quit."
+ echo "Example:"
+ echo " To create a proportional partition table on disk /dev/sda based on /home/partimag/IMAGE/sda-pt.sf, use:"
+ echo "$cmd_name /home/partimag/IMAGE/sda-pt.sf /dev/sda"
+}
+
+# Parse command-line options
+while [ $# -gt 0 ]; do
+ case "$1" in
+ -b|--batch)
+ batch_mode="yes"
+ shift;;
+ -icds|--ignore-chk-dsk-size-pt)
+ chk_tgt_disk_size_bf_mk_pt="no"
+ shift;;
+ -*) echo "${0}: ${1}: invalid option" >&2
+ USAGE >& 2
+ exit 2 ;;
+ *) break ;;
+ esac
+done
+
+# Original sfdisk format file
+orig_sf=$1 # orig_sf is like: /home/partimag/IMAGE/sda-pt.sf
+target_disk=$2 # target_disk is like: /dev/sda
+
+#
+ask_and_load_lang_set $specified_lang
+
+#
+if [ -z "$orig_sf" ]; then
+ [ "$BOOTUP" = "color" ] && $SETCOLOR_FAILURE
+ echo "No source partition table file was assigned!"
+ [ "$BOOTUP" = "color" ] && $SETCOLOR_NORMAL
+ USAGE
+ echo "$msg_program_stop!"
+ exit 1
+fi
+if [ ! -f "$orig_sf" ]; then
+ [ "$BOOTUP" = "color" ] && $SETCOLOR_FAILURE
+ echo "Source partition table file \"$orig_sf\" was _NOT_ found"
+ [ "$BOOTUP" = "color" ] && $SETCOLOR_NORMAL
+ echo "$msg_program_stop!"
+ exit 1
+fi
+
+if [ -z "$target_disk" ]; then
+ [ "$BOOTUP" = "color" ] && $SETCOLOR_FAILURE
+ echo "No target disk was assigned!"
+ [ "$BOOTUP" = "color" ] && $SETCOLOR_NORMAL
+ echo "$msg_program_stop!"
+ USAGE
+ exit 1
+fi
+
+#
+if [ "$batch_mode" != "yes" ]; then
+ [ "$BOOTUP" = "color" ] && $SETCOLOR_WARNING
+ echo "This program will create a partition table in $target_disk"
+ echo "ALL THE DATA IN THE TARGET DEVICE WILL BE ERASED!!!"
+ [ "$BOOTUP" = "color" ] && $SETCOLOR_NORMAL
+ echo -n "$msg_are_u_sure_u_want_to_continue ? (y/N) "
+ read continue_confirm_ans
+ case "$continue_confirm_ans" in
+ y|Y|[yY][eE][sS])
+ echo "$msg_ok_let_do_it!"
+ ;;
+ *)
+ echo "$msg_program_stop!"
+ exit 1
+ esac
+fi
+
+# Check if GPT disk, if yes, exit
+if `is_gpt_partitition_table_disk $target_disk`; then
+ [ "$BOOTUP" = "color" ] && $SETCOLOR_FAILURE
+ echo "Disk $target_disk is GPT format. This program only works for MBR format."
+ [ "$BOOTUP" = "color" ] && $SETCOLOR_NORMAL
+ echo "$msg_program_stop!"
+ exit 1
+fi
+
+# No size info in sda-pt.sf, we have to use sda-pt.parted.
+orig_parted_tab="${orig_sf/.sf/.parted}"
+orig_parted_dir="$(dirname $orig_sf)"
+if [ ! -e "$orig_parted_tab" ]; then
+ [ "$BOOTUP" = "color" ] && $SETCOLOR_FAILURE
+ echo "$orig_parted_tab was not found! It is required so that we know the original disk size! Maybe your Clonezilla image is too old ? You can try to create such an file in your source machine by: \"parted -s /dev/$SOURCE_DEV unit s print > $orig_parted_tab\" (Replace $SOURCE_DEV with your device name)"
+ [ "$BOOTUP" = "color" ] && $SETCOLOR_NORMAL
+ echo "$msg_program_stop!"
+ exit 1
+fi
+
+# parted output format example:
+# Disk /dev/sda: 16777215s
+# Sector size (logical/physical): 512B/512B
+# Partition Table: msdos
+#
+# Number Start End Size Type File system Flags
+# 1 63s 586844s 586782s primary boot
+# 2 586845s 978074s 391230s primary
+# 3 978075s 1955204s 977130s primary
+# 4 1955205s 16776584s 14821380s extended
+# 5 1955268s 2151764s 196497s logical
+# 6 2151828s 2542994s 391167s logical
+# 7 2543058s 16776584s 14233527s logical
+
+ori_disk_size="$(LC_ALL=C grep -E "^Disk /dev" $orig_parted_tab | awk -F":" '{print $2}' | sed -e "s/s$//g")"
+# If nothing in target disk, parted will show like this:
+# sudo parted -s /dev/sda unit s print
+# Error: Unable to open /dev/sda - unrecognised disk label.
+
+if ! LC_ALL=C parted -s $target_disk unit s print &>/dev/null; then
+ # Try to create a partition table so that we can read the size via parted -s $dev unit s print
+ echo -n "No partition table exists in target disk $target_disk, try to initialize one so that we can get the disk size by sfdisk... "
+ echo 1,,83 | sfdisk -f $target_disk &>/dev/null
+ echo "done!"
+fi
+tgt_disk_size="$(LC_ALL=C parted -s $target_disk unit s print | grep -E "^Disk /dev" | awk -F":" '{print $2}' | sed -e "s/s$//g")"
+
+# If target_disk size is larger than 2 TiB (~2.2 TB = 2,199,023,255,040 bytes), exit. It's over the MBR's limitation.
+check_mbr_disk_size_gt_2TiB $target_disk exit
+
+#
+ratio=$(echo "scale=10; $tgt_disk_size / $ori_disk_size" | bc -l) || exit 1
+echo "The ratio for target disk size to original disk size is $ratio."
+if [ "$chk_tgt_disk_size_bf_mk_pt" = "yes" ]; then
+ if [ "$(LC_ALL=C echo "$ratio < 1" | bc -l)" = "1" ]; then
+ [ "$BOOTUP" = "color" ] && $SETCOLOR_FAILURE
+ echo "The target disk (size=$tgt_disk_size sectors) is smaller than the source disk (size=$ori_disk_size sectors)!"
+ echo "Clonezilla won't be able to restore a partition image to smaller partition!"
+ [ "$BOOTUP" = "color" ] && $SETCOLOR_NORMAL
+ echo "Program terminated!"
+ exit 1
+ fi
+fi
+
+new_sf="$(mktemp /tmp/new_sf.XXXXXX)" || exit 1
+new_sf_tmp="$(mktemp /tmp/new_sf_tmp.XXXXXX)" || exit 1
+# Increase. Example for sfdisk format:
+# # partition table of /dev/sda
+# unit: sectors
+#
+# /dev/sda1 : start= 63, size= 586782, Id=83, bootable
+# /dev/sda2 : start= 586845, size= 391230, Id=82
+# /dev/sda3 : start= 978075, size= 977130, Id=83
+# /dev/sda4 : start= 1955205, size= 14821380, Id= 5
+# /dev/sda5 : start= 1955268, size= 196497, Id=83
+# /dev/sda6 : start= 2151828, size= 391167, Id=83
+# /dev/sda7 : start= 2543058, size= 14233527, Id=83
+#
+# Or
+# /dev/sda1 : start= 63, size= 196497, Id=83
+# /dev/sda2 : start= 196560, size= 16580025, Id= 5
+# /dev/sda3 : start= 0, size= 0, Id= 0
+# /dev/sda4 : start= 0, size= 0, Id= 0
+# /dev/sda5 : start= 196623, size= 16579962, Id=83
+
+# We only need those required info
+grep -E "^/dev" $orig_sf > $new_sf_tmp
+# Format the output, since something like "Id= 5" is not a good idea for us to parse
+perl -pi -e "s/Id=[[:space:]]+/Id=/g" $new_sf_tmp
+# start=291579750 or size=291563622 is not a good format for us to parse, we need a space between "=" and "number"
+# i.e.
+# No good:
+# /dev/sda1 : start= 63, size=291563622, Id= 7, bootable
+# /dev/sda2 : start=291579750, size= 20980890, Id= 7
+# /dev/sda3 : start= 0, size= 0, Id= 0
+# /dev/sda4 : start= 0, size= 0, Id= 0
+#
+# Good:
+# /dev/sda1 : start= 63, size= 291563622, Id= 7, bootable
+# /dev/sda2 : start= 291579750, size= 20980890, Id= 7
+# /dev/sda3 : start= 0, size= 0, Id= 0
+# /dev/sda4 : start= 0, size= 0, Id= 0
+
+# //NOTE// For partitions number >=10, it looks like:
+## partition table of /dev/sda
+#unit: sectors
+#
+#/dev/sda1 : start= 63, size= 976562, Id=83
+#/dev/sda2 : start= 976625, size= 1171875, Id=83
+#/dev/sda3 : start= 2148500, size= 1367187, Id=83
+#/dev/sda4 : start= 3515687, size= 13256173, Id= f
+#/dev/sda5 : start= 3515750, size= 195312, Id=83
+#/dev/sda6 : start= 3711063, size= 390624, Id=83
+#/dev/sda7 : start= 4101688, size= 585936, Id=83
+#/dev/sda8 : start= 4687625, size= 781249, Id=83
+#/dev/sda9 : start= 5468875, size= 976561, Id=83
+#/dev/sda10: start= 6445437, size= 976561, Id=83 <-- No space before ":"
+#/dev/sda11: start= 7421999, size= 976561, Id=83
+
+# Therefore we force to put a space no matter there is already space or not
+perl -pi -e "s/start=/start= /g" $new_sf_tmp
+perl -pi -e "s/size=/size= /g" $new_sf_tmp
+# Remove ":" in the temp file, since it's only for parsing, won't be used for sfdisk /dev/... < $new_sf_tmp ...
+perl -pi -e "s/://g" $new_sf_tmp
+
+#
+echo "unit: sectors" > $new_sf
+echo "" >> $new_sf
+start_no_keep=""
+size_no_keep=""
+extended_part=""
+flag_1st_logic_drv="off"
+append_to_next="0"
+# Initial gap for the 1st partition. After this, it should be 0.
+logical_part_gap="2"
+while read dev start start_no size size_no id flag; do
+ pt_no="$(LC_ALL=C get_part_number $dev | sed -r -e "s|^[^[:digit:]]*||g")"
+ if `is_partition $dev` && [ "$pt_no" -le 4 ]; then
+ # primary/extended partitions
+ [ -z "$start_no_keep" ] && start_no_keep=${start_no/,/} # The 1st one
+ [ -z "$size_no_keep" ] && size_no_keep=0
+ if [ "${size_no/,/}" -eq 0 ]; then
+ start_no=0
+ size_no=0
+ else
+ # If we found the partition is MS Windows (Vista, 7) "system reserved partition", i.e. size is about 100 MB (204800) or 200 MB (409600 sectors), and Id=7, and flag is bootable, not to expand it. i.e.:
+ # /dev/sda1 : start= 2048, size= 204800, Id= 7, bootable
+ # The files and dirs in MS Windows (Vista, 7) "system reserved partition"
+ # drwxrwxrwx 1 root root 4.0K 2011-07-20 06:22 Boot/
+ # -rwxrwxrwx 1 root root 375K 2009-07-14 01:38 bootmgr*
+ # -rwxrwxrwx 1 root root 8.0K 2011-07-20 06:22 BOOTSECT.BAK*
+ # drwxrwxrwx 1 root root 0 2011-07-20 06:31 System Volume Information/
+ # //NOTE// The above files and dirs will be shown after restoring, not the time when creating partition table. Therefore we can not mount the partition and parse them.
+ # Ref: http://windows7forums.com/windows-7-discussion/15629-hack-remove-100-mb-system-reserved-partition-when-installing-windows-7-a.html
+ expand="true"
+ if [ "${size_no/,/}" -eq "409600" -o "${size_no/,/}" -eq "204800" ]; then
+ if [ -n "$(echo $id | grep -iE "(id|type)=7")" -a \
+ -n "$(echo $flag | grep -iE "bootable")" ]; then
+ expand="false"
+ [ "$BOOTUP" = "color" ] && $SETCOLOR_WARNING
+ echo "MS Windows (Vista or 7) \"system reserved partition\" found. Not to expand this partition."
+ [ "$BOOTUP" = "color" ] && $SETCOLOR_NORMAL
+ fi
+ fi
+ # For newer Clonezilla (>=2.4.36-1drbl), a tag file (sda1.info) might exist
+ if [ -e "$orig_parted_dir/$(to_filename ${dev}).info" ]; then
+ . $orig_parted_dir/$(to_filename ${dev}).info
+ if [ "$PARTITION_TYPE" = "Win_boot_reserved" ]; then
+ expand="false"
+ fi
+ fi
+ # For Linux Swap partition
+ if [ -n "$(echo $id | grep -iE "(id|type)=82")" ]; then
+ [ "$BOOTUP" = "color" ] && $SETCOLOR_WARNING
+ echo "Linux swap partition \"$id\" found. Not to expand this partition."
+ [ "$BOOTUP" = "color" ] && $SETCOLOR_NORMAL
+ expand="false"
+ fi
+
+ if [ "$expand" = "true" ]; then
+ start_no="$(LC_ALL=C printf "%.0f" "$(echo "($start_no_keep + $size_no_keep)" | bc -l)")"
+ size_no="$(LC_ALL=C printf "%.0f" "$(echo "${size_no/,/}*$ratio + $append_to_next" | bc -l)")"
+ # Reset the space append_to_next.
+ append_to_next="0"
+ else
+ start_no="$(LC_ALL=C printf "%.0f" "$(echo "($start_no_keep + $size_no_keep)" | bc -l)")"
+ size_no="${size_no/,/}"
+ # Since we do not expand this "system reserved partition" or "swap partition",
+ # we append the space to the next partition.
+ append_to_next="$(LC_ALL=C printf "%.0f" "$(echo "${size_no}*$ratio - ${size_no}" | bc -l)")"
+ fi
+ fi
+ if [ "$size_no" -lt 0 ]; then
+ # append_to_next might be nagtive when "-icds" is enabled and ration is < 1, therefore
+ # we have to check the size_no.
+ [ "$BOOTUP" = "color" ] && $SETCOLOR_FAILURE
+ echo "The calculated size of $dev is < 0!"
+ echo "Unable to create a smaller partitions layout."
+ [ "$BOOTUP" = "color" ] && $SETCOLOR_NORMAL
+ echo "$msg_program_stop!"
+ exit 1
+ fi
+ if [ -n "$(echo $id | grep -iE "((id|type)=5|(id|type)=f)")" ]; then
+ # keep the extended partition, we need it for logical partitions.
+ extended_part="$dev"
+ extended_start_no="$start_no"
+ extended_size_no="$size_no"
+ fi
+ echo $dev : $start $start_no, $size $size_no, $id $flag >> $new_sf
+ [ "$start_no" -ne 0 ] && start_no_keep="${start_no/,/}"
+ [ "$size_no" -ne 0 ] && size_no_keep="${size_no/,/}"
+ else
+ # logical partitions
+ if [ "$flag_1st_logic_drv" = "off" ]; then
+ start_no_keep="$((extended_start_no))"
+ size_no_keep="0"
+ flag_1st_logic_drv="on"
+ fi
+ if [ "${size_no/,/}" -eq 0 ]; then
+ start_no=0
+ size_no=0
+ else
+ expand="true"
+ # For Linux Swap partition
+ if [ -n "$(echo $id | grep -iE "(id|type)=82")" ]; then
+ [ "$BOOTUP" = "color" ] && $SETCOLOR_WARNING
+ echo "Linux swap partition \"$id\" found. Not to expand this partition."
+ [ "$BOOTUP" = "color" ] && $SETCOLOR_NORMAL
+ expand="false"
+ fi
+
+ if [ "$expand" = "true" ]; then
+ start_no="$(LC_ALL=C printf "%.0f" "$(echo "($start_no_keep+$size_no_keep+$logical_part_gap)" | bc -l)")"
+ size_no="$(LC_ALL=C printf "%.0f" "$(echo "${size_no/,/}*$ratio + $append_to_next" | bc -l)")"
+ logical_part_gap="0"
+ # Reset the space append_to_next.
+ append_to_next="0"
+ else
+ start_no="$(LC_ALL=C printf "%.0f" "$(echo "($start_no_keep+$size_no_keep+$logical_part_gap)" | bc -l)")"
+ size_no="${size_no/,/}"
+ # Since we do not expand the "swap partition", we append the space to the next partition.
+ append_to_next="$(LC_ALL=C printf "%.0f" "$(echo "${size_no}*$ratio - ${size_no}" | bc -l)")"
+ fi
+ fi
+ echo $dev : $start $start_no, $size $size_no, $id $flag >> $new_sf
+ [ "$start_no" -ne 0 ] && start_no_keep="${start_no/,/}"
+ [ "$size_no" -ne 0 ] && size_no_keep="${size_no/,/}"
+ fi
+done < $new_sf_tmp
+# For sfdisk <= 0.25, only "Id=" is accepted, not "type=".
+perl -pi -e "s/type=/Id=/g" $new_sf
+
+echo "The partition table to write in $target_disk:"
+echo "*****************************************"
+cat $new_sf
+echo "*****************************************"
+
+# EXTRA_SFDISK_OPT is environment variable
+# If not --force, we force to append it since by default if sfdisk find some CHS it does not like, it won't do it without --force
+if [ -z "$(echo $EXTRA_SFDISK_OPT | grep -Ew -- "--force")" ]; then
+ EXTRA_SFDISK_OPT="$EXTRA_SFDISK_OPT --force"
+fi
+echo "Running: sfdisk $EXTRA_SFDISK_OPT $target_disk < $new_sf"
+LC_ALL=C sfdisk $EXTRA_SFDISK_OPT $target_disk < $new_sf
+echo "Partition table was created by: sfdisk $EXTRA_SFDISK_OPT $target_disk < $new_sf"
+
+[ -e "$new_sf" ] && rm -f $new_sf
+[ -e "$new_sf_tmp" ] && rm -f $new_sf_tmp
diff --git a/draft/other-tools/clonezilla/ocs-restore-ebr b/draft/other-tools/clonezilla/ocs-restore-ebr
new file mode 100755
index 0000000..6912c9a
--- /dev/null
+++ b/draft/other-tools/clonezilla/ocs-restore-ebr
@@ -0,0 +1,119 @@
+#!/bin/bash
+#!/bin/bash
+# License: GPL
+# Author: Steven Shiau <steven _at_ clonezilla org>
+# Reinstall executable code area (first 446 bytes in EBR)
+# Ref: https://en.wikipedia.org/wiki/Extended_boot_record
+# Extended boot record (EBR) is the 512-byte boot sector:
+# 446 bytes (executable code area) + 64 bytes (table of primary partitions) + 2 bytes (EBR signature; # 0xAA55) = 512 bytes.
+# EBRs have essentially the same structure as the MBR.
+#
+DRBL_SCRIPT_PATH="${DRBL_SCRIPT_PATH:-/usr/share/drbl}"
+. $DRBL_SCRIPT_PATH/sbin/drbl-conf-functions
+. /etc/drbl/drbl-ocs.conf
+. $DRBL_SCRIPT_PATH/sbin/ocs-functions
+
+# Load the config in ocs-live.conf. This is specially for Clonezilla live. It will overwrite some settings of /etc/drbl/drbl-ocs.conf, such as $DIA...
+[ -e "/etc/ocs/ocs-live.conf" ] && . /etc/ocs/ocs-live.conf
+
+#
+USAGE() {
+ echo "$ocs - To restore the EBR (Extended boot record) from an image to device"
+ echo "Usage:"
+ echo "To run $ocs:"
+ echo "$ocs [OPTION] IMAGE_NAME DEVICE"
+ echo "Options:"
+ echo "-or, --ocsroot DIR Specify DIR (absolute path) as directory ocsroot (i.e. overwrite the ocsroot assigned in drbl.conf)"
+ echo "IMAGE_NAME is the image dir name, not absolute path"
+ echo "DEVICE is the device name, e.g. sda1, sda2..."
+ echo "Ex:"
+ echo "To restore the the EBR saved in the image \"my-image\" to device sda4, run:"
+ echo " $ocs my-image sda4"
+ echo
+} # end of USAGE
+
+
+####################
+### Main program ###
+####################
+
+ocs_file="$0"
+ocs=`basename $ocs_file`
+#
+#
+while [ $# -gt 0 ]; do
+ case "$1" in
+ -or|--ocsroot)
+ # overwrite the ocsroot in drbl.conf
+ shift;
+ if [ -z "$(echo $1 |grep ^-.)" ]; then
+ # skip the -xx option, in case
+ ocsroot="$1"
+ shift;
+ fi
+ [ -z "$ocsroot" ] && USAGE && exit 1
+ ;;
+ -*) echo "${0}: ${1}: invalid option" >&2
+ USAGE >& 2
+ exit 2 ;;
+ *) break ;;
+ esac
+done
+
+target_dir="$1"
+shift
+target_part="$*"
+
+# Fedora Core 1 seems to use dumb for rc1, we have to force it use linux.
+# otherwise setterm will complain.
+[ -z "$TERM" -o "$TERM" = "dumb" ] && TERM="linux"
+echo "Setting the TERM as $TERM"
+export TERM="$TERM"
+
+#
+check_if_root
+ask_and_load_lang_set
+
+if [ -z "$target_dir" ]; then
+ [ "$BOOTUP" = "color" ] && $SETCOLOR_FAILURE
+ echo "No image was assigned!"
+ [ "$BOOTUP" = "color" ] && $SETCOLOR_NORMAL
+ USAGE
+ echo "$msg_program_stop!"
+ exit 1
+fi
+if [ -z "$target_part" ]; then
+ [ "$BOOTUP" = "color" ] && $SETCOLOR_FAILURE
+ echo "No destination partition was assigned!"
+ [ "$BOOTUP" = "color" ] && $SETCOLOR_NORMAL
+ USAGE
+ echo "$msg_program_stop!"
+ exit 1
+fi
+
+#
+target_dir_fullpath="$ocsroot/$target_dir"
+
+for ipart in $target_part; do
+ # 1st, check if this partition on a MBR disk, not GPT one.
+ dsk_pt="$target_dir_fullpath/$(to_filename $(get_diskname ${ipart}))-pt.parted"
+ if ! `is_mbr_partitition_table_file $dsk_pt`; then
+ echo "Partition $ipart is not on MBR disk. Maybe it's on GPT one. No need to restore the EBR data."
+ continue
+ fi
+ # 2nd, check if $ipart is extended partition
+ # For cciss partition, e.g. /dev/cciss/c0d0p2 will be "p2" which is got from $(get_part_number /dev/cciss/c0d0p2)", here the part_index we want is only number, hence we need to trip the leading characters.
+ index="$(LC_ALL=C get_part_number $ipart | sed -r -e "s|^[^[:digit:]]*||g")" # index is like 2
+ part_type="$(LC_ALL=C grep -Ew "^[[:space:]]*$index" ${dsk_pt} | grep -iw "extended")"
+ if [ -z "$part_type" ]; then
+ # Not extended partition
+ echo "Partition $ipart is not extended partition. No need to restore the EBR data."
+ continue
+ fi
+
+ #
+ echo -n "Restoring the first 446 bytes of EBR data for extended partition $ipart... "
+ dd if=$target_dir_fullpath/$(to_filename ${ipart})-ebr of=/dev/$ipart bs=446 count=1 &>/dev/null
+ echo "done."
+ echo $msg_delimiter_star_line
+done
diff --git a/draft/other-tools/clonezilla/ocs-restore-mbr b/draft/other-tools/clonezilla/ocs-restore-mbr
new file mode 100755
index 0000000..08fc697
--- /dev/null
+++ b/draft/other-tools/clonezilla/ocs-restore-mbr
@@ -0,0 +1,131 @@
+#!/bin/bash
+
+# Reinstall executable code area (first 446 bytes in MBR)
+
+# Ref: http://en.wikipedia.org/wiki/Master_boot_record
+# Master Boot Record (MBR) is the 512-byte boot sector:
+# 446 bytes (executable code area) + 64 bytes (table of primary partitions) + 2 bytes (MBR signature; # 0xAA55) = 512 bytes.
+# However, some people also call executable code area (first 446 bytes in MBR) as MBR.
+#
+DRBL_SCRIPT_PATH="${DRBL_SCRIPT_PATH:-/usr/share/drbl}"
+. $DRBL_SCRIPT_PATH/sbin/drbl-conf-functions
+. /etc/drbl/drbl-ocs.conf
+. $DRBL_SCRIPT_PATH/sbin/ocs-functions
+
+# Load the config in ocs-live.conf. This is specially for Clonezilla live. It will overwrite some settings of /etc/drbl/drbl-ocs.conf, such as $DIA...
+[ -e "/etc/ocs/ocs-live.conf" ] && . /etc/ocs/ocs-live.conf
+
+# Setings
+# By default we do not restore the prebuild mbr from syslinux.
+restore_prebuild_mbr="no"
+
+#
+USAGE() {
+ echo "$ocs - To restore the MBR from an image to device"
+ echo "Usage:"
+ echo "To run $ocs:"
+ echo "$ocs [OPTION] IMAGE_NAME DEVICE"
+ echo "Options:"
+ echo "-p, --prebuild_mbr Use the prebuild bootloader from syslinux (For Windows only), not from the saved image"
+ echo "-or, --ocsroot DIR Specify DIR (absolute path) as directory ocsroot (i.e. overwrite the ocsroot assigned in drbl.conf)"
+ echo "IMAGE_NAME is the image dir name, not absolute path"
+ echo "DEVICE is the device name, e.g. sda, sda..."
+ echo "Ex:"
+ echo "To restore the the mbr saved in the image \"my-image\" to device sda, run:"
+ echo " $ocs my-image sda"
+ echo
+} # end of USAGE
+
+
+####################
+### Main program ###
+####################
+
+ocs_file="$0"
+ocs=`basename $ocs_file`
+#
+#
+while [ $# -gt 0 ]; do
+ case "$1" in
+ -p|--prebuild_mbr)
+ restore_prebuild_mbr="yes"
+ shift;
+ ;;
+ -or|--ocsroot)
+ # overwrite the ocsroot in drbl.conf
+ shift;
+ if [ -z "$(echo $1 |grep ^-.)" ]; then
+ # skip the -xx option, in case
+ ocsroot="$1"
+ shift;
+ fi
+ [ -z "$ocsroot" ] && USAGE && exit 1
+ ;;
+ -*) echo "${0}: ${1}: invalid option" >&2
+ USAGE >& 2
+ exit 2 ;;
+ *) break ;;
+ esac
+done
+
+target_dir="$1"
+shift
+target_hd="$*"
+
+# Fedora Core 1 seems to use dumb for rc1, we have to force it use linux.
+# otherwise setterm will complain.
+[ -z "$TERM" -o "$TERM" = "dumb" ] && TERM="linux"
+echo "Setting the TERM as $TERM"
+export TERM="$TERM"
+
+#
+check_if_root
+ask_and_load_lang_set
+
+if [ -z "$target_dir" ]; then
+ [ "$BOOTUP" = "color" ] && $SETCOLOR_FAILURE
+ echo "No image was assigned!"
+ [ "$BOOTUP" = "color" ] && $SETCOLOR_NORMAL
+ USAGE
+ echo "$msg_program_stop!"
+ exit 1
+fi
+if [ -z "$target_hd" ]; then
+ [ "$BOOTUP" = "color" ] && $SETCOLOR_FAILURE
+ echo "No destination disk was assigned!"
+ [ "$BOOTUP" = "color" ] && $SETCOLOR_NORMAL
+ USAGE
+ echo "$msg_program_stop!"
+ exit 1
+fi
+
+#
+target_dir_fullpath="$ocsroot/$target_dir"
+
+for ihd in $target_hd; do
+ # No matter it's msdos partition table or GPT one, here we restore the MBR.
+ # If it's GPT format, this could make GPT one as "valid GPT with protective MBR",
+ # and it's still GPT format.
+ # Some OS (e.g. VMware ESXi 5.5.0) need protective MBR even it's GPT partition table.
+ # Ref:
+ # https://sourceforge.net/p/clonezilla/discussion/Clonezilla_live/thread/30662778/
+ # https://sourceforge.net/p/clonezilla/discussion/Clonezilla_live/thread/ca825570/
+
+ # Install MBR which displays a failure message on boot. This is for fault tolerance. If all the partitions are cloned successfully, the correcting MBR will be restored later. For more info, please check https://sourceforge.net/tracker/?func=detail&atid=671653&aid=2793248&group_id=115473
+ cat /usr/share/partclone/fail-mbr.bin > /dev/$ihd
+ if [ "$restore_prebuild_mbr" = "yes" ]; then
+ # For M$ Windows system, sometimes it will fail if we restore the mbr from the one we saved (Ex. hda-mbr). Another option is to use mbr.bin from syslinux
+ cat_cmd="cat $pxelinux_binsrc_dir/bios/mbr.bin > /dev/$ihd"
+ echo "Restoring the mbr.bin from syslinux to /dev/$ihd by:"
+ echo "$cmd_cmd"
+ eval $cat_cmd
+ echo "done."
+ else
+ dd_cmd="dd if=$target_dir_fullpath/$(to_filename ${ihd})-mbr of=/dev/$ihd bs=446 count=1 &>/dev/null"
+ echo "Restoring the first 446 bytes of MBR data (executable code area) for $ihd by:"
+ echo "$dd_cmd"
+ eval $dd_cmd
+ echo "done."
+ fi
+ echo $msg_delimiter_star_line
+done
diff --git a/draft/other-tools/clonezilla/ocs-sr b/draft/other-tools/clonezilla/ocs-sr
new file mode 100755
index 0000000..7ea1d9f
--- /dev/null
+++ b/draft/other-tools/clonezilla/ocs-sr
@@ -0,0 +1,1465 @@
+#!/bin/bash
+#
+# License: GPL
+# Original author: Blake, Kuo-Lien Huang
+# Description:
+# 2003/04/11 the first version, only work on /dev/hda1 & PXE
+# 2003/06/01 disk to disk mode & etherboot support
+# (the etherboot lzdsk images are located in
+# /tftpboot/etherboot-5.0.7 after DRBLCD installed)
+# 2003/07/18 `checkInodeForDevice`
+#
+# Author: Steven Shiau <steven _at_ nchc org tw>
+# 2003/07/21 the first version for Redhat, modified from Blake's version,
+# only work on /dev/hda1 & PXE
+# 2004/02/04 add the multicast function
+# 2005/01/21 add code to save/restore some partitions (not entire disk).
+# 2005/11/06 Many thanks to Christian Treczoks for providing "dd skip=1" method
+# 2006/05/06 Remove save & restore hda1, since we can use save & restore partitions.
+# 2006/12/07 Rewrite drbl-ocs to drbl-ocs + ocs-sr + ocs-functions.
+
+# input parameters:
+# save/savedisk/saveparts/restore/restoredisk/restoreparts/multicast_restore/multicast_restoredisk IMAGE-NAME DEVICES
+
+# Notes:
+# task_restorexxx/savexxx function will always use $ocsroot (/home/partimag) as mount point to do the save/restore, so it might overwrite the /home/partimag by mounting $ocsroot from server:/home2 (for example).
+# The mount action is done in function task_preprocessing
+
+ocs_file="$0"
+ocs=`basename $ocs_file`
+ocs_myself_id="$$"
+# we need ocs_ppid for ocs-functions
+ocs_ppid="$PPID"
+# log file for sfdisk when restoring
+# RESTORE_SFDISK_LOG is loaded from drbl.conf
+
+# Some initial setting
+confirm_before_clone="no"
+ocs_sr_mode=""
+gen_md5sum="no"
+gen_sha1sum="no"
+check_md5sum="no"
+check_sha1sum="no"
+gen_chksum_for_files_in_dev="no"
+chk_chksum_for_files_in_dev="no"
+chk_img_restoreable_mod_save_def="yes"
+chk_img_restoreable_mod_restore_def="yes"
+# Flag to save restoring error log
+save_restore_error_log="no"
+# Flag to check target disk size before creating partition table
+chk_tgt_disk_size_bf_mk_pt="yes"
+# Flag to update EFI NVRAM after restoring a disk
+update_efi_nvram=""
+
+# Load DRBL setting and functions
+DRBL_SCRIPT_PATH="${DRBL_SCRIPT_PATH:-/usr/share/drbl}"
+
+. $DRBL_SCRIPT_PATH/sbin/drbl-conf-functions
+. /etc/drbl/drbl-ocs.conf
+. $DRBL_SCRIPT_PATH/sbin/ocs-functions
+
+# Load the config in ocs-live.conf. This is specially for Clonezilla live. It will overwrite some settings of /etc/drbl/drbl-ocs.conf, such as $DIA...
+[ -e "/etc/ocs/ocs-live.conf" ] && . /etc/ocs/ocs-live.conf
+
+# Fedora Core 1 seems to use dumb for rc1, we have to force it use linux.
+# otherwise setterm will complain.
+[ -z "$TERM" -o "$TERM" = "dumb" ] && TERM="linux"
+echo "Setting the TERM as $TERM"
+export TERM="$TERM"
+
+#
+check_if_root
+
+#
+USAGE() {
+ echo "Usage:"
+ echo "To save or restore image"
+ echo "$ocs [OPTION] {savedisk|saveparts|restoredisk|restoreparts} IMAGE_NAME DEVICE"
+ echo
+ echo " Options for saving:"
+ USAGE_common_save
+ echo " -i, --image-size SIZE Set the split image file volume size SIZE (MB). When $ocs is run with -x, the default SIZE is set as $VOL_LIMIT_IN_INTERACTIVE, if without -x, we will not split it."
+ USAGE_reserved_word_for_save
+ echo
+ echo " Options for restoring:"
+ USAGE_common_restore
+ echo " --mcast-port NO Assign the udp port number for multicast restore. This is used by clonezilla server. Normally it's not necessary to manually assign this option."
+ USAGE_reserved_word_for_restore
+ echo
+ echo " General options:"
+ USAGE_common_general
+ dialog_like_prog_help_prompt
+ echo " -x, --interactive Interactive mode to save or restore."
+ echo
+ echo "Example:"
+ echo " To save or restore image in client (Only that DRBL client will join, and its local partitions is NOT mounted). NOTE!!! You should run the command in DRBL client or you have to make sure the target device is NOT busy!."
+ echo " To save all the data in local first IDE harddrive 'hda' as image 'IMAGE1', use ntfsclone instead of partimage, and lzop compression (NOTE!!! You should run the command in DRBL client or make sure hda is NOT busy/mounted!):"
+ echo " $ocs --use-ntfsclone -z3 savedisk IMAGE1 hda"
+ echo
+ echo " To save the data in first and second partitions in local first IDE harddrive 'hda' as image 'IMAGE2', use ntfsclone instead of partimage, and lzop compression (NOTE!!! You should run the command in DRBL client, or make sure hda is NOT busy/mounted!):"
+ echo " $ocs" '--use-ntfsclone -z3 saveparts IMAGE2 "hda1 hda2"'
+ echo
+ echo " To restore image "IMAGE1" to local hda. grub-install will be run after cloning (image IMAGE1 is already in DRBL server. NOTE!!! You should run the command in DRBL client or make sure hda is NOT busy/mounted!):"
+ echo " $ocs -g auto restoredisk IMAGE1 hda"
+ echo
+ echo " To restore image first and second partitions from "IMAGE2" to local hda1 and hda2. grub-install will be run after cloning (image IMAGE2 is already in DRBL server. NOTE!!! You should run the command in DRBL client or make sure hda is NOT busy/mounted!):"
+ echo " $ocs" '-g auto restoreparts IMAGE2 "hda1 hda2"'
+ echo
+ echo " To save disk(s)/partitition(s) as an image or restore an image to disk(s)/partitition(s) interactively, use:"
+ echo " $ocs -x"
+}
+#
+parse_ocs_sr_cmd_options_with_dash() {
+ # Parse command-line options
+ # It's UGLY to use "shift; n_shift=$((n_shift+1))"
+ # However, we want to reuse the parse_ocs_sr_cmd_options_with_dash,
+ # a problem here:
+ # The parameters (-b -g auto --nogui --max-time-to-wait 300 --language 0 multicast_restoreparts sarge-base "hda1 hda3" 2232), when put it as $*, it will
+ # become (-b -g auto --nogui --max-time-to-wait 300 --language 0 multicast_restoreparts sarge-base hda1 hda3 2232), you see, "hda1 hda3" -> hda1 hda3
+ # Then everyting is in a mess... so now the best solution I can find is to
+ # calculate the n_shift, then parse it to original shell, not in the function.
+
+ n_shift=0
+ while [ $# -gt 0 ]; do
+ case "$1" in
+ -l|--language)
+ shift; n_shift=$((n_shift+1))
+ if [ -z "$(echo $1 |grep ^-.)" ]; then
+ # skip the -xx option, in case
+ specified_lang="$1"
+ shift; n_shift=$((n_shift+1))
+ fi
+ [ -z "$specified_lang" ] && USAGE && exit 1
+ ;;
+ -g|--grub-install)
+ install_grub="on"
+ shift; n_shift=$((n_shift+1))
+ # skip the -xx option, in case
+ if [ -z "$(echo $1 |grep ^-.)" ]; then
+ grub_partition=$1
+ shift; n_shift=$((n_shift+1))
+ fi
+ [ -z "$grub_partition" ] && USAGE && exit 1
+ ;;
+ -f|--from-part-in-img)
+ shift; n_shift=$((n_shift+1))
+ # skip the -xx option, in case
+ if [ -z "$(echo $1 |grep ^-.)" ]; then
+ source_part=$1
+ shift; n_shift=$((n_shift+1))
+ fi
+ [ -z "$source_part" ] && USAGE && exit 1
+ ;;
+ -a|--no-force-dma-on)
+ force_dma_on="no"
+ shift; n_shift=$((n_shift+1));;
+ -k|--no-fdisk|--no-create-partition)
+ create_part="no"
+ shift; n_shift=$((n_shift+1));;
+ -k1|--fdisk-proportion)
+ create_part="yes"
+ create_part_type="proportion"
+ shift; n_shift=$((n_shift+1));;
+ -k2|--fdisk-manual)
+ create_part="yes"
+ create_part_type="manual"
+ shift; n_shift=$((n_shift+1));;
+ -t|--no-restore-mbr)
+ restore_mbr="no"
+ shift; n_shift=$((n_shift+1));;
+ -t1|--restore-raw-mbr)
+ # The flag to restore syslinux mbr.bin to M$ windows system.
+ restore_prebuild_mbr="yes"
+ shift; n_shift=$((n_shift+1));;
+ -t2|--no-restore-ebr)
+ restore_ebr="no"
+ shift; n_shift=$((n_shift+1));;
+ -u|--select-img-in-client)
+ select_img_in_client="yes"
+ shift; n_shift=$((n_shift+1));;
+ -e|--load-geometry)
+ load_HD_CHS_from_img="yes"
+ shift; n_shift=$((n_shift+1));;
+ -e1|--change-geometry)
+ change_ntfs_boot_chs="on"
+ shift; n_shift=$((n_shift+1))
+ # skip the -xx option, in case
+ if [ -z "$(echo $1 |grep ^-.)" ]; then
+ ntfs_boot_partition=$1
+ shift; n_shift=$((n_shift+1))
+ fi
+ [ -z "$ntfs_boot_partition" ] && USAGE && exit 1
+ ;;
+ -e2|--load-geometry-from-edd)
+ use_HD_CHS_from_EDD_pref="yes"
+ shift; n_shift=$((n_shift+1));;
+ -y|-y0|--always-restore|--always-restore-default-local)
+ always_restore="yes"
+ pxe_menu_default_mode="local"
+ shift; n_shift=$((n_shift+1));;
+ -y1|--always-restore-default-clone)
+ always_restore="yes"
+ pxe_menu_default_mode="clone"
+ shift; n_shift=$((n_shift+1));;
+ -y2|--always-restore-default-drbl)
+ always_restore="yes"
+ pxe_menu_default_mode="drbl"
+ shift; n_shift=$((n_shift+1));;
+ -c|--confirm)
+ confirm_before_clone="yes"
+ shift; n_shift=$((n_shift+1));;
+ -sc|-scs|--skip-check-restorable|--skip-check-restorable-s)
+ # Flag to check if the image is restorable
+ chk_img_restoreable_mod_save="no"
+ shift; n_shift=$((n_shift+1));;
+ -scr|--skip-check-restorable-r)
+ # Flag to check if the image is restorable
+ chk_img_restoreable_mod_restore="no"
+ shift; n_shift=$((n_shift+1));;
+ -srel|--save-restore-error-log)
+ # Flag to save restoring log
+ save_restore_error_log="yes"
+ shift; n_shift=$((n_shift+1));;
+ -w|--wait-time)
+ shift; n_shift=$((n_shift+1))
+ if [ -z "$(echo $1 |grep ^-.)" ]; then
+ # skip the -xx option, in case
+ TIME_to_wait="$1"
+ shift; n_shift=$((n_shift+1))
+ fi
+ ;;
+ --debug=?*)
+ debug_level=${1#--debug=}
+ shift; n_shift=$((n_shift+1));;
+ -b|-batch|--batch)
+ ocs_batch_mode="on"
+ shift; n_shift=$((n_shift+1));;
+ -d|--debug-mode)
+ debug_mode="on"
+ shift; n_shift=$((n_shift+1));;
+ -d0|--dialog)
+ DIA="dialog"
+ shift;;
+ -d1|--Xdialog)
+ DIA="Xdialog"
+ shift;;
+ -d2|--whiptail)
+ DIA="whiptail"
+ shift;;
+ -d3|--gdialog)
+ DIA="gdialog"
+ shift;;
+ -d4|--kdialog)
+ DIA="kdialog"
+ shift;;
+ -enc|--enc-ocs-img)
+ encrypt_ocs_img="yes"
+ shift; n_shift=$((n_shift+1));;
+ -senc|--skip-enc-ocs-img)
+ encrypt_ocs_img="no"
+ shift; n_shift=$((n_shift+1));;
+ -m|--module)
+ shift; n_shift=$((n_shift+1))
+ if [ -z "$(echo $1 |grep ^-.)" ]; then
+ # skip the -xx option, in case
+ module_to_load=$1
+ shift; n_shift=$((n_shift+1))
+ fi
+ [ -z "$module_to_load" ] && USAGE && exit 1
+ ;;
+ -r|--resize-partition)
+ resize_partition="on"
+ shift; n_shift=$((n_shift+1));;
+ -v|--verbose)
+ verbose="on"
+ shift; n_shift=$((n_shift+1));;
+ -z0|--no-compress)
+ IMG_CLONE_CMP="cat"
+ shift; n_shift=$((n_shift+1));;
+ -z1|--gzip-compress)
+ IMG_CLONE_CMP="gzip -c $extra_gzip_opt"
+ shift; n_shift=$((n_shift+1));;
+ -z1p|--smp-gzip-compress)
+ IMG_CLONE_CMP="pigz -c $extra_pigz_opt"
+ shift; n_shift=$((n_shift+1));;
+ -z2|--bz2-compress)
+ IMG_CLONE_CMP="bzip2 -c $extra_bzip2_opt"
+ shift; n_shift=$((n_shift+1));;
+ -z2p|--smp-bzip2-compress)
+ if [ "$parallel_bzip2_prog" = "pbzip2" ]; then
+ IMG_CLONE_CMP="pbzip2 -c $extra_pbzip2_opt"
+ elif [ "$parallel_bzip2_prog" = "lbzip2" ]; then
+ IMG_CLONE_CMP="lbzip2 -c $extra_lbzip2_opt"
+ fi
+ shift; n_shift=$((n_shift+1));;
+ -z3|--lzo-compress)
+ # Now only for ntfsclone, partimage not yet.
+ IMG_CLONE_CMP="lzop -c $extra_lzop_opt"
+ shift; n_shift=$((n_shift+1));;
+ -z4|--lzma-compress)
+ IMG_CLONE_CMP="lzma -c $extra_lzma_opt"
+ shift; n_shift=$((n_shift+1));;
+ -z5|--xz-compress)
+ IMG_CLONE_CMP="xz -c $extra_xz_opt"
+ shift; n_shift=$((n_shift+1));;
+ -z5p|--smp-xz-compress)
+ IMG_CLONE_CMP="pixz $extra_pixz_opt"
+ shift; n_shift=$((n_shift+1));;
+ -z6|--lzip-compress)
+ IMG_CLONE_CMP="lzip -c $extra_lzip_opt"
+ shift; n_shift=$((n_shift+1));;
+ -z6p|--smp-lzip-compress)
+ IMG_CLONE_CMP="plzip -c $extra_plzip_opt"
+ shift; n_shift=$((n_shift+1));;
+ -z7|--lrzip-compress)
+ # The option for lrzip is different from that of gzip (not -c)
+ # Although lrzip uses "-p value Set processor count to override number of threads" to assign the CPU number, however, from the manual of lrzip:
+ # -p value
+ # Set the number of processor count to determine the number of
+ # threads to run. Normally lrzip will scale according to the num‐
+ # ber of CPUs it detects. Using this will override the value in
+ # case you wish to use less CPUs to either decrease the load on
+ # your machine, or to improve compression. Setting it to 1 will
+ # maximise compression but will not attempt to use more than one
+ # CPU.
+ # Therefore there is no need to assign a parallel version of lrzip.
+ IMG_CLONE_CMP="lrzip -q - $extra_lrzip_opt"
+ shift; n_shift=$((n_shift+1));;
+ -nogui|--nogui)
+ # -nogui is for backward compatable, better to use --nogui
+ nogui="on"
+ shift; n_shift=$((n_shift+1));;
+ -ntfs-ok|--ntfs-ok)
+ # if ntfs integrity is assumed OK, do not check
+ ntfs_integrity_check="no"
+ shift; n_shift=$((n_shift+1));;
+ -rm-win-swap-hib|--rm-win-swap-hib)
+ # Remove page and hibernation files in M$ windows
+ rm_win_swap_hib="yes"
+ shift; n_shift=$((n_shift+1));;
+ -rescue|--rescue)
+ rescue_mode="on"
+ shift; n_shift=$((n_shift+1));;
+ -sfsck|--skip-fsck-src-part)
+ fsck_src_part_intr="no"
+ fsck_src_part_auto="no"
+ shift; n_shift=$((n_shift+1));;
+ -fsck-src-part|--fsck-src-part|-fsck)
+ fsck_src_part_intr="yes"
+ shift; n_shift=$((n_shift+1));;
+ -fsck-src-part-y|--fsck-src-part-y|-fsck-y)
+ fsck_src_part_auto="yes"
+ shift; n_shift=$((n_shift+1));;
+ -mp|--mount-point)
+ shift; n_shift=$((n_shift+1));
+ if [ -z "$(echo $1 |grep ^-.)" ]; then
+ # skip the -xx option, in case
+ mount_point=$1
+ shift; n_shift=$((n_shift+1))
+ fi
+ [ -z "$mount_point" ] && USAGE && exit 1
+ ;;
+ -or|--ocsroot)
+ # overwrite the ocsroot in drbl.conf
+ shift; n_shift=$((n_shift+1));
+ if [ -z "$(echo $1 |grep ^-.)" ]; then
+ # skip the -xx option, in case
+ ocsroot="$1"
+ shift; n_shift=$((n_shift+1))
+ fi
+ [ -z "$ocsroot" ] && USAGE && exit 1
+ ;;
+ -i|--image-size)
+ shift; n_shift=$((n_shift+1))
+ if [ -z "$(echo $1 |grep ^-.)" ]; then
+ # skip the -xx option, in case
+ VOL_LIMIT=$1
+ shift; n_shift=$((n_shift+1))
+ fi
+ [ -z "$VOL_LIMIT" ] && USAGE && exit 1
+ ;;
+ --max-time-to-wait)
+ # ocs-sr need to know the --max-time-to-wait so that when sleeping
+ # between partitions restoring clone won't timeout.
+ shift; n_shift=$((n_shift+1))
+ if [ -z "$(echo $1 |grep ^-.)" ]; then
+ # skip the -xx option, in case
+ mcast_max_wait_time="$1"
+ shift; n_shift=$((n_shift+1))
+ fi
+ [ -z "$mcast_max_wait_time" ] && USAGE && exit 1
+ ;;
+ -q|--use-ntfsclone)
+ USE_NTFSCLONE="yes"
+ shift; n_shift=$((n_shift+1));;
+ -q1|--force-to-use-dd)
+ FORCE_TO_USE_DD="yes"
+ shift; n_shift=$((n_shift+1));;
+ -q2|--use-partclone)
+ USE_PARTCLONE="yes"
+ shift; n_shift=$((n_shift+1));;
+ -j|--create-part-by-sfdisk)
+ # We leave this option for backward compatability.
+ create_part_by_sfdisk="yes"
+ shift; n_shift=$((n_shift+1));;
+ -j0|--create-part-by-dd)
+ create_part_by_sfdisk="no"
+ shift; n_shift=$((n_shift+1));;
+ -j1|--dump-mbr-in-the-end)
+ dump_mbr_in_the_end="yes"
+ shift; n_shift=$((n_shift+1));;
+ -j2|--clone-hidden-data)
+ clone_hidden_data="yes"
+ shift; n_shift=$((n_shift+1));;
+ -icrc|--icrc)
+ do_partclone_crc_check="no"
+ shift; n_shift=$((n_shift+1));;
+ -irvd|--irvd)
+ rm_ntfs_vol_dirty_flag="no"
+ shift; n_shift=$((n_shift+1));;
+ -irhr|--irhr)
+ do_rm_hardware_record="no"
+ shift; n_shift=$((n_shift+1));;
+ -ius|--ius)
+ do_update_syslinux="no"
+ shift; n_shift=$((n_shift+1));;
+ -hn0)
+ shift; n_shift=$((n_shift+1))
+ change_win_hostname="By_IP"
+ if [ -z "$(echo $1 |grep ^-.)" ]; then
+ # skip the -xx option, in case
+ win_hostname_prefix="$1"
+ shift; n_shift=$((n_shift+1))
+ fi
+ [ -z "$win_hostname_prefix" ] && USAGE && exit 1
+ ;;
+ -hn1)
+ shift; n_shift=$((n_shift+1))
+ change_win_hostname="By_MAC"
+ if [ -z "$(echo $1 |grep ^-.)" ]; then
+ # skip the -xx option, in case
+ win_hostname_prefix="$1"
+ shift; n_shift=$((n_shift+1))
+ fi
+ [ -z "$win_hostname_prefix" ] && USAGE && exit 1
+ ;;
+ -o|-o1|--run-postrun-dir)
+ # -o is for backward compatability
+ run_postrun_dir="yes"
+ shift; n_shift=$((n_shift+1));;
+ -o0|--run-prerun-dir)
+ run_prerun_dir="yes"
+ shift; n_shift=$((n_shift+1));;
+ -ns|--ntfs-progress-in-image-dir)
+ ntfsclone_progress="image_dir"
+ shift; n_shift=$((n_shift+1));;
+ -gm|--gen-md5sum)
+ gen_md5sum="yes"
+ shift; n_shift=$((n_shift+1));;
+ -gs|--gen-sha1sum)
+ gen_sha1sum="yes"
+ shift; n_shift=$((n_shift+1));;
+ -cm|--check-md5sum)
+ check_md5sum="yes"
+ shift; n_shift=$((n_shift+1));;
+ -cs|--check-sha1sum)
+ check_sha1sum="yes"
+ shift; n_shift=$((n_shift+1));;
+ -gmf|--gen-chksum-for-files-in-dev)
+ gen_chksum_for_files_in_dev="yes"
+ shift; n_shift=$((n_shift+1));;
+ -cmf|--chk-chksum-for-files-in-dev)
+ chk_chksum_for_files_in_dev="yes"
+ shift; n_shift=$((n_shift+1));;
+ -x|--interactive)
+ ocs_sr_mode="interactive"
+ shift; n_shift=$((n_shift+1));;
+ --mcast-port)
+ shift; n_shift=$((n_shift+1));
+ if [ -z "$(echo $1 |grep ^-.)" ]; then
+ # skip the -xx option, in case
+ ocs_sr_mcast_port="$1"
+ shift; n_shift=$((n_shift+1))
+ fi
+ [ -z "$ocs_sr_mcast_port" ] && USAGE && exit 1
+ ;;
+ -p|--postaction)
+ shift; n_shift=$((n_shift+1));
+ if [ -z "$(echo $1 |grep ^-.)" ]; then
+ # skip the -xx option, in case
+ postaction="$1"
+ shift; n_shift=$((n_shift+1))
+ fi
+ [ -z "$postaction" ] && USAGE && exit 1
+ ;;
+ --restore-only)
+ ocs_x_mode="restore_only"
+ shift; n_shift=$((n_shift+1));;
+ --save-only)
+ ocs_x_mode="save_only"
+ shift; n_shift=$((n_shift+1));;
+ -um|--user-mode)
+ shift; n_shift=$((n_shift+1));
+ # skip the -xx option, in case
+ if [ -z "$(echo $1 |grep ^-.)" ]; then
+ ocs_user_mode="$1"
+ shift; n_shift=$((n_shift+1));
+ fi
+ [ -z "$ocs_user_mode" ] && USAGE && exit 1
+ ;;
+ -icds|--ignore-chk-dsk-size-pt)
+ chk_tgt_disk_size_bf_mk_pt="no"
+ shift; n_shift=$((n_shift+1));;
+ -iefi|--ignore-update-efi-nvram)
+ update_efi_nvram="no"
+ shift; n_shift=$((n_shift+1));;
+ -*) echo "${0}: ${1}: invalid option" >&2
+ USAGE >& 2
+ exit 2 ;;
+ *) break ;;
+ esac
+ done
+} # end of parse_ocs_sr_cmd_options_with_dash
+#
+save_only_dia_des() {
+ savedisk_msg_1="savedisk"
+ savedisk_msg_2="$(rep_whspc_w_udrsc "$msg_clonezilla_save_local_disk")"
+ saveparts_msg_1="saveparts"
+ saveparts_msg_2="$(rep_whspc_w_udrsc "$msg_clonezilla_save_local_parts")"
+}
+#
+restore_only_dia_des() {
+ restoredisk_msg_1="restoredisk"
+ restoredisk_msg_2="$(rep_whspc_w_udrsc "$msg_clonezilla_restore_local_disk")"
+ restoreparts_msg_1="restoreparts"
+ restoreparts_msg_2="$(rep_whspc_w_udrsc "$msg_clonezilla_restore_local_parts")"
+}
+recovery_clonezilla_live_dia_des() {
+ recovery_clonezilla_live_msg_1="recovery-iso-zip"
+ recovery_clonezilla_live_msg_2="$(rep_whspc_w_udrsc "$msg_recovery_clonezilla_live")"
+}
+chk_img_restorable_dia_des() {
+ chk_img_restorable_msg_1="chk-img-restorable"
+ chk_img_restorable_msg_2="$(rep_whspc_w_udrsc "$msg_check_if_image_restorable")"
+}
+convert_img_compression_dia_des() {
+ convert_img_compression_msg_1="cvt-img-compression"
+ convert_img_compression_msg_2="$(rep_whspc_w_udrsc "$msg_convert_img_compression")"
+}
+encrypt_img_dia_des() {
+ encrypt_img_msg_1="encrypt-img"
+ encrypt_img_msg_2="$(rep_whspc_w_udrsc "$msg_encrypt_existing_img")"
+}
+decrypt_img_dia_des() {
+ decrypt_img_msg_1="decrypt-img"
+ decrypt_img_msg_2="$(rep_whspc_w_udrsc "$msg_decrypt_existing_img")"
+}
+p2v_img_dia_des() {
+ p2v_img_msg_1="p2v-img"
+ p2v_img_msg_2="$(rep_whspc_w_udrsc "$msg_p2v_existing_img")"
+}
+one_image_to_multiple_disks_dia_des() {
+ one_2_m_disks_msg_1="1-2-mdisks"
+ one_2_m_disks_msg_2="$(rep_whspc_w_udrsc "$msg_restore_1_image_to_multiple_local_disks")"
+}
+# functions to process options.
+wrap_up_opt() {
+ [ -z "$VOL_LIMIT" ] && VOL_LIMIT=$VOL_LIMIT_DEFAULT
+
+ # change to other mount point for extra harddisk
+ # Note: functions get_existing_disk_image, get_existing_parts_image and get_existing_partitions_from_img will use $imagedir
+ if [ -n "$mount_point" ]; then
+ echo "Option -mp|--mount-point is assigned."
+ echo "Using the image root directory $mount_point instead of $ocsroot."
+ imagedir="$mount_point"
+ else
+ imagedir="$ocsroot"
+ fi
+
+ check_ocs_input_params
+
+ # Set/Reset init values
+ PARTIMAGE_SAVE_OPT="$PARTIMAGE_SAVE_OPT_INIT --debug=$debug_level"
+ PARTIMAGE_RESTORE_OPT="$PARTIMAGE_RESTORE_OPT_INIT --debug=$debug_level"
+ PARTCLONE_SAVE_OPT="$PARTCLONE_SAVE_OPT_INIT"
+ PARTCLONE_RESTORE_OPT="$PARTCLONE_RESTORE_OPT_INIT"
+ if [ "$nogui" = "on" ]; then
+ # Partimage default uses TUI, if noguid, we have to run it with -B gui=no
+ PARTIMAGE_SAVE_OPT="$PARTIMAGE_SAVE_OPT -B gui=no"
+ PARTIMAGE_RESTORE_OPT="$PARTIMAGE_RESTORE_OPT -B gui=no"
+ fi
+ if [ "$nogui" = "off" ]; then
+ # TUI is on.
+ # Use the TUI mode for partclone (i.e. run with -N, otherwise by default partclone runs only text output)
+ # //NOTE// From Partclone 0.2.52, if -N is set for partclone in save mode, the ncurse interface is able to be shown in stderr.
+ PARTCLONE_SAVE_OPT="$PARTCLONE_SAVE_OPT -N"
+ PARTCLONE_RESTORE_OPT="$PARTCLONE_RESTORE_OPT_INIT -N"
+ fi
+ if [ "$rescue_mode" = "on" ]; then
+ PARTCLONE_SAVE_OPT="$PARTCLONE_SAVE_OPT --rescue"
+ fi
+ if [ "$do_partclone_crc_check" = "no" ]; then
+ PARTCLONE_RESTORE_OPT="$PARTCLONE_RESTORE_OPT --ignore_crc"
+ fi
+
+ echo $msg_delimiter_star_line
+ [ "$BOOTUP" = "color" ] && $SETCOLOR_WARNING
+ [ "$create_part" = "no" ] && echo "$msg_do_not_create_part"
+ [ "$restore_mbr" = "no" ] && echo "$msg_do_not_restore_mbr"
+ [ "$select_img_in_client" = "yes" ] && echo "$msg_you_have_to_input_image_names_in_client"
+ [ "$BOOTUP" = "color" ] && $SETCOLOR_NORMAL
+
+ # Append postaction, since we did not ask that in set_ocs_sr_extra_param and we might already set that.
+ if [ -n "$postaction" -a \
+ -z "$(echo $OCS_OPTS | grep -Ewo -- "-p")" ]
+ then
+ OCS_OPTS="$OCS_OPTS -p $postaction"
+ fi
+ if [ "$verbose" = "on" ]; then
+ PARTCLONE_SAVE_OPT="$PARTCLONE_SAVE_OPT -d"
+ PARTCLONE_RESTORE_OPT="$PARTCLONE_RESTORE_OPT -d"
+ echo "OCS_OPTS: $OCS_OPTS"
+ echo "PARTIMAGE_SAVE_OPT: $PARTIMAGE_SAVE_OPT"
+ echo "PARTIMAGE_RESTORE_OPT: $PARTIMAGE_RESTORE_OPT"
+ echo "PARTCLONE_SAVE_OPT: $PARTCLONE_SAVE_OPT"
+ echo "PARTCLONE_RESTORE_OPT: $PARTCLONE_RESTORE_OPT"
+ # The default output for udpcast stderr is surpressed, now turn it on
+ udpcast_stderr="/dev/stderr"
+ fi
+ # If VOL_LIMIT is too large, split won't work. Check and fix it if required.
+ check_and_fix_vol_limit_if_required
+} # end of wrap_up_opt
+#
+wrap_up_opt_after_interactive_selection() {
+ wrap_up_opt
+ # For case when interactive mode the option "-scr" or "-scs" is used.
+ # i.e. command like: "ocs-sr -x -scr" or "ocs-sr -x "-scs"
+ if [ -z "$chk_img_restoreable_mod_save" ]; then
+ chk_img_restoreable_mod_save="$chk_img_restoreable_mod_save_def"
+ fi
+ if [ -z "$chk_img_restoreable_mod_restore" ]; then
+ chk_img_restoreable_mod_restore="$chk_img_restoreable_mod_restore_def"
+ fi
+ # Shall we set default postaction if empty here?
+ #[ -z "$postaction" ] && postaction="$POSTACTION_DEF"
+
+} # end of wrap_up_opt_after_interactive_selection
+#
+run_ocs_sr_again_prompt() {
+ local img dev
+ # OCS_OPTS is a global variable
+ img="$1"
+ dev="$2"
+ if [ "$ocs_sr_mode" = "interactive" ]; then
+ run_again_fname="/tmp/ocs-$img-`date +%F-%H-%M`"
+ [ "$BOOTUP" = "color" ] && $SETCOLOR_SUCCESS
+ echo PS. $msg_run_drbl_ocs_again_cmd | tee --append ${OCS_LOGFILE}
+ echo $0 $OCS_OPTS $ocs_sr_type $img $dev >> ${OCS_LOGFILE}
+ echo $0 $OCS_OPTS $ocs_sr_type $img $dev | tee $run_again_fname
+ echo "$msg_ocs_sr_again_command_saved_filename: $run_again_fname"
+ [ "$BOOTUP" = "color" ] && $SETCOLOR_NORMAL
+ [ -e "$run_again_fname" ] && chmod 755 $run_again_fname
+ echo $msg_delimiter_star_line | tee --append ${OCS_LOGFILE}
+ if [ "$ocs_batch_mode" != "on" ]; then
+ echo -n "$msg_press_enter_to_continue "
+ read
+ fi
+ fi
+} # end of run_ocs_sr_again_prompt
+#
+save_ocs_sr_related_vars() {
+ # Function to save ocs-related variables so that some customized program can use.
+ # The old one will be overwritten
+ mkdir -p /var/lib/clonezilla/
+ echo "ocs_cmd=\"$ocs\"" > /var/lib/clonezilla/ocs-vars
+ echo "target_dir=\"$target_dir\"" >> /var/lib/clonezilla/ocs-vars
+ echo "target_hd=\"$target_hd\"" >> /var/lib/clonezilla/ocs-vars
+ echo "postaction=\"$postaction\"" >> /var/lib/clonezilla/ocs-vars
+ echo "ocs_sr_mode=\"$ocs_sr_mode\"" >> /var/lib/clonezilla/ocs-vars
+} # end of save_ocs_related_var
+#
+create_clonezilla_live_recovery_iso_zip() {
+ # Target command Ex:
+ # ocs-iso -g en -t -k NONE -e "-b -c restoredisk sarge-r5 hda" sarge-r5
+ # ocs-live-dev -c -g en -t -k NONE -e "-b -c restoredisk sarge-r5 hda" sarge-r5
+ local REC_TMP ans_
+ REC_TMP=`mktemp /tmp/ocs_recovery_tmp.XXXXXX`
+ trap "[ -f "$REC_TMP" ] && rm -f $REC_TMP" HUP INT QUIT TERM EXIT
+
+ # Ask image name first (disk or parts image)
+ if [ "$ocs_sr_img_name" = "ask_user" ]; then
+ # Since get_target_dir_name_when_restoring_parts can search disk and parts image, we use this function instead of get_target_dir_name_when_restoring_disk
+ get_target_dir_name_when_restoring_parts # get $target_dir
+ else
+ target_dir="$ocs_sr_img_name"
+ fi
+ check_input_target_image "$ocsroot/$target_dir"
+ # Check if the image is disk or parts
+ if [ -e "$ocsroot/$target_dir/disk" ]; then
+ rec_dev_action=restoredisk
+ else
+ rec_dev_action=restoreparts
+ fi
+
+ # ask target disk/parts
+ case "$rec_dev_action" in
+ "restoredisk")
+ dev_prompt_and_example="$msg_input_device_name_for_recovery_iso_zip ($msg_ex: 'hda' $msg_or 'sda' $msg_or 'hda hdb' $msg_or 'sda sdb') \n$msg_linux_disk_MS_mapping\n$msg_prompt_to_use_ask_user_for_later_choose"
+ # savedisk_preset is borrowed from drbl-ocs.conf
+ dev_preset="$(get_disk_list_from_img $ocsroot/$target_dir)"
+ ;;
+ "restoreparts")
+ dev_prompt_and_example="$msg_input_device_name_for_recovery_iso_zip ($msg_ex: 'hda1 hda2' $msg_or 'sda1 sda2') \n$msg_linux_parts_MS_mapping\n$msg_prompt_to_use_ask_user_for_later_choose"
+ # saveparts_preset is borrowed from drbl-ocs.conf
+ dev_preset="$(get_parts_list_from_img $ocsroot/$target_dir)"
+ ;;
+ esac
+ ASK_DEV_NAME=1
+ while [ "$ASK_DEV_NAME" -ne 0 ]; do
+ $DIA --backtitle "$msg_nchc_free_software_labs" --title "$msg_nchc_clonezilla | $msg_mode: $ocs_mode_prompt" \
+ --inputbox "$dev_prompt_and_example" 0 0 "$dev_preset" 2> $REC_TMP
+ rec_dev_name="$(cat $REC_TMP)"
+ if [ -z "$rec_dev_name" ]; then
+ $DIA --backtitle "$msg_nchc_free_software_labs" --title "$msg_nchc_clonezilla" \
+ --yesno "$msg_you_must_input_device_name_to_be_restored! $msg_do_u_want_to_do_it_again" 0 0
+ ans_="$?"
+ case "$ans_" in
+ 0) # yes is chosen
+ ASK_DEV_NAME=1;;
+ 1) # no is chosen
+ echo "$msg_program_stop!"
+ [ -f "$REC_TMP" ] && rm -f $REC_TMP
+ exit 1;;
+ esac
+ else
+ ASK_DEV_NAME=0
+ fi
+ done
+ echo > $REC_TMP # clean REC_TMP
+
+ # ask if want to set ocs extra param for recovery iso/zip
+ set_ocs_sr_extra_param restore $REC_TMP $ocs_sr_type
+ OCS_OPTS="$(cat $REC_TMP)"
+ parse_ocs_sr_cmd_options_with_dash $OCS_OPTS
+ OCS_OPTS="$(echo $OCS_OPTS)" # make it in a line
+ echo > $REC_TMP # clean REC_TMP
+
+ # Ask language to be used in live iso/zip
+ # $DRBL_SCRIPT_PATH/lang/bash/$lang
+ get_existing_language $REC_TMP
+ rec_lang="$(cat $REC_TMP)"
+
+ # Ask keymap
+ ASK_KEYMAP_FILE=1
+ while [ "$ASK_KEYMAP_FILE" -ne 0 ]; do
+ $DIA --backtitle "$msg_nchc_free_software_labs" --title "$msg_nchc_clonezilla | $msg_mode: $ocs_mode_prompt" \
+ --inputbox "$keymap_file_prompt_and_example" 0 0 "NONE" 2> $REC_TMP
+ rec_keymap_file="$(cat $REC_TMP)"
+ if [ -z "$rec_keymap_file" ]; then
+ $DIA --backtitle "$msg_nchc_free_software_labs" --title "$msg_nchc_clonezilla | $msg_mode: $ocs_mode_prompt" \
+ --yesno "$msg_you_must_input_keymap_file! $msg_do_u_want_to_do_it_again" 0 0
+ ans_="$?"
+ case "$ans_" in
+ 0) # yes is chosen
+ ASK_KEYMAP_FILE=1;;
+ 1) # no is chosen
+ echo "$msg_program_stop!"
+ [ -f "$REC_TMP" ] && rm -f $REC_TMP
+ exit 1;;
+ esac
+ else
+ ASK_KEYMAP_FILE=0
+ fi
+ done
+ echo > $REC_TMP # clean REC_TMP
+
+ # Generage iso, zip or both ?
+ $DIA --backtitle "$msg_nchc_free_software_labs" --title "$msg_nchc_clonezilla | $msg_mode: $ocs_mode_prompt" \
+ --menu "$msg_recovery_clonezilla_file_type $msg_choose_file_type" \
+ 0 0 0 \
+ "iso" "$msg_create_recovery_clonezilla_live_iso" \
+ "zip" "$msg_create_recovery_clonezilla_live_zip" \
+ "both" "$msg_create_recovery_clonezilla_live_iso_and_zip" \
+ 2> $REC_TMP
+ rec_file_type="$(cat $REC_TMP)"
+
+ [ -f "$REC_TMP" ] && rm -f $REC_TMP
+
+ # Run ocs-iso/ocs-live-dev
+ # Ex:ocs-iso -g en -t -k NONE -e "-b -c restoredisk sarge-r5 hda" sarge-r5
+ # ocs-live-dev -c -g en -t -k NONE -e "-b -c restoredisk sarge-r5 hda" sarge-r5
+ (cd $ocsroot
+ case "$rec_file_type" in
+ iso)
+ run_again_fname="/tmp/ocs-iso-zip-`date +%F-%H-%M`"
+ [ "$BOOTUP" = "color" ] && $SETCOLOR_SUCCESS
+ echo PS. $msg_run_drbl_ocs_again_cmd | tee --append ${OCS_LOGFILE}
+ echo ocs-iso -g $rec_lang -t -k $rec_keymap_file -e \"$OCS_OPTS $rec_dev_action $target_dir $rec_dev_name\" $target_dir >> ${OCS_LOGFILE}
+ echo ocs-iso -g $rec_lang -t -k $rec_keymap_file -e \"$OCS_OPTS $rec_dev_action $target_dir $rec_dev_name\" $target_dir | tee $run_again_fname
+ [ -e "$run_again_fname" ] && chmod 755 $run_again_fname
+ echo "$msg_ocs_sr_again_command_saved_filename: $run_again_fname"
+ echo "$msg_the_output_file_is_in_dir: $ocsroot"
+ [ "$BOOTUP" = "color" ] && $SETCOLOR_NORMAL
+ echo -n "$msg_press_enter_to_continue"
+ read
+ ocs-iso -g $rec_lang -t -k $rec_keymap_file -e "$OCS_OPTS $rec_dev_action $target_dir $rec_dev_name" $target_dir
+ ;;
+ zip)
+ run_again_fname="/tmp/ocs-iso-zip-`date +%F-%H-%M`"
+ [ "$BOOTUP" = "color" ] && $SETCOLOR_SUCCESS
+ echo PS. $msg_run_drbl_ocs_again_cmd | tee --append ${OCS_LOGFILE}
+ echo ocs-live-dev -c -g $rec_lang -t -k $rec_keymap_file -e \"$OCS_OPTS $rec_dev_action $target_dir $rec_dev_name\" $target_dir >> ${OCS_LOGFILE}
+ echo ocs-live-dev -c -g $rec_lang -t -k $rec_keymap_file -e \"$OCS_OPTS $rec_dev_action $target_dir $rec_dev_name\" $target_dir | tee $run_again_fname
+ [ -e "$run_again_fname" ] && chmod 755 $run_again_fname
+ echo "$msg_ocs_sr_again_command_saved_filename: $run_again_fname"
+ echo "$msg_the_output_file_is_in_dir: $ocsroot"
+ [ "$BOOTUP" = "color" ] && $SETCOLOR_NORMAL
+ echo -n "$msg_press_enter_to_continue"
+ read
+ ocs-live-dev -c -g $rec_lang -t -k $rec_keymap_file -e "$OCS_OPTS $rec_dev_action $target_dir $rec_dev_name" $target_dir
+ ;;
+ both)
+ run_again_fname="/tmp/ocs-iso-zip-`date +%F-%H-%M`"
+ [ "$BOOTUP" = "color" ] && $SETCOLOR_SUCCESS
+ echo PS. $msg_run_drbl_ocs_again_cmd | tee --append ${OCS_LOGFILE}
+ echo ocs-iso -g $rec_lang -t -k $rec_keymap_file -e \"$OCS_OPTS $rec_dev_action $target_dir $rec_dev_name\" $target_dir >> ${OCS_LOGFILE}
+ echo ocs-iso -g $rec_lang -t -k $rec_keymap_file -e \"$OCS_OPTS $rec_dev_action $target_dir $rec_dev_name\" $target_dir | tee $run_again_fname
+ echo ocs-live-dev -c -g $rec_lang -t -k $rec_keymap_file -e \"$OCS_OPTS $rec_dev_action $target_dir $rec_dev_name\" $target_dir >> ${OCS_LOGFILE}
+ echo ocs-live-dev -c -g $rec_lang -t -k $rec_keymap_file -e \"$OCS_OPTS $rec_dev_action $target_dir $rec_dev_name\" $target_dir | tee -a $run_again_fname
+ [ -e "$run_again_fname" ] && chmod 755 $run_again_fname
+ echo "$msg_ocs_sr_again_command_saved_filename: $run_again_fname"
+ echo "$msg_the_output_file_is_in_dir: $ocsroot"
+ [ "$BOOTUP" = "color" ] && $SETCOLOR_NORMAL
+ echo -n "$msg_press_enter_to_continue"
+ read
+ ocs-iso -g $rec_lang -t -k $rec_keymap_file -e "$OCS_OPTS $rec_dev_action $target_dir $rec_dev_name" $target_dir
+ ocs-live-dev -c -g $rec_lang -t -k $rec_keymap_file -e "$OCS_OPTS $rec_dev_action $target_dir $rec_dev_name" $target_dir
+ ;;
+ esac
+ )
+} # end of create_clonezilla_live_recovery_iso_zip
+#
+get_extra_param_when_type_is_ask_user_and_interactive_mode() {
+ if [ "$ocs_sr_type_assign" = "ask_user" -o \
+ "$ocs_sr_mode" = "interactive" ]; then
+ # since this ocs-sr asks for ocs_sr type, this means the option maybe not parsed.
+ case "$ocs_sr_type" in
+ save*)
+ # ask if want to set ocs extra param
+ OCS_PARAM_TMP=`mktemp /tmp/ocs_sr_param_tmp.XXXXXX`
+ trap "[ -f "$OCS_PARAM_TMP" ] && rm -f $OCS_PARAM_TMP" HUP INT QUIT TERM EXIT
+ set_ocs_sr_extra_param save $OCS_PARAM_TMP $ocs_sr_type
+ # In interactive mode, we reset OCS_OPTS instead of appending it. Later wrap_up_opt will process more.
+ OCS_OPTS="$(cat $OCS_PARAM_TMP)"
+ [ -f "$OCS_PARAM_TMP" ] && rm -f $OCS_PARAM_TMP
+ ;;
+ restore*)
+ # Skip the multicast_restore*, since it's spawned by drbl-ocs, not from ocs-sr
+ # ask if want to set ocs extra param
+ OCS_PARAM_TMP=`mktemp /tmp/ocs_sr_param_tmp.XXXXXX`
+ trap "[ -f "$OCS_PARAM_TMP" ] && rm -f $OCS_PARAM_TMP" HUP INT QUIT TERM EXIT
+ set_ocs_sr_extra_param restore $OCS_PARAM_TMP $ocs_sr_type
+ # In interactive mode, we reset OCS_OPTS instead of appending it. Later wrap_up_opt will process more.
+ OCS_OPTS="$(cat $OCS_PARAM_TMP)"
+ [ -f "$OCS_PARAM_TMP" ] && rm -f $OCS_PARAM_TMP
+ ;;
+ exit)
+ echo "To use it again, run \"$ocs -x\"."
+ exit 2
+ ;;
+ esac
+ # Some options might be assigned in in /etc/ocs/ocs-live.conf. Here we have to add that.
+ if [ -n "$ocs_sr_save_extra_opt" -o -n "$ocs_sr_restore_extra_opt" ]; then
+ case "$ocs_sr_type" in
+ *save*) OCS_OPTS="$OCS_OPTS $ocs_sr_save_extra_opt" ;;
+ *restore*) OCS_OPTS="$OCS_OPTS $ocs_sr_restore_extra_opt" ;;
+ esac
+ fi
+ parse_ocs_sr_cmd_options_with_dash $OCS_OPTS
+ fi
+} # end of get_extra_param_when_type_is_ask_user_and_interactive_mode
+
+# OCS_OPT is for drbl-ocs using, will not put into the partimage command options
+# PARTIMAGE_RESTORE_OPT & PARTIMAGE_SAVE_OPT will put into partimage command options
+PARTIMAGE_SAVE_OPT_INIT=""
+PARTIMAGE_RESTORE_OPT_INIT=""
+OCS_OPT=""
+report_msg=""
+# Set some default values,
+nfs_restart="no"
+always_restore="no"
+pxe_menu_default_mode=""
+mcast_loop="1"
+USE_NTFSCLONE="no"
+nogui="off" # Default to turn on TUI
+FORCE_TO_USE_DD="no"
+force_dma_on="yes"
+create_part="yes"
+restore_mbr="yes"
+restore_ebr="yes"
+restore_prebuild_mbr="no"
+rm_win_swap_hib="no"
+rescue_mode="off"
+select_img_in_client="no"
+# If create_part_by_sfdisk=no, then we will use "dd if=$tgt_dir/mbr of=$sfdisk_target_hd skip=446 seek=446 bs=1 count=66" to dump the partition table.
+create_part_by_sfdisk="yes"
+# we want to make sfdisk --force if sfdisk is used.
+sfdisk_opt="--force"
+# Normally we do not dump MBR again when everything is done. However, This is an insurance for some hard drive has different numbers of cylinder, head and sector between image was saved and restored.". If the option is "yes", we will use dd to dump the MBR (total 512 Bytes, i.e. 446 bytes (executable code area) + 64 bytes (table of primary partitions) + 2 bytes (MBR signature; # 0xAA55) = 512 bytes) after disk was restored.
+dump_mbr_in_the_end="no"
+# The flag to save or restore the hidden data between MBR and 1st partition.
+clone_hidden_data="no"
+# default output for udpcast stderr is surpressed.
+# NOTE! Do not redirect it to standard output (stdin), otherwise partimage/ntfsclone pipe will get wrong image!
+udpcast_stderr="/dev/null"
+# default not to run those scripts in $OCS_PRERUN_DIR $OCS_POSTRUN_DIR
+run_prerun_dir="no"
+run_postrun_dir="no"
+# default to put the ntfsclone progress in local /tmp/ (tmpfs)
+ntfsclone_progress="local_tmp"
+# Default to do CRC checking of partclone when restoring
+do_partclone_crc_check="yes"
+# Default to remove the Linux udev MAC address records on the restored GNU/Linux
+do_rm_hardware_record="yes"
+# Default to run ocs-update-syslinux to update the syslinux files (ldlinux.sys, *.c32 and *.bin)
+do_update_syslinux="yes"
+# Default not to put yes or no for encrypting the image dir
+encrypt_ocs_img=""
+# Default to remove the ntfs volume dirty flag after it's restored.
+rm_ntfs_vol_dirty_flag="yes"
+
+# The parameters, example:
+# (-b -g auto --nogui --max-time-to-wait 300 --language 0 multicast_restoreparts sarge-base "hda1 hda3" 2232) ->
+# (multicast_restoreparts sarge-base "hda1 hda3" 2232)
+parse_ocs_sr_cmd_options_with_dash $*
+shift ${n_shift}
+ocs_sr_type_assign="$1"
+ocs_sr_img_name="$2"
+shift 2
+ocs_sr_dev="$*"
+# If the inputted dev is: "hda hdb", convert it to hda hdb (so it won't be ""hda hdb"")
+ocs_sr_dev="$(echo $ocs_sr_dev | tr -d \")"
+
+# Rotate the log file
+ocs_log_rotate $OCS_LOGFILE
+ocs_log_rotate /var/log/partclone.log
+
+#
+echo "Starting $0 at "$(LC_ALL=C date +%F' '%T' '%Z)"..." | tee --append ${OCS_LOGFILE}
+
+#
+ask_and_load_lang_set $specified_lang
+
+# check DIA
+check_DIA_set_ESC $DIA
+
+##############
+#### MAIN ####
+##############
+
+# ocs root
+if [ ! -d "$ocsroot" ]; then
+ echo "Please make sure your $ocsroot is a directory!!!"
+ # The common error is user try to create a ocsroot as /home/partimage, the default is /home/partimag (without e)
+ if [ "$ocsroot" = "/home/partimag" -a -d "/home/partimage" ]; then
+ [ "$BOOTUP" = "color" ] && $SETCOLOR_WARNING
+ echo "$msg_home_partimag_not_home_partimage"
+ [ "$BOOTUP" = "color" ] && $SETCOLOR_NORMAL
+ fi
+ echo "We can NOT go on! Press \"c\" to enter command prompt or any other key to quit the program..."
+ read fail_answer
+ case "$fail_answer" in
+ [cC]) sulogin ;;
+ *) exit 1 ;;
+ esac
+fi
+
+#
+if [ "$ocs_sr_type_assign" = "ask_user" -o \
+ "$ocs_sr_mode" = "interactive" ]; then
+ ASK_OCS_SR_TYPE=1
+ case "$ocs_x_mode" in
+ "save_only")
+ save_only_dia_des
+ ;;
+ "restore_only")
+ restore_only_dia_des
+ ;;
+ *) # Both when image exists, or only save menu
+ save_only_dia_des
+ if check_if_any_image_exists; then
+ restore_only_dia_des
+ fi
+ ;;
+ esac
+ if [ "$ocs_sr_extra_restore_mode" != "no" ]; then
+ # If there is any image in $ocsroot, we can create recovery clonezilla live iso/zip and check the image.
+ if check_if_any_image_exists; then
+ recovery_clonezilla_live_dia_des
+ chk_img_restorable_dia_des
+ one_image_to_multiple_disks_dia_des
+ convert_img_compression_dia_des
+ encrypt_img_dia_des
+ decrypt_img_dia_des
+ if type qemu-img &>/dev/null && type kvm &>/dev/null; then
+ p2v_img_dia_des
+ fi
+ fi
+ fi
+
+ #
+ [ -z "$ocs_user_mode" ] && ask_if_beginner_or_expert_mode
+
+ # Clean the previous saved env so if ocs-onthefly is cancelled, won't read the previous env and reboot/poweroff.
+ rm -f /var/lib/clonezilla/ocs-vars
+
+ #
+ echo "Choose the mode for ocs-sr"
+ TMP="$(mktemp /tmp/menu-ocs.XXXXXX)"
+ trap "[ -f "$TMP" ] && rm -f $TMP" HUP INT QUIT TERM EXIT
+ while [ "$ASK_OCS_SR_TYPE" -ne 0 ]; do
+ $DIA --backtitle "$msg_nchc_free_software_labs" \
+ --title "$msg_nchc_clonezilla: $msg_choose_mode" \
+ --menu "$msg_clonezilla_is_free_and_no_warranty\n$msg_overwrite_data_on_disk_when_restoring\n$msg_hint_multiple_choice_select_by_space\n$msg_choose_mode_ocs_sr" \
+ 0 0 0 \
+ $savedisk_msg_1 $savedisk_msg_2 \
+ $saveparts_msg_1 $saveparts_msg_2 \
+ $restoredisk_msg_1 $restoredisk_msg_2 \
+ $restoreparts_msg_1 $restoreparts_msg_2 \
+ $one_2_m_disks_msg_1 $one_2_m_disks_msg_2 \
+ $recovery_clonezilla_live_msg_1 $recovery_clonezilla_live_msg_2 \
+ $chk_img_restorable_msg_1 $chk_img_restorable_msg_2 \
+ $convert_img_compression_msg_1 $convert_img_compression_msg_2 \
+ $encrypt_img_msg_1 $encrypt_img_msg_2 \
+ $decrypt_img_msg_1 $decrypt_img_msg_2 \
+ $p2v_img_msg_1 $p2v_img_msg_2 \
+ "exit" "$msg_exit. $msg_enter_cml" \
+ 2> $TMP
+ ocs_sr_type="$(cat $TMP)"
+ if [ -z "$ocs_sr_type" ]; then
+ ASK_OCS_SR_TYPE=1
+ else
+ ASK_OCS_SR_TYPE=0
+ fi
+ done
+ [ -e "$TMP" ] && rm -f $TMP
+ [ "$ocs_sr_type" = "exit" ] && exit 0
+ [ -z "$ocs_sr_type" ] && echo "You must specify the action! Program terminated!!!" && exit 1
+ # If ocs_sr_type from ask_user, then we should let ocs_sr_img_name & ocs_sr_dev as ask_user, too
+ ocs_sr_img_name="ask_user"
+ ocs_sr_dev="ask_user"
+ # Overwrite the default HALT_REBOOT_OPT (-f -n) so that if user choose to halt/reboot, it will use normal halt/reboot sequence, then mounted device will be unmounted.
+ HALT_REBOOT_OPT=""
+ VOL_LIMIT="$VOL_LIMIT_IN_INTERACTIVE"
+else
+ ocs_sr_type="$ocs_sr_type_assign"
+fi
+
+# ocs_mode_prompt is to be shown in the title of dialog menu later
+ocs_mode_prompt="$ocs_sr_type"
+
+echo $msg_delimiter_star_line | tee --append $OCS_LOGFILE
+# Clonezilla image home
+[ "$BOOTUP" = "color" ] && $SETCOLOR_WARNING
+echo "Clonezilla image dir: $ocsroot" | tee --append $OCS_LOGFILE
+[ "$BOOTUP" = "color" ] && $SETCOLOR_NORMAL
+[ ! -d "$ocsroot" ] && mkdir -p $ocsroot
+
+# Some options might be assigned in in /etc/ocs/ocs-live.conf. Here we have to add that.
+if [ -n "$ocs_sr_save_extra_opt" -o -n "$ocs_sr_restore_extra_opt" ]; then
+ case "$ocs_sr_type" in
+ *save*) OCS_OPTS="$OCS_OPTS $ocs_sr_save_extra_opt" ;;
+ *restore*) OCS_OPTS="$OCS_OPTS $ocs_sr_restore_extra_opt" ;;
+ esac
+ parse_ocs_sr_cmd_options_with_dash $OCS_OPTS
+fi
+
+# Decide the mode for update_efi_nvram if it's not assigned. The EFI NVRAM is only updated when the mode is restoredisk or multicast_restoredisk
+if [ -z "$update_efi_nvram" ]; then
+ case "$ocs_sr_type" in
+ *restoredisk) update_efi_nvram="yes";;
+ *) update_efi_nvram="no";;
+ esac
+fi
+
+# Process options. //NOTE// There might be twice of wrap_up_opt, one is for the command options assigned, the other one is the command options got from interactive mode.
+wrap_up_opt
+
+# Before starting, we turn off swap and LVM2, unlock the busy partitions.
+turn_off_swap_and_LVM2
+
+# Boot parameter ocs_overwrite_postaction has higher priority than the action assigned by option "-p".
+overwrite_postaction_if_assigned $ocs_sr_type
+
+#
+case "$ocs_sr_type" in
+ "savedisk")
+ task_preprocessing "$ocs_sr_type"
+ MULTIPATH_INFODIR="$(mktemp -d /tmp/multipath_info.XXXXXX)"
+
+ if [ "$ocs_sr_img_name" = "ask_user" ]; then
+ get_target_dir_name_when_saving # get $target_dir
+ elif [ "$ocs_sr_img_name" = "autoname" ]; then
+ # Ref: https://sourceforge.net/projects/clonezilla/forums/forum/799287/topic/4815557
+ # We need something unique, so MAC address is a good choice
+ for i in `LC_ALL=C get-nic-devs`; do
+ prefix_mac="$(LC_ALL=C drbl-get-macadd $i)"
+ if [ -n "$prefix_mac" ]; then
+ prefix_mac="$(LC_ALL=C echo $prefix_mac | sed -e 's/://g')"
+ break
+ fi
+ done
+ # If not found, use UUID from MB manufacture
+ [ -z "$prefix_mac" ] && prefix_mac="$(LC_ALL=C dmidecode | grep -io "UUID: .*$" | head -n 1 | sed -e "s/^UUID: //g")"
+ target_dir="${prefix_mac}-$(LC_ALL=C date '+%Y-%m-%d-%H%M')-img"
+ elif [ "$ocs_sr_img_name" = "autohostname" ]; then
+ # Ref: https://sourceforge.net/projects/clonezilla/forums/forum/663168/topic/5103006
+ ip=""
+ for i in `LC_ALL=C get-nic-devs`; do
+ ip="$(drbl-get-ipadd $i)"
+ if [ -n "$ip" ]; then
+ break
+ fi
+ done
+ # If not found, give it a default name based on time.
+ if [ -z "ip" ]; then
+ prefix_host="autohostname-$(LC_ALL=C date '+%Y-%m-%d-%H%M')"
+ else
+ if LC_ALL=C host $ip &>/dev/null; then
+ # FQDN found
+ prefix_host="$(LC_ALL=C host $ip | sed -r -e "s/^.*name pointer//g" -e "s/^[[:space:]]*//g" -e "s|\.$||g")"
+ prefix_host="${prefix_host}-$(LC_ALL=C date '+%Y-%m-%d-%H%M')"
+ else
+ # FQDN not found. Give it a default name based on IP address and time.
+ prefix_host="autohostname-${ip}-$(LC_ALL=C date '+%Y-%m-%d-%H%M')"
+ fi
+ fi
+ target_dir="${prefix_host}-img"
+ elif [ "$ocs_sr_img_name" = "autoproductname" ]; then
+ # Ref: https://sourceforge.net/projects/clonezilla/forums/forum/663168/topic/5103006
+ target_dir="$(get_vendor_product_name)-img"
+ else
+ target_dir="$ocs_sr_img_name"
+ fi
+
+ if [ "$ocs_sr_dev" = "ask_user" ]; then
+ # To get $target_hd
+ get_target_hd_name_from_local_machine "$msg_local_source_disk \n$msg_linux_disk_naming $msg_press_space_to_mark_selection"
+ target_hd="$(select_VG "$target_hd")"
+ elif [ "$ocs_sr_dev" = "all" ]; then
+ get_not_busy_disks_or_parts harddisk "" "" # we will get dev_list
+ target_hd="$dev_list"
+ else
+ target_hd="$ocs_sr_dev"
+ fi
+ # check if the device exists
+ ANS_TMP=`mktemp /tmp/ocs_chkdev.XXXXXX`
+ trap "[ -f "$ANS_TMP" ] && rm -f $ANS_TMP" HUP INT QUIT TERM EXIT
+ check_if_input_device_exist $ANS_TMP $target_hd
+ target_hd="$(cat $ANS_TMP | tr -d \")"
+ [ -f "$ANS_TMP" ] && rm -f $ANS_TMP
+ get_extra_param_when_type_is_ask_user_and_interactive_mode
+ wrap_up_opt_after_interactive_selection # Since extra param inputed again, wrap up again
+
+ task_processing_after_parameters_checked
+
+ save_ocs_sr_related_vars
+ run_ocs_sr_again_prompt "$target_dir" "$target_hd"
+
+ # //NOTE// If encrypt_ocs_img="yes", after this step, ocsroot and target_dir will be changed
+ # The original ones will be kept as ocsroot_orig and target_dir_orig.
+ prepare_ecryptfs_mount_point_if_necessary
+
+ ocs-run-boot-param ocs_savedisk_prerun
+ task_savedisk "$target_dir" "$target_hd"
+ rc_savedisk="$?"
+ ocs-run-boot-param ocs_savedisk_postrun
+
+ #
+ if [ -d "$MULTIPATH_INFODIR" -a \
+ -n "$(echo $MULTIPATH_INFODIR | grep -w "multipath_info")" ]; then
+ rm -rf "$MULTIPATH_INFODIR"
+ fi
+
+ if [ "$rc_savedisk" -eq 0 -a "$chk_img_restoreable_mod_save" = "yes" ]; then
+ check_image_if_restorable "$ocsroot"
+ rc_chkimg="$?"
+ if [ "$rc_chkimg" -ne 0 ]; then
+ echo "$msg_program_stop!"
+ my_ocs_exit 1
+ fi
+ fi
+ echo $0 $OCS_OPTS $ocs_sr_type "$target_dir" "$target_hd" > $ocsroot/$target_dir/Info-saved-by-cmd.txt
+
+ task_postprocessing "$ocs_sr_type" "$target_dir"
+ ;;
+ "restoredisk")
+ task_preprocessing "$ocs_sr_type"
+ MULTIPATH_INFODIR="$(mktemp -d /tmp/multipath_info.XXXXXX)"
+
+ if [ "$ocs_sr_img_name" = "ask_user" ]; then
+ get_target_dir_name_when_restoring_disk # get $target_dir
+ elif [ "$ocs_sr_img_name" = "autoproductname" ]; then
+ # Ref: https://sourceforge.net/projects/clonezilla/forums/forum/663168/topic/5103006
+ target_dir="$(get_vendor_product_name)-img"
+ else
+ target_dir="$ocs_sr_img_name"
+ fi
+
+ check_input_target_image "$ocsroot/$target_dir"
+ source_dsk_no="$(get_disk_list_from_img $ocsroot/$target_dir | sed -e "s/ *$//g" | wc -w | awk '{print $1}')"
+
+ if [ "$ocs_sr_dev" = "ask_user" ]; then
+ if [ "$source_dsk_no" -eq 1 ]; then
+ dia_sel_opt="menu"
+ else
+ dia_sel_opt="checklist"
+ fi
+ # To get $target_hd
+ get_target_hd_name_from_local_machine "$msg_choose_the_disks_to_restore \n$msg_linux_disk_naming $msg_press_space_to_mark_selection" $dia_sel_opt
+ elif [ "$ocs_sr_dev" = "all" ]; then
+ target_hd="$(get_disk_list_from_img $ocsroot/$target_dir)"
+ else
+ target_hd="$ocs_sr_dev"
+ fi
+ get_extra_param_when_type_is_ask_user_and_interactive_mode
+ wrap_up_opt_after_interactive_selection # Since extra param inputed again, wrap up again
+
+ task_processing_after_parameters_checked
+
+ save_ocs_sr_related_vars
+ run_ocs_sr_again_prompt "$target_dir" "$target_hd"
+
+ # //NOTE// If encrypt_ocs_img="yes", after this step, ocsroot and target_dir will be changed
+ # The original ones will be kept as ocsroot_orig and target_dir_orig.
+ prepare_ecryptfs_mount_point_if_necessary
+
+ if [ -d "$ocsroot/$target_dir" -a "$chk_img_restoreable_mod_restore" = "yes" ]; then
+ check_image_if_restorable "$ocsroot"
+ rc_chkimg="$?"
+ if [ "$rc_chkimg" -ne 0 ]; then
+ echo "$msg_program_stop!"
+ my_ocs_exit 1
+ fi
+ fi
+
+ ocs-run-boot-param ocs_restoredisk_prerun
+ task_restoredisk "$target_dir" "$target_hd"
+ ocs-run-boot-param ocs_restoredisk_postrun
+
+ #
+ if [ -d "$MULTIPATH_INFODIR" -a \
+ -n "$(echo $MULTIPATH_INFODIR | grep -w "multipath_info")" ]; then
+ rm -rf "$MULTIPATH_INFODIR"
+ fi
+
+ task_postprocessing "$ocs_sr_type" "$target_dir"
+ ;;
+ "saveparts")
+ task_preprocessing "$ocs_sr_type"
+
+ if [ "$ocs_sr_img_name" = "ask_user" ]; then
+ get_target_dir_name_when_saving # get $target_dir
+ elif [ "$ocs_sr_img_name" = "autoname" ]; then
+ # Ref: https://sourceforge.net/projects/clonezilla/forums/forum/799287/topic/4815557
+ # We need something unique, so MAC address is a good choice
+ for i in `LC_ALL=C get-nic-devs`; do
+ prefix_mac="$(LC_ALL=C drbl-get-macadd $i)"
+ if [ -n "$prefix_mac" ]; then
+ prefix_mac="$(LC_ALL=C echo $prefix_mac | sed -e 's/://g')"
+ break
+ fi
+ done
+ # If not found, use UUID from MB manufacture
+ [ -z "$prefix_mac" ] && prefix_mac="$(LC_ALL=C dmidecode | grep -io "UUID: .*$" | head -n 1 | sed -e "s/^UUID: //g")"
+ target_dir="${prefix_mac}-$(LC_ALL=C date '+%Y-%m-%d-%H%M')-img"
+ elif [ "$ocs_sr_img_name" = "autohostname" ]; then
+ # Ref: https://sourceforge.net/projects/clonezilla/forums/forum/663168/topic/5103006
+ ip=""
+ for i in `LC_ALL=C get-nic-devs`; do
+ ip="$(drbl-get-ipadd $i)"
+ if [ -n "$ip" ]; then
+ break
+ fi
+ done
+ # If not found, give it a default name based on time.
+ if [ -z "ip" ]; then
+ prefix_host="autohostname-$(LC_ALL=C date '+%Y-%m-%d-%H%M')"
+ else
+ if LC_ALL=C host $ip &>/dev/null; then
+ # FQDN found
+ prefix_host="$(LC_ALL=C host $ip | sed -r -e "s/^.*name pointer//g" -e "s/^[[:space:]]*//g" -e "s|\.$||g")"
+ else
+ # FQDN not found. Give it a default name based on IP address and time.
+ prefix_host="autohostname-${ip}-$(LC_ALL=C date '+%Y-%m-%d-%H%M')"
+ fi
+ fi
+ target_dir="${prefix_host}-img"
+ elif [ "$ocs_sr_img_name" = "autoproductname" ]; then
+ # Ref: https://sourceforge.net/projects/clonezilla/forums/forum/663168/topic/5103006
+ target_dir="$(get_vendor_product_name)-img"
+ else
+ target_dir="$ocs_sr_img_name"
+ fi
+
+ if [ "$ocs_sr_dev" = "ask_user" ]; then
+ get_target_parts_name_from_local_machine # get $target_parts
+ elif [ "$ocs_sr_dev" = "all" ]; then
+ get_not_busy_disks_or_parts partition "" "" # we will get dev_list
+ target_parts="$dev_list"
+ else
+ target_parts="$ocs_sr_dev"
+ fi
+
+ # check if the device exists
+ ANS_TMP=`mktemp /tmp/ocs_chkdev.XXXXXX`
+ trap "[ -f "$ANS_TMP" ] && rm -f $ANS_TMP" HUP INT QUIT TERM EXIT
+ check_if_input_device_exist $ANS_TMP $target_parts
+ # we have to remove " (comes with checklist in dialog) so that for loop
+ # will work (Specially for FC3/4...)
+ target_parts="$(cat $ANS_TMP | tr -d \")"
+ [ -f "$ANS_TMP" ] && rm -f $ANS_TMP
+ get_extra_param_when_type_is_ask_user_and_interactive_mode
+ wrap_up_opt_after_interactive_selection # Since extra param inputed again, wrap up again
+
+ task_processing_after_parameters_checked
+
+ save_ocs_sr_related_vars
+ run_ocs_sr_again_prompt "$target_dir" "$target_parts"
+
+ # //NOTE// If encrypt_ocs_img="yes", after this step, ocsroot and target_dir will be changed
+ # The original ones will be kept as ocsroot_orig and target_dir_orig.
+ prepare_ecryptfs_mount_point_if_necessary
+
+ ocs-run-boot-param ocs_saveparts_prerun
+ task_saveparts "$target_dir" "$target_parts"
+ rc_saveparts="$?"
+ ocs-run-boot-param ocs_saveparts_postrun
+
+ #
+ if [ -d "$MULTIPATH_INFODIR" -a \
+ -n "$(echo $MULTIPATH_INFODIR | grep -w "multipath_info")" ]; then
+ rm -rf "$MULTIPATH_INFODIR"
+ fi
+
+ if [ "$rc_saveparts" -eq 0 -a "$chk_img_restoreable_mod_save" = "yes" ]; then
+ check_image_if_restorable "$ocsroot"
+ rc_chkimg="$?"
+ if [ "$rc_chkimg" -ne 0 ]; then
+ echo "$msg_program_stop!"
+ my_ocs_exit 1
+ fi
+ fi
+ echo $0 $OCS_OPTS $ocs_sr_type "$target_dir" "$target_parts" > $ocsroot/$target_dir/Info-saved-by-cmd.txt
+
+ task_postprocessing "$ocs_sr_type" "$target_dir"
+ ;;
+ "restoreparts")
+ task_preprocessing "$ocs_sr_type"
+
+ if [ "$ocs_sr_img_name" = "ask_user" ]; then
+ get_target_dir_name_when_restoring_parts # get $target_dir
+ elif [ "$ocs_sr_img_name" = "autoproductname" ]; then
+ # Ref: https://sourceforge.net/projects/clonezilla/forums/forum/663168/topic/5103006
+ target_dir="$(get_vendor_product_name)-img"
+ else
+ target_dir="$ocs_sr_img_name"
+ fi
+ check_input_target_image "$ocsroot/$target_dir"
+
+ if [ "$ocs_sr_dev" = "ask_user" ]; then
+ if [ -z "$source_part" ]; then
+ ANS_TMP=`mktemp /tmp/ocs_ans.XXXXXX`
+ trap "[ -f "$ANS_TMP" ] && rm -f $ANS_TMP" HUP INT QUIT TERM EXIT
+ get_existing_partitions_from_img $ANS_TMP $ocsroot/$target_dir no select_from_img
+ # we have to remove " (comes with checklist in dialog)
+ # so that for loop will work (Specially for FC3/4...)
+ source_part="$(cat $ANS_TMP | tr -d \")"
+ [ -f "$ANS_TMP" ] && rm -f $ANS_TMP
+ fi
+ # target name exists, but file "parts" is empty ?
+ check_target_parts $ocsroot/$target_dir/parts "$source_part"
+ src_parts_no="$(LC_ALL=C echo $source_part | wc -w)"
+ if [ "$src_parts_no" -eq 1 ]; then
+ dia_sel_opt="menu"
+ get_target_parts_name_from_local_machine "$msg_choose_the_parts_to_restore \n$msg_linux_parts_MS_mapping" $dia_sel_opt # Obtain target_parts
+ else
+ # In this case, it's too complicated to restore partitions to different partitions on destination disk.
+ # Therefore we only allow original partitions to partitions, i.e. e.g., sda1, sda2 from image -> sda1, sda2 on destination disk. Therefore "source_part" is reset to none.
+ target_parts="$source_part"
+ source_part=""
+ if [ "$ocs_batch_mode" != "on" ]; then
+ [ "$BOOTUP" = "color" ] && $SETCOLOR_WARNING
+ echo "$msg_2_or_more_parts_only_same_parts_on_dest"
+ [ "$BOOTUP" = "color" ] && $SETCOLOR_NORMAL
+ echo -n "$msg_press_enter_to_continue"
+ read
+ fi
+ fi
+ elif [ "$ocs_sr_dev" = "all" -o \
+ "$ocs_sr_dev" = "unmounted_disk" ]; then
+ target_parts="$(get_parts_list_from_img $ocsroot/$target_dir)"
+ else
+ target_parts="$ocs_sr_dev"
+ fi
+ #
+ get_extra_param_when_type_is_ask_user_and_interactive_mode
+ wrap_up_opt_after_interactive_selection # Since extra param inputed again, wrap up again
+ # Append the option if restoring to different partition on the destination disk.
+ if [ -n "$source_part" ]; then
+ OCS_OPTS="$OCS_OPTS -f $source_part"
+ fi
+
+ save_ocs_sr_related_vars
+ run_ocs_sr_again_prompt "$target_dir" "$target_parts"
+
+ # //NOTE// If encrypt_ocs_img="yes", after this step, ocsroot and target_dir will be changed
+ # The original ones will be kept as ocsroot_orig and target_dir_orig.
+ prepare_ecryptfs_mount_point_if_necessary
+
+ task_processing_after_parameters_checked
+
+ if [ -d "$ocsroot/$target_dir" -a "$chk_img_restoreable_mod_restore" = "yes" ]; then
+ check_image_if_restorable "$ocsroot"
+ rc_chkimg="$?"
+ if [ "$rc_chkimg" -ne 0 ]; then
+ echo "$msg_program_stop!"
+ my_ocs_exit 1
+ fi
+ fi
+
+ ocs-run-boot-param ocs_restoreparts_prerun
+ task_restoreparts "$target_dir" "$target_parts"
+ ocs-run-boot-param ocs_restoreparts_postrun
+
+ task_postprocessing "$ocs_sr_type" "$target_dir"
+ ;;
+ "recovery-iso-zip") create_clonezilla_live_recovery_iso_zip ;;
+ "chk-img-restorable") ocs-chkimg ;;
+ "cvt-img-compression") ocs-cvtimg-comp ;;
+ "encrypt-img") ocs-encrypt-img ;;
+ "decrypt-img") ocs-decrypt-img ;;
+ "p2v-img") ocs-img-2-vdk ;;
+ "1-2-mdisks") ocs-restore-mdisks -um $ocs_user_mode ;;
+ "multicast_restoredisk")
+ task_preprocessing "$ocs_sr_type"
+ task_processing_after_parameters_checked
+
+ task_multicast_restoredisk "$ocs_sr_img_name" "$ocs_sr_dev" "$ocs_sr_mcast_port"
+ task_postprocessing "$ocs_sr_type" ""
+ ;;
+ "multicast_restoreparts")
+ task_preprocessing "$ocs_sr_type"
+ task_processing_after_parameters_checked
+
+ task_multicast_restoreparts "$ocs_sr_img_name" "$ocs_sr_dev" "$ocs_sr_mcast_port"
+ task_postprocessing "$ocs_sr_type" ""
+ ;;
+ *)
+ USAGE
+ exit 2
+ ;;
+esac
+
+echo "Ending $0 at $(LC_ALL=C date +%F' '%T' '%Z)..." | tee --append ${OCS_LOGFILE}
+exit 0
diff --git a/draft/other-tools/fog/fog-notes.txt b/draft/other-tools/fog/fog-notes.txt
new file mode 100644
index 0000000..61a7d12
--- /dev/null
+++ b/draft/other-tools/fog/fog-notes.txt
@@ -0,0 +1,148 @@
+
+
+./src/buildroot/package/fog/scripts/usr/share/fog/lib/funcs.sh
+
+
+ pigz $PIGZ_COMP < $fifo | split -a 3 -d -b 200m - ${file}. &
+
+
+ mainuuidfilename="$imagePath/d${disk_number}.original.uuids"
+ swapuuidfilename="$imagePath/d${disk_number}.original.swapuuids"
+ sfdiskoriginalpartitionfilename="$imagePath/d${disk_number}.partitions"
+ sfdisklegacyoriginalpartitionfilename="$imagePath/d${disk_number}.original.partitions"
+ sfdiskminimumpartitionfilename="$imagePath/d${disk_number}.minimum.partitions"
+ sgdiskoriginalpartitionfilename="$imagePath/d${disk_number}.sgdisk.original.partitions"
+ fixed_size_file="$imagePath/d${disk_number}.fixed_size_partitions"
+ hasgrubfilename="$imagePath/d${disk_number}.has_grub"
+ [[ -n $sgdisk ]] && hasgrubfilename="$imagePath/d${disk_number}.grub.mbr"
+ [[ -n $sgdisk && $hasGRUB -eq 1 ]] && \
+ mbr="$imagePath/d${disk_number}.grub.mbr" || \
+ mbr="$imagePath/d${disk_number}.mbr"
+ ebrfilename="$path/d${disk_number}p${part_number}.ebr"
+
+# Save enough MBR and embedding area to capture all of GRUB
+# Strategy is to capture EVERYTHING before the first partition.
+# Then, leave a marker that this is a GRUB MBR for restoration.
+# We could get away with less storage, but more details are required
+# to parse the information correctly. It would make the process
+# more complicated.
+
+# $1 is the disk
+# $2 is the disk number
+# $3 is the image path to save the file to.
+# $4 is the determinator of sgdisk use or not
+saveGRUB() {
+
+
+ # Determine the number of sectors to copy
+ # Hack Note: print $4+0 causes the column to be interpretted as a number
+ # so the comma is tossed
+ local count=$(sfdisk -d $disk 2>/dev/null | awk /start=\ *[1-9]/'{print $4+0}' | sort -n | head -n1)
+
+
+ local has_grub=$(dd if=$disk bs=512 count=1 2>&1 | grep -i 'grub')
+
+ # Ensure that no more than 1MiB of data is copied (already have this size used elsewhere)
+ [[ $count -gt 2048 ]] && count=2048
+ local mbrfilename=""
+ MBRFileName "$imagePath" "$disk_number" "mbrfilename" "$sgdisk"
+ dd if=$disk of=$mbrfilename count=$count bs=512 >/dev/null 2>&1
+
+}
+
+
+
+hasGrubFileName() {
+ local imagePath="$1" # e.g. /net/dev/foo
+ local disk_number="$2" # e.g. 1
+ local sgdisk="$3"
+ [[ -z $imagePath ]] && handleError "No image path passed (${FUNCNAME[0]})\n Args Passed: $*"
+ [[ -z $disk_number ]] && handleError "No disk number passed (${FUNCNAME[0]})\n Args Passed: $*"
+ hasgrubfilename="$imagePath/d${disk_number}.has_grub"
+ [[ -n $sgdisk ]] && hasgrubfilename="$imagePath/d${disk_number}.grub.mbr"
+}
+
+savePartitionTablesAndBootLoaders() {
+
+ case $hasgpt in
+ 0)
+ strdots="Saving Partition Tables (MBR)"
+ case $osid in
+ 4|50|51)
+ [[ $disk_number -eq 1 ]] && strdots="Saving Partition Tables and GRUB (MBR)"
+ ;;
+ esac
+ dots "$strdots"
+ saveGRUB "$disk" "$disk_number" "$imagePath"
+ sfdisk -d $disk 2>/dev/null > $sfdiskfilename
+ [[ $have_extended_partition -ge 1 ]] && saveAllEBRs "$disk" "$disk_number" "$imagePath"
+ echo "Done"
+ ;;
+ 1)
+ dots "Saving Partition Tables (GPT)"
+ saveGRUB "$disk" "$disk_number" "$imagePath" "true"
+ sgdisk -b "$imagePath/d${disk_number}.mbr" $disk >/dev/null 2>&1
+ if [[ ! $? -eq 0 ]]; then
+ echo "Failed"
+ debugPause
+ handleError "Error trying to save GPT partition tables (${FUNCNAME[0]})\n Args Passed: $*"
+ fi
+ sfdisk -d $disk 2>/dev/null > $sfdiskfilename
+ echo "Done"
+ ;;
+ esac
+}o
+
+clearPartitionTables() {
+ sgdisk -Z $disk >/dev/null 2>&1
+}
+
+restorePartitionTablesAndBootLoaders() {
+ if [[ $table_type == GPT ]]; then
+ dots "Restoring Partition Tables (GPT)"
+ restoreGRUB "$disk" "$disk_number" "$imagePath" "true"
+ sgdisk -gel $tmpMBR $disk >/dev/null 2>&1
+ [[ ! $? -eq 0 ]] && handleError "Error trying to restore GPT partition tables (${FUNCNAME[0]})\n Args Passed: $*"
+ global_gptcheck="yes"
+ echo "Done"
+ else
+ [big cheat for MBR, dd, sfdisk for EBRs]
+ fi
+}
+
+
+savePartition() {
+ case $fstype in
+ swap)
+ echo " * Saving swap partition UUID"
+ swapUUIDFileName "$imagePath" "$disk_number"
+ saveSwapUUID "$swapuuidfilename" "$part"
+ ;;
+ *)
+ case $parttype in
+ 0x5|0xf)
+ echo " * Not capturing content of extended partition"
+ debugPause
+ EBRFileName "$imagePath" "$disk_number" "$part_number"
+ touch "$ebrfilename"
+ ;;
+ *)
+ echo " * Using partclone.$fstype"
+ debugPause
+ imgpart="$imagePath/d${disk_number}p${part_number}.img"
+ uploadFormat "$fifoname" "$imgpart"
+ partclone.$fstype -fsck-src-part -c -s $part -O $fifoname -N -f 1
+ case $? in
+ 0)
+ mv ${imgpart}.000 $imgpart >/dev/null 2>&1
+ echo " * Image Captured"
+ ;;
+ *)
+ handleError "Failed to complete capture (${FUNCNAME[0]})\n Args Passed: $*"
+ ;;
+ esac
+ ;;
+ esac
+ ;;
+ esac
+
diff --git a/draft/other-tools/fog/fog.download b/draft/other-tools/fog/fog.download
new file mode 100755
index 0000000..7190ebb
--- /dev/null
+++ b/draft/other-tools/fog/fog.download
@@ -0,0 +1,261 @@
+#!/bin/bash
+. /usr/share/fog/lib/funcs.sh
+. /bin/fog.donate
+. /bin/fog.checkin
+. /bin/fog.mount
+. /bin/fog.checkmount
+. /bin/fog.checkimgvar
+hd=""
+disks=""
+disk=""
+parts=""
+part=""
+imagePath="/images/$img"
+origmac=$mac
+mac=$(getMACAddresses | base64)
+. /bin/fog.inventory "true"
+mac="$origmac"
+origmac=""
+layPartSize="-1s"
+fog.statusreporter "$mac" "$web" & disown
+statusReporter="$!"
+fixed_size_partitions=""
+echo " * Using Image: $img"
+preparePartitions() {
+ echo " * Preparing Partition layout"
+ case $imgType in
+ [Nn])
+ case $osid in
+ 4|50|51)
+ [[ ! -d $imagePath ]] && handleError "Unable to locate image store ($0)\n Args Passed: $*"
+ prepareResizeDownloadPartitions "$hd" 1 "$imagePath" "$osid" "$imgPartitionType"
+ ;;
+ [1-2])
+ [[ ! -f $imagePath && ! -f $imagePath/$img && ! -d $imagePath ]] && handleError "Unable to locate image store ($0)\n Args Passed: $*"
+ [[ -d $imagePath && -f $imagePath/$img ]] && imagePath="$imagePath/$img"
+ if [[ -d $imagePath ]]; then
+ prepareResizeDownloadPartitions "$hd" 1 "$imagePath" "$osid" "$imgPartitionType"
+ else
+ startsector="63s"
+ restorePartitionTablesAndBootLoaders "$hd" 1 "$imagePath" "$osid" "$imgPartitionType"
+ dots "Removing partition"
+ parted -s $hd rm 1 >/dev/null 2>&1
+ if [[ ! $? -eq 0 ]]; then
+ echo "Failed"
+ debugPause
+ handleError "Fatal Error: Could not remove old partition ($0)\n Args Passed: $*"
+ fi
+ echo "Done"
+ debugPause
+ echo " * Attempting to expand/fill partitions"
+ dots "Recreating partition"
+ parted -s $hd mkpart primary ntfs 63s -- $layPartSize >/dev/null 2>&1
+ if [[ ! $? -eq 0 ]]; then
+ echo "Failed"
+ debugPause
+ handleError "Could not create partition to fill disk ($0)\n Args Passed: $*"
+ fi
+ echo "Done"
+ debugPause
+ dots "Setting boot partition"
+ parted -s $hd set 1 boot on >/dev/null 2>&1
+ if [[ ! $? -eq 0 ]]; then
+ echo "Failed"
+ debugPause
+ handleError "Fatal Error: Could not make partition bootable ($0)\n Args Passed: $*"
+ fi
+ runPartprobe "$hd"
+ echo "Done"
+ debugPause
+ getPartitions "$hd"
+ for part in $parts; do
+ [[ -e $part ]] && break
+ done
+ fi
+ ;;
+ [5-7]|9)
+ [[ ! -d $imagePath && ! -f $imagePath/sys.img.000 ]] && handleError "Unable to locate image store ($0)\n Args Passed: $*"
+ if [[ ! -f $imagePath/sys.img.000 ]]; then
+ prepareResizeDownloadPartitions "$hd" 1 "$imagePath" "$osid" "$imgPartitionType"
+ else
+ echo " * Using legacy style partition setup"
+ win7partcnt=1
+ dots "Windows Boot Partition Exists"
+ if [[ ! -f $imagePath/gpt.bak && ! -f $imagePath/rec1.img.000 && ! -f $imagePath/rec.img.000 ]]; then
+ echo "No"
+ else
+ echo "Yes"
+ [[ -f $imagePath/rec.img.000 ]] && win7partcnt=2
+ [[ -f $imagePath/rec.img.001 || -f $imagePath/gpt.bak ]] && win7partcnt=3
+ fi
+ debugPause
+ echo " * Attempting to expand/fill partitions"
+ do_fill=0
+ fillDiskWithPartitionsIsOK "$hd" "$imagePath" 1
+ case $do_fill in
+ 1)
+ fillDiskWithPartitions "$hd" "$imagePath" 1
+ echo "Done"
+ ;;
+ *)
+ startsector="2048s"
+ restorePartitionTablesAndBootLoaders "$hd" 1 "$imagePath" "$osid" "$imgPartitionType"
+ case $win7partcnt in
+ 1)
+ dots "Removing partition"
+ parted -s $hd rm 1 >/dev/null 2>&1
+ if [[ ! $? -eq 0 ]]; then
+ echo "Failed"
+ debugPause
+ handleError "Fatal Error: Could not remove old partition ($0)\n Args Passed: $*"
+ fi
+ echo "Done"
+ debugPause
+ dots "Creating main partition"
+ parted -s $hd mkpart primary ntfs $startsector -- $layPartSize >/dev/null 2>&1
+ if [[ ! $? -eq 0 ]]; then
+ echo "Failed"
+ debugPause
+ handleError "Fatal Error: Could not recreate first partition ($0)\n Args Passed: $*"
+ fi
+ echo "Done"
+ debugPause
+ dots "Setting boot partition"
+ parted -s $hd set 1 boot on >/dev/null 2>&1
+ if [[ ! $? -eq 0 ]]; then
+ echo "Failed"
+ debugPause
+ handleError "Fatal Error: Could not make partition bootable ($0)\n Args Passed: $*"
+ fi
+ echo "Done"
+ ;;
+ 2)
+ dots "Removing main partition"
+ parted -s $hd rm 1 >/dev/null 2>&1
+ if [[ ! $? -eq 0 ]]; then
+ echo "Failed"
+ debugPause
+ handleError "Fatal Error: Could not remove old main partition ($0)\n Args Passed: $*"
+ fi
+ echo "Done"
+ debugPause
+ dots "Recreating recovery partition"
+ parted -s $hd mkpart primary ntfs $startsector 206847s >/dev/null 2>&1
+ if [[ ! $? -eq 0 ]]; then
+ echo "Failed"
+ debugPause
+ handleError "Fatal Error: Could not create recovery partition ($0)\n Args Passed: $*"
+ fi
+ echo "Done"
+ debugPause
+ dots "Recreating main partition"
+ parted -s $hd mkpart primary ntfs $defaultpart2start -- $layPartSize >/dev/null 2>&1
+ if [[ ! $? -eq 0 ]]; then
+ echo "Failed"
+ debugPause
+ handleError "Fatal Error: Could not recreate main partition ($0)\n Args Passed: $*"
+ fi
+ echo "Done"
+ debugPause
+ dots "Setting boot partition"
+ parted -s $hd set 1 boot on >/dev/null 2>&1
+ if [[ ! $? -eq 0 ]]; then
+ echo "Failed"
+ debugPause
+ handleError "Fatal Error: Could not make partition bootable ($0)\nArgsPassed: $*"
+ fi
+ echo "Done"
+ ;;
+ 3)
+ dots "Removing partition data"
+ sgdisk -Z $hd >/dev/null 2>&1
+ sgdisk -gel $imagePath/gpt.bak $hd >/dev/null 2>&1
+ if [[ ! $? -eq 0 ]]; then
+ echo "Failed"
+ debugPause
+ handleError "Fatal Error: Could not recreate partitions ($0)\n Args Passed: $*"
+ fi
+ echo "Done"
+ debugPause
+ dots "Recreating sized out partitions"
+ sgdisk -x 3:$(sgdisk -E $hd) $hd >/dev/null 2>&1
+ if [[ ! $? -eq 0 ]]; then
+ echo "Failed"
+ debugPause
+ handleError "Fatal Error: Could not resize partitions ($0)\n Args Passed: $*"
+ fi
+ echo "Done"
+ ;;
+ esac
+ ;;
+ esac
+ debugPause
+ runPartprobe "$hd"
+ dots "Setting up partition variables"
+ getPartitions "$hd"
+ restoreparts=""
+ part_number=0
+ for part in $parts; do
+ getPartitionNumber "$part"
+ [[ $part_number -le $win7partcnt ]] && restoreparts="$restoreparts $part"
+ done
+ echo "Done"
+ debugPause
+ fi
+ ;;
+ esac
+ ;;
+ mps)
+ [[ ! -d $imagePath ]] && handleError "Unable to locate image store ($0)\n Args Passed: $*"
+ restorePartitionTablesAndBootLoaders "$hd" 1 "$imagePath" "$osid" "$imgPartitionType"
+ runPartprobe "$hd"
+ gptcheck="$global_gptcheck"
+ ;;
+ mpa)
+ [[ ! -d $imagePath ]] && handleError "Unable to locate image store ($0)\n Args Passed: $*"
+ getHardDisk "true"
+ disk_number=1
+ for disk in $disks; do
+ restorePartitionTablesAndBootLoaders "$disk" "$disk_number" "$imagePath" "$osid" "$imgPartitionType"
+ runPartprobe "$disk"
+ gptcheck="$global_gptcheck"
+ let disk_number+=1
+ done
+ ;;
+ esac
+}
+putDataBack() {
+ runPartprobe "$hd"
+ getPartitions "$hd"
+ [[ -z $parts ]] && echo -e " * Seems like you are trying to restore to an empty disk. Be aware this will most probably cause trouble.\n"
+ echo " +--------------------------------+"
+ echo " | Attempting to deploy image |"
+ echo " +--------------------------------+"
+ [[ $imgFormat -eq 1 || $imgLegacy -eq 1 ]] && echo " | Using Partimage |" || echo " | Using Partclone |"
+ echo " +--------------------------------+"
+ [[ $mc == yes ]] && usleep 10000000 || usleep 3000000
+ case $imgType in
+ dd)
+ restorePartition "$hd" 1 "$imagePath/$img.*" "$mc"
+ ;;
+ n|mps|mpa)
+ [[ $imgType == +(n|mps) ]] && disks="$hd"
+ case $osid in
+ [1-2])
+ [[ ! -f $imagePath && ! -d $imagePath ]] && handleError "Fatal Error: Could not locate file ($0)\n Args Passed: $*"
+ ;;
+ [5-7]|9)
+ [[ ! -d $imagePath && ! -f $imagePath/sys.img.000 ]] && handleError "Fatal Error: Could not locate file ($0)\n Args Passed: $*"
+ ;;
+ 4|50|51)
+ [[ ! -d $imagePath ]] && handleError "Fatal Error: could not locate file ($0)\n Args Passed: $*"
+ ;;
+ esac
+ performRestore "$disks" "$imagePath" "$imgPartitionType" "$mc"
+ ;;
+ esac
+}
+findHDDInfo
+[[ $nombr -eq 1 ]] && echo " * Skipping partition layout (Single Partition restore)" || preparePartitions
+[[ $imgPartitionType != mbr ]] && putDataBack || echo " * Skipping partition restore (MBR Only)"
+completeTasking
diff --git a/draft/other-tools/fog/fog.upload b/draft/other-tools/fog/fog.upload
new file mode 100755
index 0000000..8a80ae8
--- /dev/null
+++ b/draft/other-tools/fog/fog.upload
@@ -0,0 +1,216 @@
+#!/bin/bash
+. /usr/share/fog/lib/funcs.sh
+. /bin/fog.checkin
+. /bin/fog.mount
+. /bin/fog.checkmount
+. /bin/fog.checkimgvar
+imagePath="/images/$macWinSafe"
+parts=""
+part=""
+disks=""
+disk=""
+hd=""
+echo " * Preparing to send image file to server"
+percent="$pct"
+[[ $pct -lt 5 || $pct -ge 100 ]] && percent=5
+[[ $pct -lt 10 ]] && percent="0$pct"
+fog.statusreporter "$mac" "$web" & disown
+statusReporter="$!"
+prepareUploadLocation "$imagePath"
+echo " * Using Image: $img"
+# resizable image type
+# discover windows partitions
+# remove pagefile and hibernate file
+# validate XP partition location
+# save original partition table
+# save MBR
+# shrink filesystems and partitions
+# save GRUB
+# save shrunken partition table
+# clone filesystems
+# restore original MBR and partition table
+# expand filesystems
+beginUpload() {
+ case $imgType in
+ [Nn])
+ validResizeOS
+ runPartprobe "$hd"
+ getPartitions "$hd"
+ [[ $osid == @([1-2]|[4-7]|9) ]] && win7partcnt=$(echo $parts | wc -w)
+ dots "Checking for fixed partitions"
+ part_number=0
+ for part in $parts; do
+ fsTypeSetting "$part"
+ getPartitionNumber "$part"
+ case $fstype in
+ ntfs|extfs)
+ continue
+ ;;
+ *)
+ fixed_size_partitions="$fixed_size_partitions:$part_number"
+ ;;
+ esac
+ done
+ echo "Done"
+ debugPause
+ dots "Getting Windows/Linux Partition Count"
+ countPartTypes "$hd" "ntfs" "ntfscnt"
+ countPartTypes "$hd" "extfs" "extfscnt"
+ if [[ $ntfscnt -eq 0 && $extfscnt -eq 0 ]]; then
+ echo "Failed"
+ debugPause
+ handleError "No resizable partitions found ($0)\n Args Passed: $*"
+ fi
+ echo "Done"
+ debugPause
+ echo " * NTFS Partition count of: $ntfscnt"
+ debugPause
+ echo " * EXTFS Partition count of: $extfscnt"
+ debugPause
+ case $osid in
+ [4-7]|9|50|51)
+ echo " * Setting up any additional fixed parts"
+ [[ $((ntfscnt + extfscnt)) -gt 0 && $part_number -gt 1 ]] && fixed_size_partitions="$fixed_size_partitions:1"
+ part_number=0
+ for part in $parts; do
+ getPartitionNumber "$part"
+ fsTypeSetting "$part"
+ [[ $fstype != ntfs ]] && continue
+ dots "Mounting partition ($part)"
+ if [[ ! -d /bcdstore ]]; then
+ mkdir -p /bcdstore >/dev/null 2>&1
+ case $? in
+ 0)
+ ;;
+ *)
+ echo "Failed"
+ debugPause
+ handleError " * Could not create mount location ($0->${FUNCNAME[0]})\n Args Passed: $*"
+ ;;
+ esac
+ fi
+ ntfs-3g -o remove_hiberfile,rw $part /bcdstore >/tmp/ntfs-mount-output 2>&1
+ case $? in
+ 0)
+ echo "Done"
+ debugPause
+ ;;
+ *)
+ echo "Failed"
+ debugPause
+ handleError " * Could not mount $part ($0->${FUNCNAME[0]})\n Args Passed: $*\n Reason: $(cat /tmp/ntfs-mount-output)"
+ ;;
+ esac
+ dots "BCD exists on $part in /bcdstore"
+ if [[ ! -f /bcdstore/Boot/BCD ]]; then
+ umount /bcdstore >/dev/null 2>&1
+ echo "No"
+ debugPause
+ else
+ umount /bcdstore >/dev/null 2>&1
+ echo "Yes"
+ debugPause
+ if [[ $part_number -gt 1 ]]; then
+ for ((increment=1; increment < $part_number; increment++)); do
+ fixed_size_partitions="$fixed_size_partitions:$increment"
+ done
+ fi
+ fi
+ done
+ ;;
+ esac
+ # All:
+ # save the list of fixed size partitions
+ fixed_size_partitions=$(echo $fixed_size_partitions | tr ':' '\n' | uniq | tr '\n' ':' | sed 's/:$//g')
+ echo "$fixed_size_partitions" > "$imagePath/d1.fixed_size_partitions"
+ # Windows 2000/XP, Vista, 7, 8, 8.1, Linux:
+ # Save unmodified partition table for download use
+ # Start filesytem type record
+ dots "Saving original partition table"
+ saveOriginalPartitions "$hd" "$imagePath" 1
+ swapuuidfilename=""
+ swapUUIDFileName "$imagePath" 1
+ mainuuidfilename=""
+ mainUUIDFileName "$imagePath" 1
+ echo -n "" > "$imagePath/d1.original.fstypes"
+ echo -n "" > $swapuuidfilename
+ echo "Done"
+ debugPause
+ echo " * Saving original disk/parts UUIDs"
+ debugPause
+ saveUUIDInformation "$hd" "$mainuuidfilename"
+ echo " * Shrinking Partitions on disk"
+ debugPause
+ for part in $parts; do
+ clearMountedDevices "$part"
+ removePageFile "$part"
+ shrinkPartition "$part" "$imagePath/d1.original.fstypes" "$fixed_size_partitions"
+ done
+ echo " * Saving shrunken partition table"
+ debugPause
+ sfdiskminimumpartitionfilename=""
+ sfdiskMinimumPartitionFileName "$imagePath" 1
+ savePartitionTablesAndBootLoaders "$hd" 1 "$imagePath" "$osid" "$imgPartitionType" "$sfdiskminimumpartitionfilename"
+ echo " * Processing Hard Disk: $hd"
+ for part in $parts; do
+ savePartition "$part" 1 "$imagePath" "$imgPartitionType"
+ done
+ dots "Restoring Original Partition Layout"
+ restoreOriginalPartitions "$hd" "$imagePath" 1
+ runPartprobe "$hd"
+ echo "Done"
+ debugPause
+ [[ $osid -eq 2 ]] && correctVistaMBR "$hd"
+ getPartitions "$hd"
+ for part in $parts; do
+ expandPartition "$part" "$fixed_size_partitions"
+ done
+ ;;
+ mps)
+ echo " * Processing Hard Disk: $hd"
+ echo " * Saving original disk/parts UUIDs"
+ debugPause
+ mainuuidfilename=""
+ mainUUIDFileName "$imagePath" 1
+ saveUUIDInformation "$hd" "$mainuuidfilename"
+ savePartitionTablesAndBootLoaders "$hd" 1 "$imagePath" "$osid" "$imgPartitionType"
+ getPartitions "$hd"
+ for part in $parts; do
+ savePartition "$part" 1 "$imagePath" "$imgPartitionType"
+ done
+ ;;
+ mpa)
+ disk_number=1
+ for disk in $disks; do
+ debugPause
+ echo " * Processing Hard Disk: $disk"
+ echo " * Saving original disk/parts UUIDs"
+ debugPause
+ mainuuidfilename=""
+ mainUUIDFileName "$imagePath" $disk_number
+ saveUUIDInformation "$disk" "$mainuuidfilename"
+ savePartitionTablesAndBootLoaders "$disk" "$disk_number" "$imagePath" "$osid" "$imgPartitionType"
+ runPartprobe "$disk"
+ getPartitions "$disk"
+ for part in $parts; do
+ savePartition "$part" "$disk_number" "$imagePath" "$imgPartitionType"
+ done
+ echo " * Disk $disk complete"
+ let disk_number+=1
+ done
+ ;;
+ dd)
+ imgpart="$imagePath/$img"
+ mkfifo /tmp/pigz1 >/dev/null 2>&1
+ uploadFormat "/tmp/pigz1" "$imgpart"
+ partclone.imager -c -s "$hd" -O /tmp/pigz1 -N -f 1
+ rm /tmp/pigz1 >/dev/null 2>&1
+ clearScreen
+ ;;
+ esac
+}
+findHDDInfo
+echo " * Now FOG will attempt to capture the image using Partclone"
+debugPause
+beginUpload
+completeTasking
diff --git a/draft/other-tools/fog/funcs.sh b/draft/other-tools/fog/funcs.sh
new file mode 100644
index 0000000..e6f574c
--- /dev/null
+++ b/draft/other-tools/fog/funcs.sh
@@ -0,0 +1,2196 @@
+#!/bin/bash
+. /usr/share/fog/lib/partition-funcs.sh
+REG_LOCAL_MACHINE_XP="/ntfs/WINDOWS/system32/config/system"
+REG_LOCAL_MACHINE_7="/ntfs/Windows/System32/config/SYSTEM"
+# 1 to turn on massive debugging of partition table restoration
+ismajordebug=0
+#If a sub shell gets invoked and we lose kernel vars this will reimport them
+oIFS=$IFS
+for var in $(cat /proc/cmdline); do
+ IFS=$oIFS
+ read name value <<< $(echo "$var" | grep =.* | awk -F= '{name=$1;$1="";gsub(/[ \t]+$/,"",$0);gsub(/^[ \t]+/,"",$0); gsub(/[+][_][+]/," ",$0); value=$0; print name; print value;}')
+ IFS=$'\n'
+ [[ -z $value ]] && continue
+ value=$(echo $value | sed 's/\"//g')
+ printf -v "$name" -- "$value"
+done
+IFS=$oIFS
+### If USB Boot device we need a way to get the kernel args properly
+[[ $boottype == usb && -f /tmp/hinfo.txt ]] && . /tmp/hinfo.txt
+# Below Are non parameterized functions
+# These functions will run without any arguments
+#
+# Clears thes creen unless its a debug task
+clearScreen() {
+ case $isdebug in
+ [Yy][Ee][Ss]|[Yy])
+ clear
+ ;;
+ esac
+}
+# Displays the nice banner along with the running version
+displayBanner() {
+ version=$(wget -qO - http://${web}service/getversion.php 2>/dev/null)
+ echo " +------------------------------------------+"
+ echo " | ..#######:. ..,#,.. .::##::. |"
+ echo " |.:###### .:;####:......;#;.. |"
+ echo " |...##... ...##;,;##::::.##... |"
+ echo " | ,# ...##.....##:::## ..:: |"
+ echo " | ## .::###,,##. . ##.::#.:######::.|"
+ echo " |...##:::###::....#. .. .#...#. #...#:::. |"
+ echo " |..:####:.. ..##......##::## .. # |"
+ echo " | # . ...##:,;##;:::#: ... ##.. |"
+ echo " | .# . .:;####;::::.##:::;#:.. |"
+ echo " | # ..:;###.. |"
+ echo " | |"
+ echo " +------------------------------------------+"
+ echo " | Free Computer Imaging Solution |"
+ echo " +------------------------------------------+"
+ echo " | Credits: http://fogproject.org/Credits |"
+ echo " | http://fogproject.org/Credits |"
+ echo " | Released under GPL Version 3 |"
+ echo " +------------------------------------------+"
+ echo " Version: $version"
+}
+# Gets all system mac addresses except for loopback
+getMACAddresses() {
+ read ifaces <<< $(/sbin/ip -4 -o addr | awk -F'([ /])+' '/global/ {print $2}' | tr '[:space:]' '|' | sed -e 's/^[|]//g' -e 's/[|]$//g')
+ read mac_addresses <<< $(/sbin/ip -0 -o addr | awk "/$ifaces/ {print \$11}" | tr '[:space:]' '|' | sed -e 's/^[|]//g' -e 's/[|]$//g')
+ echo $mac_addresses
+}
+# Verifies that there is a network interface
+verifyNetworkConnection() {
+ dots "Verifying network interface configuration"
+ local count=$(/sbin/ip addr | awk -F'[ /]+' '/global/{print $3}' | wc -l)
+ if [[ -z $count || $count -lt 1 ]]; then
+ echo "Failed"
+ debugPause
+ handleError "No network interfaces found (${FUNCNAME[0]})\n Args Passed: $*"
+ fi
+ echo "Done"
+ debugPause
+}
+# Verifies that the OS is valid for resizing
+validResizeOS() {
+ [[ $osid != @([1-2]|4|[5-7]|9|50|51) ]] && handleError " * Invalid operating system id: $osname ($osid) (${FUNCNAME[0]})\n Args Passed: $*"
+}
+# Gets the information from the system for inventory
+doInventory() {
+ sysman=$(dmidecode -s system-manufacturer)
+ sysproduct=$(dmidecode -s system-product-name)
+ sysversion=$(dmidecode -s system-version)
+ sysserial=$(dmidecode -s system-serial-number)
+ systype=$(dmidecode -t 3 | grep Type:)
+ biosversion=$(dmidecode -s bios-version)
+ biosvendor=$(dmidecode -s bios-vendor)
+ biosdate=$(dmidecode -s bios-release-date)
+ mbman=$(dmidecode -s baseboard-manufacturer)
+ mbproductname=$(dmidecode -s baseboard-product-name)
+ mbversion=$(dmidecode -s baseboard-version)
+ mbserial=$(dmidecode -s baseboard-serial-number)
+ mbasset=$(dmidecode -s baseboard-asset-tag)
+ cpuman=$(dmidecode -s processor-manufacturer)
+ cpuversion=$(dmidecode -s processor-version)
+ cpucurrent=$(dmidecode -t 4 | grep 'Current Speed:' | head -n1)
+ cpumax=$(dmidecode -t 4 | grep 'Max Speed:' | head -n1)
+ mem=$(cat /proc/meminfo | grep MemTotal)
+ hdinfo=$(hdparm -i $hd 2>/dev/null | grep Model=)
+ caseman=$(dmidecode -s chassis-manufacturer)
+ casever=$(dmidecode -s chassis-version)
+ caseserial=$(dmidecode -s chassis-serial-number)
+ casesasset=$(dmidecode -s chassis-asset-tag)
+ sysman64=$(echo $sysman | base64)
+ sysproduct64=$(echo $sysproduct | base64)
+ sysversion64=$(echo $sysversion | base64)
+ sysserial64=$(echo $sysserial | base64)
+ systype64=$(echo $systype | base64)
+ biosversion64=$(echo $biosversion | base64)
+ biosvendor64=$(echo $biosvendor | base64)
+ biosdate64=$(echo $biosdate | base64)
+ mbman64=$(echo $mbman | base64)
+ mbproductname64=$(echo $mbproductname | base64)
+ mbversion64=$(echo $mbversion | base64)
+ mbserial64=$(echo $mbserial | base64)
+ mbasset64=$(echo $mbasset | base64)
+ cpuman64=$(echo $cpuman | base64)
+ cpuversion64=$(echo $cpuversion | base64)
+ cpucurrent64=$(echo $cpucurrent | base64)
+ cpumax64=$(echo $cpumax | base64)
+ mem64=$(echo $mem | base64)
+ hdinfo64=$(echo $hdinfo | base64)
+ caseman64=$(echo $caseman | base64)
+ casever64=$(echo $casever | base64)
+ caseserial64=$(echo $caseserial | base64)
+ casesasset64=$(echo $casesasset | base64)
+}
+# Gets the location of the SAM registry if found
+getSAMLoc() {
+ local path=""
+ local paths="/ntfs/WINDOWS/system32/config/SAM /ntfs/Windows/System32/config/SAM"
+ for path in $paths; do
+ [[ ! -f $path ]] && continue
+ sam="$path" && break
+ done
+}
+# Appends dots to the end of string up to 50 characters.
+# Makes the output more aligned and organized.
+#
+# $1 String to append dots to
+dots() {
+ local str="$*"
+ [[ -z $str ]] && handleError "No string passed (${FUNCNAME[0]})\n Args Passed: $*"
+ local pad=$(printf "%0.1s" "."{1..50})
+ printf " * %s%*.*s" "$str" 0 $((50-${#str})) "$pad"
+}
+# Enables write caching on the disk passed
+# If the disk does not support write caching this does nothing
+#
+# $1 is the drive
+enableWriteCache() {
+ local disk="$1"
+ [[ -z $disk ]] && handleError "No disk passed (${FUNCNAME[0]})\n Args Passed: $*"
+ wcache=$(hdparm -W $disk 2>/dev/null | tr -d '[[:space:]]' | awk -F= '/.*write-caching=/{print $2}')
+ if [[ -z $wcache || $wcache == notsupported ]]; then
+ echo " * Write caching not supported"
+ debugPause
+ return
+ fi
+ dots "Enabling write cache"
+ hdparm -W1 $disk >/dev/null 2>&1
+ case $? in
+ 0)
+ echo "Enabled"
+ ;;
+ *)
+ echo "Failed"
+ debugPause
+ handleWarning "Could not set caching status (${FUNCNAME[0]})"
+ return
+ ;;
+ esac
+ debugPause
+}
+# Expands partitions, as needed/capable
+#
+# $1 is the partition
+# $2 is the fixed size partitions (can be empty)
+expandPartition() {
+ local part="$1"
+ local fixed="$2"
+ [[ -z $part ]] && handleError "No partition passed (${FUNCNAME[0]})\n Args Passed: $*"
+ local disk=""
+ local part_number=0
+ getDiskFromPartition "$part"
+ getPartitionNumber "$part"
+ local is_fixed=$(echo $fixed | awk "/(^$part_number:|:$part_number:|:$part_number$|^$part_number$)/{print 1}")
+ if [[ $is_fixed -eq 1 ]]; then
+ echo " * Not expanding ($part) fixed size"
+ debugPause
+ return
+ fi
+ local fstype=""
+ fsTypeSetting $part
+ case $fstype in
+ ntfs)
+ dots "Resizing $fstype volume ($part)"
+ ntfsresize $part -f -b -P </usr/share/fog/lib/EOFNTFSRESTORE >/dev/null 2>&1
+ case $? in
+ 0)
+ echo "Done"
+ ;;
+ *)
+ echo "Failed"
+ debugPause
+ handleError "Could not resize $part (${FUNCNAME[0]})\n Args Passed: $*"
+ ;;
+ esac
+ debugPause
+ resetFlag "$part"
+ ;;
+ extfs)
+ dots "Resizing $fstype volume ($part)"
+ e2fsck -fp $part >/dev/null 2>&1
+ case $? in
+ 0)
+ ;;
+ *)
+ echo "Failed"
+ debugPause
+ handleError "Could not check before resize (${FUNCNAME[0]})\n Args Passed: $*"
+ ;;
+ esac
+ resize2fs $part >/dev/null 2>&1
+ case $? in
+ 0)
+ ;;
+ *)
+ echo "Failed"
+ debugPause
+ handleError "Could not resize $part (${FUNCNAME[0]})\n Args Passed: $*"
+ ;;
+ esac
+ e2fsck -fp $part >/dev/null 2>&1
+ case $? in
+ 0)
+ echo "Done"
+ ;;
+ *)
+ echo "Failed"
+ debugPause
+ handleError "Could not check after resize (${FUNCNAME[0]})\n Args Passed: $*"
+ ;;
+ esac
+ ;;
+ *)
+ echo " * Not expanding ($part -- $fstype)"
+ debugPause
+ ;;
+ esac
+ debugPause
+ runPartprobe "$disk"
+}
+# Gets the filesystem type of the partition passed
+#
+# $1 is the partition
+fsTypeSetting() {
+ local part="$1"
+ [[ -z $part ]] && handleError "No partition passed (${FUNCNAME[0]})\n Args Passed: $*"
+ local blk_fs=$(blkid -po udev $part | awk -F= /FS_TYPE=/'{print $2}')
+ case $blk_fs in
+ btrfs)
+ fstype="btrfs"
+ ;;
+ ext[2-4])
+ fstype="extfs"
+ ;;
+ hfsplus)
+ fstype="hfsp"
+ ;;
+ ntfs)
+ fstype="ntfs"
+ ;;
+ swap)
+ fstype="swap"
+ ;;
+ vfat)
+ fstype="fat"
+ ;;
+ xfs)
+ fstype="xfs"
+ ;;
+ *)
+ fstype="imager"
+ ;;
+ esac
+}
+# Gets the disk part table UUID
+#
+# $1 is the disk
+getDiskUUID() {
+ local disk="$1"
+ [[ -z $disk ]] && handleError "No disk passed (${FUNCNAME[0]})\n Args Passed: $*"
+ diskuuid=$(blkid -po udev $disk | awk -F= '/PART_TABLE_UUID=/{print $2}')
+}
+# Gets the partition entry name
+#
+# $1 is the partition
+getPartName() {
+ local part="$1"
+ [[ -z $part ]] && handleError "No partition passed (${FUNCNAME[0]})\n Args Passed: $*"
+ partname=$(blkid -po udev $part | awk -F= '/PART_ENTRY_NAME=/{print $2}')
+}
+# Gets the partition entry type
+#
+# $1 is the partition
+getPartType() {
+ local part="$1"
+ [[ -z $part ]] && handleError "No partition passed (${FUNCNAME[0]})\n Args Passed: $*"
+ parttype=$(blkid -po udev $part | awk -F= '/PART_ENTRY_TYPE=/{print $2}')
+}
+# Gets the partition fs UUID
+#
+# $1 is the partition
+getPartFSUUID() {
+ local part="$1"
+ [[ -z $part ]] && handleError "No partition passed (${FUNCNAME[0]})\n Args Passed: $*"
+ partfsuuid=$(blkid -po udev $part | awk -F= '/FS_UUID=/{print $2}')
+}
+# Gets the partition entry UUID
+#
+# $1 is the partition
+getPartUUID() {
+ local part="$1"
+ [[ -z $part ]] && handleError "No partition passed (${FUNCNAME[0]})\n Args Passed: $*"
+ partuuid=$(blkid -po udev $part | awk -F= '/PART_ENTRY_UUID=/{print $2}')
+}
+# Gets the entry schemed (dos, gpt, etc...)
+#
+# $1 is the partition
+getPartitionEntryScheme() {
+ local part="$1"
+ [[ -z $part ]] && handleError "No partition passed (${FUNCNAME[0]})\n Args Passed: $*"
+ scheme=$(blkid -po udev $part | awk -F= '/PART_ENTRY_SCHEME=/{print $2}')
+}
+# Checks if the partition is dos extended (mbr with logical parts)
+#
+# $1 is the partition
+partitionIsDosExtended() {
+ local part="$1"
+ [[ -z $part ]] && handleError "No partition passed (${FUNCNAME[0]})\n Args Passed: $*"
+ local scheme=""
+ getPartitionEntryScheme "$part"
+ debugEcho "scheme = $scheme" 1>&2
+ case $scheme in
+ dos)
+ echo "no"
+ ;;
+ *)
+ local parttype=""
+ getPartType "$part"
+ debugEcho "parttype = $parttype" 1>&2
+ [[ $parttype == +(0x5|0xf) ]] && echo "yes" || echo "no"
+ ;;
+ esac
+ debugPause
+}
+# Returns the block size of a partition
+#
+# $1 is the partition
+# $2 is the variable to set
+getPartBlockSize() {
+ local part="$1"
+ local varVar="$2"
+ [[ -z $part ]] && handleError "No partition passed (${FUNCNAME[0]})\n Args Passed: $*"
+ [[ -z $varVar ]] && handleError "No variable to set passed (${FUNCNAME[0]})\n Args Passed: $*"
+ printf -v "$varVar" $(blockdev --getpbsz $part)
+}
+# Prepares location info for uploads
+#
+# $1 is the image path
+prepareUploadLocation() {
+ local imagePath="$1"
+ [[ -z $imagePath ]] && handleError "No image path passed (${FUNCNAME[0]})\n Args Passed: $*"
+ dots "Preparing backup location"
+ if [[ ! -d $imagePath ]]; then
+ mkdir -p $imagePath >/dev/null 2>&1
+ case $? in
+ 0)
+ ;;
+ *)
+ echo "Failed"
+ debugPause
+ handleError "Failed to create image capture path (${FUNCNAME[0]})\n Args Passed: $*"
+ ;;
+ esac
+ fi
+ echo "Done"
+ debugPause
+ dots "Setting permission on $imagePath"
+ chmod -R 777 $imagePath >/dev/null 2>&1
+ case $? in
+ 0)
+ echo "Done"
+ ;;
+ *)
+ echo "Failed"
+ debugPause
+ handleError "Failed to set permissions (${FUNCNAME[0]})\n Args Passed: $*"
+ ;;
+ esac
+ debugPause
+ dots "Removing any pre-existing files"
+ rm -Rf $imagePath/* >/dev/null 2>&1
+ case $? in
+ 0)
+ echo "Done"
+ ;;
+ *)
+ echo "Failed"
+ debugPause
+ handleError "Could not clean files (${FUNCNAME[0]})\n Args Passed: $*"
+ ;;
+ esac
+ debugPause
+}
+# Shrinks partitions for upload (resizable images only)
+#
+# $1 is the partition
+# $2 is the fstypes file location
+# $3 is the fixed partition numbers empty ok
+shrinkPartition() {
+ local part="$1"
+ local fstypefile="$2"
+ local fixed="$3"
+ [[ -z $part ]] && handleError "No partition passed (${FUNCNAME[0]})\n Args Passed: $*"
+ [[ -z $fstypefile ]] && handleError "No type file passed (${FUNCNAME[0]})\n Args Passed: $*"
+ local disk=""
+ local part_number=0
+ getDiskFromPartition "$part"
+ getPartitionNumber "$part"
+ local is_fixed=$(echo $fixed | awk "/(^$part_number:|:$part_number:|:$part_number$|^$part_number$)/{print 1}")
+ if [[ $is_fixed -eq 1 ]]; then
+ echo " * Not shrinking ($part) fixed size"
+ debugPause
+ return
+ fi
+ local fstype=""
+ fsTypeSetting "$part"
+ echo "$part $fstype" >> $fstypefile
+ local size=0
+ local tmpoutput=""
+ local sizentfsresize=0
+ local sizeextresize=0
+ local sizefd=0
+ local tmp_success=""
+ local test_string=""
+ local do_resizefs=0
+ local do_resizepart=0
+ local extminsize=0
+ local block_size=0
+ local sizeextresize=0
+ local adjustedfdsize=0
+ local part_block_size=0
+ case $fstype in
+ ntfs)
+ ntfsresize -f -i -v -P $part >/tmp/tmpoutput.txt 2>&1
+ if [[ ! $? -eq 0 ]]; then
+ handleError " * (${FUNCNAME[0]})\n Args Passed: $*\n\nFatal Error, unable to find size data out on $part. Cmd: ntfsresize -f -i -v -P $part"
+ fi
+ tmpoutput=$(cat /tmp/tmpoutput.txt)
+ size=$(cat /tmp/tmpoutput.txt | grep "You might resize" | cut -d" " -f5)
+ [[ -z $size ]] && handleError " * (${FUNCNAME[0]})\n Args Passed: $*\n\nFatal Error, Unable to determine possible ntfs size\n * To better help you debug we will run the ntfs resize\n\t but this time with full output, please wait!\n\t $(cat /tmp/tmpoutput.txt)"
+ rm /tmp/tmpoutput.txt >/dev/null 2>&1
+ sizentfsresize=$((size / 1000))
+ let sizentfsresize+=300000
+ sizentfsresize=$((sizentfsresize * 1${percent} / 100))
+ sizefd=$((sizentfsresize * 103 / 100))
+ echo " * Possible resize partition size: $sizentfsresize k"
+ dots "Running resize test $part"
+ ntfsresize -f -n -s ${sizentfsresize}k $part </usr/share/fog/lib/EOFNTFSRESTORE >/tmp/tmpoutput.txt 2>&1
+ local ntfsstatus="$?"
+ tmpoutput=$(cat /tmp/tmpoutput.txt)
+ test_string=$(cat /tmp/tmpoutput.txt | egrep -io "(ended successfully|bigger than the device size|volume size is already OK)" | tr -d '[[:space:]]')
+ echo "Done"
+ debugPause
+ rm /tmp/tmpoutput.txt >/dev/null 2>&1
+ case $test_string in
+ endedsuccessfully)
+ echo " * Resize test was successful"
+ do_resizefs=1
+ do_resizepart=1
+ ntfsstatus=0
+ ;;
+ biggerthanthedevicesize)
+ echo " * Not resizing filesystem $part (part too small)"
+ ntfsstatus=0
+ ;;
+ volumesizeisalreadyOK)
+ echo " * Not resizing filesystem $part (already OK)"
+ do_resizepart=1
+ ntfsstatus=0
+ ;;
+ esac
+ [[ ! $ntfsstatus -eq 0 ]] && handleError "Resize test failed!\n $tmpoutput\n (${FUNCNAME[0]})\n Args Passed: $*"
+ if [[ $do_resizefs -eq 1 ]]; then
+ debugPause
+ dots "Resizing filesystem"
+ ntfsresize -f -s ${sizentfsresize}k $part < /usr/share/fog/lib/EOFNTFS >/dev/null 2>&1
+ case $? in
+ 0)
+ echo "Done"
+ ;;
+ *)
+ echo "Failed"
+ debugPause
+ handleError "Could not resize disk (${FUNCNAME[0]})\n Args Passed: $*"
+ ;;
+ esac
+ fi
+ if [[ $do_resizepart -eq 1 ]]; then
+ debugPause
+ dots "Resizing partition $part"
+ getPartBlockSize "$part" "part_block_size"
+ case $osid in
+ [1-2]|4)
+ resizePartition "$part" "$sizentfsresize" "$imagePath"
+ [[ $osid -eq 2 ]] && correctVistaMBR "$disk"
+ ;;
+ [5-7]|9)
+ [[ $part_number -eq $win7partcnt ]] && part_start=$(blkid -po udev $part 2>/dev/null | awk -F= '/PART_ENTRY_OFFSET=/{printf("%.0f\n",$2*'$part_block_size'/1000)}') || part_start=1048576
+ if [[ -z $part_start || $part_start -lt 1 ]]; then
+ echo "Failed"
+ debugPause
+ handleError "Unable to determine disk start location (${FUNCNAME[0]})\n Args Passed: $*"
+ fi
+ adjustedfdsize=$((sizefd + part_start))
+ resizePartition "$part" "$adjustedfdsize" "$imagePath"
+ ;;
+ esac
+ echo "Done"
+ fi
+ resetFlag "$part"
+ ;;
+ extfs)
+ dots "Checking $fstype volume ($part)"
+ e2fsck -fp $part >/dev/null 2>&1
+ case $? in
+ 0)
+ echo "Done"
+ ;;
+ *)
+ echo "Failed"
+ debugPause
+ handleError "e2fsck failed to check $part (${FUNCNAME[0]})\n Args Passed: $*"
+ ;;
+ esac
+ debugPause
+ extminsize=$(resize2fs -P $part 2>/dev/null | awk -F': ' '{print $2}')
+ block_size=$(dumpe2fs -h $part 2>/dev/null | awk /^Block\ size:/'{print $3}')
+ size=$((extminsize * block_size))
+ sizeextresize=$((size * 103 / 100 / 1024))
+ [[ -z $sizeextresize || $sizeextresize -lt 1 ]] && handleError "Error calculating the new size of extfs ($part) (${FUNCNAME[0]})\n Args Passed: $*"
+ dots "Shrinking $fstype volume ($part)"
+ resize2fs $part -M >/dev/null 2>&1
+ case $? in
+ 0)
+ echo "Done"
+ ;;
+ *)
+ echo "Failed"
+ debugPause
+ handleError "Could not shrink $fstype volume ($part) (${FUNCNAME[0]})\n Args Passed: $*"
+ ;;
+ esac
+ debugPause
+ dots "Shrinking $part partition"
+ resizePartition "$part" "$sizeextresize" "$imagePath"
+ echo "Done"
+ debugPause
+ dots "Resizing $fstype volume ($part)"
+ resize2fs $part >/dev/null 2>&1
+ case $? in
+ 0)
+ ;;
+ *)
+ echo "Failed"
+ debugPause
+ handleError "Could resize $fstype volume ($part) (${FUNCNAME[0]})\n Args Passed: $*"
+ ;;
+ esac
+ e2fsck -fp $part >/dev/null 2>&1
+ case $? in
+ 0)
+ echo "Done"
+ ;;
+ *)
+ echo "Failed"
+ debugPause
+ handleError "Could not check expanded volume ($part) (${FUNCNAME[0]})\n Args Passed: $*"
+ ;;
+ esac
+ ;;
+ *)
+ echo " * Not shrinking ($part $fstype)"
+ ;;
+ esac
+ debugPause
+}
+# Resets the dirty bits on a partition
+#
+# $1 is the part
+resetFlag() {
+ local part="$1"
+ [[ -z $part ]] && handleError "No partition passed (${FUNCNAME[0]})\n Args Passed: $*"
+ local fstype=""
+ fsTypeSetting "$part"
+ case $fstype in
+ ntfs)
+ dots "Clearing ntfs flag"
+ ntfsfix -b -d $part >/dev/null 2>&1
+ case $? in
+ 0)
+ echo "Done"
+ ;;
+ *)
+ echo "Failed"
+ ;;
+ esac
+ ;;
+ esac
+}
+# Counts the partitions containing the fs type as passed
+#
+# $1 is the disk
+# $2 is the part type to look for
+# $3 is the variable to store the count into. This is
+# a variable variable
+countPartTypes() {
+ local disk="$1"
+ local parttype="$2"
+ local varVar="$3"
+ [[ -z $disk ]] && handleError "No disk passed (${FUNCNAME[0]})\n Args Passed: $*"
+ [[ -z $parttype ]] && handleError "No partition type passed (${FUNCNAME[0]})\n Args Passed: $*"
+ [[ -z $varVar ]] && handleError "No variable to set passed (${FUNCNAME[0]})\n Args Passed: $*"
+ local count=0
+ local fstype=""
+ local parts=""
+ local part=""
+ getPartitions "$disk"
+ for part in $parts; do
+ fsTypeSetting "$part"
+ case $fstype in
+ $parttype)
+ let count+=1
+ ;;
+ esac
+ done
+ printf -v "$varVar" "$count"
+}
+# Writes the image to the disk
+#
+# $1 = Source File
+# $2 = Target
+# $3 = mc task or not (not required)
+writeImage() {
+ local file="$1"
+ local target="$2"
+ local mc="$3"
+ [[ -z $target ]] && handleError "No target to place image passed (${FUNCNAME[0]})\n Args Passed: $*"
+ mkfifo /tmp/pigz1
+ case $mc in
+ yes)
+ udp-receiver --nokbd --portbase $port --ttl 32 --mcast-rdv-address $storageip 2>/dev/null >/tmp/pigz1 &
+ ;;
+ *)
+ [[ -z $file ]] && handleError "No source file passed (${FUNCNAME[0]})\n Args Passed: $*"
+ cat $file >/tmp/pigz1 &
+ ;;
+ esac
+ if [[ $imgFormat -eq 1 || $imgLegacy -eq 1 ]]; then
+ echo " * Imaging using Partimage"
+ pigz -d -c </tmp/pigz1 | partimage restore $target stdin -f3 -b 2>/tmp/status.fog
+ else
+ echo " * Imaging using Partclone"
+ pigz -d -c </tmp/pigz1 | partclone.restore --ignore_crc -O $target -N -f 1
+ fi
+ [[ ! $? -eq 0 ]] && handleError "Image failed to restore and exited with exit code $? (${FUNCNAME[0]})\n Args Passed: $*"
+ rm -rf /tmp/pigz1 >/dev/null 2>&1
+}
+# Gets the valid restore parts. They're only
+# valid if the partition data exists for
+# the partitions on the server
+#
+# $1 = Disk (e.g. /dev/sdb)
+# $2 = Disk number (e.g. 1)
+# $3 = ImagePath (e.g. /net/foo)
+getValidRestorePartitions() {
+ local disk="$1"
+ local disk_number="$2"
+ local imagePath="$3"
+ local setrestoreparts="$4"
+ [[ -z $disk ]] && handleError "No disk passed (${FUNCNAME[0]})\n Args Passed: $*"
+ [[ -z $disk_number ]] && handleError "No disk number passed (${FUNCNAME[0]})\n Args Passed: $*"
+ [[ -z $imagePath ]] && handleError "No image path passed (${FUNCNAME[0]})\n Args Passed: $*"
+ local valid_parts=""
+ local parts=""
+ local part=""
+ local imgpart=""
+ local part_number=0
+ getPartitions "$disk"
+ for part in $parts; do
+ getPartitionNumber "$part"
+ [[ $imgPartitionType != all && $imgPartitionType != $part_number ]] && continue
+ case $osid in
+ [1-2])
+ [[ ! -f $imagePath ]] && imgpart="$imagePath/d${disk_number}p${part_number}.img*" || imgpart="$imagePath"
+ ;;
+ 4|[5-7]|9)
+ [[ ! -f $imagePath/sys.img.000 ]] && imgpart="$imagePath/d${disk_number}p${part_number}.img*"
+ if [[ -z $imgpart ]]; then
+ case $win7partcnt in
+ 1)
+ [[ $part_number -eq 1 ]] && imgpart="$imagePath/sys.img.*"
+ ;;
+ 2)
+ [[ $part_number -eq 1 ]] && imgpart="$imagePath/rec.img.000"
+ [[ $part_number -eq 2 ]] && imgpart="$imagePath/sys.img.*"
+ ;;
+ 3)
+ [[ $part_number -eq 1 ]] && imgpart="$imagePath/rec.img.000"
+ [[ $part_number -eq 2 ]] && imgpart="$imagePath/rec.img.001"
+ [[ $part_number -eq 3 ]] && imgpart="$imagePath/sys.img.*"
+ ;;
+ esac
+ fi
+ ;;
+ *)
+ imgpart="$imagePath/d${disk_number}p${part_number}.img*"
+ ;;
+ esac
+ ls $imgpart >/dev/null 2>&1
+ [[ $? -eq 0 ]] && valid_parts="$valid_parts $part"
+ done
+ [[ -z $setrestoreparts ]] && restoreparts=$(echo $valid_parts | uniq | sort -V) || restoreparts="$(echo $setrestoreparts | uniq | sort -V)"
+}
+# Makes all swap partitions and sets uuid's in linux setups
+#
+# $1 = Disk (e.g. /dev/sdb)
+# $2 = Disk number (e.g. 1)
+# $3 = ImagePath (e.g. /net/foo)
+# $4 = ImagePartitionType (e.g. all, mbr, 1, 2, 3, etc.)
+makeAllSwapSystems() {
+ local disk="$1"
+ local disk_number="$2"
+ local imagePath="$3"
+ local imgPartitionType="$4"
+ [[ -z $disk ]] && handleError "No disk passed (${FUNCNAME[0]})\n Args Passed: $*"
+ [[ -z $disk_number ]] && handleError "No drive number passed (${FUNCNAME[0]})\n Args Passed: $*"
+ [[ -z $imagePath ]] && handleError "No image path passed (${FUNCNAME[0]})\n Args Passed: $*"
+ [[ -z $imgPartitionType ]] && handleError "No image partition type passed (${FUNCNAME[0]})\n Args Passed: $*"
+ local swapuuidfilename=""
+ swapUUIDFileName "$imagePath" "$disk_number"
+ local parts=""
+ local part=""
+ local part_number=0
+ getPartitions "$disk"
+ for part in $parts; do
+ getPartitionNumber "$part"
+ [[ $imgPartitionType == all || $imgPartitionType -eq $part_number ]] && makeSwapSystem "$swapuuidfilename" "$part"
+ done
+ runPartprobe "$disk"
+}
+# Changes the hostname on windows systems
+#
+# $1 = Partition
+changeHostname() {
+ local part="$1"
+ [[ -z $part ]] && handleError "No partition passed (${FUNCNAME[0]})\n Args Passed: $*"
+ [[ -z $hostname || $hostearly -eq 0 ]] && return
+ REG_HOSTNAME_KEY1="\ControlSet001\Services\Tcpip\Parameters\NV Hostname"
+ REG_HOSTNAME_KEY2="\ControlSet001\Services\Tcpip\Parameters\Hostname"
+ REG_HOSTNAME_KEY3="\ControlSet001\Services\Tcpip\Parameters\NV HostName"
+ REG_HOSTNAME_KEY4="\ControlSet001\Services\Tcpip\Parameters\HostName"
+ REG_HOSTNAME_KEY5="\ControlSet001\Control\ComputerName\ActiveComputerName\ComputerName"
+ REG_HOSTNAME_KEY6="\ControlSet001\Control\ComputerName\ComputerName\ComputerName"
+ REG_HOSTNAME_KEY7="\ControlSet001\services\Tcpip\Parameters\NV Hostname"
+ REG_HOSTNAME_KEY8="\ControlSet001\services\Tcpip\Parameters\Hostname"
+ REG_HOSTNAME_KEY9="\ControlSet001\services\Tcpip\Parameters\NV HostName"
+ REG_HOSTNAME_KEY10="\ControlSet001\services\Tcpip\Parameters\HostName"
+ REG_HOSTNAME_KEY11="\CurrentControlSet\Services\Tcpip\Parameters\NV Hostname"
+ REG_HOSTNAME_KEY12="\CurrentControlSet\Services\Tcpip\Parameters\Hostname"
+ REG_HOSTNAME_KEY13="\CurrentControlSet\Services\Tcpip\Parameters\NV HostName"
+ REG_HOSTNAME_KEY14="\CurrentControlSet\Services\Tcpip\Parameters\HostName"
+ REG_HOSTNAME_KEY15="\CurrentControlSet\Control\ComputerName\ActiveComputerName\ComputerName"
+ REG_HOSTNAME_KEY16="\CurrentControlSet\Control\ComputerName\ComputerName\ComputerName"
+ REG_HOSTNAME_KEY17="\CurrentControlSet\services\Tcpip\Parameters\NV Hostname"
+ REG_HOSTNAME_KEY18="\CurrentControlSet\services\Tcpip\Parameters\Hostname"
+ REG_HOSTNAME_KEY19="\CurrentControlSet\services\Tcpip\Parameters\NV HostName"
+ REG_HOSTNAME_KEY20="\CurrentControlSet\services\Tcpip\Parameters\HostName"
+ dots "Mounting directory"
+ if [[ ! -d /ntfs ]]; then
+ mkdir -p /ntfs >/dev/null 2>&1
+ if [[ ! $? -eq 0 ]]; then
+ echo "Failed"
+ debugPause
+ handleError " * Could not create mount location (${FUNCNAME[0]})\n Args Passed: $*"
+ fi
+ fi
+ umount /ntfs >/dev/null 2>&1
+ ntfs-3g -o remove_hiberfile,rw $part /ntfs >/tmp/ntfs-mount-output 2>&1
+ case $? in
+ 0)
+ echo "Done"
+ debugPause
+ ;;
+ *)
+ echo "Failed"
+ debugPause
+ handleError " * Could not mount $part (${FUNCNAME[0]})\n Args Passed: $*\n Reason: $(cat /tmp/ntfs-mount-output)"
+ ;;
+ esac
+ if [[ ! -f /usr/share/fog/lib/EOFREG ]]; then
+ key1="$REG_HOSTNAME_KEY1"
+ key2="$REG_HOSTNAME_KEY2"
+ key3="$REG_HOSTNAME_KEY3"
+ key4="$REG_HOSTNAME_KEY4"
+ key5="$REG_HOSTNAME_KEY5"
+ key6="$REG_HOSTNAME_KEY6"
+ key7="$REG_HOSTNAME_KEY7"
+ key8="$REG_HOSTNAME_KEY8"
+ key9="$REG_HOSTNAME_KEY9"
+ key10="$REG_HOSTNAME_KEY10"
+ key11="$REG_HOSTNAME_KEY11"
+ key12="$REG_HOSTNAME_KEY12"
+ key13="$REG_HOSTNAME_KEY13"
+ key14="$REG_HOSTNAME_KEY14"
+ key15="$REG_HOSTNAME_KEY15"
+ key16="$REG_HOSTNAME_KEY16"
+ key17="$REG_HOSTNAME_KEY17"
+ key18="$REG_HOSTNAME_KEY18"
+ key19="$REG_HOSTNAME_KEY19"
+ key20="$REG_HOSTNAME_KEY20"
+ case $osid in
+ 1)
+ regfile="$REG_LOCAL_MACHINE_XP"
+ ;;
+ 2|4|[5-7]|9)
+ regfile="$REG_LOCAL_MACHINE_7"
+ ;;
+ esac
+ echo "ed $key1" >/usr/share/fog/lib/EOFREG
+ echo "$hostname" >>/usr/share/fog/lib/EOFREG
+ echo "ed $key2" >>/usr/share/fog/lib/EOFREG
+ echo "$hostname" >>/usr/share/fog/lib/EOFREG
+ echo "ed $key3" >>/usr/share/fog/lib/EOFREG
+ echo "$hostname" >>/usr/share/fog/lib/EOFREG
+ echo "ed $key4" >>/usr/share/fog/lib/EOFREG
+ echo "$hostname" >>/usr/share/fog/lib/EOFREG
+ echo "ed $key5" >>/usr/share/fog/lib/EOFREG
+ echo "$hostname" >>/usr/share/fog/lib/EOFREG
+ echo "ed $key6" >>/usr/share/fog/lib/EOFREG
+ echo "$hostname" >>/usr/share/fog/lib/EOFREG
+ echo "ed $key7" >>/usr/share/fog/lib/EOFREG
+ echo "$hostname" >>/usr/share/fog/lib/EOFREG
+ echo "ed $key8" >>/usr/share/fog/lib/EOFREG
+ echo "$hostname" >>/usr/share/fog/lib/EOFREG
+ echo "ed $key9" >>/usr/share/fog/lib/EOFREG
+ echo "$hostname" >>/usr/share/fog/lib/EOFREG
+ echo "ed $key10" >>/usr/share/fog/lib/EOFREG
+ echo "$hostname" >>/usr/share/fog/lib/EOFREG
+ echo "ed $key11" >>/usr/share/fog/lib/EOFREG
+ echo "$hostname" >>/usr/share/fog/lib/EOFREG
+ echo "ed $key12" >>/usr/share/fog/lib/EOFREG
+ echo "$hostname" >>/usr/share/fog/lib/EOFREG
+ echo "ed $key13" >>/usr/share/fog/lib/EOFREG
+ echo "$hostname" >>/usr/share/fog/lib/EOFREG
+ echo "ed $key14" >>/usr/share/fog/lib/EOFREG
+ echo "$hostname" >>/usr/share/fog/lib/EOFREG
+ echo "ed $key15" >>/usr/share/fog/lib/EOFREG
+ echo "$hostname" >>/usr/share/fog/lib/EOFREG
+ echo "ed $key16" >>/usr/share/fog/lib/EOFREG
+ echo "$hostname" >>/usr/share/fog/lib/EOFREG
+ echo "ed $key17" >>/usr/share/fog/lib/EOFREG
+ echo "$hostname" >>/usr/share/fog/lib/EOFREG
+ echo "ed $key18" >>/usr/share/fog/lib/EOFREG
+ echo "$hostname" >>/usr/share/fog/lib/EOFREG
+ echo "ed $key19" >>/usr/share/fog/lib/EOFREG
+ echo "$hostname" >>/usr/share/fog/lib/EOFREG
+ echo "ed $key20" >>/usr/share/fog/lib/EOFREG
+ echo "$hostname" >>/usr/share/fog/lib/EOFREG
+ echo "q" >> /usr/share/fog/lib/EOFREG
+ echo "y" >> /usr/share/fog/lib/EOFREG
+ echo >> /usr/share/fog/lib/EOFREG
+ fi
+ if [[ -e $regfile ]]; then
+ dots "Changing hostname"
+ reged -e $regfile < /usr/share/fog/lib/EOFREG >/dev/null 2>&1
+ case $? in
+ [0-2])
+ echo "Done"
+ debugPause
+ ;;
+ *)
+ echo "Failed"
+ debugPause
+ umount /ntfs >/dev/null 2>&1
+ echo " * Failed to change hostname"
+ return
+ ;;
+ esac
+ fi
+ rm -rf /usr/share/fog/lib/EOFREG
+ umount /ntfs >/dev/null 2>&1
+}
+# Fixes windows 7/8 boot, though may need
+# to be updated to only impact windows 7
+# in which case we need a more dynamic method
+#
+# $1 is the partition
+fixWin7boot() {
+ local part="$1"
+ [[ -z $part ]] && handleError "No partition passed (${FUNCNAME[0]})\n Args Passed: $*"
+ [[ $osid != [5-7] ]] && return
+ local fstype=""
+ fsTypeSetting "$part"
+ [[ $fstype != ntfs ]] && return
+ dots "Mounting partition"
+ if [[ ! -d /bcdstore ]]; then
+ mkdir -p /bcdstore >/dev/null 2>&1
+ case $? in
+ 0)
+ ;;
+ *)
+ echo "Failed"
+ debugPause
+ handleError " * Could not create mount location (${FUNCNAME[0]})\n Args Passed: $*"
+ ;;
+ esac
+ fi
+ ntfs-3g -o remove_hiberfile,rw $part /bcdstore >/tmp/ntfs-mount-output 2>&1
+ case $? in
+ 0)
+ echo "Done"
+ debugPause
+ ;;
+ *)
+ echo "Failed"
+ debugPause
+ handleError " * Could not mount $part (${FUNCNAME[0]})\n Args Passed: $*\n Reason: $(cat /tmp/ntfs-mount-output)"
+ ;;
+ esac
+ if [[ ! -f /bcdstore/Boot/BCD ]]; then
+ umount /bcdstore >/dev/null 2>&1
+ return
+ fi
+ dots "Backing up and replacing BCD"
+ mv /bcdstore/Boot/BCD{,.bak} >/dev/null 2>&1
+ case $? in
+ 0)
+ ;;
+ *)
+ echo "Failed"
+ debugPause
+ umount /bcdstore >/dev/null 2>&1
+ echo " * Could not create backup"
+ return
+ ;;
+ esac
+ cp /usr/share/fog/BCD /bcdstore/Boot/BCD >/dev/null 2>&1
+ case $? in
+ 0)
+ echo "Done"
+ debugPause
+ umount /bcdstore >/dev/null 2>&1
+ ;;
+ *)
+ echo "Failed"
+ debugPause
+ umount /bcdstore >/dev/null 2>&1
+ echo " * Could not copy our bcd file"
+ return
+ ;;
+ esac
+ umount /bcdstore >/dev/null 2>&1
+}
+# Clears out windows hiber and page files
+#
+# $1 is the partition
+clearMountedDevices() {
+ local part="$1"
+ [[ -z $part ]] && handleError "No partition passed (${FUNCNAME[0]})\n Args Passed: $*"
+ if [[ ! -d /ntfs ]]; then
+ mkdir -p /ntfs >/dev/null 2>&1
+ case $? in
+ 0)
+ umount /ntfs >/dev/null 2>&1
+ ;;
+ *)
+ handleError "Could not create mount point /ntfs (${FUNCNAME[0]})\n Args Passed: $*"
+ ;;
+ esac
+ fi
+ case $osid in
+ 4|[5-7]|9)
+ local fstype=""
+ fsTypeSetting "$part"
+ REG_HOSTNAME_MOUNTED_DEVICES_7="\MountedDevices"
+ if [[ ! -f /usr/share/fog/lib/EOFMOUNT ]]; then
+ echo "cd $REG_HOSTNAME_MOUNTED_DEVICES_7" >/usr/share/fog/lib/EOFMOUNT
+ echo "dellallv" >>/usr/share/fog/lib/EOFMOUNT
+ echo "q" >>/usr/share/fog/lib/EOFMOUNT
+ echo "y" >>/usr/share/fog/lib/EOFMOUNT
+ echo >> /usr/share/fog/lib/EOFMOUNT
+ fi
+ case $fstype in
+ ntfs)
+ dots "Clearing part ($part)"
+ ntfs-3g -o remove_hiberfile,rw $part /ntfs >/tmp/ntfs-mount-output 2>&1
+ case $? in
+ 0)
+ ;;
+ *)
+ echo "Failed"
+ debugPause
+ handleError " * Could not mount $part (${FUNCNAME[0]})\n Args Passed: $*\n Reason: $(cat /tmp/ntfs-mount-output)"
+ ;;
+ esac
+ if [[ ! -f $REG_LOCAL_MACHINE_7 ]]; then
+ echo "Reg file not found"
+ debugPause
+ umount /ntfs >/dev/null 2>&1
+ return
+ fi
+ reged -e $REG_LOCAL_MACHINE_7 </usr/share/fog/lib/EOFMOUNT >/dev/null 2>&1
+ case $? in
+ [0-2])
+ echo "Done"
+ debugPause
+ umount /ntfs >/dev/null 2>&1
+ ;;
+ *)
+ echo "Failed"
+ debugPause
+ /umount /ntfs >/dev/null 2>&1
+ echo " * Could not clear partition $part"
+ return
+ ;;
+ esac
+ ;;
+ esac
+ ;;
+ esac
+}
+# Only removes the page file
+#
+# $1 is the device name of the windows system partition
+removePageFile() {
+ local part="$1"
+ [[ -z $part ]] && handleError "No partition passed (${FUNCNAME[0]})\n Args Passed: $*"
+ local fstype=""
+ fsTypeSetting "$part"
+ [[ ! $ignorepg -eq 1 ]] && return
+ case $osid in
+ [1-2]|4|[5-7]|[9]|50|51)
+ case $fstype in
+ ntfs)
+ dots "Mounting partition ($part)"
+ if [[ ! -d /ntfs ]]; then
+ mkdir -p /ntfs >/dev/null 2>&1
+ case $? in
+ 0)
+ ;;
+ *)
+ echo "Failed"
+ debugPause
+ handleError " * Could not create mount location (${FUNCNAME[0]})\n Args Passed: $*"
+ ;;
+ esac
+ fi
+ umount /ntfs >/dev/null 2>&1
+ ntfs-3g -o remove_hiberfile,rw $part /ntfs >/tmp/ntfs-mount-output 2>&1
+ case $? in
+ 0)
+ echo "Done"
+ debugPause
+ ;;
+ *)
+ echo "Failed"
+ debugPause
+ handleError " * Could not mount $part (${FUNCNAME[0]})\n Args Passed: $*\n Reason: $(cat /tmp/ntfs-mount-output)"
+ ;;
+ esac
+ if [[ -f /ntfs/pagefile.sys ]]; then
+ dots "Removing page file"
+ rm -rf /ntfs/pagefile.sys >/dev/null 2>&1
+ case $? in
+ 0)
+ echo "Done"
+ debugPause
+ ;;
+ *)
+ echo "Failed"
+ debugPause
+ echo " * Could not delete the page file"
+ ;;
+ esac
+ fi
+ if [[ -f /ntfs/hiberfil.sys ]]; then
+ dots "Removing hibernate file"
+ rm -rf /ntfs/hiberfil.sys >/dev/null 2>&1
+ case $? in
+ 0)
+ echo "Done"
+ debugPause
+ ;;
+ *)
+ echo "Failed"
+ debugPause
+ umount /ntfs >/dev/null 2>&1
+ echo " * Could not delete the hibernate file"
+ ;;
+ esac
+ fi
+ umount /ntfs >/dev/null 2>&1
+ ;;
+ esac
+ ;;
+ esac
+}
+# Sets OS mbr, as needed, and returns the Name
+# based on the OS id passed.
+#
+# $1 the osid to determine the os and mbr
+determineOS() {
+ local osid="$1"
+ [[ -z $osid ]] && handleError "No os id passed (${FUNCNAME[0]})\n Args Passed: $*"
+ case $osid in
+ 1)
+ osname="Windows XP"
+ mbrfile="/usr/share/fog/mbr/xp.mbr"
+ ;;
+ 2)
+ osname="Windows Vista"
+ mbrfile="/usr/share/fog/mbr/vista.mbr"
+ ;;
+ 3)
+ osname="Windows 98"
+ mbrfile=""
+ ;;
+ 4)
+ osname="Windows (Other)"
+ mbrfile=""
+ ;;
+ 5)
+ osname="Windows 7"
+ mbrfile="/usr/share/fog/mbr/win7.mbr"
+ defaultpart2start="105906176B"
+ ;;
+ 6)
+ osname="Windows 8"
+ mbrfile="/usr/share/fog/mbr/win8.mbr"
+ defaultpart2start="368050176B"
+ ;;
+ 7)
+ osname="Windows 8.1"
+ mbrfile="/usr/share/fog/mbr/win8.mbr"
+ defaultpart2start="368050176B"
+ ;;
+ 8)
+ osname="Apple Mac OS"
+ mbrfile=""
+ ;;
+ 9)
+ osname="Windows 10"
+ mbrfile=""
+ ;;
+ 50)
+ osname="Linux"
+ mbrfile=""
+ ;;
+ 51)
+ osname="Chromium OS"
+ mbrfile=""
+ ;;
+ 99)
+ osname="Other OS"
+ mbrfile=""
+ ;;
+ *)
+ handleError " * Invalid OS ID ($osid) (${FUNCNAME[0]})\n Args Passed: $*"
+ ;;
+ esac
+}
+# Converts the string (seconds) passed to human understanding
+#
+# $1 the seconds to convert
+sec2string() {
+ local T="$1"
+ [[ -z $T ]] && handleError "No string passed (${FUNCNAME[0]})\n Args Passed: $*"
+ local d=$((T/60/60/24))
+ local H=$((T/60/60%24))
+ local i=$((T/60%60))
+ local s=$((T%60))
+ local dayspace=''
+ local hourspace=''
+ local minspace=''
+ [[ $H > 0 ]] && dayspace=' '
+ [[ $i > 0 ]] && hourspace=':'
+ [[ $s > 0 ]] && minspace=':'
+ (($d > 0)) && printf '%d day%s' "$d" "$dayspace"
+ (($H > 0)) && printf '%d%s' "$H" "$hourspace"
+ (($i > 0)) && printf '%d%s' "$i" "$minspace"
+ (($s > 0)) && printf '%d' "$s"
+}
+# Returns the disk based off the partition passed
+#
+# $1 is the partition to grab the disk from
+getDiskFromPartition() {
+ local part="$1"
+ [[ -z $part ]] && handleError "No partition passed (${FUNCNAME[0]})\n Args Passed: $*"
+ disk=$(echo $part | sed 's/p\?[0-9]\+$//g')
+}
+# Returns the number of the partition passed
+#
+# $1 is the partition to get the partition number for
+getPartitionNumber() {
+ local part="$1"
+ [[ -z $part ]] && handleError "No partition passed (${FUNCNAME[0]})\n Args Passed: $*"
+ part_number=$(echo $part | grep -o '[0-9]*$')
+}
+# $1 is the partition to search for.
+getPartitions() {
+ local disk="$1"
+ [[ -z $disk ]] && disk="$hd"
+ [[ -z $disk ]] && handleError "No disk found (${FUNCNAME[0]})\n Args Passed: $*"
+ parts=$(lsblk -I 3,8,9,179,259 -lpno KNAME,TYPE $disk | awk '{if ($2 ~ /part/ || $2 ~ /md/) print $1}' | sort -V | uniq)
+}
+# Gets the hard drive on the host
+# Note: This function makes a best guess
+getHardDisk() {
+ [[ -n $fdrive ]] && hd=$(echo $fdrive)
+ [[ -n $hd ]] && return
+ local devs=$(lsblk -dpno KNAME -I 3,8,9,179,259 | uniq | sort -V)
+ disks=$(echo $devs)
+ [[ -z $disks ]] && handleError "Cannot find disk on system (${FUNCNAME[0]})\n Args Passed: $*"
+ [[ $1 == true ]] && return
+ for hd in $disks; do
+ break
+ done
+}
+# Finds the hard drive info and set's up the type
+findHDDInfo() {
+ case $imgType in
+ [Nn]|mps|dd)
+ dots "Looking for Hard Disk"
+ getHardDisk
+ if [[ -z $hd ]]; then
+ echo "Failed"
+ debugPause
+ handleError "Could not find hard disk ($0)\n Args Passed: $*"
+ fi
+ echo "Done"
+ debugPause
+ case $type in
+ down)
+ diskSize=$(lsblk --bytes -dplno SIZE -I 3,8,9,179,259 $hd)
+ [[ $diskSize -gt 2199023255552 ]] && layPartSize="2tB"
+ echo " * Using Disk: $hd"
+ [[ $imgType == +([nN]) ]] && validResizeOS
+ enableWriteCache "$hd"
+ ;;
+ up)
+ dots "Reading Partition Tables"
+ runPartprobe "$hd"
+ getPartitions "$hd"
+ if [[ -z $parts ]]; then
+ echo "Failed"
+ debugPause
+ handleError "Could not find partitions ($0)\n Args Passed: $*"
+ fi
+ echo "Done"
+ debugPause
+ ;;
+ esac
+ echo " * Using Hard Disk: $hd"
+ ;;
+ mpa)
+ dots "Looking for Hard Disks"
+ getHardDisk "true"
+ if [[ -z $disks ]]; then
+ echo "Failed"
+ debugPause
+ handleError "Could not find any disks ($0)\n Args Passed: $*"
+ fi
+ echo "Done"
+ debugPause
+ case $type in
+ up)
+ for disk in $disks; do
+ dots "Reading Partition Tables on $disk"
+ getPartitions "$disk"
+ if [[ -z $parts ]]; then
+ echo "Failed"
+ debugPause
+ echo " * No partitions for disk $disk"
+ debugPause
+ continue
+ fi
+ echo "Done"
+ debugPause
+ done
+ ;;
+ esac
+ echo " * Using Disks: $disks"
+ ;;
+ esac
+}
+
+# Imaging complete
+completeTasking() {
+ case $type in
+ up)
+ chmod -R 777 "$imagePath" >/dev/null 2>&1
+ killStatusReporter
+ . /bin/fog.imgcomplete
+ ;;
+ down)
+ killStatusReporter
+ if [[ -f /images/postdownloadscripts/fog.postdownload ]]; then
+ postdownpath="/images/postdownloadscripts/"
+ . ${postdownpath}fog.postdownload
+ fi
+ [[ $capone -eq 1 ]] && exit 0
+ if [[ $osid == +([1-2]|4|[5-7]|9) ]]; then
+ for disk in $disks; do
+ getPartitions "$disk"
+ for part in $parts; do
+ fsTypeSetting "$part"
+ [[ $fstype == ntfs ]] && changeHostname "$part"
+ done
+ done
+ fi
+ . /bin/fog.imgcomplete
+ ;;
+ esac
+}
+# Corrects mbr layout for Vista OS
+#
+# $1 is the disk to correct for
+correctVistaMBR() {
+ local disk="$1"
+ [[ -z $disk ]] && handleError "No disk passed (${FUNCNAME[0]})\n Args Passed: $*"
+ dots "Correcting Vista MBR"
+ dd if=$disk of=/tmp.mbr count=1 bs=512 >/dev/null 2>&1
+ case $? in
+ 0)
+ ;;
+ *)
+ echo "Failed"
+ debugPause
+ handleError "Could not create backup (${FUNCNAME[0]})\n Args Passed: $*"
+ ;;
+ esac
+ xxd /tmp.mbr /tmp.mbr.txt >/dev/null 2>&1
+ case $? in
+ 0)
+ ;;
+ *)
+ echo "Failed"
+ debugPause
+ handleError "xxd command failed (${FUNCNAME[0]})\n Args Passed: $*"
+ ;;
+ esac
+ rm /tmp.mbr >/dev/null 2>&1
+ case $? in
+ 0)
+ ;;
+ *)
+ echo "Failed"
+ debugPause
+ handleError "Couldn't remove /tmp.mbr file (${FUNCNAME[0]})\n Args Passed: $*"
+ ;;
+ esac
+ fogmbrfix /tmp.mbr.txt /tmp.mbr.fix.txt >/dev/null 2>&1
+ case $? in
+ 0)
+ ;;
+ *)
+ echo "Failed"
+ debugPause
+ handleError "fogmbrfix failed to operate (${FUNCNAME[0]})\n Args Passed: $*"
+ ;;
+ esac
+ rm /tmp.mbr.txt >/dev/null 2>&1
+ case $? in
+ 0)
+ ;;
+ *)
+ echo "Failed"
+ debugPause
+ handleError "Could not remove the text file (${FUNCNAME[0]})\n Args Passed: $*"
+ ;;
+ esac
+ xxd -r /tmp.mbr.fix.txt /mbr.mbr >/dev/null 2>&1
+ case $? in
+ 0)
+ ;;
+ *)
+ echo "Failed"
+ debugPause
+ handleError "Could not run second xxd command (${FUNCNAME[0]})\n Args Passed: $*"
+ ;;
+ esac
+ rm /tmp.mbr.fix.txt >/dev/null 2>&1
+ case $? in
+ 0)
+ ;;
+ *)
+ echo "Failed"
+ debugPause
+ handleError "Could not remove the fix file (${FUNCNAME[0]})\n Args Passed: $*"
+ ;;
+ esac
+ dd if=/mbr.mbr of="$disk" count=1 bs=512 >/dev/null 2>&1
+ case $? in
+ 0)
+ echo "Done"
+ ;;
+ *)
+ echo "Failed"
+ debugPause
+ handleError "Could not apply fixed MBR (${FUNCNAME[0]})\n Args Passed: $*"
+ ;;
+ esac
+ debugPause
+}
+# Prints an error with visible information
+#
+# $1 is the string to inform what went wrong
+handleError() {
+ local str="$1"
+ local parts=""
+ local part=""
+ echo "##############################################################################"
+ echo "# #"
+ echo "# An error has been detected! #"
+ echo "# #"
+ echo "##############################################################################"
+ echo -e "$str"
+ #
+ # expand the file systems in the restored partitions
+ #
+ # Windows 7, 8, 8.1:
+ # Windows 2000/XP, Vista:
+ # Linux:
+ if [[ -n $2 ]]; then
+ case $osid in
+ [1-2]|4|[5-7]|9|50|51)
+ if [[ -n "$hd" ]]; then
+ getPartitions "$hd"
+ for part in $parts; do
+ expandPartition "$part"
+ done
+ fi
+ ;;
+ esac
+ fi
+ if [[ -z $isdebug ]]; then
+ echo "##############################################################################"
+ echo "# #"
+ echo "# Computer will reboot in 1 minute #"
+ echo "# #"
+ echo "##############################################################################"
+ usleep 60000000
+ else
+ debugPause
+ fi
+ exit 1
+}
+# Prints a visible banner describing an issue but not breaking
+#
+# $1 The string to inform the user what the problem is
+handleWarning() {
+ local str="$1"
+ echo "##############################################################################"
+ echo "# #"
+ echo "# A warning has been detected! #"
+ echo "# #"
+ echo "##############################################################################"
+ echo -e "$str"
+ echo "##############################################################################"
+ echo "# #"
+ echo "# Will continue in 1 minute #"
+ echo "# #"
+ echo "##############################################################################"
+ usleep 60000000
+ debugPause
+}
+# Re-reads the partition table of the disk passed
+#
+# $1 is the disk
+runPartprobe() {
+ local disk="$1"
+ [[ -z $disk ]] && handleError "No disk passed (${FUNCNAME[0]})\n Args Passed: $*"
+ umount /ntfs /bcdstore >/dev/null 2>&1
+ udevadm settle
+ blockdev --rereadpt $disk >/dev/null 2>&1
+ [[ ! $? -eq 0 ]] && handleError "Failed to read back partitions (${FUNCNAME[0]})\n Args Passed: $*"
+}
+# Sends a command list to a file for use when debugging
+#
+# $1 The string of the command needed to run.
+debugCommand() {
+ local str="$1"
+ case $isdebug in
+ [Yy][Ee][Ss]|[Yy])
+ echo -e "$str" >> /tmp/cmdlist
+ ;;
+ esac
+}
+# Escapes the passed item where needed
+#
+# $1 the item that needs to be escaped
+escapeItem() {
+ local item="$1"
+ echo $item | sed -r 's%/%\\/%g'
+}
+# uploadFormat
+# Description:
+# Tells the system what format to upload in, whether split or not.
+# Expects first argument to be the fifo to send to.
+# Expects part of the filename in the case of resizable
+# will append 000 001 002 automatically
+#
+# $1 The fifo name (file in file out)
+# $2 The file to upload into on the server
+uploadFormat() {
+ local fifo="$1"
+ local file="$2"
+ [[ -z $fifo ]] && handleError "Missing file in file out (${FUNCNAME[0]})\n Args Passed: $*"
+ [[ -z $file ]] && handleError "Missing file name to store (${FUNCNAME[0]})\n Args Passed: $*"
+ [[ ! -e $fifo ]] && mkfifo $fifo >/dev/null 2>&1
+ case $imgFormat in
+ 2)
+ pigz $PIGZ_COMP < $fifo | split -a 3 -d -b 200m - ${file}. &
+ ;;
+ *)
+ pigz $PIGZ_COMP < $fifo > ${file}.000 &
+ ;;
+ esac
+}
+# Thank you, fractal13 Code Base
+#
+# Save enough MBR and embedding area to capture all of GRUB
+# Strategy is to capture EVERYTHING before the first partition.
+# Then, leave a marker that this is a GRUB MBR for restoration.
+# We could get away with less storage, but more details are required
+# to parse the information correctly. It would make the process
+# more complicated.
+#
+# See the discussion about the diskboot.img and the sector list
+# here: http://banane-krumm.de/bootloader/grub2.html
+#
+# Expects:
+# the device name (e.g. /dev/sda) as the first parameter,
+# the disk number (e.g. 1) as the second parameter
+# the directory to store images in (e.g. /image/dev/xyz) as the third parameter
+#
+# $1 is the disk
+# $2 is the disk number
+# $3 is the image path to save the file to.
+# $4 is the determinator of sgdisk use or not
+saveGRUB() {
+ local disk="$1"
+ local disk_number="$2"
+ local imagePath="$3"
+ local sgdisk="$4"
+ [[ -z $disk ]] && handleError "No disk passed (${FUNCNAME[0]})\n Args Passed: $*"
+ [[ -z $disk_number ]] && handleError "No drive number passed (${FUNCNAME[0]})\n Args Passed: $*"
+ [[ -z $imagePath ]] && handleError "No image path passed (${FUNCNAME[0]})\n Args Passed: $*"
+ # Determine the number of sectors to copy
+ # Hack Note: print $4+0 causes the column to be interpretted as a number
+ # so the comma is tossed
+ local count=$(sfdisk -d $disk 2>/dev/null | awk /start=\ *[1-9]/'{print $4+0}' | sort -n | head -n1)
+ local has_grub=$(dd if=$disk bs=512 count=1 2>&1 | grep -i 'grub')
+ local hasgrubfilename=""
+ if [[ -n $has_grub ]]; then
+ hasGrubFileName "$imagePath" "$disk_number" "$sgdisk"
+ touch $hasgrubfilename
+ fi
+ # Ensure that no more than 1MiB of data is copied (already have this size used elsewhere)
+ [[ $count -gt 2048 ]] && count=2048
+ local mbrfilename=""
+ MBRFileName "$imagePath" "$disk_number" "mbrfilename" "$sgdisk"
+ dd if=$disk of=$mbrfilename count=$count bs=512 >/dev/null 2>&1
+}
+# Checks for the existence of the grub embedding area in the image directory.
+# Echos 1 for true, and 0 for false.
+#
+# Expects:
+# the device name (e.g. /dev/sda) as the first parameter,
+# the disk number (e.g. 1) as the second parameter
+# the directory images stored in (e.g. /image/xyz) as the third parameter
+# $1 is the disk
+# $2 is the disk number
+# $3 is the image path
+# $4 is the sgdisk determinator
+hasGRUB() {
+ local disk_number="$1"
+ local imagePath="$2"
+ local sgdisk="$3"
+ [[ -z $disk_number ]] && handleError "No drive number passed (${FUNCNAME[0]})\n Args Passed: $*"
+ [[ -z $imagePath ]] && handleError "No image path passed (${FUNCNAME[0]})\n Args Passed: $*"
+ local hasgrubfilename=""
+ hasGrubFileName "$imagePath" "$disk_number" "$sgdisk"
+ hasGRUB=0
+ [[ -e $hasgrubfilename ]] && hasGRUB=1
+}
+# Restore the grub boot record and all of the embedding area data
+# necessary for grub2.
+#
+# Expects:
+# the device name (e.g. /dev/sda) as the first parameter,
+# the disk number (e.g. 1) as the second parameter
+# the directory images stored in (e.g. /image/xyz) as the third parameter
+# $1 is the disk
+# $2 is the disk number
+# $3 is the image path
+# $4 is the sgdisk determinator
+restoreGRUB() {
+ local disk="$1"
+ local disk_number="$2"
+ local imagePath="$3"
+ local sgdisk="$4"
+ [[ -z $disk ]] && handleError "No disk passed (${FUNCNAME[0]})\n Args Passed: $*"
+ [[ -z $disk_number ]] && handleError "No drive number passed (${FUNCNAME[0]})\n Args Passed: $*"
+ [[ -z $imagePath ]] && handleError "No image path passed (${FUNCNAME[0]})\n Args Passed: $*"
+ local tmpMBR=""
+ MBRFileName "$imagePath" "$disk_number" "tmpMBR" "$sgdisk"
+ local count=$(du -B 512 $tmpMBR | awk '{print $1}')
+ [[ $count -eq 8 ]] && count=1
+ dd if=$tmpMBR of=$disk bs=512 count=$count >/dev/null 2>&1
+ runPartprobe "$disk"
+}
+# Waits for enter if system is debug type
+debugPause() {
+ case $isdebug in
+ [Yy][Ee][Ss]|[Yy])
+ echo " * Press [Enter] key to continue"
+ read -p "$*"
+ ;;
+ *)
+ return
+ ;;
+ esac
+}
+debugEcho() {
+ local str="$*"
+ case $isdebug in
+ [Yy][Ee][Ss]|[Yy])
+ echo "$str"
+ ;;
+ *)
+ return
+ ;;
+ esac
+}
+majorDebugEcho() {
+ [[ $ismajordebug -gt 1 ]] && echo "$*"
+}
+majorDebugPause() {
+ [[ ! $ismajordebug -gt 0 ]] && return
+ echo " * Press [Enter] key to continue"
+ read -p "$*"
+}
+swapUUIDFileName() {
+ local imagePath="$1" # e.g. /net/dev/foo
+ local disk_number="$2" # e.g. 1
+ [[ -z $imagePath ]] && handleError "No image path passed (${FUNCNAME[0]})\n Args Passed: $*"
+ [[ -z $disk_number ]] && handleError "No drive number passed (${FUNCNAME[0]})\n Args Passed: $*"
+ swapuuidfilename="$imagePath/d${disk_number}.original.swapuuids"
+}
+mainUUIDFileName() {
+ local imagePath="$1"
+ local disk_number="$2" # e.g. 1
+ [[ -z $imagePath ]] && handleError "No image path passed (${FUNCNAME[0]})\n Args Passed: $*"
+ [[ -z $disk_number ]] && handleError "No drive number passed (${FUNCNAME[0]})\n Args Passed: $*"
+ mainuuidfilename="$imagePath/d${disk_number}.original.uuids"
+}
+sfdiskPartitionFileName() {
+ local imagePath="$1" # e.g. /net/dev/foo
+ local disk_number="$2" # e.g. 1
+ [[ -z $imagePath ]] && handleError "No image path passed (${FUNCNAME[0]})\n Args Passed: $*"
+ [[ -z $disk_number ]] && handleError "No drive number passed (${FUNCNAME[0]})\n Args Passed: $*"
+ sfdiskoriginalpartitionfilename="$imagePath/d${disk_number}.partitions"
+}
+sfdiskLegacyOriginalPartitionFileName() {
+ local imagePath="$1" # e.g. /net/dev/foo
+ local disk_number="$2" # e.g. 1
+ [[ -z $imagePath ]] && handleError "No image path passed (${FUNCNAME[0]})\n Args Passed: $*"
+ [[ -z $disk_number ]] && handleError "No drive number passed (${FUNCNAME[0]})\n Args Passed: $*"
+ sfdisklegacyoriginalpartitionfilename="$imagePath/d${disk_number}.original.partitions"
+}
+sfdiskMinimumPartitionFileName() {
+ local imagePath="$1" # e.g. /net/dev/foo
+ local disk_number="$2" # e.g. 1
+ [[ -z $imagePath ]] && handleError "No image path passed (${FUNCNAME[0]})\n Args Passed: $*"
+ [[ -z $disk_number ]] && handleError "No drive number passed (${FUNCNAME[0]})\n Args Passed: $*"
+ sfdiskminimumpartitionfilename="$imagePath/d${disk_number}.minimum.partitions"
+}
+sfdiskOriginalPartitionFileName() {
+ local imagePath="$1" # e.g. /net/dev/foo
+ local disk_number="$2" # e.g. 1
+ [[ -z $imagePath ]] && handleError "No image path passed (${FUNCNAME[0]})\n Args Passed: $*"
+ [[ -z $disk_number ]] && handleError "No disk number passed (${FUNCNAME[0]})\n Args Passed: $*"
+ sfdiskPartitionFileName "$imagePath" "$disk_number"
+}
+sgdiskOriginalPartitionFileName() {
+ local imagePath="$1" # e.g. /net/dev/foo
+ local disk_number="$2" # e.g. 1
+ [[ -z $imagePath ]] && handleError "No image path passed (${FUNCNAME[0]})\n Args Passed: $*"
+ [[ -z $disk_number ]] && handleError "No disk number passed (${FUNCNAME[0]})\n Args Passed: $*"
+ sgdiskoriginalpartitionfilename="$imagePath/d${disk_number}.sgdisk.original.partitions"
+}
+fixedSizePartitionsFileName() {
+ local imagePath="$1" # e.g. /net/dev/foo
+ local disk_number="$2" # e.g. 1
+ [[ -z $imagePath ]] && handleError "No image path passed (${FUNCNAME[0]})\n Args Passed: $*"
+ [[ -z $disk_number ]] && handleError "No disk number passed (${FUNCNAME[0]})\n Args Passed: $*"
+ fixed_size_file="$imagePath/d${disk_number}.fixed_size_partitions"
+}
+hasGrubFileName() {
+ local imagePath="$1" # e.g. /net/dev/foo
+ local disk_number="$2" # e.g. 1
+ local sgdisk="$3"
+ [[ -z $imagePath ]] && handleError "No image path passed (${FUNCNAME[0]})\n Args Passed: $*"
+ [[ -z $disk_number ]] && handleError "No disk number passed (${FUNCNAME[0]})\n Args Passed: $*"
+ hasgrubfilename="$imagePath/d${disk_number}.has_grub"
+ [[ -n $sgdisk ]] && hasgrubfilename="$imagePath/d${disk_number}.grub.mbr"
+}
+MBRFileName() {
+ local imagePath="$1" # e.g. /net/dev/foo
+ local disk_number="$2" # e.g. 1
+ local varVar="$3"
+ local sgdisk="$4"
+ [[ -z $imagePath ]] && handleError "No image path passed (${FUNCNAME[0]})\n Args Passed: $*"
+ [[ -z $disk_number ]] && handleError "No disk number passed (${FUNCNAME[0]})\n Args Passed: $*"
+ [[ -z $varVar ]] && handleError "No variable to set passed (${FUNCNAME[0]})\n Args Passed: $*"
+ local mbr=""
+ local hasGRUB=0
+ hasGRUB "$disk_number" "$imagePath" "$sgdisk"
+ [[ -n $sgdisk && $hasGRUB -eq 1 ]] && mbr="$imagePath/d${disk_number}.grub.mbr" || mbr="$imagePath/d${disk_number}.mbr"
+ case $type in
+ down)
+ [[ ! -f $mbr && -n $mbrfile ]] && mbr="$mbrfile"
+ printf -v "$varVar" "$mbr"
+ [[ -z $mbr ]] && handleError "Image store corrupt, unable to locate MBR, no default file specified (${FUNCNAME[0]})\n Args Passed: $*\n $varVar Variable set to: ${!varVar}"
+ [[ ! -f $mbr ]] && handleError "Image store corrupt, unable to locate MBR, no file found (${FUNCNAME[0]})\n Args Passed: $*\n Variable set to: ${!varVar}\n $varVar Variable set to: ${!varVar}"
+ ;;
+ up)
+ printf -v "$varVar" "$mbr"
+ ;;
+ esac
+}
+EBRFileName() {
+ local path="$1" # e.g. /net/dev/foo
+ local disk_number="$2" # e.g. 1
+ local part_number="$3" # e.g. 5
+ [[ -z $path ]] && handleError "No path passed (${FUNCNAME[0]})\n Args Passed: $*"
+ [[ -z $disk_number ]] && handleError "No disk number passed (${FUNCNAME[0]})\n Args Passed: $*"
+ [[ -z $part_number ]] && handleError "No partition number passed (${FUNCNAME[0]})\n Args Passed: $*"
+ ebrfilename="$path/d${disk_number}p${part_number}.ebr"
+}
+tmpEBRFileName() {
+ local disk_number="$1"
+ local part_number="$2"
+ [[ -z $disk_number ]] && handleError "No disk number passed (${FUNCNAME[0]})\n Args Passed: $*"
+ [[ -z $part_number ]] && handleError "No partition number passed (${FUNCNAME[0]})\n Args Passed: $*"
+ local ebrfilename=""
+ EBRFileName "/tmp" "$disk_number" "$disk_number"
+ tmpebrfilename="$ebrfilename"
+}
+#
+# Works for MBR/DOS or GPT style partition tables
+# Only saves PT information if the type is "all" or "mbr"
+#
+# For MBR/DOS style PT
+# Saves the MBR as everything before the start of the first partition (512+ bytes)
+# This includes the DOS MBR or GRUB. Don't know about other bootloaders
+# This includes the 4 primary partitions
+# The EBR of extended and logical partitions is actually the first 512 bytes of
+# the partition, so we don't need to save/restore them here.
+#
+#
+savePartitionTablesAndBootLoaders() {
+ local disk="$1" # e.g. /dev/sda
+ local disk_number="$2" # e.g. 1
+ local imagePath="$3" # e.g. /net/dev/foo
+ local osid="$4" # e.g. 50
+ local imgPartitionType="$5"
+ local sfdiskfilename="$6"
+ [[ -z $disk ]] && handleError "No disk passed (${FUNCNAME[0]})\n Args Passed: $*"
+ [[ -z $disk_number ]] && handleError "No drive number passed (${FUNCNAME[0]})\n Args Passed: $*"
+ [[ -z $imagePath ]] && handleError "No image path passed (${FUNCNAME[0]})\n Args Passed: $*"
+ [[ -z $osid ]] && handleError "No osid passed (${FUNCNAME[0]})\n Args Passed: $*"
+ [[ -z $imgPartitionType ]] && handleError "No img part type passed (${FUNCNAME[0]})\n Args Passed: $*"
+ if [[ -z $sfdiskfilename ]]; then
+ sfdiskPartitionFileName "$imagePath" "$disk_number"
+ sfdiskfilename="$sfdiskoriginalpartitionfilename"
+ fi
+ local hasgpt=0
+ hasGPT "$disk"
+ local have_extended_partition=0 # e.g. 0 or 1-n (extended partition count)
+ local strdots=""
+ [[ $hasgpt -eq 0 ]] && have_extended_partition=$(sfdisk -l $disk 2>/dev/null | egrep "^${disk}.* (Extended|W95 Ext'd \(LBA\))$" | wc -l)
+ runPartprobe "$disk"
+ case $hasgpt in
+ 0)
+ strdots="Saving Partition Tables (MBR)"
+ case $osid in
+ 4|50|51)
+ [[ $disk_number -eq 1 ]] && strdots="Saving Partition Tables and GRUB (MBR)"
+ ;;
+ esac
+ dots "$strdots"
+ saveGRUB "$disk" "$disk_number" "$imagePath"
+ sfdisk -d $disk 2>/dev/null > $sfdiskfilename
+ [[ $have_extended_partition -ge 1 ]] && saveAllEBRs "$disk" "$disk_number" "$imagePath"
+ echo "Done"
+ ;;
+ 1)
+ dots "Saving Partition Tables (GPT)"
+ saveGRUB "$disk" "$disk_number" "$imagePath" "true"
+ sgdisk -b "$imagePath/d${disk_number}.mbr" $disk >/dev/null 2>&1
+ if [[ ! $? -eq 0 ]]; then
+ echo "Failed"
+ debugPause
+ handleError "Error trying to save GPT partition tables (${FUNCNAME[0]})\n Args Passed: $*"
+ fi
+ sfdisk -d $disk 2>/dev/null > $sfdiskfilename
+ echo "Done"
+ ;;
+ esac
+ runPartprobe "$disk"
+ debugPause
+}
+clearPartitionTables() {
+ local disk="$1"
+ [[ -z $disk ]] && handleError "No disk passed (${FUNCNAME[0]})\n Args Passed: $*"
+ [[ $nombr -eq 1 ]] && return
+ dots "Erasing current MBR/GPT Tables"
+ sgdisk -Z $disk >/dev/null 2>&1
+ case $? in
+ 0)
+ echo "Done"
+ ;;
+ 2)
+ echo "Done, but cleared corrupted partition."
+ ;;
+ *)
+ echo "Failed"
+ debugPause
+ handleError "Error trying to erase partition tables (${FUNCNAME[0]})\n Args Passed: $*"
+ ;;
+ esac
+ runPartprobe "$disk"
+ debugPause
+}
+# Restores the partition tables and boot loaders
+#
+# $1 is the disk
+# $2 is the disk number
+# $3 is the image path
+# $4 is the osid
+# $5 is the image partition type
+restorePartitionTablesAndBootLoaders() {
+ local disk="$1"
+ local disk_number="$2"
+ local imagePath="$3"
+ local osid="$4"
+ local imgPartitionType="$5"
+ [[ -z $disk ]] && handleError "No disk passed (${FUNCNAME[0]})\n Args Passed: $*"
+ [[ -z $disk_number ]] && handleError "No drive number passed (${FUNCNAME[0]})\n Args Passed: $*"
+ [[ -z $imagePath ]] && handleError "No image path passed (${FUNCNAME[0]})\n Args Passed: $*"
+ [[ -z $osid ]] && handleError "No osid passed (${FUNCNAME[0]})\n Args Passed: $*"
+ [[ -z $imgPartitionType ]] && handleError "No image part type passed (${FUNCNAME[0]})\n Args Passed: $*"
+ local tmpMBR=""
+ local strdots=""
+ if [[ $nombr -eq 1 ]]; then
+ echo " * Skipping partition tables and MBR"
+ debugPause
+ return
+ fi
+ clearPartitionTables "$disk"
+ majorDebugEcho "Partition table should be empty now."
+ majorDebugShowCurrentPartitionTable "$disk" "$disk_number"
+ majorDebugPause
+ MBRFileName "$imagePath" "$disk_number" "tmpMBR"
+ [[ ! -f $tmpMBR ]] && handleError "Image Store Corrupt: Unable to locate MBR (${FUNCNAME[0]})\n Args Passed: $*"
+ local table_type=""
+ getDesiredPartitionTableType "$imagePath" "$disk_number"
+ majorDebugEcho "Trying to restore to $table_type partition table."
+ if [[ $table_type == GPT ]]; then
+ dots "Restoring Partition Tables (GPT)"
+ restoreGRUB "$disk" "$disk_number" "$imagePath" "true"
+ sgdisk -gel $tmpMBR $disk >/dev/null 2>&1
+ [[ ! $? -eq 0 ]] && handleError "Error trying to restore GPT partition tables (${FUNCNAME[0]})\n Args Passed: $*"
+ global_gptcheck="yes"
+ echo "Done"
+ else
+ case $osid in
+ 50|51)
+ strdots="Restoring Partition Tables and GRUB (MBR)"
+ ;;
+ *)
+ strdots="Restoring Partition Tables (MBR)"
+ ;;
+ esac
+ dots "$strdots"
+ restoreGRUB "$disk" "$disk_number" "$imagePath"
+ echo "Done"
+ debugPause
+ majorDebugShowCurrentPartitionTable "$disk" "$disk_number"
+ majorDebugPause
+ ebrcount=$(ls -1 $imagePath/*.ebr 2>/dev/null | wc -l)
+ [[ $ebrcount -gt 0 ]] && restoreAllEBRs "$disk" "$disk_number" "$imagePath" "$imgPartitionType"
+ local sfdiskoriginalpartitionfilename=""
+ local sfdisklegacyoriginalpartitionfilename=""
+ sfdiskPartitionFileName "$imagePath" "$disk_number"
+ sfdiskLegacyOriginalPartitionFileName "$imagePath" "$disk_number"
+ if [[ -r $sfdiskoriginalpartitionfilename ]]; then
+ dots "Inserting Extended partitions"
+ sfdisk $disk < $sfdiskoriginalpartitionfilename >/dev/null 2>&1
+ case $? in
+ 0)
+ echo "Done"
+ ;;
+ *)
+ echo "Failed"
+ ;;
+ esac
+ elif [[ -e $sfdisklegacyoriginalpartitionfilename ]]; then
+ dots "Extended partitions (legacy)"
+ sfdisk $disk < $sfdisklegacyoriginalpartitionfilename >/dev/null 2>&1
+ case $? in
+ 0)
+ echo "Done"
+ ;;
+ *)
+ echo "Failed"
+ ;;
+ esac
+ else
+ echo " * No extended partitions"
+ fi
+ fi
+ debugPause
+ runPartprobe "$disk"
+ majorDebugShowCurrentPartitionTable "$disk" "$disk_number"
+ majorDebugPause
+}
+savePartition() {
+ local part="$1"
+ local disk_number="$2"
+ local imagePath="$3"
+ [[ -z $part ]] && handleError "No partition passed (${FUNCNAME[0]})\n Args Passed: $*"
+ [[ -z $disk_number ]] && handleError "No drive number passed (${FUNCNAME[0]})\n Args Passed: $*"
+ [[ -z $imagePath ]] && handleError "No image path passed (${FUNCNAME[0]})\n Args Passed: $*"
+ local part_number=0
+ getPartitionNumber "$part"
+ local fstype=""
+ local parttype=""
+ local imgpart=""
+ local fifoname="/tmp/pigz1"
+ if [[ $imgPartitionType != all && $imgPartitionType != $part_number ]]; then
+ echo " * Skipping partition $part ($part_number)"
+ debugPause
+ return
+ fi
+ echo " * Processing Partition: $part ($part_number)"
+ debugPause
+ fsTypeSetting "$part"
+ getPartType "$part"
+ local ebrfilename=""
+ local swapuuidfilename=""
+ case $fstype in
+ swap)
+ echo " * Saving swap partition UUID"
+ swapUUIDFileName "$imagePath" "$disk_number"
+ saveSwapUUID "$swapuuidfilename" "$part"
+ ;;
+ *)
+ case $parttype in
+ 0x5|0xf)
+ echo " * Not capturing content of extended partition"
+ debugPause
+ EBRFileName "$imagePath" "$disk_number" "$part_number"
+ touch "$ebrfilename"
+ ;;
+ *)
+ echo " * Using partclone.$fstype"
+ debugPause
+ imgpart="$imagePath/d${disk_number}p${part_number}.img"
+ uploadFormat "$fifoname" "$imgpart"
+ partclone.$fstype -fsck-src-part -c -s $part -O $fifoname -N -f 1
+ case $? in
+ 0)
+ mv ${imgpart}.000 $imgpart >/dev/null 2>&1
+ echo " * Image Captured"
+ ;;
+ *)
+ handleError "Failed to complete capture (${FUNCNAME[0]})\n Args Passed: $*"
+ ;;
+ esac
+ ;;
+ esac
+ ;;
+ esac
+ rm -rf $fifoname >/dev/null 2>&1
+ debugPause
+}
+restorePartition() {
+ local part="$1"
+ local disk_number="$2"
+ local imagePath="$3"
+ local mc="$4"
+ [[ -z $part ]] && handleError "No partition passed (${FUNCNAME[0]})\n Args Passed: $*"
+ [[ -z $disk_number ]] && handleError "No disk number passed (${FUNCNAME[0]})\n Args Passed: $*"
+ [[ -z $imagePath ]] && handleError "No image path passed (${FUNCNAME[0]})\n Args Passed: $*"
+ if [[ $imgPartitionType != all && $imgPartitionType != $part_number ]]; then
+ echo " * Skipping partition: $part ($part_number)"
+ debugPause
+ return
+ fi
+ local imgpart=""
+ local ebrfilename=""
+ local disk=""
+ local part_number=0
+ getDiskFromPartition "$part"
+ getPartitionNumber "$part"
+ echo " * Processing Partition: $part ($part_number)"
+ debugPause
+ case $imgType in
+ dd)
+ imgpart="$imagePath"
+ ;;
+ n|mps|mpa)
+ case $osid in
+ [1-2])
+ [[ -f $imagePath ]] && imgpart="$imagePath" || imgpart="$imagePath/d${disk_number}p${part_number}.img*"
+ ;;
+ 4|8|50|51)
+ imgpart="$imagePath/d${disk_number}p${part_number}.img*"
+ ;;
+ [5-7]|9)
+ [[ ! -f $imagePath/sys.img.000 ]] && imgpart="$imagePath/d${disk_number}p${part_number}.img*"
+ if [[ -z $imgpart ]] ;then
+ case $win7partcnt in
+ 1)
+ imgpart="$imagePath/sys.img.*"
+ ;;
+ 2)
+ case $part_number in
+ 1)
+ imgpart="$imagePath/rec.img.000"
+ ;;
+ 2)
+ imgpart="$imagePath/sys.img.*"
+ ;;
+ esac
+ ;;
+ 3)
+ case $part_number in
+ 1)
+ imgpart="$imagePath/rec.img.000"
+ ;;
+ 2)
+ imgpart="$imagePath/rec.img.001"
+ ;;
+ 3)
+ imgpart="$imagePath/sys.img.*"
+ ;;
+ esac
+ ;;
+ esac
+ fi
+ ;;
+ esac
+ ;;
+ *)
+ handleError "Invalid Image Type $imgType (${FUNCNAME[0]})\n Args Passed: $*"
+ ;;
+ esac
+ ls $imgpart >/dev/null 2>&1
+ if [[ ! $? -eq 0 ]]; then
+ EBRFileName "$imagePath" "$disk_number" "$part_number"
+ [[ -e $ebrfilename ]] && echo " * Not deploying content of extended partition" || echo " * Partition File Missing: $imgpart"
+ runPartprobe "$disk"
+ return
+ fi
+ writeImage "$imgpart" "$part" "$mc"
+ runPartprobe "$disk"
+ resetFlag "$part"
+}
+runFixparts() {
+ local disk="$1"
+ [[ -z $disk ]] && handleError "No disk passed (${FUNCNAME[0]})\n Args Passed: $*"
+ dots "Attempting fixparts"
+ fixparts $disk </usr/share/fog/lib/EOFFIXPARTS >/dev/null 2>&1
+ case $? in
+ 0)
+ echo "Done"
+ ;;
+ *)
+ echo "Failed"
+ debugPause
+ handleError "Could not fix partition layout (${FUNCNAME[0]})\n Args Passed: $*" "yes"
+ ;;
+ esac
+ debugPause
+ runPartprobe "$disk"
+}
+killStatusReporter() {
+ dots "Stopping FOG Status Reporter"
+ kill -9 $statusReporter >/dev/null 2>&1
+ case $? in
+ 0)
+ echo "Done"
+ ;;
+ *)
+ echo "Failed"
+ ;;
+ esac
+ debugPause
+}
+prepareResizeDownloadPartitions() {
+ local disk="$1"
+ local disk_number="$2"
+ local imagePath="$3"
+ local osid="$4"
+ local imgPartitionType="$5"
+ [[ -z $disk ]] && handleError "No disk passed (${FUNCNAME[0]})\n Args Passed: $*"
+ [[ -z $disk_number ]] && handleError "No disk number passed (${FUNCNAME[0]})\n Args Passed: $*"
+ [[ -z $imagePath ]] && handleError "No image path passed (${FUNCNAME[0]})\n Args Passed: $*"
+ [[ -z $osid ]] && handleError "No osid passed (${FUNCNAME[0]})\n Args Passed: $*"
+ [[ -z $imgPartitionType ]] && handleError "No image partition type passed (${FUNCNAME[0]})\n Args Passed: $*"
+ if [[ $nombr -eq 1 ]]; then
+ echo -e " * Skipping partition preperation\n"
+ debugPause
+ return
+ fi
+ restorePartitionTablesAndBootLoaders "$disk" "$disk_number" "$imagePath" "$osid" "$imgPartitionType"
+ local do_fill=0
+ fillDiskWithPartitionsIsOK "$disk" "$imagePath" "$disk_number"
+ majorDebugEcho "Filling disk = $do_fill"
+ dots "Attempting to expand/fill partitions"
+ if [[ $do_fill -eq 0 ]]; then
+ echo "Failed"
+ debugPause
+ handleError "Fatal Error: Could not resize partitions (${FUNCNAME[0]})\n Args Passed: $*"
+ fi
+ fillDiskWithPartitions "$disk" "$imagePath" "$disk_number"
+ echo "Done"
+ debugPause
+ runPartprobe "$disk"
+}
+# $1 is the disks
+# $2 is the image path
+# $3 is the image partition type (either all or partition number)
+# $4 is the flag to say whether this is multicast or not
+performRestore() {
+ local disks="$1"
+ local disk=""
+ local imagePath="$2"
+ local imgPartitionType="$3"
+ local mc="$4"
+ [[ -z $disks ]] && handleError "No disks passed (${FUNCNAME[0]})\n Args Passed: $*"
+ [[ -z $imagePath ]] && handleError "No image path passed (${FUNCNAME[0]})\n Args Passed: $*"
+ [[ -z $imgPartitionType ]] && handleError "No partition type passed (${FUNCNAME[0]})\n Args Passed: $*"
+ local disk_number=1
+ local part_number=0
+ local restoreparts=""
+ local mainuuidfilename=""
+ [[ $imgType =~ [Nn] ]] && local tmpebrfilename=""
+ for disk in $disks; do
+ mainuuidfilename=""
+ mainUUIDFileName "$imagePath" "$disk_number"
+ getValidRestorePartitions "$disk" "$disk_number" "$imagePath" "$restoreparts"
+ [[ -z $restoreparts ]] && handleError "No image file(s) found that would match the partition(s) to be restored (${FUNCNAME[0]})\n Args Passed: $*"
+ for restorepart in $restoreparts; do
+ getPartitionNumber "$restorepart"
+ [[ $imgType =~ [Nn] ]] && tmpEBRFileName "$disk_number" "$part_number"
+ restorePartition "$restorepart" "$disk_number" "$imagePath" "$mc"
+ [[ $imgType =~ [Nn] ]] && restoreEBR "$restorepart" "$tmpebrfilename"
+ [[ $imgType =~ [Nn] ]] && expandPartition "$restorepart" "$fixed_size_partitions"
+ [[ $osid == +([5-7]) && $imgType =~ [Nn] ]] && fixWin7boot "$restorepart"
+ done
+ restoreparts=""
+ echo " * Resetting UUIDs for $disk"
+ debugPause
+ restoreUUIDInformation "$disk" "$mainuuidfilename"
+ echo " * Resettings swap systems"
+ debugPause
+ makeAllSwapSystems "$disk" "$disk_number" "$imagePath" "$imgPartitionType"
+ let disk_number+=1
+ done
+}
diff --git a/draft/other-tools/fog/partition-funcs.sh b/draft/other-tools/fog/partition-funcs.sh
new file mode 100644
index 0000000..8d77ec5
--- /dev/null
+++ b/draft/other-tools/fog/partition-funcs.sh
@@ -0,0 +1,811 @@
+#!/bin/bash
+#
+# These functions are for dealing with resizing of partitions.
+# They currently work for MBR and Extended partition tables.
+# THE DO NOT WORK FOR GPT.
+# It is assumed that at most 1 extended partition will exist,
+# with any number of logical partitions.
+# Requires the sfdisk tool.
+# Assumes that sfdisk's "unit: sectors" means 512 byte sectors.
+#
+# $1 is the name of the disk drive
+# $2 is name of file to save to.
+saveSfdiskPartitions() {
+ local disk="$1"
+ local file="$2"
+ [[ -z $disk ]] && handleError "No disk passed (${FUNCNAME[0]})\n Args Passed: $*"
+ [[ -z $file ]] && handleError "No file to save to (${FUNCNAME[0]})\n Args Passed: $*"
+ sfdisk -d $disk 2>/dev/null > $file
+ [[ ! $? -eq 0 ]] && majorDebugEcho "sfdisk failed in (${FUNCNAME[0]})"
+}
+# $1 is the name of the disk drive
+# $2 is name of file to save to.
+saveUUIDInformation() {
+ local disk="$1"
+ local file="$2"
+ [[ -z $disk ]] && handleError "No disk passed (${FUNCNAME[0]})\n Args Passed: $*"
+ [[ -z $file ]] && handleError "No file to save to passed (${FUNCNAME[0]})\n Args Passed: $*"
+ local hasgpt=0
+ hasGPT "$disk"
+ [[ $hasgpt -eq 0 ]] && return
+ rm -f $file
+ touch $file
+ local diskuuid=""
+ local partuuid=""
+ local partfsuuid=""
+ local parts=""
+ local part=""
+ local part_number=""
+ local strtoadd=""
+ local is_swap=0
+ getDiskUUID "$disk"
+ echo "$disk $diskuuid" >> $file
+ getPartitions "$disk"
+ for part in $parts; do
+ getPartitionNumber "$part"
+ partitionIsSwap "$part"
+ [[ $is_swap -gt 0 ]] && continue
+ getPartUUID "$part"
+ getPartFSUUID "$part"
+ [[ -n $partfsuuid ]] && strtoadd="$part $part_number:$partfsuuid"
+ [[ -n $partuuid ]] && strtoadd="$strtoadd $part_number:$partuuid"
+ echo "$strtoadd" >> $file
+ strtoadd=""
+ done
+}
+# $1 is the name of the disk drive
+# $2 is name of file to restore from
+restoreUUIDInformation() {
+ local disk="$1"
+ local file="$2"
+ [[ -z $disk ]] && handleError "No disk passed (${FUNCNAME[0]})\n Args Passed: $*"
+ [[ -z $file ]] && handleError "No file to load from passed (${FUNCNAME[0]})\n Args Passed: $*"
+ [[ ! -r $file ]] && return
+ local diskuuid=""
+ local partuuid=""
+ local escape_disk=$(escapeItem $disk)
+ local escape_part=""
+ local is_swap=0
+ diskuuid=$(awk "/^$escape_disk\ /{print \$2}" $file)
+ [[ -n $diskuuid ]] && sgdisk -U $diskuuid $disk >/dev/null 2>&1
+ [[ ! $? -eq 0 ]] && handleError "Failed to set disk guid (sgdisk -U) (${FUNCNAME[0]})\n Args Passed: $*"
+ getPartitions "$disk"
+ for part in $parts; do
+ partitionIsSwap "$part"
+ [[ $is_swap -gt 0 ]] && continue
+ escape_part=$(escapeItem $part)
+ local oIFS=$IFS
+ local IFS=$'\n'
+ read partuuid parttype <<< $(awk "/^$escape_part\ /{printf(\"%s\n%s\",\$2,\$3)}" $file)
+ IFS=$oIFS
+ [[ -n $parttype ]] && sgdisk -t $parttype $disk >/dev/null 2>&1 || true
+ [[ ! $? -eq 0 ]] && handleError " Failed to set partition type (sgdisk -t) (${FUNCNAME[0]})\n Args Passed: $*"
+ [[ -n $partuuid ]] && sgdisk -u $partuuid $disk >/dev/null 2>&1 || true
+ [[ ! $? -eq 0 ]] && handleError "Failed to set partition guid (sgdisk -u) (${FUNCNAME[0]})\n Args Passed: $*"
+ done
+}
+# $1 is the name of the disk drive
+# $2 is name of file to load from.
+applySfdiskPartitions() {
+ local disk="$1"
+ local file="$2"
+ [[ -z $disk ]] && handleError "No disk passed (${FUNCNAME[0]})\n Args Passed: $*"
+ [[ -z $file ]] && handleError "No file to receive from passed (${FUNCNAME[0]})\n Args Passed: $*"
+ sfdisk $disk < $file >/dev/null 2>&1
+ [[ ! $? -eq 0 ]] && majorDebugEcho "sfdisk failed in (${FUNCNAME[0]})"
+}
+# $1 is the name of the disk drive
+# $2 is the name of file to load from.
+applySgdiskPartitions() {
+ local disk="$1"
+ local file="$2"
+ [[ -z $disk ]] && handleError "No disk passed (${FUNCNAME[0]})\n Args Passed: $*"
+ [[ -z $file ]] && handleError "No file to receive from passed (${FUNCNAME[0]})\n Args Passed: $*"
+ local escape_disk=$(escapeItem $disk)
+ local diskguid=$(awk -F: "/^$escape_disk:/{print \$3}" $file)
+ sgdisk -Z $disk >/dev/null 2>&1
+ [[ ! $? -eq 0 ]] && handleError "Failed to restore partitions (sgdisk -Z) (${FUNCNAME[0]})\n Args Passed: $*"
+ sgdisk -U $diskguid $disk >/dev/null 2>&1
+ [[ ! $? -eq 0 ]] && handleError "Failed to restore partitions (sgdisk -U) (${FUNCNAME[0]})\n Args Passed: $*"
+ local parts=""
+ local part=""
+ local part_number=""
+ local escape_part=""
+ local partstart=""
+ local partend=""
+ local parttype=""
+ local partcode=""
+ local partname=""
+ local awk_part_vars=""
+ getPartitions "$disk"
+ for part in $parts; do
+ escape_part=$(escapeItem $part)
+ getParititionNumber "$part"
+ awk_part_vars=$(awk -F: "/^$escape_part:/{printf(\"%d %d %d %d\",\$3,\$4,\$5,\$6)}" $file)
+ read partcode partstart partend partname <<< $awk_part_vars
+ parttype=$(awk -F: "/^part:$part_number:/{print \$5}" $file)
+ sgdisk -n $part_number:$partstart:$partend $disk >/dev/null 2>&1
+ [[ ! $? -eq 0 ]] && handleError "Failed to restore partition (sgdisk -n) (${FUNCNAME[0]})\n Args Passed: $*"
+ sgdisk -c $part_number:$partname $disk >/dev/null 2>&1
+ [[ ! $? -eq 0 ]] && handleError "Failed to restore partition (sgdisk -c) (${FUNCNAME[0]})\n Args Passed: $*"
+ sgdisk -t $part_number:$parttype $disk >/dev/null 2>&1
+ [[ ! $? -eq 0 ]] && handleError "Failed to restore partition (sgdisk -t) (${FUNCNAME[0]})\n Args Passed: $*"
+ sgdisk -u $part_number:$partcode $disk >/dev/null 2>&1
+ [[ ! $? -eq 0 ]] && handleError "Failed to restore partition (sgdisk -u) (${FUNCNAME[0]})\n Args Passed: $*"
+ done
+}
+# $1 is the name of the disk drive
+# $2 is name of file to load from.
+restoreSfdiskPartitions() {
+ local disk="$1"
+ local file="$2"
+ [[ -z $disk ]] && handleError "No disk passed (${FUNCNAME[0]})\n Args Passed: $*"
+ [[ -z $file ]] && handleError "No file to receive from passed (${FUNCNAME[0]})\n Args Passed: $*"
+ applySfdiskPartitions "$disk" "$file"
+ fdisk $disk < /usr/share/fog/lib/EOFRESTOREPART >/dev/null 2>&1
+ [[ ! $? -eq 0 ]] && majorDebugEcho "fdisk failed in (${FUNCNAME[0]})"
+}
+# $1 is the name of the disk drive
+# $2 is name of file to restore from.
+restoreSgdiskPartitions() {
+ local disk="$1"
+ local file="$2"
+ [[ -z $disk ]] && handleError "No disk passed (${FUNCNAME[0]})\n Args Passed: $*"
+ [[ -z $file ]] && handleError "No file to restore from (${FUNCNAME[0]})\n Args Passed: $*"
+ applySgdiskPartitions "$disk" "$file"
+}
+# $1 is the name of the disk drive
+hasExtendedPartition() {
+ local disk="$1"
+ [[ -z $disk ]] && handleError "No disk passed (${FUNCNAME[0]})\n Args Passed: $*"
+ sfdisk -d $disk 2>/dev/null | egrep '(Id|type)=\ *[5f]' | wc -l
+ [[ ! $? -eq 0 ]] && majorDebugEcho "sfdisk failed in (${FUNCNAME[0]})"
+}
+# $1 is the name of the partition device (e.g. /dev/sda3)
+partitionHasEBR() {
+ local part="$1"
+ [[ -z $part ]] && handleError "No partition passed (${FUNCNAME[0]})\n Args Passed: $*"
+ local part_number=0
+ local disk=""
+ local parttype=""
+ getDiskFromPartition "$part"
+ getPartitionNumber "$part"
+ getPartType "$part"
+ hasEBR=0
+ [[ $part_number -ge 5 ]] && hasEBR=1
+ [[ $parttype == +(0x5|0xf) ]] && hasEBR=1
+}
+# $1 is the name of the partition device (e.g. /dev/sda3)
+# $2 is the name of the file to save to (e.g. /net/dev/foo/d1p4.ebr)
+saveEBR() {
+ local part="$1"
+ local file="$2"
+ [[ -z $part ]] && handleError "No partition passed (${FUNCNAME[0]})\n Args Passed: $*"
+ [[ -z $file ]] && handleError "No file to receive from passed (${FUNCNAME[0]})\n Args Passed: $*"
+ local disk=""
+ getDiskFromPartition "$part"
+ local table_type=""
+ getPartitionTableType "$disk"
+ [[ $table_type != MBR ]] && return
+ local hasEBR=0
+ partitionHasEBR "$part"
+ [[ ! $hasEBR -gt 0 ]] && return
+ dots "Saving EBR for ($part)"
+ dd if=$part of=$file bs=512 count=1 >/dev/null 2>&1
+ case $? in
+ 0)
+ echo "Done"
+ debugPause
+ ;;
+ *)
+ echo "Failed"
+ debugPause
+ handleError "Could not backup EBR (${FUNCNAME[0]})\n Args Passed: $*"
+ ;;
+ esac
+}
+# $1 = DriveName (e.g. /dev/sdb)
+# $2 = DriveNumber (e.g. 1)
+# $3 = ImagePath (e.g. /net/foo)
+saveAllEBRs() {
+ local disk="$1"
+ local disk_number="$2"
+ local imagePath="$3"
+ [[ -z $disk ]] && handleError "No disk passed (${FUNCNAME[0]})\n Args Passed: $*"
+ [[ -z $disk_number ]] && handleError "No drive number passed (${FUNCNAME[0]})\n Args Passed: $*"
+ [[ -z $imagePath ]] && handleError "No image path passed (${FUNCNAME[0]})\n Args Passed: $*"
+ local parts=""
+ local part=""
+ local part_number=0
+ local ebrfilename=""
+ getPartitions "$disk"
+ for part in $parts; do
+ getPartitionNumber "$part"
+ EBRFileName "$imagePath" "$disk_number" "$part_number"
+ saveEBR "$part" "$ebrfilename"
+ done
+}
+# $1 is the name of the partition device (e.g. /dev/sda3)
+# $2 is the name of the file to restore from (e.g. /net/foo/d1p4.ebr)
+restoreEBR() {
+ local part="$1"
+ local file="$2"
+ [[ -z $part ]] && handleError "No disk passed (${FUNCNAME[0]})\n Args Passed: $*"
+ [[ -z $file ]] && handleError "No file to restore from passed (${FUNCNAME[0]})\n Args Passed: $*"
+ local disk=""
+ local table_type=""
+ getDiskFromPartition "$part"
+ getPartitionTableType "$disk"
+ [[ $table_type != MBR ]] && return
+ local hasEBR=0
+ partitionHasEBR "$part"
+ [[ ! $hasEBR -gt 0 ]] && return
+ [[ ! -e $file ]] && return
+ dots "Restoring EBR for ($part)"
+ dd of=$part if=$file bs=512 count=1 >/dev/null 2>&1
+ case $? in
+ 0)
+ echo "Done"
+ debugPause
+ ;;
+ *)
+ echo "Failed"
+ debugPause
+ handleError "Could not reload EBR data (${FUNCNAME[0]})\n Args Passed: $*"
+ ;;
+ esac
+}
+# $1 = DriveName (e.g. /dev/sdb)
+# $2 = DriveNumber (e.g. 1)
+# $3 = ImagePath (e.g. /net/foo)
+# $4 = ImagePartitionType (e.g. all, mbr, 1, 2, 3, etc.)
+restoreAllEBRs() {
+ local disk="$1"
+ local disk_number="$2"
+ local imagePath="$3"
+ local imgPartitionType="$4"
+ local ebffilename=""
+ [[ -z $disk ]] && handleError "No disk passed (${FUNCNAME[0]})\n Args Passed: $*"
+ [[ -z $disk_number ]] && handleError "No drive number passed (${FUNCNAME[0]})\n Args Passed: $*"
+ [[ -z $imagePath ]] && handleError "No image path passed (${FUNCNAME[0]})\n Args Passed: $*"
+ [[ -z $imgPartitionType ]] && handleError "No partition type passed (${FUNCNAME[0]})\n Args Passed: $*"
+ local parts=""
+ local part=""
+ local part_number=0
+ local ebrfilename=""
+ getPartitions "$disk"
+ for part in $parts; do
+ getPartitionNumber "$part"
+ [[ $imgPartitionType != all && $imgPartitionType != $part_number ]] && continue
+ EBRFileName "$imagePath" "$disk_number" "$part_number"
+ restoreEBR "$part" "$ebrfilename"
+ done
+ runPartprobe "$disk"
+}
+# $1 is the name of the partition device (e.g. /dev/sda3)
+partitionIsSwap() {
+ local part="$1"
+ [[ -z $part ]] && handleError "No partition passed (${FUNCNAME[0]})\n Args Passed: $*"
+ local fstype=""
+ fsTypeSetting "$part"
+ is_swap=0
+ [[ $fstype == swap ]] && is_swap=1
+}
+# $1 is the location of the file to store uuids in
+# $2 is the partition device name
+saveSwapUUID() {
+ local file="$1"
+ local part="$2"
+ [[ -z $part ]] && handleError "No partition passed (${FUNCNAME[0]})\n Args Passed: $*"
+ [[ -z $file ]] && handleError "No file to receive from passed (${FUNCNAME[0]})\n Args Passed: $*"
+ local is_swap=0
+ partitionIsSwap "$part"
+ [[ $is_swap -eq 0 ]] && return
+ local uuid=$(blkid -s UUID $2 | cut -d\" -f2)
+ [[ -z $uuid ]] && return
+ echo " * Saving UUID ($uuid) for ($part)"
+ echo "$part $uuid" >> $file
+}
+# Linux swap partition strategy:
+#
+# Upload:
+#
+# In "n" mode, the empty swapUUIDFileName is created first. Then as each
+# partition is saved, if it is swap then saveSwapUUID is called.
+# In "mps" and "mpa" mode, savePartition is called for each partition.
+# savePartition then calles saveSwapUUID if the partition is swap.
+#
+# When uploading an image, the swapUUIDFileName (e.g. /images/foo/d1.original.swapuuids)
+# is created. For $imgPartitionType == "all", all swap partition UUIDs are saved.
+# For $imgPartitionType == "$partnum", the partition's UUID is saved, if it is a swap partition.
+# For all others, the swapUUIDFileName will not exist, or will be empty.
+#
+#
+# Download:
+#
+# When downloading an image, makeAllSwapSystems will be called.
+# In "n" mode this is done for those images without special configurations,
+# after normal partition restoration.
+# In "mps" mode this is always done
+# In "mpa" mode this is always done, for all disks.
+# makeAllSwapSystems will determine using
+# $imagePartitionType == "all" or == "$partnum" whether to
+# process the swapUUIDFileName contents. For each matching partition,
+# mkswap is used, and the UUID is set appropriately.
+#
+# Relevant functions:
+# swapUUIDFileName ImagePath DriveNumber
+# echos the standardized name for the UUID filename
+# partitionIsSwap PartitionName
+# echos 1 or 0 if fsTypeSetting says partition is or is not a swap partition.
+# makeSwapSystem SwapUUIDFileName PartitionName
+# if it finds partition in UUID file, then calls mkswap
+# makeAllSwapSystems DriveName DriveNumber ImagePath ImagePartitionType
+# checks ImagePartitionType for a match before calling makeSwapSystem
+# saveSwapUUID SwapUUIDFileName PartitionName
+# checks if paritionIsSwap, if so, obtains UUID and saves it
+# saveAllSwapUUIDs DriveName DriveNumber ImagePath
+# checks all partitions if partitionIsSwap, calles saveSwapUUID
+# savePartition:
+# calls saveSwapUUID for swap partitions
+#
+#
+# $1 = DriveName (e.g. /dev/sdb)
+# $2 = DriveNumber (e.g. 1)
+# $3 = ImagePath (e.g. /net/foo)
+saveAllSwapUUIDs() {
+ local disk="$1"
+ local disk_number="$2"
+ local imagePath="$3"
+ [[ -z $disk ]] && handleError "No disk passed (${FUNCNAME[0]})\n Args Passed: $*"
+ [[ -z $disk_number ]] && handleError "No drive number passed (${FUNCNAME[0]})\n Args Passed: $*"
+ [[ -z $imagePath ]] && handleError "No image path passed (${FUNCNAME[0]})\n Args Passed: $*"
+ local swapuuidfilename=""
+ swapUUIDFileName "$imagePath" "$disk_number"
+ local parts=""
+ local part=""
+ local is_swap=0
+ getPartitions "$disk"
+ for part in $parts; do
+ partitionIsSwap "$part"
+ [[ $is_swap -eq 0 ]] && continue
+ saveSwapUUID "$swapuuidfilename" "$part"
+ done
+}
+# $1 is the location of the file uuids are stored in
+# $2 is the partition device name
+makeSwapSystem() {
+ local file="$1"
+ local part="$2"
+ [[ -z $part ]] && handleError "No partition passed (${FUNCNAME[0]})\n Args Passed: $*"
+ [[ -z $file ]] && handleError "No file passed (${FUNCNAME[0]})\n Args Passed: $*"
+ local uuid=""
+ local option=""
+ local disk=""
+ getDiskFromPartition "$part"
+ local parttype=0
+ local hasgpt=""
+ local escape_part=$(escapeItem $part)
+ hasGPT "$disk"
+ case $hasgpt in
+ 1)
+ uuid=$(awk "/^$escape_part/{print \$2}" $file)
+ [[ -n $uuid ]] && parttype=82
+ ;;
+ 0)
+ parttype=$(sfdisk -d $disk 2>/dev/null | awk -F[,=] "/^$escape_part/{print \$6}")
+ ;;
+ esac
+ [[ ! $parttype -eq 82 ]] && return
+ [[ -n $uuid ]] && option="-U $uuid"
+ dots "Restoring swap partition"
+ mkswap $option $part >/dev/null 2>&1
+ case $? in
+ 0)
+ echo "Done"
+ ;;
+ *)
+ echo "Failed"
+ debugPause
+ handleError "Could not create swap on $part (${FUNCNAME[0]})\n Args Passed: $*"
+ ;;
+ esac
+ debugPause
+}
+# $1 is the partition device (e.g. /dev/sda1)
+# $2 is the new desired size in 1024 (1k) blocks
+# $3 is the image path (e.g. /net/dev/foo)
+resizeSfdiskPartition() {
+ local part="$1"
+ local size="$2"
+ local imagePath="$3"
+ [[ -z $part ]] && handleError "No partition passed (${FUNCNAME[0]})\n Args Passed: $*"
+ [[ -z $size ]] && handleError "No desired size passed (${FUNCNAME[0]})\n Args Passed: $*"
+ [[ -z $imagePath ]] && handleError "No image path passed (${FUNCNAME[0]})\n Args Passed: $*"
+ local disk=""
+ getDiskFromPartition "$part"
+ local tmp_file="/tmp/sfdisk.$$"
+ local tmp_file2="/tmp/sfdisk2.$$"
+ saveSfdiskPartitions "$disk" "$tmp_file"
+ processSfdisk "$tmp_file" resize "$part" "$size" > "$tmp_file2"
+ if [[ $ismajordebug -gt 0 ]]; then
+ majorDebugEcho "Trying to fill the disk with these partitions:"
+ cat $tmp_file2
+ majorDebugPause
+ fi
+ applySfdiskPartitions "$disk" "$tmp_file2"
+ local sfdiskminimumpartitionfilename=""
+ sfdiskMinimumPartitionFileName "$imagePath" 1
+ saveSfdiskPartitions "$disk" "$imagePath"
+}
+# $1 is the disk device (e.g. /dev/sda)
+# $2 is the name of the original sfdisk -d output file used as a template
+# $3 is the : separated list of fixed size partitions (e.g. 1:2)
+# swap partitions are automatically added. Empty string is
+# ok.
+fillSfdiskWithPartitions() {
+ local disk="$1"
+ local file="$2"
+ local fixed="$3"
+ [[ -z $disk ]] && handleError "No disk passed (${FUNCNAME[0]})\n Args Passed: $*"
+ [[ -z $file ]] && handleError "No file to use passed (${FUNCNAME[0]})\n Args Passed: $*"
+ local disk_size=$(blockdev --getsize64 $disk | awk '{printf("%d\n",$1/1024);}')
+ local tmp_file2="/tmp/sfdisk2.$$"
+ processSfdisk "$file" filldisk "$disk" "$disk_size" "$fixed" > "$tmp_file2"
+ if [[ $ismajordebug -gt 0 ]]; then
+ majorDebugEcho "Trying to fill with the disk with these partititions:"
+ cat $tmp_file2
+ majorDebugPause
+ fi
+ [[ $? -eq 0 ]] && applySfdiskPartitions "$disk" "$tmp_file2"
+ runPartprobe "$disk"
+ rm -f $tmp_file2
+ majorDebugEcho "Applied the preceding table."
+ majorDebugShowCurrentPartitionTable "$disk" 1
+ majorDebugPause
+}
+#
+# processSfdisk() processes the output of sfdisk -d
+# and creates a new sfdisk -d like output, applying
+# the requested action. Read below to see the actions
+#
+# $1 the name of a file that is the output of sfdisk -d
+# $2 is the action "resize|other?"
+# $3 is the first parameter
+# $4 is the second parameter
+# ...
+#
+# actions:
+# processSfdisk foo.sfdisk resize /dev/sda1 100000
+# foo.sfdisk = sfdisk -d output
+# resize = action
+# /dev/sda1 = partition to modify
+# 100000 = 1024 byte blocks size to make it
+# output: new sfdisk -d like output
+#
+# processSfdisk foo.sfdisk move /dev/sda1 100000
+# foo.sfdisk = sfdisk -d output
+# move = action
+# /dev/sda1 = partition to modify
+# 100000 = 1024 byte blocks size to move it to
+# output: new sfdisk -d like output
+#
+# processSfdisk foo.sfdisk filldisk /dev/sda 100000 1:3:6
+# foo.sfdisk = sfdisk -d output
+# filldisk = action
+# /dev/sda = disk to modify
+# 100000 = 1024 byte blocks size of disk
+# 1:3:6 = partition numbers that are fixed in size, : separated
+# output: new sfdisk -d like output
+#
+# example file data
+# /dev/sda1 : start= 2048, size= 204800, Id= 7, bootable
+# /dev/sda2 : start= 206848, size= 50573312, Id= 7
+# /dev/sda3 : start= 50780160, size= 2048, Id=83
+# /dev/sda4 : start= 50784254, size= 16322562, Id= 5
+# /dev/sda5 : start= 50784256, size= 7811072, Id=83
+# /dev/sda6 : start= 58597376, size= 8509440, Id=82
+#
+processSfdisk() {
+ local data="$1"
+ local action="$2"
+ local target="$3"
+ local size="$4"
+ local fixed="$5"
+ [[ -z $data ]] && handleError "No data passed (${FUNCNAME[0]})\n Args Passed: $*"
+ [[ -z $action ]] && handleError "No action passed (${FUNCNAME[0]})\n Args Passed: $*"
+ [[ -z $target ]] && handleError "Device (disk or partition) not passed (${FUNCNAME[0]})\n Args Passed: $*"
+ [[ -z $size ]] && handleError "No desired size passed (${FUNCNAME[0]})\n Args Passed: $*"
+ local minstart=$(awk -F'[ ,]+' '/start/{if ($4) print $4}' $data | sort -n | head -1)
+ local chunksize=""
+ getPartBlockSize "$disk" "chunksize"
+ case $osid in
+ [1-2])
+ [[ -z $minstart ]] && chunksize=512
+ [[ -z $minstart ]] && minstart=63
+ ;;
+ esac
+ local awkArgs="-v CHUNK_SIZE=$chunksize -v MIN_START=$minstart"
+ awkArgs="$awkArgs -v action=$action -v target=$target -v sizePos=$size"
+ [[ -n $fixed ]] && awkArgs="$awkArgs -v fixedList=$fixed"
+ # process with external awk script
+ /usr/share/fog/lib/procsfdisk.awk $awkArgs $data
+}
+#
+# GPT Functions below
+#
+# $1 : device name of drive
+getPartitionTableType() {
+ local disk="$1"
+ [[ -z $disk ]] && handleError "No disk passed (${FUNCNAME[0]})\n Args Passed: $*"
+ local mbr=$(yes '' | gdisk -l $disk | awk '/^\ *MBR:/{print $2}')
+ local gpt=$(yes '' | gdisk -l $disk | awk '/^\ *GPT:/{print $2}')
+ local type=""
+ local mbrtype=""
+ local gpttype=""
+ case $mbr in
+ present|MBR)
+ mbrtype="MBR"
+ ;;
+ hybrid)
+ mbrtype="HYBRID"
+ ;;
+ protective|not)
+ mbrtype=""
+ ;;
+ esac
+ case $gpt in
+ present|damaged)
+ gpttype="GPT"
+ ;;
+ not)
+ gpttype=""
+ ;;
+ esac
+ [[ -z $gpttype && -z $mbrtype ]] && handleError "Cannot determine partition type (${FUNCNAME[0]})\n Args Passed: $*"
+ [[ -n $gpttype && -n $mbrtype ]] && table_type="$gpttype-$mbrtype"
+ [[ -n $gpttype && -z $mbrtype ]] && table_type="$gpttype"
+ [[ -z $gpttype && -n $mbrtype ]] && table_type="$mbrtype"
+}
+#
+# Detect the desired partition table type,
+# using the available files in imagePath, don't rely
+# on the actual disk.
+#
+# Assumes GPT or MBR. Uses first 8 bytes of second block
+# which should hold "EFI PART". (https://en.wikipedia.org/wiki/GUID_Partition_Table#Partition_table_header_.28LBA_1.29)
+#
+# $1 : imagePath (e.g. /images/foo)
+# $2 : disk number (e.g. 1)
+getDesiredPartitionTableType() {
+ local imagePath="$1"
+ local disk_number="$2"
+ [[ -z $disk_number ]] && handleError "No drive number passed (${FUNCNAME[0]})\n Args Passed: $*"
+ [[ -z $imagePath ]] && handleError "No image path passed (${FUNCNAME[0]})\n Args Passed: $*"
+ table_type="MBR"
+ local mbrfilename=""
+ MBRFileName "$imagePath" "$disk_number" "mbrfilename"
+ [[ ! -r $mbrfilename ]] && return
+ local tmpfile="/tmp/gptsig"
+ dd skip=512 bs=1 if=$mbrfilename of=$tmpfile count=8 >/dev/null 2>&1
+ touch $tmpfile
+ local gptsig=$(cat $tmpfile)
+ [[ $gptsig == "EFI PART" ]] && table_type="GPT"
+}
+# $1 : device name of drive
+hasHybridMBR() {
+ local disk="$1"
+ [[ -z $disk ]] && handleError "No disk passed (${FUNCNAME[0]})\n Args Passed: $*"
+ local mbr=$(gdisk -l $disk | awk '/^\ *MBR:/{print $2}')
+ [[ $mbr == hybrid ]] && echo 1 || echo 0
+}
+# $1 : device name of drive
+hasGPT() {
+ local disk="$1"
+ [[ -z $disk ]] && handleError "No disk passed (${FUNCNAME[0]})\n Args Passed: $*"
+ local gpt=$(gdisk -l $disk | awk -F'[(: )]' '/GPT:/ {print $5}')
+ [[ $gpt == present ]] && hasgpt=1
+ [[ $gpt == not ]] && hasgpt=0
+}
+#
+# Detect the partition table type, then call the correct
+# resizePartition function
+#
+# $1 is the partition device (e.g. /dev/sda1)
+# $2 is the new desired size in 1024 (1k) blocks
+# $3 is the image path (e.g. /net/dev/foo)
+resizePartition() {
+ local part="$1"
+ local size="$2"
+ local imagePath="$3"
+ [[ -z $part ]] && handleError "No partition passed (${FUNCNAME[0]})\n Args Passed: $*"
+ [[ -z $size ]] && handleError "No size passed (${FUNCNAME[0]})\n Args Passed: $*"
+ [[ -z $imagePath ]] && handleError "No image path passed (${FUNCNAME[0]})\n Args Passed: $*"
+ local disk=""
+ local table_type=""
+ getDiskFromPartition "$part"
+ getPartitionTableType "$disk"
+ case $table_type in
+ MBR|GPT)
+ local sfdiskoriginalpartitionfilename=""
+ local sfdisklegacyoriginalpartitionfilename=""
+ resizeSfdiskPartition "$part" "$size" "$imagePath"
+ ;;
+ *)
+ handleError "Unexpected partition table type: $table_type (${FUNCNAME[0]})\n Args Passed: $*"
+ ;;
+ esac
+ # make sure kernel knows about the changes
+ runPartprobe "$disk"
+}
+#
+# Detect the partition table type, then save all relevant
+# partition information
+#
+# $1 : device name of the drive
+# $2 : imagePath
+# $3 : disk number
+saveOriginalPartitions() {
+ local disk="$1"
+ local imagePath="$2"
+ local disk_number="$3"
+ [[ -z $disk ]] && handleError "No disk passed (${FUNCNAME[0]})\n Args Passed: $*"
+ [[ -z $imagePath ]] && handleError "No image path passed (${FUNCNAME[0]})\n Args Passed: $*"
+ [[ -z $disk_number ]] && handleError "No disk number passed (${FUNCNAME[0]})\n Args Passed: $*"
+ local table_type=""
+ getPartitionTableType "$disk"
+ case $table_type in
+ MBR|GPT)
+ local sfdiskoriginalpartitionfilename=""
+ sfdiskOriginalPartitionFileName "$imagePath" "$disk_number"
+ saveSfdiskPartitions "$disk" "$sfdiskoriginalpartitionfilename"
+ ;;
+ GPT-MBR)
+ echo "Failed"
+ debugPause
+ runFixparts "$disk"
+ dots "Retrying to save partition table"
+ saveOriginalPartitions "$disk" "$imagePath" "$disk_number"
+ ;;
+ *)
+ echo "Failed"
+ debugPause
+ handleError "Unexpected partition table type: $table_type (${FUNCNAME[0]})\n Args Passed: $*"
+ ;;
+ esac
+ runPartprobe "$disk"
+}
+#
+# Detect the partition table type, then restore partition
+# sizes, using saved partition information
+#
+# $1 : device name of the drive
+# $2 : imagePath
+# $3 : disk number
+restoreOriginalPartitions() {
+ local disk="$1"
+ local imagePath="$2"
+ local disk_number="$3"
+ [[ -z $disk ]] && handleError "No disk passed (${FUNCNAME[0]})\n Args Passed: $*"
+ [[ -z $imagePath ]] && handleError "No image path passed (${FUNCNAME[0]})\n Args Passed: $*"
+ [[ -z $disk_number ]] && handleError "No disk number passed (${FUNCNAME[0]})\n Args Passed: $*"
+ local table_type=""
+ getPartitionTableType "$disk"
+ case $table_type in
+ MBR|GPT)
+ local sfdiskoriginalpartitionfilename=""
+ local sfdisklegacyoriginalpartitionfilename=""
+ local sgdiskoriginalpartitionfilename=""
+ local cmdtorun='restoreSfdiskPartitions'
+ sfdiskOriginalPartitionFileName "$imagePath" "$disk_number"
+ sfdiskLegacyOriginalPartitionFileName "$imagePath" "$disk_number"
+ sgdiskOriginalPartitionFileName "$imagePath" "$disk_number"
+ local filename="$sfdiskoriginalpartitionfilename"
+ [[ ! -r $filename ]] && filename="$sfdisklegacyoriginalpartitionfilename"
+ [[ ! -r $filename ]] && filename="$sgdiskoriginalpartitionfilename" && cmdtorun='restoreSgdiskPartitions'
+ [[ ! -r $filename ]] && handleError "Failed to find a restore file (${FUNCNAME[0]})\n Args Passed: $*"
+ $cmdtorun "$disk" "$filename"
+ ;;
+ *)
+ handleError "Unexpected partition table type: $table_type (${FUNCNAME[0]})\n Args Passed: $*"
+ ;;
+ esac
+ # make sure kernel knows about the changes
+ runPartprobe "$disk"
+}
+#
+# Detect the partition table type, the fill the disk with
+# the partitions, using the correct routine.
+#
+# $1 : the disk device (e.g. /dev/sda)
+# $2 : imagePath (e.g. /images/foo)
+# $3 : disk number (e.g. 1)
+fillDiskWithPartitions() {
+ local disk="$1"
+ local imagePath="$2"
+ local disk_number="$3"
+ [[ -z $disk ]] && handleError "No disk passed (${FUNCNAME[0]})\n Args Passed: $*"
+ [[ -z $imagePath ]] && handleError "No image path passed (${FUNCNAME[0]})\n Args Passed: $*"
+ [[ -z $disk_number ]] && handleError "No disk number passed (${FUNCNAME[0]})\n Args Passed: $*"
+ local fixed_size_file=""
+ fixedSizePartitionsFileName "$imagePath" "$disk_number"
+ [[ -r $fixed_size_file ]] && fixed_size_partitions=$(cat $fixed_size_file)
+ local table_type=""
+ getDesiredPartitionTableType "$imagePath" "$disk_number"
+ local sfdiskoriginalpartitionfilename=""
+ local sfdisklegacyoriginalpartitionfilename=""
+ local sgdiskoriginalpartitionfilename=""
+ case $table_type in
+ MBR|GPT)
+ sfdiskOriginalPartitionFileName "$imagePath" "$disk_number"
+ sfdiskLegacyOriginalPartitionFileName "$imagePath" "$disk_number"
+ sgdiskOriginalPartitionFileName "$imagePath" "$disk_number"
+ local filename="$sfdiskoriginalpartitionfilename"
+ local cmdtorun='fillSfdiskWithPartitions'
+ [[ ! -r $filename ]] && filename="$sfdisklegacyoriginalpartitionfilename"
+ [[ ! -r $filename ]] && filename="$sgdiskoriginalpartitionfilename"
+ [[ $filename == $sgdiskoriginalpartitionfilename ]] && cmdtorun='fillSgdiskWithPartitions'
+ [[ ! -r $filename ]] && handleError "Failed to find a restore file (${FUNCNAME[0]})\n Args Passed: $*"
+ $cmdtorun "$disk" "$filename" "$fixed_size_partitions"
+ ;;
+ *)
+ echo "Failed"
+ debugPause
+ handleError "Unexpected partition table type: $table_type (${FUNCNAME[0]})\n Args Passed: $*"
+ ;;
+ esac
+ # make sure kernel knows about the changes
+ runPartprobe "$disk"
+}
+#
+# Check if it will be ok to call fillDiskWithPartitions
+#
+# $1 : the disk device (e.g. /dev/sda)
+# $2 : imagePath (e.g. /images/foo)
+# $3 : disk number (e.g. 1)
+fillDiskWithPartitionsIsOK() {
+ local disk="$1"
+ local imagePath="$2"
+ local disk_number="$3"
+ [[ -z $disk ]] && handleError "No disk passed (${FUNCNAME[0]})\n Args Passed: $*"
+ [[ -z $imagePath ]] && handleError "No image path passed (${FUNCNAME[0]})\n Args Passed: $*"
+ [[ -z $disk_number ]] && handleError "No disk number passed (${FUNCNAME[0]})\n Args Passed: $*"
+ local table_type=""
+ getDesiredPartitionTableType "$imagePath" "$disk_number"
+ local filename=""
+ local sfdiskoriginalpartitionfilename=""
+ local sfdisklegacyoriginalpartitionfilename=""
+ local sgdiskoriginalpartitionfilename=""
+ do_fill=1
+ case $table_type in
+ MBR|GPT)
+ sfdiskOriginalPartitionFileName "$imagePath" "$disk_number"
+ sfdiskLegacyOriginalPartitionFileName "$imagePath" "$disk_number"
+ sgdiskOriginalPartitionFileName "$imagePath" "$disk_number"
+ filename="$sfdiskoriginalpartitionfilename"
+ [[ ! -r $filename ]] && filename="$sfdisklegacyoriginalpartitionfilename"
+ [[ ! -r $filename ]] && filename="$sgdiskoriginalpartitionfilename"
+ [[ ! -r $filename ]] && do_fill=0
+ ;;
+ esac
+}
+#
+# Show the current partition table
+#
+# $1 : the disk device (e.g. /dev/sda)
+# $2 : disk number (e.g. 1)
+majorDebugShowCurrentPartitionTable() {
+ [[ $ismajordebug -le 0 ]] && return
+ local disk="$1"
+ local disk_number="$2"
+ [[ -z $disk ]] && handleError "No disk passed (${FUNCNAME[0]})\n Args Passed: $*"
+ [[ -z $disk_number ]] && handleError "No disk number passed (${FUNCNAME[0]})\n Args Passed: $*"
+ local table_type=""
+ getDesiredPartitionTableType "$imagePath" "$disk_number"
+ echo "Current partition table:"
+ case $table_type in
+ MBR|GPT)
+ sfdisk -d $disk
+ ;;
+ esac
+}
diff --git a/draft/other-tools/fog/procsfdisk.awk b/draft/other-tools/fog/procsfdisk.awk
new file mode 100644
index 0000000..ac18749
--- /dev/null
+++ b/draft/other-tools/fog/procsfdisk.awk
@@ -0,0 +1,361 @@
+#!/usr/bin/awk -f
+
+#$data is the filename of the output of sfdisk -d
+
+#cat $data | awk -F, '\
+
+# For readability, function parameters are on the first line. Locally scoped
+# variables are on the following lines.
+
+function display_output(partition_names, partitions, \
+ pName) {
+ if (!unit) {
+ unit = "sectors";
+ }
+ if (!label) {
+ type = "Id=";
+ } else {
+ type = "type=";
+ }
+ if (label && labelid && device) {
+ printf("label: %s\n", label);
+ printf("label-id: %s\n", labelid);
+ printf("device: %s\n", device);
+ }
+ printf("unit: %s\n\n", unit);
+ for(pName in partition_names) {
+ printf("%s : start=%10d, size=%10d, %s%2s", partitions[pName, "device"], partitions[pName, "start"], partitions[pName, "size"],
+ type, partitions[pName, "type"]);
+ if(label == "dos") {
+ if(partitions[pName, "flags"] != "") {
+ printf("%s", partitions[pName, "flags"]);
+ }
+ } else if (label == "gpt") {
+ if(partitions[pName, "uuid"] != "") {
+ printf(", uuid=%s", partitions[pName, "uuid"]);
+ }
+ if(partitions[pName, "name"] != "") {
+ printf(", name=%s", partitions[pName, "name"]);
+ }
+ if(partitions[pName, "attrs"] != "") {
+ printf(", attrs=%s", partitions[pName, "attrs"]);
+ }
+ } else {
+ if(partitions[pName, "flags"] != "") {
+ printf("%s", partitions[pName, "flags"]);
+ }
+ }
+ printf("\n");
+ }
+}
+
+function check_overlap(partition_names, partitions, new_part_name, new_start, new_size, \
+ extended_margin, new_type, new_part_number, pName, p_type, p_start, p_size, p_part_number) {
+ extended_margin = 2;
+ new_type = partitions[new_part_name, "type"];
+ new_start = new_start + 0;
+ new_size = new_size + 0;
+ new_part_number = partitions[new_part_name, "number"] + 0;
+ for(pName in partition_names) {
+ p_type = partitions[pName, "type"];
+ p_start = partitions[pName, "start"] + 0;
+ p_size = partitions[pName, "size"] + 0;
+ p_part_number = partitions[pName, "number"] + 0;
+ # no overlap with self
+ if(new_part_name == pName) { continue; }
+ # ignore empty partitions
+ if(p_size == 0) { continue; }
+ # extended partitions must overlap logical partitions, but leave room for the extended partition table
+ if((p_type == "5" || p_type == "f") && (new_part_number >= 5)) {
+ # new_start is outside of [p_start+margin, p_start + p_size) OR
+ # new_start + new_size is outside of (p_start+margin, p_start + p_size]
+ if((new_start < p_start + extended_margin || new_start >= p_start + p_size) || (new_start + new_size <= p_start + extended_margin || new_start + new_size > p_start + p_size)) {
+ return 1;
+ }
+ }
+ # extended partitions must overlap logical partitions, but leave room for the extended partition table
+ else if((new_type == "5" || new_type == "f") && (p_part_number >= 5)) {
+ # logical partition must be contained in extended partition
+ # p_start is outside of [new_start+margin, new_start + new_size) OR
+ # p_start + p_size is outside of (new_start+margin, new_start + new_size]
+ if((p_start < new_start + extended_margin || p_start >= new_start + new_size) || (p_start + p_size <= new_start + extended_margin || p_start + p_size > new_start + new_size)) {
+ return 1;
+ }
+ }
+ # all other overlap possibilities
+ else {
+ # new_start is inside of [p_start, p_start + p_size) OR
+ # new_start + new_size is inside of (p_start, p_start + p_size]
+ if((new_start >= p_start && new_start < p_start + p_size) || (new_start + new_size > p_start && new_start + new_size <= p_start + p_size)) {
+ return 1;
+ }
+ # p_start is inside of [new_start, new_start + new_size) OR
+ # p_start + p_size is inside of (new_start, new_start + new_size]
+ if((p_start >= new_start && p_start < new_start + new_size) || (p_start + p_size > new_start && p_start + p_size <= new_start + new_size)) {
+ return 1;
+ }
+ }
+ }
+ return 0;
+}
+
+function check_all_partitions(partition_names, partitions, \
+ pName, p_start, p_size) {
+ for(pName in partition_names) {
+ p_start = partitions[pName, "start"] + 0;
+ p_size = partitions[pName, "size"] + 0;
+ if(check_overlap(partition_names, partitions, pName, p_start, p_size) != 0) {
+ printf("ERROR in new partition table, quitting.\n");
+ printf("ERROR: %s has an overlap.\n", pName);
+ #exit(1);
+ }
+ }
+ printf("# Partition table is consistent.\n");
+}
+
+function resize_partition(partition_names, partitions, args, \
+ pName, new_start, new_size) {
+ for(pName in partition_names) {
+ if(pName == target) {
+ if(unit == "sectors") {
+ new_start = partitions[pName, "start"];
+ new_size = sizePos*2;
+ if(check_overlap(partition_names, partitions, target, new_start, new_size) == 0) {
+ partitions[target, "start"] = new_start;
+ partitions[target, "size"] = new_size;
+ }
+ }
+ }
+ }
+}
+
+function move_partition(partition_names, partitions, args, \
+ pName, new_start, new_size) {
+ for(pName in partition_names) {
+ if(pName == target) {
+ if(unit == "sectors") {
+ new_start = (sizePos*2);
+ new_start = new_start - new_start % CHUNK_SIZE;
+ if(new_start < MIN_START) { new_start = MIN_START; }
+ new_size = partitions[pName, "size"];
+ if(check_overlap(partition_names, partitions, target, new_start, new_size) == 0) {
+ partitions[target, "start"] = new_start;
+ partitions[target, "size"] = new_size;
+ }
+ }
+ }
+ }
+}
+
+function fill_disk(partition_names, partitions, args, \
+ disk, disk_size, n, fixed_partitions, original_variable, \
+ original_fixed, new_variable, new_fixed, new_logical, pName, \
+ p_type, p_number, p_size, found, i, partition_starts, \
+ ordered_starts, old_sorted_in, curr_start) {
+ # processSfdisk foo.sfdisk filldisk /dev/sda 100000 1:3:6
+ # foo.sfdisk = sfdisk -d output
+ # filldisk = action
+ # /dev/sda = disk to modify
+ # 100000 = 1024 byte blocks size of disk
+ # 1:3:6 = partition numbers that are fixed in size, : separated
+ disk = target;
+ disk_size = sizePos*2;
+ # add swap partitions to the fixed list
+ for(pName in partition_names) {
+ p_type = partitions[pName, "type"];
+ p_number = partitions[pName, "number"] + "";
+ if(p_type == "82") {
+ fixedList = fixedList ":" p_number;
+ }
+ }
+ n = split(fixedList, fixed_partitions, ":");
+ #
+ # Find the total fixed and variable space
+ #
+ original_variable = 0;
+ original_fixed = MIN_START;
+ for(pName in partition_names) {
+ p_type = partitions[pName, "type"];
+ p_number = partitions[pName, "number"] + 0;
+ p_size = partitions[pName, "size"] + 0;
+ partition_starts[partitions[pName, "start"] + 0] = pName;
+ # skip extended partition, only count its logicals and the CHUNK for its partition table
+ if(p_type == "5" || p_type == "f") {
+ original_fixed += CHUNK_SIZE;
+ continue;
+ }
+ # + CHUNK_SIZE to allow for margin after each logical partition (required if 2 or more logical partitions exist)
+ if(p_number >= 5) {
+ original_fixed += CHUNK_SIZE;
+ }
+ if(p_size == 0) { fixed_partitions[pName] = p_number; };
+ found = 0; for(i in fixed_partitions) { if(fixed_partitions[i] == p_number) { found = 1; } };
+ if(found) {
+ original_fixed += partitions[pName, "size"];
+ } else {
+ original_variable += partitions[pName, "size"];
+ }
+ }
+ #
+ # Assign the new sizes to partitions
+ #
+ new_fixed = original_fixed;
+ new_variable = disk_size - original_fixed;
+ new_logical = 0;
+ for(pName in partition_names) {
+ p_type = partitions[pName, "type"];
+ p_number = partitions[pName, "number"] + 0;
+ p_size = partitions[pName, "size"] + 0;
+ found = 0;
+ for(i in fixed_partitions) {
+ if(fixed_partitions[i] == p_number) {
+ found = 1;
+ }
+ };
+ if(p_type == "5" || p_type == "f") {
+ partitions[pName, "newsize"] = CHUNK_SIZE;
+ partitions[pName, "size"] = partitions[pName, "newsize"] - partitions[pName, "newsize"] % CHUNK_SIZE;
+ } else if(found) {
+ partitions[pName, "newsize"] = p_size;
+ partitions[pName, "size"] = partitions[pName, "newsize"];
+ } else {
+ partitions[pName, "newsize"] = (new_variable*p_size/original_variable);
+ partitions[pName, "size"] = partitions[pName, "newsize"] - partitions[pName, "newsize"] % CHUNK_SIZE;
+ }
+ if(p_number >= 5) {
+ # + CHUNK_SIZE to allow for margin after each logical partition (required if 2 or more logical partitions exist)
+ new_logical += partitions[pName, "size"] + CHUNK_SIZE;
+ }
+ }
+ #
+ # Assign the new size to the extended partition
+ #
+ for(pName in partition_names) {
+ p_type = partitions[pName, "type"];
+ p_number = partitions[pName, "number"] + 0;
+ p_size = partitions[pName, "size"] + 0;
+ if(p_type == "5" || p_type == "f") {
+ partitions[pName, "newsize"] += new_logical;
+ partitions[pName, "size"] = partitions[pName, "newsize"] - partitions[pName, "newsize"] % CHUNK_SIZE;
+ }
+ }
+ #
+ # Assign the new start positions
+ #
+ asort(partition_starts, ordered_starts, "@ind_num_asc");
+ old_sorted_in = PROCINFO["sorted_in"];
+ PROCINFO["sorted_in"] = "@ind_num_asc";
+ curr_start = MIN_START;
+ for(i in ordered_starts) {
+ pName = ordered_starts[i];
+ p_type = partitions[pName, "type"];
+ p_number = partitions[pName, "number"] + 0;
+ p_size = partitions[pName, "size"] + 0;
+ p_start = partitions[pName, "start"] + 0;
+ for (j in fixed_partitions) {
+ if (fixed_partitions[j] == p_number) {
+ curr_start = p_start;
+ }
+ }
+ if(p_size > 0) {
+ partitions[pName, "start"] = curr_start;
+ }
+ if(p_type == "5" || p_type == "f") {
+ curr_start += CHUNK_SIZE;
+ } else {
+ curr_start += p_size;
+ }
+ # + CHUNK_SIZE to allow for margin after each logical partition (required if 2 or more logical partitions exist)
+ if(p_number >= 5) {
+ curr_start += CHUNK_SIZE;
+ }
+ }
+ PROCINFO["sorted_in"] = old_sorted_in;
+ check_all_partitions(partition_names, partitions);
+}
+
+BEGIN{
+ #Arguments - Use "-v var=val" when calling this script
+ #CHUNK_SIZE;
+ #MIN_START;
+ #action;
+ #target;
+ #sizePos;
+ #fixedList;
+ label = "";
+ unit = "";
+ partitions[0] = "";
+ partition_names[0] = "";
+}
+
+/^label:/{ label = $2 }
+/^label-id:/{ labelid = $2 }
+/^device:/{ device = $2 }
+/^unit:/{ unit = $2; }
+
+/start=/{
+ # Get Partition Name
+ part_name=$1
+ partitions[part_name, "device"] = part_name
+ partition_names[part_name] = part_name
+
+ # Isolate Partition Number
+ # The regex can handle devices like mmcblk0p3
+ part_number = gensub(/^[^0-9]*[0-9]*[^0-9]+/, "", 1, part_name)
+ partitions[part_name, "number"] = part_number
+
+ # Separate attributes
+ split($0, fields, ",")
+
+ # Get start value
+ gsub(/.*start= */, "", fields[1])
+ partitions[part_name, "start"] = fields[1]
+ # Get size value
+ gsub(/.*size= */, "", fields[2])
+ partitions[part_name, "size"] = fields[2]
+ # Get type/id value
+ gsub(/.*(type|Id)= */, "", fields[3])
+ partitions[part_name, "type"] = fields[3]
+
+ if ( label == "dos" )
+ {
+ split($0, typeList, "type=")
+ part_flags = gensub(/^[^\,$]*/, "",1,typeList[2])
+ partitions[part_name, "flags"] = part_flags;
+ }
+ # GPT elements
+ else if ( label == "gpt" )
+ {
+ # Get uuid value
+ gsub(/.*uuid= */, "", fields[4])
+ partitions[part_name, "uuid"] = fields[4]
+ # Get name value
+ gsub(/.*name= */, "", fields[5])
+ partitions[part_name, "name"] = fields[5]
+ # Get attrs value
+ if (fields[6])
+ {
+ gsub(/.*attrs= */, "", fields[6])
+ partitions[part_name, "attrs"] = fields[6]
+ }
+ }
+ else
+ {
+ split($0, typeList, "Id=")
+ part_flags = gensub(/^[^\,$]*/, "",1,typeList[2])
+ partitions[part_name, "flags"] = part_flags;
+ }
+}
+
+END{
+ delete partitions[0];
+ delete partition_names[0];
+ if(action == "resize") {
+ resize_partition(partition_names, partitions, args);
+ } else if(action == "move") {
+ move_partition(partition_names, partitions, args);
+ } else if(action == "filldisk") {
+ fill_disk(partition_names, partitions, args);
+ }
+ display_output(partition_names, partitions);
+}
diff --git a/draft/other-tools/to-see.txt b/draft/other-tools/to-see.txt
new file mode 100644
index 0000000..926e036
--- /dev/null
+++ b/draft/other-tools/to-see.txt
@@ -0,0 +1,14 @@
+
+http://www.drbl-winroll.org/
+https://wiki.archlinux.org/index.php/Fdisk
+sfdisk -d /dev/sda > sda.dump
+sfdisk /dev/sda < sda.dump
+
+sgdisk -b=sgdisk-sda.bak
+sgdisk -l=sgdisk-sda.bak
+
+If both drives will be in the same computer, you need to randomize the GUID's:
+# sgdisk -G /dev/sdc
+
+
+
diff --git a/draft/uftp-push.sh b/draft/uftp-push.sh
new file mode 100755
index 0000000..ca82c13
--- /dev/null
+++ b/draft/uftp-push.sh
@@ -0,0 +1,8 @@
+IMGDIR=/home/lpouzenc/git/eficast/testimage2
+#RATE_OPTS="-R90000 -W 400 -s 20"
+RATE_OPTS="-C tfmcc -s 20"
+LOG_OPTS="-x2 -S /tmp/uftpd.csv"
+ADDR_OPTS="-M ff02::42 -P ff02::43 -I 3/6" # -I 3/6 is br1 ipv6
+cd /tmp/
+/home/lpouzenc/git/eficast/work/uftp/uftp -f $RATE_OPTS $LOG_OPTS $ADDR_OPTS -- $IMGDIR/*
+
diff --git a/draft/uftp/bug-ts.txt b/draft/uftp/bug-ts.txt
new file mode 100644
index 0000000..304865b
--- /dev/null
+++ b/draft/uftp/bug-ts.txt
@@ -0,0 +1,495 @@
+lpouzenc@lud-x200s:~/git/eficast/uftp/uftp-4.9.2$ ./uftp -f -C tfmcc -s 20 -x2 -S /tmp/uftpd.csv -M ff02::42 -P ff02::43 -I 3/6 -- /home/lpouzenc/git/eficast/testimage/10-restore-mbr.00.sh /home/lpouzenc/git/eficast/testimage/10-restore-mbr.aa /home/lpouzenc/git/eficast/testimage/20-partclone-sda2.img.00.sh /home/lpouzenc/git/eficast/testimage/20-partclone-sda2.img.aa /home/lpouzenc/git/eficast/testimage/20-partclone-sda2.img.ab /home/lpouzenc/git/eficast/testimage/20-partclone-sda2.img.ac /home/lpouzenc/git/eficast/testimage/20-partclone-sda2.img.ad /home/lpouzenc/git/eficast/testimage/20-partclone-sda2.img.ae /home/lpouzenc/git/eficast/testimage/20-partclone-sda2.img.af /home/lpouzenc/git/eficast/testimage/20-partclone-sda2.img.ag /home/lpouzenc/git/eficast/testimage/20-partclone-sda2.img.ah /home/lpouzenc/git/eficast/testimage/99-end
+
+
+UFTP version 4.9.2 Copyright (C) 2001-2016 Dennis A. Bush
+Starting at Mon Dec 19 14:41:09 2016
+Transfer rate: dynamic via TFMCC
+Using private multicast address ff02::43 Group ID: 1AAE76CE
+Initializing group
+Sending ANNOUNCE 1
+Received REGISTER from client 0x7AECB2FE
+Sending REG_CONF 2.1
+Sending ANNOUNCE 2
+Sending ANNOUNCE 3
+Sending ANNOUNCE 4
+Sending ANNOUNCE 5
+Sending ANNOUNCE 6
+Sending ANNOUNCE 7
+Sending ANNOUNCE 8
+Sending ANNOUNCE 9
+Sending ANNOUNCE 10
+Sending ANNOUNCE 11
+Sending ANNOUNCE 12
+Sending ANNOUNCE 13
+Sending ANNOUNCE 14
+Sending ANNOUNCE 15
+Sending ANNOUNCE 16
+Sending ANNOUNCE 17
+Sending ANNOUNCE 18
+Sending ANNOUNCE 19
+Sending ANNOUNCE 20
+----- 10-restore-mbr.00.sh -----
+File ID: 0001 Name: 10-restore-mbr.00.sh
+ sending as: 10-restore-mbr.00.sh
+Bytes: 50 Blocks: 1 Sections: 1
+Sending FILEINFO 1.1
+Received FILEINFO_ACK from client 0x7AECB2FE
+Sending file
+Starting pass 1
+Sending section 0
+Sending DONE 1.1
+Got COMPLETE from client 0x7AECB2FE
+Transfer status:
+Host: 0x7AECB2FE Status: Completed time: 0.016 seconds
+Total elapsed time: 0.016 seconds
+Overall throughput: 2.99 KB/s
+----- 10-restore-mbr.aa -----
+File ID: 0002 Name: 10-restore-mbr.aa
+ sending as: 10-restore-mbr.aa
+Bytes: 512 Blocks: 1 Sections: 1
+Sending FILEINFO 1.1
+Received FILEINFO_ACK from client 0x7AECB2FE
+Sending file
+Starting pass 1
+Sending section 0
+Sending DONE 1.1
+Got COMPLETE from client 0x7AECB2FE
+Transfer status:
+Host: 0x7AECB2FE Status: Completed time: 0.078 seconds
+Total elapsed time: 0.078 seconds
+Overall throughput: 6.39 KB/s
+----- 20-partclone-sda2.img.00.sh -----
+File ID: 0003 Name: 20-partclone-sda2.img.00.sh
+ sending as: 20-partclone-sda2.img.00.sh
+Bytes: 112 Blocks: 1 Sections: 1
+Sending FILEINFO 1.1
+Received FILEINFO_ACK from client 0x7AECB2FE
+Sending file
+Starting pass 1
+Sending section 0
+Sending DONE 1.1
+Got COMPLETE from client 0x7AECB2FE
+Transfer status:
+Host: 0x7AECB2FE Status: Completed time: 0.134 seconds
+Total elapsed time: 0.134 seconds
+Overall throughput: 0.81 KB/s
+----- 20-partclone-sda2.img.aa -----
+File ID: 0004 Name: 20-partclone-sda2.img.aa
+ sending as: 20-partclone-sda2.img.aa
+Bytes: 104857600 Blocks: 80660 Sections: 8
+Sending FILEINFO 1.1
+Received FILEINFO_ACK from client 0x7AECB2FE
+Sending file
+Starting pass 1
+Sending section 0
+Sending section 1
+Got 412 NAKs for section 0 from client 0x7AECB2FE
+Sending section 2
+Sending section 3
+Got 181 NAKs for section 2 from client 0x7AECB2FE
+Sending section 4
+Got 192 NAKs for section 3 from client 0x7AECB2FE
+Sending section 5
+Got 233 NAKs for section 4 from client 0x7AECB2FE
+Sending section 6
+Got 584 NAKs for section 5 from client 0x7AECB2FE
+Sending section 7
+Got 32 NAKs for section 6 from client 0x7AECB2FE
+Sending DONE 1.1
+Got 192 NAKs for section 7 from client 0x7AECB2FE
+Sending DONE 2.1
+Starting pass 2
+Sending section 0
+Sending section 2
+Sending section 3
+Sending section 4
+Sending section 5
+Got 46 NAKs for section 0 from client 0x7AECB2FE
+Got 104 NAKs for section 3 from client 0x7AECB2FE
+Sending section 6
+Sending section 7
+Sending DONE 1.1
+Got 120 NAKs for section 4 from client 0x7AECB2FE
+Got 13 NAKs for section 7 from client 0x7AECB2FE
+Sending DONE 2.1
+Starting pass 3
+Sending section 0
+Sending section 3
+Sending section 4
+Sending section 7
+Sending DONE 1.1
+Got 87 NAKs for section 2 from client 0x7AECB2FE
+Sending DONE 2.1
+Starting pass 4
+Sending section 2
+Sending DONE 1.1
+Got 348 NAKs for section 5 from client 0x7AECB2FE
+Got 21 NAKs for section 6 from client 0x7AECB2FE
+Sending DONE 2.1
+Starting pass 5
+Sending section 5
+Sending section 6
+Sending DONE 1.1
+Sending DONE 2.1
+Got 158 NAKs for section 5 from client 0x7AECB2FE
+Got 21 NAKs for section 6 from client 0x7AECB2FE
+Sending DONE 3.1
+Starting pass 6
+Sending section 5
+Sending section 6
+Sending DONE 1.1
+Got COMPLETE from client 0x7AECB2FE
+Transfer status:
+Host: 0x7AECB2FE Status: Completed time: 24.500 seconds
+Total elapsed time: 24.500 seconds
+Overall throughput: 4179.58 KB/s
+----- 20-partclone-sda2.img.ab -----
+File ID: 0005 Name: 20-partclone-sda2.img.ab
+ sending as: 20-partclone-sda2.img.ab
+Bytes: 104857600 Blocks: 80660 Sections: 8
+Sending FILEINFO 1.1
+Received FILEINFO_ACK from client 0x7AECB2FE
+Sending file
+Starting pass 1
+Sending section 0
+Sending section 1
+Got 217 NAKs for section 0 from client 0x7AECB2FE
+Sending section 2
+Got 81 NAKs for section 1 from client 0x7AECB2FE
+Sending section 3
+Got 16 NAKs for section 2 from client 0x7AECB2FE
+Sending section 4
+Got 337 NAKs for section 3 from client 0x7AECB2FE
+Sending section 5
+Got 60 NAKs for section 4 from client 0x7AECB2FE
+Sending section 6
+Got 116 NAKs for section 5 from client 0x7AECB2FE
+Sending section 7
+Got 274 NAKs for section 6 from client 0x7AECB2FE
+Sending DONE 1.1
+Got 215 NAKs for section 7 from client 0x7AECB2FE
+Sending DONE 2.1
+Starting pass 2
+Sending section 0
+Sending section 1
+Sending section 2
+Sending section 3
+Sending section 4
+Sending section 5
+Sending section 6
+Got 19 NAKs for section 3 from client 0x7AECB2FE
+Sending section 7
+Sending DONE 1.1
+Sending DONE 2.1
+Starting pass 3
+Sending section 3
+Sending DONE 1.1
+Got 21 NAKs for section 4 from client 0x7AECB2FE
+Got 1 NAKs for section 5 from client 0x7AECB2FE
+Sending DONE 2.1
+Starting pass 4
+Sending section 4
+Sending section 5
+Sending DONE 1.1
+Got COMPLETE from client 0x7AECB2FE
+Transfer status:
+Host: 0x7AECB2FE Status: Completed time: 19.133 seconds
+Total elapsed time: 19.133 seconds
+Overall throughput: 5351.90 KB/s
+----- 20-partclone-sda2.img.ac -----
+File ID: 0006 Name: 20-partclone-sda2.img.ac
+ sending as: 20-partclone-sda2.img.ac
+Bytes: 104857600 Blocks: 80660 Sections: 8
+Sending FILEINFO 1.1
+Received FILEINFO_ACK from client 0x7AECB2FE
+Sending file
+Starting pass 1
+Sending section 0
+Sending section 1
+Got 109 NAKs for section 0 from client 0x7AECB2FE
+Sending section 2
+Got 47 NAKs for section 1 from client 0x7AECB2FE
+Sending section 3
+Got 477 NAKs for section 2 from client 0x7AECB2FE
+Sending section 4
+Got 397 NAKs for section 3 from client 0x7AECB2FE
+Sending section 5
+Got 169 NAKs for section 4 from client 0x7AECB2FE
+Sending section 6
+Got 273 NAKs for section 5 from client 0x7AECB2FE
+Sending section 7
+Got 40 NAKs for section 6 from client 0x7AECB2FE
+Sending DONE 1.1
+Got 277 NAKs for section 7 from client 0x7AECB2FE
+Sending DONE 2.1
+Starting pass 2
+Sending section 0
+Sending section 1
+Sending section 2
+Sending section 3
+Sending section 4
+Got 283 NAKs for section 2 from client 0x7AECB2FE
+Sending section 5
+Got 142 NAKs for section 3 from client 0x7AECB2FE
+Sending section 6
+Sending section 7
+Sending DONE 1.1
+Sending DONE 2.1
+Starting pass 3
+Sending section 2
+Sending section 3
+Sending DONE 1.1
+Got COMPLETE from client 0x7AECB2FE
+Transfer status:
+Host: 0x7AECB2FE Status: Completed time: 18.411 seconds
+Total elapsed time: 18.411 seconds
+Overall throughput: 5562.02 KB/s
+----- 20-partclone-sda2.img.ad -----
+File ID: 0007 Name: 20-partclone-sda2.img.ad
+ sending as: 20-partclone-sda2.img.ad
+Bytes: 104857600 Blocks: 80660 Sections: 8
+Sending FILEINFO 1.1
+Received FILEINFO_ACK from client 0x7AECB2FE
+Sending file
+Starting pass 1
+Sending section 0
+Sending section 1
+Got 652 NAKs for section 0 from client 0x7AECB2FE
+Sending section 2
+Sending section 3
+Got 509 NAKs for section 2 from client 0x7AECB2FE
+Sending section 4
+Got 143 NAKs for section 3 from client 0x7AECB2FE
+Sending section 5
+Got 303 NAKs for section 4 from client 0x7AECB2FE
+Sending section 6
+Got 58 NAKs for section 5 from client 0x7AECB2FE
+Sending section 7
+Got 244 NAKs for section 6 from client 0x7AECB2FE
+Sending DONE 1.1
+Sending DONE 2.1
+Starting pass 2
+Sending section 0
+Sending section 2
+Sending section 3
+Sending section 4
+Got 245 NAKs for section 0 from client 0x7AECB2FE
+Sending section 5
+Sending section 6
+Sending DONE 1.1
+Got 316 NAKs for section 2 from client 0x7AECB2FE
+Got 204 NAKs for section 4 from client 0x7AECB2FE
+Sending DONE 2.1
+Starting pass 3
+Sending section 0
+Sending section 2
+Got 191 NAKs for section 6 from client 0x7AECB2FE
+Sending section 4
+Sending section 6
+Sending DONE 1.1
+Got 31 NAKs for section 5 from client 0x7AECB2FE
+Sending DONE 2.1
+Starting pass 4
+Sending section 5
+Sending DONE 1.1
+Got 37 NAKs for section 2 from client 0x7AECB2FE
+Got 99 NAKs for section 3 from client 0x7AECB2FE
+Sending DONE 2.1
+Starting pass 5
+Sending section 2
+Sending section 3
+Sending DONE 1.1
+Got COMPLETE from client 0x7AECB2FE
+Transfer status:
+Host: 0x7AECB2FE Status: Completed time: 20.795 seconds
+Total elapsed time: 20.795 seconds
+Overall throughput: 4924.27 KB/s
+----- 20-partclone-sda2.img.ae -----
+File ID: 0008 Name: 20-partclone-sda2.img.ae
+ sending as: 20-partclone-sda2.img.ae
+Bytes: 104857600 Blocks: 80660 Sections: 8
+Sending FILEINFO 1.1
+Received FILEINFO_ACK from client 0x7AECB2FE
+Sending file
+Starting pass 1
+Sending section 0
+Sending section 1
+Got 81 NAKs for section 0 from client 0x7AECB2FE
+Sending section 2
+Got 235 NAKs for section 1 from client 0x7AECB2FE
+Sending section 3
+Sending section 4
+Got 684 NAKs for section 3 from client 0x7AECB2FE
+Sending section 5
+Sending section 6
+Got 273 NAKs for section 5 from client 0x7AECB2FE
+Sending section 7
+Got 411 NAKs for section 6 from client 0x7AECB2FE
+Sending DONE 1.1
+Got 75 NAKs for section 7 from client 0x7AECB2FE
+Sending DONE 2.1
+Starting pass 2
+Sending section 0
+Sending section 1
+Sending section 3
+Got 21 NAKs for section 1 from client 0x7AECB2FE
+Sending section 5
+Sending section 6
+Sending section 7
+Sending DONE 1.1
+Got 474 NAKs for section 3 from client 0x7AECB2FE
+Sending DONE 2.1
+Starting pass 3
+Sending section 1
+Sending section 3
+Got 34 NAKs for section 7 from client 0x7AECB2FE
+Sending section 7
+Sending DONE 1.1
+Got 24 NAKs for section 3 from client 0x7AECB2FE
+Got 174 NAKs for section 5 from client 0x7AECB2FE
+Got 253 NAKs for section 6 from client 0x7AECB2FE
+Sending DONE 2.1
+Starting pass 4
+Sending section 3
+Sending section 5
+Sending section 6
+Sending DONE 1.1
+Got COMPLETE from client 0x7AECB2FE
+Transfer status:
+Host: 0x7AECB2FE Status: Completed time: 18.859 seconds
+Total elapsed time: 18.859 seconds
+Overall throughput: 5429.67 KB/s
+----- 20-partclone-sda2.img.af -----
+File ID: 0009 Name: 20-partclone-sda2.img.af
+ sending as: 20-partclone-sda2.img.af
+Bytes: 104857600 Blocks: 80660 Sections: 8
+Sending FILEINFO 1.1
+Received FILEINFO_ACK from client 0x7AECB2FE
+Sending file
+Starting pass 1
+Sending section 0
+Sending section 1
+Got 54 NAKs for section 0 from client 0x7AECB2FE
+Sending section 2
+Got 359 NAKs for section 1 from client 0x7AECB2FE
+Sending section 3
+Got 174 NAKs for section 2 from client 0x7AECB2FE
+Sending section 4
+Got 191 NAKs for section 3 from client 0x7AECB2FE
+Sending section 5
+Got 260 NAKs for section 4 from client 0x7AECB2FE
+Sending section 6
+Got 259 NAKs for section 5 from client 0x7AECB2FE
+Sending section 7
+Got 27 NAKs for section 6 from client 0x7AECB2FE
+Sending DONE 1.1
+Got 225 NAKs for section 7 from client 0x7AECB2FE
+Sending DONE 2.1
+Starting pass 2
+Sending section 0
+Sending section 1
+Sending section 2
+Sending section 3
+Sending section 4
+Got 15 NAKs for section 2 from client 0x7AECB2FE
+Sending section 5
+Sending section 6
+Sending section 7
+Got 45 NAKs for section 4 from client 0x7AECB2FE
+Sending DONE 1.1
+Got 40 NAKs for section 5 from client 0x7AECB2FE
+Sending DONE 2.1
+Starting pass 3
+Sending section 2
+Sending section 4
+Sending section 5
+Sending DONE 1.1
+Got 58 NAKs for section 3 from client 0x7AECB2FE
+Sending DONE 2.1
+Starting pass 4
+Sending section 3
+Sending DONE 1.1
+Got 2 NAKs for section 6 from client 0x7AECB2FE
+Got 34 NAKs for section 7 from client 0x7AECB2FE
+Sending DONE 2.1
+Starting pass 5
+Sending section 6
+Sending section 7
+Sending DONE 1.1
+Got COMPLETE from client 0x7AECB2FE
+Transfer status:
+Host: 0x7AECB2FE Status: Completed time: 19.850 seconds
+Total elapsed time: 19.850 seconds
+Overall throughput: 5158.63 KB/s
+----- 20-partclone-sda2.img.ag -----
+File ID: 000A Name: 20-partclone-sda2.img.ag
+ sending as: 20-partclone-sda2.img.ag
+Bytes: 104857600 Blocks: 80660 Sections: 8
+Sending FILEINFO 1.1
+Received FILEINFO_ACK from client 0x7AECB2FE
+Sending file
+Starting pass 1
+Sending section 0
+Sending section 1
+Got 493 NAKs for section 0 from client 0x7AECB2FE
+Sending section 2
+Got 54 NAKs for section 1 from client 0x7AECB2FE
+Sending section 3
+Got 573 NAKs for section 2 from client 0x7AECB2FE
+Sending section 4
+Sending section 5
+Got 319 NAKs for section 4 from client 0x7AECB2FE
+Sending section 6
+Got 37 NAKs for section 5 from client 0x7AECB2FE
+Sending section 7
+Got 212 NAKs for section 6 from client 0x7AECB2FE
+Sending DONE 1.1
+Got 43 NAKs for section 7 from client 0x7AECB2FE
+Sending DONE 2.1
+Starting pass 2
+Sending section 0
+Sending section 1
+Sending section 2
+Sending section 4
+Sending section 5
+Sending section 6
+Sending section 7
+Sending DONE 1.1
+Got 238 NAKs for section 2 from client 0x7AECB2FE
+Sending DONE 2.1
+Starting pass 3
+Sending section 2
+Sending DONE 1.1
+Got 27 NAKs for section 7 from client 0x7AECB2FE
+Got 145 NAKs for section 4 from client 0x7AECB2FE
+Got 26 NAKs for section 5 from client 0x7AECB2FE
+Got 124 NAKs for section 6 from client 0x7AECB2FE
+Got 27 NAKs for section 7 from client 0x7AECB2FE
+Sending DONE 2.1
+Starting pass 4
+Sending section 4
+Sending section 5
+Sending section 6
+Sending section 7
+Sending DONE 1.1
+Got COMPLETE from client 0x7AECB2FE
+Transfer status:
+Host: 0x7AECB2FE Status: Completed time: 19.665 seconds
+Total elapsed time: 19.665 seconds
+Overall throughput: 5207.31 KB/s
+----- 20-partclone-sda2.img.ah -----
+File ID: 000B Name: 20-partclone-sda2.img.ah
+ sending as: 20-partclone-sda2.img.ah
+Bytes: 23054408 Blocks: 17735 Sections: 2
+Sending FILEINFO 1.1
+Received FILEINFO_ACK from client 0x7AECB2FE
+Sending file
+Starting pass 1
+Sending section 0
+Sending section 1
+Got 82 NAKs for section 0 from client 0x7AECB2FE
+Sending DONE 1.1
+<-------------- 100% CPU and no activity on network. Attached ddd, tried to save session but it fails to save the core file.
+ Abandon
+lpouzenc@lud-x200s:~/git/eficast/uftp/uftp-4.9.2$
+
diff --git a/draft/uftp/notes.txt b/draft/uftp/notes.txt
new file mode 100644
index 0000000..25ee4b0
--- /dev/null
+++ b/draft/uftp/notes.txt
@@ -0,0 +1,42 @@
+
+SRCPART=/dev/sda2
+DESTDIR=/home
+PARTCLONE_EXTRAOPTS="-N"
+SPLIT_SIZE=1G
+
+partclone.imager -c -s $SRCPART -o - $PARTCLONE_EXTRAOPTS | split -b$SPLIT_SIZE - $DESTDIR/partclone-sda2.img.
+
+
+
+----------------
+
+PARTCLONE_EXTRAOPTS="-N"
+DSTPART=/dev/sda2
+./sinkcat.sh /dev/shm/sink | partclone.restore $PARTCLONE_EXTRAOPTS -o $DSTPART
+
+----------------------
+
+# setsockopt(3, SOL_IPV6, 0x2a /* IPV6_??? */, "\2\0\0\0\0\0\0\0\n\0\0\0\0\0\0\0\377\3\0\0\0\0\0\0\0\0\0\1\0\2\0\3"..., 136) = -1 EADDRINUSE (Address already in use)
+#write(2, "Error joining multicast group", 29Error joining multicast group) = 29
+#write(2, ": Address already in use", 24: Address already in use) = 24
+
+ADDR_OPTS="-M ff02::42 -I 2/6" # Bug si IP multiples sur même interface
+LOG_OPTS="-x3 -S /tmp/uftpd.csv -L /dev/shm/uftpd.log "
+DIR_OPTS="-T /dev/shm/tmp -D /dev/shm/uftpd -i"
+mkdir /dev/shm/{tmp,uftpd}
+
+./uftpd -q -B 2097152 $LOG_OPTS $ADDR_OPTS $DIR_OPTS
+[ $? -eq 0 ] && netstat -nlu | grep -q ':1044 ' && echo -e '\033[42m' ----- UFTPD ready ----- '\033[40m'
+
+./sinkdo.sh /dev/shm/uftpd /dev/shm/sink
+
+
+---------------------------------------------------
+
+IMGDIR=/home/lpouzenc/git/eficast/testimage
+#RATE_OPTS="-R90000 -W 400 -s 20"
+RATE_OPTS="-C tfmcc -s 20"
+LOG_OPTS="-x2 -S /tmp/uftpd.csv"
+ADDR_OPTS="-M ff02::42 -P ff02::43 -I 3/6" # -I 3/6 is virbr0 ipv6
+
+./uftp -f $RATE_OPTS $LOG_OPTS $ADDR_OPTS -- $IMGDIR/*
diff --git a/draft/uftp/sinkcat.sh b/draft/uftp/sinkcat.sh
new file mode 100755
index 0000000..20ac948
--- /dev/null
+++ b/draft/uftp/sinkcat.sh
@@ -0,0 +1,39 @@
+#!/bin/busybox sh
+
+if [ $# -ne 1 ]
+then cat <<EOT
+Usage: $(basename $0) <sink-dir>
+ Concatenate then delete files as soon they appear in sink-dir to stdout.
+ If multiple files are found in sink, the first in alphabetical order is choosen.
+ <sink-dir> must not exists, this program must create it (avoiding mistakes).
+ Dropping an empty file in dir-sink will clean exit this program.
+EOT
+ exit 1
+fi
+
+SINKDIR=$1
+mkdir "$SINKDIR" && cd "$SINKDIR"
+if [ $? -ne 0 ]
+then echo "Cannot mkdir/chdir to '$SINKDIR'" >&2
+ exit 2
+fi
+
+while true
+do
+ f=$(ls | head -n1)
+ if [ -n "$f" ]
+ then if [ -f "$f" -a -r "$f" ]
+ then size=$(stat -c'%s' -- "$f")
+ # Do the actual work on the following line
+ cat -- "$f" && rm -v -- "$f" >&2
+ # Normal exit condition
+ if [ $size -eq 0 ]
+ then cd / && rmdir -v -- "$SINKDIR" >&2
+ exit 0
+ fi
+ else echo "'$SINKDIR/$f' is not a readable file" >&2
+ exit 3
+ fi
+ fi
+ sleep 1
+done
diff --git a/draft/uftp/sinkdo.sh b/draft/uftp/sinkdo.sh
new file mode 100755
index 0000000..1b46437
--- /dev/null
+++ b/draft/uftp/sinkdo.sh
@@ -0,0 +1,57 @@
+#!/bin/busybox sh
+
+if [ $# -ne 2 ]
+then cat <<EOT
+Usage: $(basename $0) <land-dir> <sink-dir>
+EOT
+ exit 1
+fi
+
+LANDDIR=$1
+SINKDIR=$2
+cd "$LANDDIR" || exit 2
+
+curtask="(start)"
+while true
+do
+ f=$(ls | head -n1)
+ if [ -z "$f" ]
+ then sleep 1
+ continue
+ fi
+ if [ ! -f "$f" -o ! -r "$f" ]
+ then echo "'$LANDDIR/$f' is not a readable file" >&2
+ exit 2
+ fi
+ size=$(stat -c'%s' -- "$f")
+ task=${f:0:2}
+ if [ "$curtask" == "$task" ]
+ then # Next file of an already started task
+ mv "$f" "$SINKDIR/"
+ else # Switch to the next task
+ if [ -d "$SINKDIR" ]
+ then # Inform sinkcat about end-of-data
+ touch -- "$SINKDIR/zz"
+ wait
+ # sinkcat always rmdir "$SINKDIR" on normal exit
+ if [ -d "$SINKDIR" ]
+ then echo "Task $task has ran into troubles" >&2
+ exit 3
+ fi
+ fi
+ echo "Switching from task $curtask to $task" >&2
+ curtask=$task
+ if [ "$task" == "99" ]
+ then # Normal exit condition
+ rm -v "$f" >&2
+ reset
+ echo "All task completed sucessfully" >&2
+ exit 0
+ else # Start a new task
+ chmod +x -- "$f" # XXX Checks on $f (is a script ?)
+ sinkcat.sh $SINKDIR | "./$f" &
+ while [ ! -d "$SINKDIR" ]; do sleep 1; done
+ rm -v "$f" >&2
+ fi
+ fi
+done
diff --git a/draft/uftp/trace-lost-buf.txt b/draft/uftp/trace-lost-buf.txt
new file mode 100644
index 0000000..38d737e
--- /dev/null
+++ b/draft/uftp/trace-lost-buf.txt
@@ -0,0 +1,485 @@
+UFTP version 4.9.2 Copyright (C) 2001-2016 Dennis A. Bush
+--25769-- REDIR: 0x57d3ff0 (libc.so.6:__GI_memcmp) redirected to 0x4c2eca0 (__GI_memcmp)
+--25769-- REDIR: 0x57cf200 (libc.so.6:__GI_strcmp) redirected to 0x4c2d220 (__GI_strcmp)
+--25769-- REDIR: 0x57d0dd0 (libc.so.6:strnlen) redirected to 0x4c2c120 (strnlen)
+Starting at Mon Dec 19 17:26:27 2016
+Transfer rate: dynamic via TFMCC
+Using private multicast address ff02::43 Group ID: ED047C7C
+Initializing group
+--25769-- REDIR: 0xffffffffff600000 (???:???) redirected to 0x380bd803 (vgPlain_amd64_linux_REDIR_FOR_vgettimeofday)
+Sending ANNOUNCE 1
+Received REGISTER from client 0x7AECB2FE
+Sending REG_CONF 2.1
+Sending ANNOUNCE 2
+Sending ANNOUNCE 3
+Sending ANNOUNCE 4
+Sending ANNOUNCE 5
+Sending ANNOUNCE 6
+Sending ANNOUNCE 7
+Sending ANNOUNCE 8
+Sending ANNOUNCE 9
+Sending ANNOUNCE 10
+Sending ANNOUNCE 11
+Sending ANNOUNCE 12
+Sending ANNOUNCE 13
+Sending ANNOUNCE 14
+Sending ANNOUNCE 15
+Sending ANNOUNCE 16
+Sending ANNOUNCE 17
+Sending ANNOUNCE 18
+Sending ANNOUNCE 19
+Sending ANNOUNCE 20
+--25769-- REDIR: 0x57d1030 (libc.so.6:strncmp) redirected to 0x4a23730 (_vgnU_ifunc_wrapper)
+--25769-- REDIR: 0x586e9f0 (libc.so.6:__strncmp_sse42) redirected to 0x4c2ca30 (__strncmp_sse42)
+----- 10-restore-mbr.00.sh -----
+File ID: 0001 Name: 10-restore-mbr.00.sh
+ sending as: 10-restore-mbr.00.sh
+Bytes: 50 Blocks: 1 Sections: 1
+Sending FILEINFO 1.1
+Received FILEINFO_ACK from client 0x7AECB2FE
+Sending file
+Starting pass 1
+Sending section 0
+Sending DONE 1.1
+Got COMPLETE from client 0x7AECB2FE
+Transfer status:
+Host: 0x7AECB2FE Status: Completed time: 0.212 seconds
+Total elapsed time: 0.212 seconds
+Overall throughput: 0.23 KB/s
+----- 10-restore-mbr.aa -----
+File ID: 0002 Name: 10-restore-mbr.aa
+ sending as: 10-restore-mbr.aa
+Bytes: 512 Blocks: 1 Sections: 1
+Sending FILEINFO 1.1
+Received FILEINFO_ACK from client 0x7AECB2FE
+Sending file
+Starting pass 1
+Sending section 0
+Sending DONE 1.1
+Got COMPLETE from client 0x7AECB2FE
+Transfer status:
+Host: 0x7AECB2FE Status: Completed time: 0.045 seconds
+Total elapsed time: 0.045 seconds
+Overall throughput: 11.01 KB/s
+----- 20-partclone-sda2.img.00.sh -----
+File ID: 0003 Name: 20-partclone-sda2.img.00.sh
+ sending as: 20-partclone-sda2.img.00.sh
+Bytes: 112 Blocks: 1 Sections: 1
+Sending FILEINFO 1.1
+Received FILEINFO_ACK from client 0x7AECB2FE
+Sending file
+Starting pass 1
+Sending section 0
+Sending DONE 1.1
+Got COMPLETE from client 0x7AECB2FE
+Transfer status:
+Host: 0x7AECB2FE Status: Completed time: 0.100 seconds
+Total elapsed time: 0.100 seconds
+Overall throughput: 1.10 KB/s
+----- 20-partclone-sda2.img.aa -----
+File ID: 0004 Name: 20-partclone-sda2.img.aa
+ sending as: 20-partclone-sda2.img.aa
+Bytes: 104857600 Blocks: 80660 Sections: 8
+Sending FILEINFO 1.1
+Received FILEINFO_ACK from client 0x7AECB2FE
+Sending file
+Starting pass 1
+Sending section 0
+Sending section 1
+Got 468 NAKs for section 0 from client 0x7AECB2FE
+Sending section 2
+Sending section 3
+Got 363 NAKs for section 2 from client 0x7AECB2FE
+Sending section 4
+Got 525 NAKs for section 3 from client 0x7AECB2FE
+Sending section 5
+Got 157 NAKs for section 4 from client 0x7AECB2FE
+Sending section 6
+Got 40 NAKs for section 5 from client 0x7AECB2FE
+Sending section 7
+Got 133 NAKs for section 6 from client 0x7AECB2FE
+Sending DONE 1.1
+Got 45 NAKs for section 7 from client 0x7AECB2FE
+Sending DONE 2.1
+Starting pass 2
+Sending section 0
+Sending section 2
+Got 15 NAKs for section 0 from client 0x7AECB2FE
+Sending section 3
+Sending section 4
+Sending section 5
+Got 213 NAKs for section 3 from client 0x7AECB2FE
+Sending section 6
+Sending section 7
+Sending DONE 1.1
+Sending DONE 2.1
+Starting pass 3
+Sending section 0
+Sending section 3
+Got COMPLETE from client 0x7AECB2FE
+Transfer status:
+Host: 0x7AECB2FE Status: Completed time: 25.278 seconds
+Total elapsed time: 25.278 seconds
+Overall throughput: 4051.00 KB/s
+----- 20-partclone-sda2.img.ab -----
+File ID: 0005 Name: 20-partclone-sda2.img.ab
+ sending as: 20-partclone-sda2.img.ab
+Bytes: 104857600 Blocks: 80660 Sections: 8
+Sending FILEINFO 1.1
+Received FILEINFO_ACK from client 0x7AECB2FE
+Sending file
+Starting pass 1
+Sending section 0
+Sending section 1
+Got 85 NAKs for section 0 from client 0x7AECB2FE
+Sending section 2
+Got 141 NAKs for section 1 from client 0x7AECB2FE
+Sending section 3
+Got 86 NAKs for section 2 from client 0x7AECB2FE
+Sending section 4
+Got 511 NAKs for section 3 from client 0x7AECB2FE
+Sending section 5
+Got 35 NAKs for section 4 from client 0x7AECB2FE
+Sending section 6
+Got 785 NAKs for section 5 from client 0x7AECB2FE
+Sending section 7
+Sending DONE 1.1
+Got 238 NAKs for section 7 from client 0x7AECB2FE
+Sending DONE 2.1
+Starting pass 2
+Sending section 0
+Sending section 1
+Sending section 2
+Sending section 3
+Sending section 4
+Sending section 5
+Got 129 NAKs for section 3 from client 0x7AECB2FE
+Sending section 7
+Sending DONE 1.1
+Got 354 NAKs for section 5 from client 0x7AECB2FE
+Sending DONE 2.1
+Starting pass 3
+Sending section 3
+Sending section 5
+Sending DONE 1.1
+Got 28 NAKs for section 5 from client 0x7AECB2FE
+Sending DONE 2.1
+Starting pass 4
+Sending section 5
+Sending DONE 1.1
+Got COMPLETE from client 0x7AECB2FE
+Transfer status:
+Host: 0x7AECB2FE Status: Completed time: 23.408 seconds
+Total elapsed time: 23.408 seconds
+Overall throughput: 4374.56 KB/s
+----- 20-partclone-sda2.img.ac -----
+File ID: 0006 Name: 20-partclone-sda2.img.ac
+ sending as: 20-partclone-sda2.img.ac
+Bytes: 104857600 Blocks: 80660 Sections: 8
+Sending FILEINFO 1.1
+Received FILEINFO_ACK from client 0x7AECB2FE
+Sending file
+Starting pass 1
+Sending section 0
+Sending section 1
+Got 10 NAKs for section 0 from client 0x7AECB2FE
+Sending section 2
+Got 158 NAKs for section 1 from client 0x7AECB2FE
+Sending section 3
+Got 254 NAKs for section 2 from client 0x7AECB2FE
+Sending section 4
+Got 162 NAKs for section 3 from client 0x7AECB2FE
+Sending section 5
+Got 482 NAKs for section 4 from client 0x7AECB2FE
+Sending section 6
+Got 152 NAKs for section 5 from client 0x7AECB2FE
+Sending section 7
+Got 273 NAKs for section 6 from client 0x7AECB2FE
+Sending DONE 1.1
+Got 195 NAKs for section 7 from client 0x7AECB2FE
+Sending DONE 2.1
+Starting pass 2
+Sending section 0
+Sending section 1
+Sending section 2
+Sending section 3
+Sending section 4
+Sending section 5
+Sending section 6
+Got 240 NAKs for section 4 from client 0x7AECB2FE
+Sending section 7
+Sending DONE 1.1
+Got 2 NAKs for section 6 from client 0x7AECB2FE
+Sending DONE 2.1
+Starting pass 3
+Sending section 4
+Sending section 6
+Sending DONE 1.1
+Got 25 NAKs for section 4 from client 0x7AECB2FE
+Got 53 NAKs for section 5 from client 0x7AECB2FE
+Sending DONE 2.1
+Starting pass 4
+Sending section 4
+Sending section 5
+Sending DONE 1.1
+Got 52 NAKs for section 3 from client 0x7AECB2FE
+Sending DONE 2.1
+Starting pass 5
+Sending section 3
+Sending DONE 1.1
+Got COMPLETE from client 0x7AECB2FE
+Transfer status:
+Host: 0x7AECB2FE Status: Completed time: 21.765 seconds
+Total elapsed time: 21.765 seconds
+Overall throughput: 4704.82 KB/s
+----- 20-partclone-sda2.img.ad -----
+File ID: 0007 Name: 20-partclone-sda2.img.ad
+ sending as: 20-partclone-sda2.img.ad
+Bytes: 104857600 Blocks: 80660 Sections: 8
+Sending FILEINFO 1.1
+Received FILEINFO_ACK from client 0x7AECB2FE
+Sending file
+Starting pass 1
+Sending section 0
+Sending section 1
+Got 202 NAKs for section 0 from client 0x7AECB2FE
+Sending section 2
+Got 116 NAKs for section 1 from client 0x7AECB2FE
+Sending section 3
+Got 341 NAKs for section 2 from client 0x7AECB2FE
+Sending section 4
+Sending section 5
+Got 744 NAKs for section 4 from client 0x7AECB2FE
+Sending section 6
+Sending section 7
+Got 634 NAKs for section 6 from client 0x7AECB2FE
+Sending DONE 1.1
+Got 422 NAKs for section 7 from client 0x7AECB2FE
+Sending DONE 2.1
+Starting pass 2
+Sending section 0
+Sending section 1
+Sending section 2
+Sending section 4
+Sending section 6
+Sending section 7
+Sending DONE 1.1
+Got COMPLETE from client 0x7AECB2FE
+Transfer status:
+Host: 0x7AECB2FE Status: Completed time: 21.724 seconds
+Total elapsed time: 21.724 seconds
+Overall throughput: 4713.78 KB/s
+----- 20-partclone-sda2.img.ae -----
+File ID: 0008 Name: 20-partclone-sda2.img.ae
+ sending as: 20-partclone-sda2.img.ae
+Bytes: 104857600 Blocks: 80660 Sections: 8
+Sending FILEINFO 1.1
+Received FILEINFO_ACK from client 0x7AECB2FE
+Sending file
+Starting pass 1
+Sending section 0
+Sending section 1
+Got 197 NAKs for section 0 from client 0x7AECB2FE
+Sending section 2
+Got 28 NAKs for section 1 from client 0x7AECB2FE
+Sending section 3
+Got 784 NAKs for section 2 from client 0x7AECB2FE
+Sending section 4
+Got 140 NAKs for section 3 from client 0x7AECB2FE
+Sending section 5
+Got 203 NAKs for section 4 from client 0x7AECB2FE
+Sending section 6
+Got 257 NAKs for section 5 from client 0x7AECB2FE
+Sending section 7
+Got 279 NAKs for section 6 from client 0x7AECB2FE
+Sending DONE 1.1
+Got 104 NAKs for section 7 from client 0x7AECB2FE
+Sending DONE 2.1
+Starting pass 2
+Sending section 0
+Sending section 1
+Sending section 2
+Sending section 3
+Sending section 4
+Got 400 NAKs for section 2 from client 0x7AECB2FE
+Sending section 5
+Sending section 6
+Sending section 7
+Sending DONE 1.1
+Sending DONE 2.1
+Starting pass 3
+Sending section 2
+Sending DONE 1.1
+Got 32 NAKs for section 5 from client 0x7AECB2FE
+Sending DONE 2.1
+Starting pass 4
+Sending section 5
+Sending DONE 1.1
+Got COMPLETE from client 0x7AECB2FE
+Transfer status:
+Host: 0x7AECB2FE Status: Completed time: 21.747 seconds
+Total elapsed time: 21.747 seconds
+Overall throughput: 4708.69 KB/s
+----- 20-partclone-sda2.img.af -----
+File ID: 0009 Name: 20-partclone-sda2.img.af
+ sending as: 20-partclone-sda2.img.af
+Bytes: 104857600 Blocks: 80660 Sections: 8
+Sending FILEINFO 1.1
+Received FILEINFO_ACK from client 0x7AECB2FE
+Sending file
+Starting pass 1
+Sending section 0
+Sending section 1
+Got 310 NAKs for section 0 from client 0x7AECB2FE
+Sending section 2
+Got 162 NAKs for section 1 from client 0x7AECB2FE
+Sending section 3
+Got 326 NAKs for section 2 from client 0x7AECB2FE
+Sending section 4
+Sending section 5
+Got 59 NAKs for section 4 from client 0x7AECB2FE
+Sending section 6
+Got 502 NAKs for section 5 from client 0x7AECB2FE
+Sending section 7
+Got 308 NAKs for section 6 from client 0x7AECB2FE
+Sending DONE 1.1
+Sending DONE 2.1
+Starting pass 2
+Sending section 0
+Sending section 1
+Sending section 2
+Sending section 4
+Sending section 5
+Sending section 6
+Sending DONE 1.1
+Got 122 NAKs for section 5 from client 0x7AECB2FE
+Got 40 NAKs for section 6 from client 0x7AECB2FE
+Sending DONE 2.1
+Starting pass 3
+Sending section 5
+Sending section 6
+Sending DONE 1.1
+Got COMPLETE from client 0x7AECB2FE
+Transfer status:
+Host: 0x7AECB2FE Status: Completed time: 23.670 seconds
+Total elapsed time: 23.670 seconds
+Overall throughput: 4326.17 KB/s
+----- 20-partclone-sda2.img.ag -----
+File ID: 000A Name: 20-partclone-sda2.img.ag
+ sending as: 20-partclone-sda2.img.ag
+Bytes: 104857600 Blocks: 80660 Sections: 8
+Sending FILEINFO 1.1
+Received FILEINFO_ACK from client 0x7AECB2FE
+Sending file
+Starting pass 1
+Sending section 0
+Sending section 1
+Got 337 NAKs for section 0 from client 0x7AECB2FE
+Sending section 2
+Sending section 3
+Got 1629 NAKs for section 2 from client 0x7AECB2FE
+Sending section 4
+Got 289 NAKs for section 3 from client 0x7AECB2FE
+Sending section 5
+Got 372 NAKs for section 4 from client 0x7AECB2FE
+Sending section 6
+Got 33 NAKs for section 5 from client 0x7AECB2FE
+Sending section 7
+Got 525 NAKs for section 6 from client 0x7AECB2FE
+Sending DONE 1.1
+Got 157 NAKs for section 7 from client 0x7AECB2FE
+Sending DONE 2.1
+Starting pass 2
+Sending section 0
+Sending section 2
+Got 26 NAKs for section 0 from client 0x7AECB2FE
+Sending section 3
+Got 4 NAKs for section 2 from client 0x7AECB2FE
+Sending section 4
+Sending section 5
+Sending section 6
+Sending section 7
+Sending DONE 1.1
+Sending DONE 2.1
+Starting pass 3
+Sending section 0
+Sending section 2
+Sending DONE 1.1
+Got COMPLETE from client 0x7AECB2FE
+Transfer status:
+Host: 0x7AECB2FE Status: Completed time: 21.727 seconds
+Total elapsed time: 21.727 seconds
+Overall throughput: 4712.95 KB/s
+----- 20-partclone-sda2.img.ah -----
+File ID: 000B Name: 20-partclone-sda2.img.ah
+ sending as: 20-partclone-sda2.img.ah
+Bytes: 23054408 Blocks: 17735 Sections: 2
+Sending FILEINFO 1.1
+Rejecting COMPLETE from 0x7AECB2FE: invalid file ID 000A, expected 000B
+Received FILEINFO_ACK from client 0x7AECB2FE
+Sending file
+Starting pass 1
+Sending section 0
+Sending section 1
+Got 194 NAKs for section 0 from client 0x7AECB2FE
+Sending DONE 1.1
+Got 81 NAKs for section 1 from client 0x7AECB2FE
+Sending DONE 2.1
+Starting pass 2
+Sending section 0
+Sending section 1
+Sending DONE 1.1
+Got COMPLETE from client 0x7AECB2FE
+Transfer status:
+Host: 0x7AECB2FE Status: Completed time: 9.598 seconds
+Total elapsed time: 9.598 seconds
+Overall throughput: 2345.77 KB/s
+----- 99-end -----
+File ID: 000C Name: 99-end
+ sending as: 99-end
+Bytes: 0 Blocks: 0 Sections: 0
+Sending FILEINFO 1.1
+Received FILEINFO_ACK from client 0x7AECB2FE
+Sending file
+Starting pass 1
+Sending DONE 1.1
+Got COMPLETE from client 0x7AECB2FE
+Transfer status:
+Host: 0x7AECB2FE Status: Completed time: 0.007 seconds
+Total elapsed time: 0.007 seconds
+Overall throughput: 0.00 KB/s
+-----------------------------
+Finishing group
+Sending DONE 1.1
+Got COMPLETE from client 0x7AECB2FE
+Late completions:
+Sending DONE_CONF 2.1
+Group complete
+uftp: Finishing at Mon Dec 19 17:29:20 2016
+==25769==
+==25769== HEAP SUMMARY:
+==25769== in use at exit: 1,300 bytes in 1 blocks
+==25769== total heap usage: 20,607 allocs, 20,606 frees, 3,601,627,892 bytes allocated
+==25769==
+==25769== Searching for pointers to 1 not-freed blocks
+==25769== Checked 25,760,600 bytes
+==25769==
+==25769== 1,300 bytes in 1 blocks are definitely lost in loss record 1 of 1
+==25769== at 0x4C2AD10: calloc (vg_replace_malloc.c:623)
+==25769== by 0x407C1C: safe_calloc (uftp_common.c:2058)
+==25769== by 0x40FE89: create_cc_list (server_transfer.c:129)
+==25769== by 0x418FD1: transfer_receive_thread (server_phase.c:859)
+==25769== by 0x41A9AC: transfer_phase (server_phase.c:1186)
+==25769== by 0x414420: send_file (server_send.c:199)
+==25769== by 0x415A60: send_files (server_send.c:588)
+==25769== by 0x4214D9: main (server_main.c:42)
+==25769==
+==25769== LEAK SUMMARY:
+==25769== definitely lost: 1,300 bytes in 1 blocks
+==25769== indirectly lost: 0 bytes in 0 blocks
+==25769== possibly lost: 0 bytes in 0 blocks
+==25769== still reachable: 0 bytes in 0 blocks
+==25769== suppressed: 0 bytes in 0 blocks
+==25769==
+==25769== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
+==25769== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)