summaryrefslogtreecommitdiff
path: root/pkgtools/pbulk
diff options
context:
space:
mode:
authorjoerg <joerg@pkgsrc.org>2007-06-19 19:49:55 +0000
committerjoerg <joerg@pkgsrc.org>2007-06-19 19:49:55 +0000
commit8d27258d171aef0d2263235dfe4aa1625210f6ce (patch)
treeae0c40ea599746592b1eb6d44554877141bd3552 /pkgtools/pbulk
parent529ec2b59ed2c6045597c0a8846033638c433fa6 (diff)
downloadpkgsrc-8d27258d171aef0d2263235dfe4aa1625210f6ce.tar.gz
Initial import of pbulk, the new pkgsrc bulk build framework.
Discussion of various parts of the design with jlam@, wiz@, tls@ and many other developers. Special thanks to David Maxwell for testing the initial prototype and finding some bugs with Coverity Prevent. Supported by Google's Summer of Code 2007 project. OK for import during the freeze: jlam@ From DESCR: pbulk is the modular bulk build framework for pkgsrc. This package contains: - pbulk-scan, a program to scan the entire pkgsrc tree or a list of directories therein for packages and dependencies. Distributed operation using a master/client mode is supported. - pbulk-resolve, a program to resolve the dependencies from the output of pbulk-scan - pbulk-build, the build scheduler. Distributed builds via TCP are supported. - bulkbuild and related scripts to implement full and limited bulk builds on top of those programs. The pbulk framework is considered experimental.
Diffstat (limited to 'pkgtools/pbulk')
-rw-r--r--pkgtools/pbulk/DESCR19
-rw-r--r--pkgtools/pbulk/Makefile53
-rw-r--r--pkgtools/pbulk/PLIST29
-rw-r--r--pkgtools/pbulk/files/pbulk/Makefile5
-rw-r--r--pkgtools/pbulk/files/pbulk/Makefile.inc18
-rw-r--r--pkgtools/pbulk/files/pbulk/lib/Makefile11
-rw-r--r--pkgtools/pbulk/files/pbulk/lib/alloc.c95
-rw-r--r--pkgtools/pbulk/files/pbulk/lib/atomic.c83
-rw-r--r--pkgtools/pbulk/files/pbulk/lib/event.c146
-rw-r--r--pkgtools/pbulk/files/pbulk/lib/exec.c82
-rw-r--r--pkgtools/pbulk/files/pbulk/lib/match.c482
-rw-r--r--pkgtools/pbulk/files/pbulk/lib/netaddr.c74
-rw-r--r--pkgtools/pbulk/files/pbulk/lib/pbulk.h65
-rw-r--r--pkgtools/pbulk/files/pbulk/lib/read_child.c84
-rw-r--r--pkgtools/pbulk/files/pbulk/lib/read_file.c71
-rw-r--r--pkgtools/pbulk/files/pbulk/pbuild/Makefile6
-rw-r--r--pkgtools/pbulk/files/pbulk/pbuild/client.c116
-rw-r--r--pkgtools/pbulk/files/pbulk/pbuild/jobs.c555
-rw-r--r--pkgtools/pbulk/files/pbulk/pbuild/master.c290
-rw-r--r--pkgtools/pbulk/files/pbulk/pbuild/pbuild.c230
-rw-r--r--pkgtools/pbulk/files/pbulk/pbuild/pbuild.h92
-rw-r--r--pkgtools/pbulk/files/pbulk/pbuild/pbulk-build.1132
-rw-r--r--pkgtools/pbulk/files/pbulk/pbuild/stat.c108
-rw-r--r--pkgtools/pbulk/files/pbulk/pbulk.conf69
-rw-r--r--pkgtools/pbulk/files/pbulk/presolve/Makefile8
-rw-r--r--pkgtools/pbulk/files/pbulk/presolve/pbulk-resolve.177
-rw-r--r--pkgtools/pbulk/files/pbulk/presolve/presolve.c426
-rw-r--r--pkgtools/pbulk/files/pbulk/pscan/Makefile6
-rw-r--r--pkgtools/pbulk/files/pbulk/pscan/client.c120
-rw-r--r--pkgtools/pbulk/files/pbulk/pscan/jobs.c267
-rw-r--r--pkgtools/pbulk/files/pbulk/pscan/master.c240
-rw-r--r--pkgtools/pbulk/files/pbulk/pscan/pbulk-scan.195
-rw-r--r--pkgtools/pbulk/files/pbulk/pscan/pscan.c243
-rw-r--r--pkgtools/pbulk/files/pbulk/pscan/pscan.h60
-rw-r--r--pkgtools/pbulk/files/pbulk/scripts/Makefile13
-rwxr-xr-xpkgtools/pbulk/files/pbulk/scripts/build64
-rwxr-xr-xpkgtools/pbulk/files/pbulk/scripts/build-client-start10
-rwxr-xr-xpkgtools/pbulk/files/pbulk/scripts/bulkbuild12
-rwxr-xr-xpkgtools/pbulk/files/pbulk/scripts/client-clean11
-rwxr-xr-xpkgtools/pbulk/files/pbulk/scripts/compute-packages.awk73
-rwxr-xr-xpkgtools/pbulk/files/pbulk/scripts/create-broken-graph.awk91
-rwxr-xr-xpkgtools/pbulk/files/pbulk/scripts/create-report-html.awk244
-rwxr-xr-xpkgtools/pbulk/files/pbulk/scripts/create-report-txt.awk197
-rwxr-xr-xpkgtools/pbulk/files/pbulk/scripts/create-report.awk93
-rwxr-xr-xpkgtools/pbulk/files/pbulk/scripts/pkg-build102
-rwxr-xr-xpkgtools/pbulk/files/pbulk/scripts/pkg-up-to-date64
-rwxr-xr-xpkgtools/pbulk/files/pbulk/scripts/pre-build55
-rwxr-xr-xpkgtools/pbulk/files/pbulk/scripts/report103
-rwxr-xr-xpkgtools/pbulk/files/pbulk/scripts/scan88
-rwxr-xr-xpkgtools/pbulk/files/pbulk/scripts/scan-client-start10
-rwxr-xr-xpkgtools/pbulk/files/pbulk/scripts/upload45
51 files changed, 5732 insertions, 0 deletions
diff --git a/pkgtools/pbulk/DESCR b/pkgtools/pbulk/DESCR
new file mode 100644
index 00000000000..24f05660d14
--- /dev/null
+++ b/pkgtools/pbulk/DESCR
@@ -0,0 +1,19 @@
+pbulk is the modular bulk build framework for pkgsrc.
+
+This package contains:
+
+- pbulk-scan, a program to scan the entire pkgsrc tree
+ or a list of directories therein for packages and
+ dependencies. Distributed operation using a master/client
+ mode is supported.
+
+- pbulk-resolve, a program to resolve the dependencies
+ from the output of pbulk-scan
+
+- pbulk-build, the build scheduler. Distributed builds via TCP
+ are supported.
+
+- bulkbuild and related scripts to implement full and limited
+ bulk builds on top of those programs.
+
+The pbulk framework is considered experimental.
diff --git a/pkgtools/pbulk/Makefile b/pkgtools/pbulk/Makefile
new file mode 100644
index 00000000000..a0c660eef63
--- /dev/null
+++ b/pkgtools/pbulk/Makefile
@@ -0,0 +1,53 @@
+# $NetBSD: Makefile,v 1.1.1.1 2007/06/19 19:49:55 joerg Exp $
+
+DISTNAME= pbulk-0.1
+CATEGORIES= pkgtools
+MASTER_SITES= # empty
+DISTFILES= # empty
+
+MAINTAINER= joerg@NetBSD.org
+HOMEPAGE= ftp://ftp.NetBSD.org/pub/NetBSD/packages/pkgsrc/doc/pkgsrc.html
+COMMENT= Modular bulk build framework
+
+PKG_DESTDIR_SUPPORT= user-destdir
+
+WRKSRC= ${WRKDIR}/pbulk
+EXTRACT_ONLY= # empty
+NO_CHECKSUM= YES
+
+USE_TOOLS+= awk:run bzip2:run digest:run gzip:run ident:run make:run \
+ mail:run
+DEPENDS+= rsync-[0-9]*:../../net/rsync
+
+SUBST_CLASSES+= tools
+SUBST_STAGE.tools= pre-configure
+SUBST_MESSAGE.tools= Fixing references to tools
+SUBST_FILES.tools= pbulk.conf scripts/build scripts/build-client-start \
+ scripts/bulkbuild scripts/client-clean \
+ scripts/pkg-build scripts/pkg-up-to-date scripts/pre-build \
+ scripts/report scripts/scan scripts/scan-client-start scripts/upload \
+ scripts/compute-packages.awk scripts/create-broken-graph.awk \
+ scripts/create-report-html.awk scripts/create-report-txt.awk \
+ scripts/create-report.awk
+SUBST_VARS.tools= AWK BZIP2 DIGEST GZIP_CMD IDENT MAKE MAIL_CMD \
+ PBULK_CONFIG PKG_INFO PREFIX SH
+
+CONF_FILES+= share/examples/pbulk/pbulk.conf ${PKG_SYSCONFDIR}/pbulk.conf
+
+PBULK_CONFIG= ${PKG_SYSCONFDIR}/pbulk.conf
+
+INSTALLATION_DIRS= bin libexec/pbulk \
+ ${PKGMANDIR}/cat1 ${PKGMANDIR}/man1 \
+ share/examples/pbulk
+INSTALL_ENV+= MANDIR=${PREFIX}/${PKGMANDIR}
+
+.include "../../mk/bsd.prefs.mk"
+
+do-extract:
+ ${CP} -r ${FILESDIR}/pbulk ${WRKDIR}
+
+post-install:
+ ${INSTALL_DATA} ${WRKSRC}/pbulk.conf ${DESTDIR}${PREFIX}/share/examples/pbulk/pbulk.conf
+
+.include "../../devel/libevent/buildlink3.mk"
+.include "../../mk/bsd.pkg.mk"
diff --git a/pkgtools/pbulk/PLIST b/pkgtools/pbulk/PLIST
new file mode 100644
index 00000000000..4d6d6373cf4
--- /dev/null
+++ b/pkgtools/pbulk/PLIST
@@ -0,0 +1,29 @@
+@comment $NetBSD: PLIST,v 1.1.1.1 2007/06/19 19:49:55 joerg Exp $
+bin/bulkbuild
+bin/pbulk-build
+bin/pbulk-resolve
+bin/pbulk-scan
+libexec/pbulk/build
+libexec/pbulk/build-client-start
+libexec/pbulk/client-clean
+libexec/pbulk/compute-packages
+libexec/pbulk/create-broken-graph
+libexec/pbulk/create-report
+libexec/pbulk/create-report-html
+libexec/pbulk/create-report-txt
+libexec/pbulk/pkg-build
+libexec/pbulk/pkg-up-to-date
+libexec/pbulk/pre-build
+libexec/pbulk/report
+libexec/pbulk/scan
+libexec/pbulk/scan-client-start
+libexec/pbulk/upload
+man/cat1/pbulk-build.0
+man/cat1/pbulk-resolve.0
+man/cat1/pbulk-scan.0
+man/man1/pbulk-build.1
+man/man1/pbulk-resolve.1
+man/man1/pbulk-scan.1
+share/examples/pbulk/pbulk.conf
+@dirrm share/examples/pbulk
+@dirrm libexec/pbulk
diff --git a/pkgtools/pbulk/files/pbulk/Makefile b/pkgtools/pbulk/files/pbulk/Makefile
new file mode 100644
index 00000000000..5e86689bdd6
--- /dev/null
+++ b/pkgtools/pbulk/files/pbulk/Makefile
@@ -0,0 +1,5 @@
+# $NetBSD: Makefile,v 1.1.1.1 2007/06/19 19:49:55 joerg Exp $
+
+SUBDIR= lib presolve pscan pbuild scripts
+
+.include <bsd.subdir.mk>
diff --git a/pkgtools/pbulk/files/pbulk/Makefile.inc b/pkgtools/pbulk/files/pbulk/Makefile.inc
new file mode 100644
index 00000000000..bef4d404b47
--- /dev/null
+++ b/pkgtools/pbulk/files/pbulk/Makefile.inc
@@ -0,0 +1,18 @@
+# $NetBSD: Makefile.inc,v 1.1.1.1 2007/06/19 19:49:55 joerg Exp $
+
+BINDIR?= ${PREFIX}/bin
+
+WARNS= 4
+
+.if !defined(LIBPBULK_ONLY)
+CPPFLAGS+= -I${.CURDIR}/../lib
+DPADD+= ${.OBJDIR}/../lib/libpbulk.a
+LDADD+= -L${.OBJDIR}/../lib -lpbulk
+.endif
+
+.if !defined(NO_LIBEVENT_NEEDED)
+CPPFLAGS+=
+.if !defined(LIBPBULK_ONLY)
+LDADD+= -levent
+.endif
+.endif
diff --git a/pkgtools/pbulk/files/pbulk/lib/Makefile b/pkgtools/pbulk/files/pbulk/lib/Makefile
new file mode 100644
index 00000000000..83136668f17
--- /dev/null
+++ b/pkgtools/pbulk/files/pbulk/lib/Makefile
@@ -0,0 +1,11 @@
+# $NetBSD: Makefile,v 1.1.1.1 2007/06/19 19:49:59 joerg Exp $
+
+LIB= pbulk
+MKPRIVATELIB= yes
+MKLINKLIB= no
+MKPIC= no
+SRCS= alloc.c atomic.c event.c exec.c match.c read_child.c read_file.c netaddr.c
+
+LIBPBULK_ONLY= # defined
+
+.include <bsd.lib.mk>
diff --git a/pkgtools/pbulk/files/pbulk/lib/alloc.c b/pkgtools/pbulk/files/pbulk/lib/alloc.c
new file mode 100644
index 00000000000..348bd3dc3f8
--- /dev/null
+++ b/pkgtools/pbulk/files/pbulk/lib/alloc.c
@@ -0,0 +1,95 @@
+/* $NetBSD: alloc.c,v 1.1.1.1 2007/06/19 19:49:58 joerg Exp $ */
+
+/*-
+ * Copyright (c) 2007 Joerg Sonnenberger <joerg@NetBSD.org>.
+ * All rights reserved.
+ *
+ * This code was developed as part of Google's Summer of Code 2007 program.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <err.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "pbulk.h"
+
+char *
+xasprintf(const char *fmt, ...)
+{
+ va_list ap;
+ char *buf;
+
+ va_start(ap, fmt);
+ if (vasprintf(&buf, fmt, ap) == -1)
+ err(1, "asprintf failed");
+ va_end(ap);
+ return buf;
+}
+
+void *
+xmalloc(size_t len)
+{
+ void *ptr;
+
+ if ((ptr = malloc(len)) == NULL)
+ err(1, "malloc failed");
+ return ptr;
+}
+
+void *
+xrealloc(void *buf, size_t len)
+{
+ void *ptr;
+
+ if ((ptr = realloc(buf, len)) == NULL)
+ err(1, "realloc failed");
+ return ptr;
+}
+
+char *
+xstrdup(const char *str)
+{
+ char *buf;
+
+ if ((buf = strdup(str)) == NULL)
+ err(1, "strdup failed");
+ return buf;
+}
+
+char *
+xstrndup(const char *str, size_t len)
+{
+ char *buf;
+
+ buf = xmalloc(len + 1);
+ strncpy(buf, str, len);
+ buf[len] = '\0';
+
+ return buf;
+}
diff --git a/pkgtools/pbulk/files/pbulk/lib/atomic.c b/pkgtools/pbulk/files/pbulk/lib/atomic.c
new file mode 100644
index 00000000000..d94267e5966
--- /dev/null
+++ b/pkgtools/pbulk/files/pbulk/lib/atomic.c
@@ -0,0 +1,83 @@
+/* $NetBSD: atomic.c,v 1.1.1.1 2007/06/19 19:49:59 joerg Exp $ */
+
+/*-
+ * Copyright (c) 2007 Joerg Sonnenberger <joerg@NetBSD.org>.
+ * All rights reserved.
+ *
+ * This code was developed as part of Google's Summer of Code 2007 program.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/stat.h>
+#include <err.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "pbulk.h"
+
+ssize_t
+atomic_read(int fd, void *real_buf, size_t bytes)
+{
+ char *buf = real_buf;
+ ssize_t len;
+ ssize_t ret;
+
+ len = 0;
+
+ do {
+ ret = read(fd, buf, bytes);
+ if (ret == -1)
+ return ret;
+ buf += ret;
+ len += ret;
+ bytes -= ret;
+ } while (ret != 0 && bytes != 0);
+
+ return len;
+}
+
+ssize_t
+atomic_write(int fd, const void *real_buf, size_t bytes)
+{
+ const char *buf = real_buf;
+ ssize_t len;
+ ssize_t ret;
+
+ len = 0;
+
+ do {
+ ret = write(fd, buf, bytes);
+ if (ret == -1)
+ return ret;
+ buf += ret;
+ len += ret;
+ bytes -= ret;
+ } while (ret != 0 && bytes != 0);
+
+ return len;
+}
diff --git a/pkgtools/pbulk/files/pbulk/lib/event.c b/pkgtools/pbulk/files/pbulk/lib/event.c
new file mode 100644
index 00000000000..1e2851583e0
--- /dev/null
+++ b/pkgtools/pbulk/files/pbulk/lib/event.c
@@ -0,0 +1,146 @@
+/* $NetBSD: event.c,v 1.1.1.1 2007/06/19 19:49:58 joerg Exp $ */
+
+/*-
+ * Copyright (c) 2007 Joerg Sonnenberger <joerg@NetBSD.org>.
+ * All rights reserved.
+ *
+ * This code was developed as part of Google's Summer of Code 2007 program.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/ioctl.h>
+#include <sys/time.h>
+#include <event.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "pbulk.h"
+
+struct deferred_read_arg {
+ void *cb_arg;
+ void (*cb_ok)(void *);
+ void (*cb_error)(void *);
+
+ struct event ev;
+ int fd;
+
+ char *buf;
+ size_t remaining;
+};
+
+struct deferred_write_arg {
+ void *cb_arg;
+ void (*cb_ok)(void *);
+ void (*cb_error)(void *);
+
+ struct event ev;
+ int fd;
+
+ const char *buf;
+ size_t remaining;
+};
+
+static void
+deferred_read_handler(int fd, short event, void *arg)
+{
+ struct deferred_read_arg *data = arg;
+ ssize_t received;
+
+ received = read(data->fd, data->buf, data->remaining);
+ if (received == -1 || received == 0) {
+ (*data->cb_error)(data->cb_arg);
+ free(data);
+ return;
+ }
+ data->buf += received;
+ data->remaining -= received;
+ if (data->remaining == 0) {
+ (*data->cb_ok)(data->cb_arg);
+ free(data);
+ return;
+ }
+ event_add(&data->ev, NULL);
+}
+
+void
+deferred_read(int fd, void *buf, size_t buf_len, void *arg,
+ void (*cb_ok)(void *), void (*cb_error)(void *))
+{
+ struct deferred_read_arg *data;
+
+ data = xmalloc(sizeof(*data));
+ data->cb_arg = arg;
+ data->cb_ok = cb_ok;
+ data->cb_error = cb_error;
+ data->fd = fd;
+ data->buf = buf;
+ data->remaining = buf_len;
+
+ event_set(&data->ev, data->fd, EV_READ,
+ deferred_read_handler, data);
+ event_add(&data->ev, NULL);
+}
+
+static void
+deferred_write_handler(int fd, short event, void *arg)
+{
+ struct deferred_write_arg *data = arg;
+ ssize_t sent;
+
+ sent = write(data->fd, data->buf, data->remaining);
+ if (sent == -1 || sent == 0) {
+ (*data->cb_error)(data->cb_arg);
+ free(data);
+ return;
+ }
+ data->buf += sent;
+ data->remaining -= sent;
+ if (data->remaining == 0) {
+ (*data->cb_ok)(data->cb_arg);
+ free(data);
+ return;
+ }
+ event_add(&data->ev, NULL);
+}
+
+void
+deferred_write(int fd, const void *buf, size_t buf_len, void *arg,
+ void (*cb_ok)(void *), void (*cb_error)(void *))
+{
+ struct deferred_write_arg *data;
+
+ data = xmalloc(sizeof(*data));
+ data->cb_arg = arg;
+ data->cb_ok = cb_ok;
+ data->cb_error = cb_error;
+ data->fd = fd;
+ data->buf = buf;
+ data->remaining = buf_len;
+
+ event_set(&data->ev, data->fd, EV_WRITE,
+ deferred_write_handler, data);
+ event_add(&data->ev, NULL);
+}
diff --git a/pkgtools/pbulk/files/pbulk/lib/exec.c b/pkgtools/pbulk/files/pbulk/lib/exec.c
new file mode 100644
index 00000000000..2ad8e5b6edc
--- /dev/null
+++ b/pkgtools/pbulk/files/pbulk/lib/exec.c
@@ -0,0 +1,82 @@
+/* $NetBSD: exec.c,v 1.1.1.1 2007/06/19 19:49:58 joerg Exp $ */
+
+/*-
+ * Copyright (c) 2007 Joerg Sonnenberger <joerg@NetBSD.org>.
+ * All rights reserved.
+ *
+ * This code was developed as part of Google's Summer of Code 2007 program.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <err.h>
+#include <inttypes.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "pbulk.h"
+
+#define UNCONST(x) ((void *)(uintptr_t)(x))
+
+pid_t
+fork_chdir_exec(const char *dir, const char *cmd, const char * const *argv, int *out)
+{
+ static const char chdir_err[] = "Cannot change directory to \"";
+ static const char dup2_err[] = "Cannot reassign stdout of child";
+ pid_t child;
+ int output_pipe[2];
+
+ if (pipe(output_pipe) == -1)
+ err(1, "Cannot create pipe for output");
+
+ if ((child = vfork()) != 0) {
+ if (child == -1) {
+ (void)close(output_pipe[0]);
+ (void)close(output_pipe[1]);
+ } else {
+ *out = output_pipe[0];
+ (void)close(output_pipe[1]);
+ }
+ return child;
+ }
+
+ (void)close(output_pipe[0]);
+ if (chdir(dir) == -1) {
+ (void)write(STDERR_FILENO, chdir_err, sizeof(chdir_err) - 1);
+ (void)write(STDERR_FILENO, dir, strlen(dir));
+ (void)write(STDERR_FILENO, "\".\n", 3);
+ _exit(1);
+ }
+ if (output_pipe[1] != STDOUT_FILENO) {
+ if (dup2(output_pipe[1], STDOUT_FILENO) == -1) {
+ (void)write(STDERR_FILENO, dup2_err, sizeof(dup2_err) - 1);
+ _exit(1);
+ }
+ (void)close(output_pipe[1]);
+ }
+ (void)execvp(cmd, UNCONST(argv));
+ _exit(1);
+ /* NOTREACHED */
+}
diff --git a/pkgtools/pbulk/files/pbulk/lib/match.c b/pkgtools/pbulk/files/pbulk/lib/match.c
new file mode 100644
index 00000000000..3e245ccafde
--- /dev/null
+++ b/pkgtools/pbulk/files/pbulk/lib/match.c
@@ -0,0 +1,482 @@
+/* $NetBSD: match.c,v 1.1.1.1 2007/06/19 19:49:59 joerg Exp $ */
+
+/*
+ * Copyright © 2002 Alistair G. Crooks. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <ctype.h>
+#include <err.h>
+#include <fnmatch.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "pbulk.h"
+
+static int dewey_cmp(const char *, int, const char *);
+static int dewey_match(const char *, const char *);
+static int dewey_mktest(int *, const char *);
+
+enum {
+ MaxPathSize = PATH_MAX
+};
+
+enum {
+ DEWEY_LT,
+ DEWEY_LE,
+ DEWEY_EQ,
+ DEWEY_GE,
+ DEWEY_GT,
+ DEWEY_NE
+};
+
+const char *
+pkg_order(const char *match1, const char *match2)
+{
+ const char *v1, *v2;
+
+ v1 = strrchr(match1, '-');
+ v2 = strrchr(match2, '-');
+
+ if (v1 == NULL || v2 == NULL)
+ errx(1, "Internal error");
+
+ if (dewey_cmp(v1, DEWEY_GT, v2))
+ return match1;
+ else
+ return match2;
+}
+
+#ifndef MIN
+#define MIN(a,b) (((a) < (b)) ? (a) : (b))
+#endif
+
+#ifndef MAX
+#define MAX(a,b) (((a) > (b)) ? (a) : (b))
+#endif
+
+#define NEWARRAY(type,ptr,size,where,action) do { \
+ if ((ptr = (type *) calloc(sizeof(type), (unsigned)(size))) == NULL) { \
+ warn("%s: can't allocate %lu bytes", where, \
+ (unsigned long)(size * sizeof(type))); \
+ action; \
+ } \
+} while( /* CONSTCOND */ 0)
+
+#define RENEW(type,ptr,size,where,action) do { \
+ type *newptr; \
+ if ((newptr = (type *) realloc(ptr, sizeof(type) * (size))) == NULL) { \
+ warn("%s: can't realloc %lu bytes", where, \
+ (unsigned long)(size * sizeof(type))); \
+ action; \
+ } \
+ ptr = newptr; \
+} while( /* CONSTCOND */ 0)
+
+#define NEW(type, ptr, where, action) NEWARRAY(type, ptr, 1, where, action)
+
+#define FREE(ptr) (void) free(ptr)
+
+#define ALLOC(type, v, size, c, init, where, action) do { \
+ if (size == 0) { \
+ size = init; \
+ NEWARRAY(type, v, size, where ": new", action); \
+ } else if (c == size) { \
+ size *= 2; \
+ RENEW(type, v, size, where ": renew", action); \
+ } \
+} while( /* CONSTCOND */ 0)
+
+
+#define PKG_PATTERN_MAX 1024
+typedef int (*matchfn) (const char *, void *);
+
+/* do not modify these values, or things will NOT work */
+enum {
+ Alpha = -3,
+ Beta = -2,
+ RC = -1,
+ Dot = 0,
+ Patch = 1
+};
+
+/* this struct defines a version number */
+typedef struct arr_t {
+ unsigned c; /* # of version numbers */
+ unsigned size; /* size of array */
+ int *v; /* array of decimal numbers */
+ int netbsd; /* any "nb" suffix */
+} arr_t;
+
+/* this struct describes a test */
+typedef struct test_t {
+ const char *s; /* string representation */
+ unsigned len; /* length of string */
+ int t; /* enumerated type of test */
+} test_t;
+
+
+/* the tests that are recognised. */
+static const test_t tests[] = {
+ { "<=", 2, DEWEY_LE },
+ { "<", 1, DEWEY_LT },
+ { ">=", 2, DEWEY_GE },
+ { ">", 1, DEWEY_GT },
+ { "==", 2, DEWEY_EQ },
+ { "!=", 2, DEWEY_NE },
+ { NULL, 0, 0 }
+};
+
+static const test_t modifiers[] = {
+ { "alpha", 5, Alpha },
+ { "beta", 4, Beta },
+ { "pre", 3, RC },
+ { "rc", 2, RC },
+ { "pl", 2, Dot },
+ { "_", 1, Dot },
+ { ".", 1, Dot },
+ { NULL, 0, 0 }
+};
+
+
+
+/* locate the test in the tests array */
+static int
+dewey_mktest(int *op, const char *test)
+{
+ const test_t *tp;
+
+ for (tp = tests ; tp->s ; tp++) {
+ if (strncasecmp(test, tp->s, tp->len) == 0) {
+ *op = tp->t;
+ return tp->len;
+ }
+ }
+ return -1;
+}
+
+/*
+ * make a component of a version number.
+ * '.' encodes as Dot which is '0'
+ * '_' encodes as 'patch level', or 'Dot', which is 0.
+ * 'pl' encodes as 'patch level', or 'Dot', which is 0.
+ * 'alpha' encodes as 'alpha version', or Alpha, which is -3.
+ * 'beta' encodes as 'beta version', or Beta, which is -2.
+ * 'rc' encodes as 'release candidate', or RC, which is -1.
+ * 'nb' encodes as 'netbsd version', which is used after all other tests
+ */
+static int
+mkcomponent(arr_t *ap, const char *num)
+{
+ static const char alphas[] = "abcdefghijklmnopqrstuvwxyz";
+ const test_t *modp;
+ int n;
+ const char *cp;
+
+ if (*num == 0) {
+ return 0;
+ }
+ ALLOC(int, ap->v, ap->size, ap->c, 62, "mkver", exit(EXIT_FAILURE));
+ if (isdigit((unsigned char)*num)) {
+ for (cp = num, n = 0 ; isdigit((unsigned char)*num) ; num++) {
+ n = (n * 10) + (*num - '0');
+ }
+ ap->v[ap->c++] = n;
+ return (int)(num - cp);
+ }
+ for (modp = modifiers ; modp->s ; modp++) {
+ if (strncasecmp(num, modp->s, modp->len) == 0) {
+ ap->v[ap->c++] = modp->t;
+ return modp->len;
+ }
+ }
+ if (strncasecmp(num, "nb", 2) == 0) {
+ for (cp = num, num += 2, n = 0 ; isdigit((unsigned char)*num) ; num++) {
+ n = (n * 10) + (*num - '0');
+ }
+ ap->netbsd = n;
+ return (int)(num - cp);
+ }
+ if (isalpha((unsigned char)*num)) {
+ ap->v[ap->c++] = Dot;
+ cp = strchr(alphas, tolower((unsigned char)*num));
+ ALLOC(int, ap->v, ap->size, ap->c, 62, "mkver", exit(EXIT_FAILURE));
+ ap->v[ap->c++] = (int)(cp - alphas) + 1;
+ return 1;
+ }
+ return 1;
+}
+
+/* make a version number string into an array of comparable 64bit ints */
+static int
+mkversion(arr_t *ap, const char *num)
+{
+ (void) memset(ap, 0, sizeof(arr_t));
+ while (*num) {
+ num += mkcomponent(ap, num);
+ }
+ return 1;
+}
+
+#define DIGIT(v, c, n) (((n) < (c)) ? v[n] : 0)
+
+/* compare the result against the test we were expecting */
+static int
+result(int cmp, int tst)
+{
+ switch(tst) {
+ case DEWEY_LT:
+ return cmp < 0;
+ case DEWEY_LE:
+ return cmp <= 0;
+ case DEWEY_GT:
+ return cmp > 0;
+ case DEWEY_GE:
+ return cmp >= 0;
+ case DEWEY_EQ:
+ return cmp == 0;
+ case DEWEY_NE:
+ return cmp != 0;
+ default:
+ return 0;
+ }
+}
+
+/* do the test on the 2 vectors */
+static int
+vtest(arr_t *lhs, int tst, arr_t *rhs)
+{
+ int cmp;
+ int c;
+ int i;
+
+ for (i = 0, c = MAX(lhs->c, rhs->c) ; i < c ; i++) {
+ if ((cmp = DIGIT(lhs->v, lhs->c, i) - DIGIT(rhs->v, rhs->c, i)) != 0) {
+ return result(cmp, tst);
+ }
+ }
+ return result(lhs->netbsd - rhs->netbsd, tst);
+}
+
+/*
+ * Compare two dewey decimal numbers
+ */
+static int
+dewey_cmp(const char *lhs, int op, const char *rhs)
+{
+ arr_t right;
+ arr_t left;
+
+ (void) memset(&left, 0, sizeof(left));
+ if (!mkversion(&left, lhs)) {
+ return 0;
+ }
+ (void) memset(&right, 0, sizeof(right));
+ if (!mkversion(&right, rhs)) {
+ return 0;
+ }
+ return vtest(&left, op, &right);
+}
+
+/*
+ * Perform dewey match on "pkg" against "pattern".
+ * Return 1 on match, 0 on non-match, -1 on error.
+ */
+static int
+dewey_match(const char *pattern, const char *pkg)
+{
+ const char *version;
+ const char *sep, *sep2;
+ int op, op2;
+ int n;
+
+ /* compare names */
+ if ((version=strrchr(pkg, '-')) == NULL) {
+ return 0;
+ }
+ if ((sep = strpbrk(pattern, "<>")) == NULL)
+ return -1;
+ /* compare name lengths */
+ if ((sep-pattern != version-pkg) ||
+ strncmp(pkg, pattern, (size_t)(version-pkg)) != 0)
+ return 0;
+ version++;
+
+ /* extract comparison operator */
+ if ((n = dewey_mktest(&op, sep)) < 0) {
+ return 0;
+ }
+ /* skip operator */
+ sep += n;
+
+ /* if greater than, look for less than */
+ sep2 = NULL;
+ if (op == DEWEY_GT || op == DEWEY_GE) {
+ if ((sep2 = strchr(sep, '<')) != NULL) {
+ if ((n = dewey_mktest(&op2, sep2)) < 0) {
+ return 0;
+ }
+ /* compare upper limit */
+ if (!dewey_cmp(version, op2, sep2+n))
+ return 0;
+ }
+ }
+
+ /* compare only pattern / lower limit */
+ if (sep2) {
+ char ver[PKG_PATTERN_MAX];
+
+ strlcpy(ver, sep, MIN(sizeof(ver), sep2-sep+1));
+ if (dewey_cmp(version, op, ver))
+ return 1;
+ }
+ else {
+ if (dewey_cmp(version, op, sep))
+ return 1;
+ }
+
+ return 0;
+}
+
+
+/*
+ * FreeBSD install - a package for the installation and maintainance
+ * of non-core utilities.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * Jordan K. Hubbard
+ * 18 July 1993
+ *
+ * Miscellaneous string utilities.
+ *
+ */
+
+
+/*
+ * Perform alternate match on "pkg" against "pattern",
+ * calling pmatch (recursively) to resolve any other patterns.
+ * Return 1 on match, 0 otherwise
+ */
+static int
+alternate_match(const char *pattern, const char *pkg)
+{
+ char *sep;
+ char buf[MaxPathSize];
+ char *last;
+ char *alt;
+ char *cp;
+ int cnt;
+ int found;
+
+ if ((sep = strchr(pattern, '{')) == (char *) NULL) {
+ errx(EXIT_FAILURE, "alternate_match(): '{' expected in `%s'", pattern);
+ }
+ (void) strncpy(buf, pattern, (size_t) (sep - pattern));
+ alt = &buf[sep - pattern];
+ last = (char *) NULL;
+ for (cnt = 0, cp = sep; *cp && last == (char *) NULL; cp++) {
+ if (*cp == '{') {
+ cnt++;
+ } else if (*cp == '}' && --cnt == 0 && last == (char *) NULL) {
+ last = cp + 1;
+ }
+ }
+ if (cnt != 0) {
+ errx(EXIT_FAILURE, "Malformed alternate `%s'", pattern);
+ }
+ for (found = 0, cp = sep + 1; *sep != '}'; cp = sep + 1) {
+ for (cnt = 0, sep = cp; cnt > 0 || (cnt == 0 && *sep != '}' && *sep != ','); sep++) {
+ if (*sep == '{') {
+ cnt++;
+ } else if (*sep == '}') {
+ cnt--;
+ }
+ }
+ (void) snprintf(alt, sizeof(buf) - (alt - buf), "%.*s%s", (int) (sep - cp), cp, last);
+ if (pkg_match(buf, pkg) == 1) {
+ found = 1;
+ }
+ }
+ return found;
+}
+
+/*
+ * Perform glob match on "pkg" against "pattern".
+ * Return 1 on match, 0 otherwise
+ */
+static int
+glob_match(const char *pattern, const char *pkg)
+{
+ return fnmatch(pattern, pkg, FNM_PERIOD) == 0;
+}
+
+/*
+ * Perform simple match on "pkg" against "pattern".
+ * Return 1 on match, 0 otherwise
+ */
+static int
+simple_match(const char *pattern, const char *pkg)
+{
+ return strcmp(pattern, pkg) == 0;
+}
+
+/*
+ * Match pkg against pattern, return 1 if matching, 0 else
+ */
+int
+pkg_match(const char *pattern, const char *pkg)
+{
+ if (strchr(pattern, '{') != (char *) NULL) {
+ /* emulate csh-type alternates */
+ return alternate_match(pattern, pkg);
+ }
+ if (strpbrk(pattern, "<>") != (char *) NULL) {
+ int ret;
+
+ /* perform relational dewey match on version number */
+ ret = dewey_match(pattern, pkg);
+ if (ret < 0)
+ errx(EXIT_FAILURE, "dewey_match returned error");
+ return ret;
+ }
+ if (strpbrk(pattern, "*?[]") != (char *) NULL) {
+ /* glob match */
+ return glob_match(pattern, pkg);
+ }
+
+ /* no alternate, dewey or glob match -> simple compare */
+ return simple_match(pattern, pkg);
+}
diff --git a/pkgtools/pbulk/files/pbulk/lib/netaddr.c b/pkgtools/pbulk/files/pbulk/lib/netaddr.c
new file mode 100644
index 00000000000..9c5e06fcf0b
--- /dev/null
+++ b/pkgtools/pbulk/files/pbulk/lib/netaddr.c
@@ -0,0 +1,74 @@
+/* $NetBSD: netaddr.c,v 1.1.1.1 2007/06/19 19:49:58 joerg Exp $ */
+
+/*-
+ * Copyright (c) 2007 Joerg Sonnenberger <joerg@NetBSD.org>.
+ * All rights reserved.
+ *
+ * This code was developed as part of Google's Summer of Code 2007 program.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <errno.h>
+#include <netinet/in.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "pbulk.h"
+
+int
+parse_sockaddr_in(const char *str, struct sockaddr_in *addr)
+{
+ const char *port_sep;
+ char *port_end;
+ struct in_addr in;
+ unsigned long tmp;
+
+ if ((port_sep = strrchr(str, ':')) != NULL) {
+ char *addr_part = strdup(str);
+
+ addr_part[port_sep - str] = '\0';
+ if (inet_aton(addr_part, &in) == 0) {
+ free(addr_part);
+ return -1;
+ }
+ free(addr_part);
+ str = port_sep + 1;
+ } else {
+ memset(&in, 0, sizeof(in));
+ }
+
+ errno = 0;
+ tmp = strtoul(str, &port_end, 10);
+ if (*str == '\0' || *port_end != '\0' || errno != 0 || tmp > 0xfffful)
+ return -1;
+ addr->sin_port = htons((in_port_t)tmp);
+ addr->sin_addr = in;
+ addr->sin_len = sizeof(*addr);
+ addr->sin_family = AF_INET;
+ return 0;
+}
diff --git a/pkgtools/pbulk/files/pbulk/lib/pbulk.h b/pkgtools/pbulk/files/pbulk/lib/pbulk.h
new file mode 100644
index 00000000000..de3cbac1ef0
--- /dev/null
+++ b/pkgtools/pbulk/files/pbulk/lib/pbulk.h
@@ -0,0 +1,65 @@
+/* $NetBSD: pbulk.h,v 1.1.1.1 2007/06/19 19:49:58 joerg Exp $ */
+
+/*-
+ * Copyright (c) 2007 Joerg Sonnenberger <joerg@NetBSD.org>.
+ * All rights reserved.
+ *
+ * This code was developed as part of Google's Summer of Code 2007 program.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <netinet/in.h>
+#include <unistd.h>
+
+#if defined(__GNUC__) && __GNUC__ >= 2
+char *xasprintf(const char *, ...)
+ __attribute__((__format__(__printf__, 1, 2)));
+#else
+char *xasprintf(const char *, ...);
+#endif
+
+void deferred_read(int fd, void *, size_t, void *,
+ void (*)(void *), void (*)(void *));
+void deferred_write(int fd, const void *, size_t,
+ void *, void (*)(void *), void (*)(void *));
+
+ssize_t atomic_read(int, void *, size_t);
+ssize_t atomic_write(int, const void *, size_t);
+
+
+int parse_sockaddr_in(const char *, struct sockaddr_in *);
+pid_t fork_chdir_exec(const char *, const char *,
+ const char * const *, int *);
+char *read_from_child(const char *, const char *,
+ const char * const *);
+char *read_from_file(int fd);
+void *xmalloc(size_t);
+void *xrealloc(void *, size_t);
+char *xstrdup(const char *);
+char *xstrndup(const char *, size_t);
+
+int pkg_match(const char *, const char *);
+const char *pkg_order(const char *, const char *);
diff --git a/pkgtools/pbulk/files/pbulk/lib/read_child.c b/pkgtools/pbulk/files/pbulk/lib/read_child.c
new file mode 100644
index 00000000000..6c0e0ad4262
--- /dev/null
+++ b/pkgtools/pbulk/files/pbulk/lib/read_child.c
@@ -0,0 +1,84 @@
+/* $NetBSD: read_child.c,v 1.1.1.1 2007/06/19 19:49:58 joerg Exp $ */
+
+/*-
+ * Copyright (c) 2007 Joerg Sonnenberger <joerg@NetBSD.org>.
+ * All rights reserved.
+ *
+ * This code was developed as part of Google's Summer of Code 2007 program.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/wait.h>
+#include <err.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "pbulk.h"
+
+char *
+read_from_child(const char *dir, const char *cmd, const char * const *argv)
+{
+ const char *error;
+ char *buf;
+ size_t buf_len, cur_len;
+ ssize_t bytes_read;
+ int fd, status;
+ pid_t child;
+
+ if ((child = fork_chdir_exec(dir, cmd, argv, &fd)) == -1)
+ return NULL;
+
+ cur_len = 0;
+ buf_len = 4096;
+ buf = xmalloc(buf_len + 1);
+
+ while ((bytes_read = read(fd, buf + cur_len, buf_len - cur_len)) > 0) {
+ cur_len += bytes_read;
+ if (cur_len * 2 < buf_len)
+ continue;
+ buf_len *= 2;
+ buf = xrealloc(buf, buf_len + 1);
+ }
+ if (bytes_read == -1) {
+ error = "read failed";
+ (void)close(fd);
+ (void)kill(child, SIGTERM);
+ (void)waitpid(child, &status, 0);
+ err(1, error);
+ }
+
+ (void)close(fd);
+ (void)waitpid(child, &status, 0);
+
+ if (status != 0 || memchr(buf, 0, cur_len) != NULL) {
+ free(buf);
+ return NULL;
+ }
+
+ buf[cur_len] = '\0';
+ return buf;
+}
diff --git a/pkgtools/pbulk/files/pbulk/lib/read_file.c b/pkgtools/pbulk/files/pbulk/lib/read_file.c
new file mode 100644
index 00000000000..3277f88a5a4
--- /dev/null
+++ b/pkgtools/pbulk/files/pbulk/lib/read_file.c
@@ -0,0 +1,71 @@
+/* $NetBSD: read_file.c,v 1.1.1.1 2007/06/19 19:49:58 joerg Exp $ */
+
+/*-
+ * Copyright (c) 2007 Joerg Sonnenberger <joerg@NetBSD.org>.
+ * All rights reserved.
+ *
+ * This code was developed as part of Google's Summer of Code 2007 program.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/stat.h>
+#include <err.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "pbulk.h"
+
+char *
+read_from_file(int fd)
+{
+ struct stat sb;
+ size_t input_len;
+ ssize_t bytes_read;
+ char *input;
+
+ if (fstat(fd, &sb) == -1)
+ err(1, "Cannot stat input");
+
+ if ((sb.st_mode & S_IFMT) != S_IFREG)
+ errx(1, "Input is not regular file");
+ if (sb.st_size > SSIZE_MAX)
+ errx(1, "Input too large");
+
+ input_len = (size_t)sb.st_size;
+ input = xmalloc(input_len + 1);
+ if ((bytes_read = read(fd, input, input_len)) == -1)
+ err(1, "Failed to read input");
+ if (bytes_read != sb.st_size)
+ errx(1, "Unexpected short read");
+
+ input[input_len] = '\0';
+ if (strlen(input) != input_len)
+ errx(1, "Invalid input");
+
+ return input;
+}
diff --git a/pkgtools/pbulk/files/pbulk/pbuild/Makefile b/pkgtools/pbulk/files/pbulk/pbuild/Makefile
new file mode 100644
index 00000000000..d5ed9ebb2ce
--- /dev/null
+++ b/pkgtools/pbulk/files/pbulk/pbuild/Makefile
@@ -0,0 +1,6 @@
+# $NetBSD: Makefile,v 1.1.1.1 2007/06/19 19:49:56 joerg Exp $
+
+PROG= pbulk-build
+SRCS= pbuild.c jobs.c client.c master.c stat.c
+
+.include <bsd.prog.mk>
diff --git a/pkgtools/pbulk/files/pbulk/pbuild/client.c b/pkgtools/pbulk/files/pbulk/pbuild/client.c
new file mode 100644
index 00000000000..7faf5bf2bb6
--- /dev/null
+++ b/pkgtools/pbulk/files/pbulk/pbuild/client.c
@@ -0,0 +1,116 @@
+/* $NetBSD: client.c,v 1.1.1.1 2007/06/19 19:49:56 joerg Exp $ */
+
+/*-
+ * Copyright (c) 2007 Joerg Sonnenberger <joerg@NetBSD.org>.
+ * All rights reserved.
+ *
+ * This code was developed as part of Google's Summer of Code 2007 program.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/socket.h>
+#include <err.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <arpa/inet.h>
+
+#include "pbulk.h"
+#include "pbuild.h"
+
+void
+client_mode(const char *client_port)
+{
+ struct sockaddr_in dst;
+ uint32_t build_info_len;
+ ssize_t recv_bytes, sent_bytes;
+ char *build_info;
+ int fd;
+
+ if (parse_sockaddr_in(client_port, &dst))
+ errx(1, "Could not parse addr/port");
+
+ fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+ if (fd == -1)
+ err(1, "Could not create socket");
+ if (connect(fd, (struct sockaddr *)&dst, dst.sin_len) == -1)
+ err(1, "Could not connect socket");
+
+loop:
+ sent_bytes = atomic_write(fd, "G", 1);
+ if (sent_bytes == -1)
+ err(1, "Could not write to socket");
+ if (sent_bytes == 0)
+ exit(0);
+ if (sent_bytes != 1)
+ errx(1, "Premature end of stream while writing to socket");
+
+ recv_bytes = atomic_read(fd, &build_info_len, 4);
+ if (recv_bytes == 0 || (recv_bytes == -1 && errno == ECONNRESET))
+ exit(0);
+ if (recv_bytes == -1)
+ err(1, "Could not read from socket");
+ if (recv_bytes != 4)
+ errx(1, "Premature end while reading build info from socket");
+ build_info_len = ntohl(build_info_len);
+ if (build_info_len < 10 || build_info_len > 0xffffff)
+ errx(1, "Invalid build info length from master");
+
+ build_info = xmalloc(build_info_len + 1);
+ build_info[build_info_len] = '\0';
+ recv_bytes = atomic_read(fd, build_info, build_info_len);
+ if (recv_bytes == -1)
+ err(1, "Could not read from socket");
+ if (recv_bytes != build_info_len || strlen(build_info) != build_info_len)
+ errx(1, "Premature end of stream while reading path from socket");
+
+ if (verbosity > 0) {
+ const char *begin, *end;
+
+ if (strncmp(build_info, "PKGNAME=", 8) != 0)
+ err(1, "Inconsistent build info from server");
+ begin = build_info + 8;
+ if ((end = strchr(begin, '\n')) == NULL)
+ err(1, "Inconsistent build info from server");
+ printf("Building package %.*s", (int)(end - begin), begin);
+ fflush(stdout);
+ }
+
+ if (build_package(build_info, build_info_len) == 0)
+ sent_bytes = atomic_write(fd, "D", 1);
+ else
+ sent_bytes = atomic_write(fd, "F", 1);
+ if (sent_bytes == -1)
+ err(1, "Could not write to socket");
+ if (sent_bytes != 1)
+ errx(1, "Premature end of stream while writing to socket");
+ free(build_info);
+ goto loop;
+}
diff --git a/pkgtools/pbulk/files/pbulk/pbuild/jobs.c b/pkgtools/pbulk/files/pbulk/pbuild/jobs.c
new file mode 100644
index 00000000000..ee562f61fd2
--- /dev/null
+++ b/pkgtools/pbulk/files/pbulk/pbuild/jobs.c
@@ -0,0 +1,555 @@
+/* $NetBSD: jobs.c,v 1.1.1.1 2007/06/19 19:49:56 joerg Exp $ */
+
+/*-
+ * Copyright (c) 2007 Joerg Sonnenberger <joerg@NetBSD.org>.
+ * All rights reserved.
+ *
+ * This code was developed as part of Google's Summer of Code 2007 program.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/stat.h>
+#include <err.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "pbulk.h"
+#include "pbuild.h"
+
+static int log_success;
+static int log_failed;
+
+SLIST_HEAD(buildhash, build_job);
+
+static void build_tree(void);
+static void mark_initial(void);
+static struct buildhash *get_hash_chain(const char *);
+static void hash_entries(void);
+static void add_to_build_list(struct build_job *);
+
+static struct build_job *jobs;
+static size_t allocated_jobs, len_jobs;
+static char *scan_output_content;
+
+static TAILQ_HEAD(, build_job) buildable_jobs;
+
+static void
+ts_printf(const char *fmt, ...)
+{
+ struct tm *tm;
+ time_t now;
+ va_list ap;
+ char buf[512];
+
+ if (verbosity >= 2) {
+ now = time(NULL);
+ tm = localtime(&now);
+ if (strftime(buf, sizeof(buf), "%F %R", tm) == 0)
+ errx(1, "Formatted time doesn't fit into buffer");
+
+ (void)printf("%s ", buf);
+ }
+
+ va_start(ap, fmt);
+ (void)vprintf(fmt, ap);
+ va_end(ap);
+ (void)fflush(stdout);
+}
+
+static const char *
+find_content(struct build_job *job, const char *prefix)
+{
+ size_t len = strlen(prefix);
+ const char *line;
+
+ for (line = job->begin; line != job->end; line = strchr(line, '\n') + 1) {
+ if (strncmp(line, prefix, len) == 0)
+ return line + len;
+ }
+ return NULL;
+}
+
+static char *
+pkgname_dup(const char *line)
+{
+ const char *pkgname;
+ char *pkgname_end;
+ size_t pkgname_len;
+
+ if (strncmp(line, "PKGNAME=", 8) != 0)
+ return NULL;
+ pkgname = line + 8;
+ pkgname_end = strchr(pkgname, '\n');
+ pkgname_len = pkgname_end - pkgname;
+ if (pkgname_end == NULL || pkgname_len < 4 ||
+ strcspn(pkgname, " \t\n") != pkgname_len)
+ return NULL;
+ return xstrndup(pkgname, pkgname_len);
+}
+
+static const char *
+pbulk_item_end(const char *line)
+{
+ const char *line_end;
+
+ do {
+ line_end = strchr(line, '\n');
+ if (line_end == NULL)
+ return NULL;
+ line = line_end + 1;
+ if (strncmp(line, "PKGNAME=", 8) == 0)
+ return line;
+ } while (*line != '\0');
+
+ return line;
+}
+
+SLIST_HEAD(depth_tree_head, build_job);
+
+static void
+compute_tree_depth_rec(struct build_job *job, struct build_job *root,
+ struct depth_tree_head *head, int *count)
+{
+ struct dependency_list *dep_iter;
+ struct build_job *job_iter;
+
+ if (job == root && *count != 0)
+ errx(1, "Cyclic dependency for package %s", job->pkgname);
+
+ SLIST_FOREACH(job_iter, head, depth_tree_link) {
+ if (job_iter == job)
+ return;
+ }
+ SLIST_INSERT_HEAD(head, job, depth_tree_link);
+ *count = *count + 1;
+ SLIST_FOREACH(dep_iter, &job->depending_pkgs, depends_link)
+ compute_tree_depth_rec(dep_iter->dependency, root, head, count);
+}
+
+static void
+compute_tree_depth(struct build_job *job)
+{
+ struct depth_tree_head head;
+
+ SLIST_INIT(&head);
+ job->pkg_depth = 0;
+ compute_tree_depth_rec(job, job, &head, &job->pkg_depth);
+}
+
+static void
+mark_restricted_rec(struct build_job *job)
+{
+ struct dependency_list *iter;
+
+ if (job->unrestricted_subset == 0)
+ return;
+
+ job->unrestricted_subset = 0;
+
+ SLIST_FOREACH(iter, &job->depending_pkgs, depends_link)
+ mark_restricted_rec(iter->dependency);
+}
+
+static void
+mark_restricted(void)
+{
+ size_t i;
+ const char *restricted, *no_bin_on_ftp;
+
+ for (i = 0; i < len_jobs; ++i) {
+ restricted = find_content(&jobs[i], "RESTRICTED=");
+ no_bin_on_ftp = find_content(&jobs[i], "NO_BIN_ON_FTP=");
+ if ((restricted != NULL && *restricted != '\0' && *restricted != '\n') ||
+ (no_bin_on_ftp != NULL && *no_bin_on_ftp != '\0' && *no_bin_on_ftp != '\n'))
+ mark_restricted_rec(&jobs[i]);
+ }
+}
+
+void
+init_jobs(const char *scan_output, const char *success_file, const char *error_file)
+{
+ char *input;
+ const char *input_iter;
+ int fd;
+ size_t i;
+
+ TAILQ_INIT(&buildable_jobs);
+
+ if ((fd = open(scan_output, O_RDONLY, 0)) == -1)
+ err(1, "Cannot open input");
+
+ if ((log_success = open(success_file, O_RDWR | O_CREAT, 0666)) == -1)
+ err(1, "Cannot open log file for successful builds");
+
+ if ((log_failed = open(error_file, O_RDWR | O_CREAT, 0666)) == -1)
+ err(1, "Cannot open log file for failed builds");
+
+ input = read_from_file(fd);
+ (void)close(fd);
+
+ if (allocated_jobs == 0) {
+ allocated_jobs = 1024;
+ jobs = xmalloc(allocated_jobs * sizeof(*jobs));
+ }
+
+ input_iter = input;
+ while ((jobs[len_jobs].pkgname = pkgname_dup(input_iter)) != NULL) {
+ jobs[len_jobs].begin = input_iter;
+ jobs[len_jobs].end = pbulk_item_end(input_iter);
+ jobs[len_jobs].state = JOB_INIT;
+ jobs[len_jobs].open_depends = 0;
+ jobs[len_jobs].unrestricted_subset = 1;
+ SLIST_INIT(&jobs[len_jobs].depending_pkgs);
+ if (jobs[len_jobs].end == NULL)
+ errx(1, "Invalid input");
+ input_iter = jobs[len_jobs].end;
+ ++len_jobs;
+ if (len_jobs == allocated_jobs) {
+ allocated_jobs *= 2;
+ jobs = xrealloc(jobs, allocated_jobs * sizeof(*jobs));
+ }
+ }
+
+ if (*input_iter != '\0')
+ errx(1, "Invalid input");
+ scan_output_content = input;
+
+ hash_entries();
+ build_tree();
+
+ for (i = 0; i < len_jobs; ++i)
+ compute_tree_depth(&jobs[i]);
+
+ mark_initial();
+ mark_restricted();
+
+ for (i = 0; i < len_jobs; ++i) {
+ if (jobs[i].state == JOB_INIT)
+ process_job(&jobs[i], JOB_OPEN, 0);
+ }
+}
+
+static void
+mark_initial_state(int fd, enum job_state state, const char *type)
+{
+ char *input;
+ const char *input_iter;
+ struct build_job *iter;
+ size_t len;
+
+ input = read_from_file(fd);
+ input_iter = input;
+
+ while (*input_iter != '\0') {
+ len = strcspn(input_iter, "\n");
+ SLIST_FOREACH(iter, get_hash_chain(input_iter), hash_link) {
+ if (strncmp(iter->pkgname, input_iter, len) == 0 &&
+ iter->pkgname[len] == '\0')
+ break;
+ }
+ if (iter == NULL)
+ errx(1, "Package from %s build log doesn't exist: %.*s", type, (int)len, input_iter);
+
+ process_job(iter, state, 0);
+
+ input_iter = strchr(input_iter, '\n');
+ if (input_iter == NULL)
+ errx(1, "Invalid input");
+ ++input_iter;
+ }
+ free(input);
+}
+
+static void
+mark_initial(void)
+{
+ const char *line;
+ size_t i;
+
+ for (i = 0; i < len_jobs; ++i) {
+ if ((line = find_content(&jobs[i], "PKG_SKIP_REASON=")) == NULL)
+ errx(1, "Invalid scan output");
+ if (*line != '\n') {
+ process_job(&jobs[i], JOB_PREFAILED, 0);
+ continue;
+ }
+ if ((line = find_content(&jobs[i], "PKG_FAIL_REASON=")) == NULL)
+ errx(1, "Invalid scan output");
+ if (*line != '\n') {
+ process_job(&jobs[i], JOB_PREFAILED, 0);
+ continue;
+ }
+ }
+
+ mark_initial_state(log_success, JOB_DONE, "succesful");
+ mark_initial_state(log_failed, JOB_FAILED, "failing");
+}
+
+static void
+add_to_build_list(struct build_job *job)
+{
+ struct build_job *iter;
+
+ TAILQ_FOREACH(iter, &buildable_jobs, build_link) {
+ if (iter->pkg_depth < job->pkg_depth) {
+ TAILQ_INSERT_BEFORE(iter, job, build_link);
+ return;
+ }
+ }
+ TAILQ_INSERT_TAIL(&buildable_jobs, job, build_link);
+}
+
+static void
+build_tree(void)
+{
+ size_t i, len;
+ struct build_job *iter, *job;
+ struct dependency_list *dep;
+ const char *depends;
+
+ for (i = 0; i < len_jobs; ++i) {
+ job = &jobs[i];
+
+ if ((depends = find_content(job, "DEPENDS=")) == NULL)
+ continue;
+
+ depends += strspn(depends, " \t");
+
+ while ((len = strcspn(depends, " \t\n")) != 0) {
+ SLIST_FOREACH(iter, get_hash_chain(depends), hash_link) {
+ if (strncmp(iter->pkgname, depends, len) == 0 &&
+ iter->pkgname[len] == '\0')
+ break;
+ }
+ if (iter == NULL)
+ errx(1, "Dependency doesn't exist");
+
+ dep = xmalloc(sizeof(*dep));
+ dep->dependency = job;
+ SLIST_INSERT_HEAD(&iter->depending_pkgs, dep, depends_link);
+ ++job->open_depends;
+
+ depends += len;
+ depends += strspn(depends, " \t");
+ }
+ }
+}
+
+struct build_job *
+get_job(void)
+{
+ struct build_job *job;
+
+ if ((job = TAILQ_FIRST(&buildable_jobs)) != NULL) {
+ TAILQ_REMOVE(&buildable_jobs, job, build_link);
+
+ process_job(job, JOB_IN_PROCESSING, 0);
+ }
+ return job;
+}
+
+static void
+recursive_mark_broken(struct build_job *job, enum job_state state)
+{
+ struct dependency_list *iter;
+
+ SLIST_FOREACH(iter, &job->depending_pkgs, depends_link) {
+ if (iter->dependency->state == JOB_OPEN ||
+ iter->dependency->state == JOB_INIT)
+ process_job(iter->dependency, state, 0);
+ }
+}
+
+void
+process_job(struct build_job *job, enum job_state state, int log_state)
+{
+ struct dependency_list *iter;
+ char *buf;
+
+ job->state = state;
+
+ switch (state) {
+ case JOB_INIT:
+ errx(1, "Programming error");
+ case JOB_DONE:
+ SLIST_FOREACH(iter, &job->depending_pkgs, depends_link) {
+ --iter->dependency->open_depends;
+ if (iter->dependency->open_depends == 0 &&
+ iter->dependency->state == JOB_OPEN)
+ add_to_build_list(iter->dependency);
+ }
+ if (log_state) {
+ buf = xasprintf("%s\n", job->pkgname);
+ if (write(log_success, buf, strlen(buf)) == -1)
+ err(1, "Cannot log successful build");
+ free(buf);
+ }
+ if (verbosity >= 1)
+ ts_printf("Successfully finished build of %s\n", job->pkgname);
+ break;
+ case JOB_FAILED:
+ if (log_state) {
+ buf = xasprintf("%s\n", job->pkgname);
+ if (write(log_failed, buf, strlen(buf)) == -1)
+ err(1, "Cannot log failed build");
+ free(buf);
+ }
+ if (verbosity >= 1)
+ ts_printf("Failed to build of %s\n", job->pkgname);
+ /* FALLTHROUGH */
+ case JOB_INDIRECT_FAILED:
+ recursive_mark_broken(job, JOB_INDIRECT_FAILED);
+ break;
+ case JOB_PREFAILED:
+ case JOB_INDIRECT_PREFAILED:
+ /* FALLTHROUGH */
+ recursive_mark_broken(job, JOB_INDIRECT_PREFAILED);
+ break;
+ case JOB_IN_PROCESSING:
+ if (verbosity >= 1)
+ ts_printf("Starting build of %s\n", job->pkgname);
+ break;
+ case JOB_OPEN:
+ if (job->open_depends == 0)
+ add_to_build_list(job);
+ break;
+ }
+}
+
+void
+build_stats(struct build_stat *st)
+{
+ size_t i;
+
+ st->open_jobs = 0;
+ st->in_processing = 0;
+ st->failed = 0;
+ st->prefailed = 0;
+ st->indirect_failed = 0;
+ st->indirect_prefailed = 0;
+ st->done = 0;
+
+ for (i = 0; i < len_jobs; ++i) {
+ switch (jobs[i].state) {
+ case JOB_DONE:
+ ++st->done;
+ break;
+ case JOB_FAILED:
+ ++st->failed;
+ break;
+ case JOB_PREFAILED:
+ ++st->prefailed;
+ break;
+ case JOB_INDIRECT_FAILED:
+ ++st->indirect_failed;
+ break;
+ case JOB_INDIRECT_PREFAILED:
+ ++st->indirect_prefailed;
+ break;
+ case JOB_OPEN:
+ ++st->open_jobs;
+ break;
+ case JOB_IN_PROCESSING:
+ ++st->in_processing;
+ break;
+ case JOB_INIT:
+ break;
+ }
+ }
+}
+
+void
+finish_build(const char *report_file)
+{
+ FILE *report;
+ size_t i;
+ const char *status;
+
+ if ((report = fopen(report_file, "w")) == NULL)
+ err(1, "Cannot open report file");
+
+ for (i = 0; i < len_jobs; ++i) {
+ switch (jobs[i].state) {
+ case JOB_DONE:
+ status = "done";
+ break;
+ case JOB_FAILED:
+ status = "failed";
+ break;
+ case JOB_PREFAILED:
+ status = "prefailed";
+ break;
+ case JOB_INDIRECT_FAILED:
+ status = "indirect-failed";
+ break;
+ case JOB_INDIRECT_PREFAILED:
+ status = "indirect-prefailed";
+ break;
+ case JOB_OPEN:
+ status = "open";
+ break;
+ case JOB_IN_PROCESSING:
+ status = "in_processing";
+ break;
+ default:
+ errx(1, "internal error");
+ }
+ fprintf(report, "%s|%s|%s|%d\n", jobs[i].pkgname, status,
+ jobs[i].unrestricted_subset == 0 ? "restricted" : "",
+ jobs[i].pkg_depth);
+ }
+}
+
+#define HASH_SIZE 4096
+#define HASH_ITEM(x) (((unsigned char)(x)[0] + (unsigned char)(x)[1] * 257 + (unsigned char)(x)[1] * 65537) & (HASH_SIZE - 1))
+
+static struct buildhash hash_table[HASH_SIZE];
+
+static void
+hash_entries(void)
+{
+ size_t i, hash;
+
+ for (i = 0; i < HASH_SIZE; ++i)
+ SLIST_INIT(&hash_table[i]);
+
+ for (i = 0; i < len_jobs; ++i) {
+ hash = HASH_ITEM(jobs[i].pkgname);
+ SLIST_INSERT_HEAD(&hash_table[hash], &jobs[i], hash_link);
+ }
+}
+
+static struct buildhash *
+get_hash_chain(const char *pkgname)
+{
+ return &hash_table[HASH_ITEM(pkgname)];
+}
diff --git a/pkgtools/pbulk/files/pbulk/pbuild/master.c b/pkgtools/pbulk/files/pbulk/pbuild/master.c
new file mode 100644
index 00000000000..fad3870c551
--- /dev/null
+++ b/pkgtools/pbulk/files/pbulk/pbuild/master.c
@@ -0,0 +1,290 @@
+/* $NetBSD: master.c,v 1.1.1.1 2007/06/19 19:49:56 joerg Exp $ */
+
+/*-
+ * Copyright (c) 2007 Joerg Sonnenberger <joerg@NetBSD.org>.
+ * All rights reserved.
+ *
+ * This code was developed as part of Google's Summer of Code 2007 program.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/ioctl.h>
+#include <sys/queue.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <sys/wait.h>
+#include <err.h>
+#include <event.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "pbulk.h"
+#include "pbuild.h"
+
+static LIST_HEAD(, build_peer) active_peers, inactive_peers, unassigned_peers;
+
+struct build_peer {
+ LIST_ENTRY(build_peer) peer_link;
+
+ struct build_job *job;
+
+ int fd;
+ char tmp_buf[4];
+ char *buf;
+};
+
+static void assign_job(void *);
+static void recv_command(struct build_peer *);
+
+static void
+kill_peer(void *arg)
+{
+ struct build_peer *peer = arg;
+
+ (void)close(peer->fd);
+ LIST_REMOVE(peer, peer_link);
+ if (peer->job != NULL)
+ process_job(peer->job, JOB_OPEN, 1);
+ free(peer->buf);
+ free(peer);
+
+ peer = LIST_FIRST(&unassigned_peers);
+ if (peer != NULL)
+ assign_job(peer);
+}
+
+static void
+finish_job(void *arg)
+{
+ struct build_peer *peer = arg;
+
+ LIST_REMOVE(peer, peer_link);
+ if (peer->tmp_buf[0] == 'D')
+ process_job(peer->job, JOB_DONE, 1);
+ else if (peer->tmp_buf[0] == 'F')
+ process_job(peer->job, JOB_FAILED, 1);
+ else
+ kill_peer(peer);
+ peer->job = NULL;
+ recv_command(peer);
+
+ peer = LIST_FIRST(&unassigned_peers);
+ if (peer != NULL)
+ assign_job(peer);
+}
+
+static void
+recv_status(void *arg)
+{
+ struct build_peer *peer = arg;
+
+ deferred_read(peer->fd, peer->tmp_buf, 1, peer, finish_job,
+ kill_peer);
+}
+
+static void
+send_build_info(void *arg)
+{
+ struct build_peer *peer = arg;
+
+ deferred_write(peer->fd, peer->job->begin, peer->job->end - peer->job->begin, peer, recv_status,
+ kill_peer);
+}
+
+static void
+sent_build_stats(void *arg)
+{
+ struct build_peer *peer = arg;
+
+ free(peer->buf);
+ peer->buf = NULL;
+
+ assign_job(peer);
+}
+
+static void
+send_build_stats(struct build_peer *peer)
+{
+ struct build_stat st;
+ uint32_t tmp;
+
+ build_stats(&st);
+
+ peer->buf = xmalloc(7 * 4);
+
+ tmp = htonl(st.open_jobs);
+ (void)memcpy(peer->buf, &tmp, 4);
+
+ tmp = htonl(st.in_processing);
+ (void)memcpy(peer->buf + 4, &tmp, 4);
+
+ tmp = htonl(st.failed);
+ (void)memcpy(peer->buf + 8, &tmp, 4);
+
+ tmp = htonl(st.prefailed);
+ (void)memcpy(peer->buf + 12, &tmp, 4);
+
+ tmp = htonl(st.indirect_failed);
+ (void)memcpy(peer->buf + 16, &tmp, 4);
+
+ tmp = htonl(st.indirect_prefailed);
+ (void)memcpy(peer->buf + 20, &tmp, 4);
+
+ tmp = htonl(st.done);
+ (void)memcpy(peer->buf + 24, &tmp, 4);
+
+ deferred_write(peer->fd, peer->buf, 7 * 4, peer, sent_build_stats, kill_peer);
+}
+
+static void
+assign_job(void *arg)
+{
+ struct build_peer *peer = arg;
+ size_t build_info_len;
+ uint32_t net_build_info_len;
+
+ if (peer->tmp_buf[0] == 'S') {
+ send_build_stats(peer);
+ return;
+ }
+
+ if (peer->tmp_buf[0] != 'G') {
+ kill_peer(peer);
+ return;
+ }
+
+ LIST_REMOVE(peer, peer_link);
+
+ peer->job = get_job();
+ if (peer->job == NULL) {
+ LIST_INSERT_HEAD(&unassigned_peers, peer, peer_link);
+ if (LIST_EMPTY(&active_peers))
+ event_loopexit(NULL);
+ return;
+ }
+
+ LIST_INSERT_HEAD(&active_peers, peer, peer_link);
+
+ build_info_len = peer->job->end - peer->job->begin;
+ if (build_info_len > 0xffffff)
+ errx(1, "Build info too long");
+
+ net_build_info_len = htonl(build_info_len);
+ (void)memcpy(peer->tmp_buf, &net_build_info_len, 4);
+
+ deferred_write(peer->fd, peer->tmp_buf, 4, peer, send_build_info,
+ kill_peer);
+
+ peer = LIST_FIRST(&unassigned_peers);
+ if (peer != NULL)
+ assign_job(peer);
+}
+
+static void
+recv_command(struct build_peer *peer)
+{
+ LIST_INSERT_HEAD(&inactive_peers, peer, peer_link);
+
+ deferred_read(peer->fd, peer->tmp_buf, 1, peer, assign_job,
+ kill_peer);
+}
+
+static void
+listen_handler(int sock, short event, void *arg)
+{
+ struct build_peer *peer;
+ struct sockaddr_in src;
+ socklen_t src_len;
+ int fd, ioctl_arg;
+
+ src_len = sizeof(src);
+ if ((fd = accept(sock, (struct sockaddr *)&src, &src_len)) == -1) {
+ warn("Could not accept connection");
+ return;
+ }
+ ioctl_arg = 1;
+ if (ioctl(fd, FIONBIO, &ioctl_arg) == -1) {
+ (void)close(fd);
+ warn("Could not set non-blocking IO");
+ return;
+ }
+
+ peer = xmalloc(sizeof(*peer));
+ peer->fd = fd;
+ peer->buf = NULL;
+ recv_command(peer);
+}
+
+void
+master_mode(const char *master_port, const char *start_script)
+{
+ struct event listen_event;
+ struct sockaddr_in dst;
+ int fd;
+
+ LIST_INIT(&active_peers);
+ LIST_INIT(&inactive_peers);
+ LIST_INIT(&unassigned_peers);
+
+ event_init();
+
+ if (parse_sockaddr_in(master_port, &dst))
+ errx(1, "Could not parse addr/port");
+
+ fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+ if (fd == -1)
+ err(1, "Could not create socket");
+ if (ioctl(fd, FIOCLEX, NULL) == -1)
+ err(1, "Could not set close-on-exec flag");
+ if (bind(fd, (struct sockaddr *)&dst, dst.sin_len) == -1)
+ err(1, "Could not bind socket");
+ if (listen(fd, 5) == -1)
+ err(1, "Could not listen on socket");
+
+ if (start_script) {
+ pid_t child;
+ int status;
+
+ if ((child = vfork()) == 0) {
+ execlp(start_script, start_script, (char *)NULL);
+ _exit(255);
+ }
+ if (child == -1)
+ err(1, "Could not fork start script");
+ waitpid(child, &status, 0);
+ if (status != 0)
+ err(1, "Start script failed");
+ }
+
+ event_set(&listen_event, fd, EV_READ | EV_PERSIST, listen_handler, NULL);
+ event_add(&listen_event, NULL);
+
+ event_dispatch();
+
+ (void)close(fd);
+}
diff --git a/pkgtools/pbulk/files/pbulk/pbuild/pbuild.c b/pkgtools/pbulk/files/pbulk/pbuild/pbuild.c
new file mode 100644
index 00000000000..6d0368a1094
--- /dev/null
+++ b/pkgtools/pbulk/files/pbulk/pbuild/pbuild.c
@@ -0,0 +1,230 @@
+/* $NetBSD: pbuild.c,v 1.1.1.1 2007/06/19 19:49:56 joerg Exp $ */
+
+/*-
+ * Copyright (c) 2007 Joerg Sonnenberger <joerg@NetBSD.org>.
+ * All rights reserved.
+ *
+ * This code was developed as part of Google's Summer of Code 2007 program.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/wait.h>
+#include <err.h>
+#include <errno.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#include "pbulk.h"
+#include "pbuild.h"
+
+int verbosity;
+
+static const char *build_path;
+static const char *build_cmd;
+
+static void standalone_mode(void);
+
+static void
+usage(void)
+{
+ (void)fprintf(stderr, "usage: pbulk-build -s <master>\n");
+ (void)fprintf(stderr, "usage: pbulk-build [ -v ] -c <master> -b <build script>\n");
+ (void)fprintf(stderr, "usage: pbulk-build [ -v ] [ -I <start> ] [ -r <report> ] -m <port> <tree scan> <success file> <error file>\n");
+ (void)fprintf(stderr, "usage: pbulk-build [ -v ] [ -r <report> ] -b <build script> <tree scan> <success file> <error file>\n");
+ exit(1);
+}
+
+int
+main(int argc, char **argv)
+{
+ const char *stat_port;
+ const char *client_port;
+ const char *master_port;
+ const char *report_file;
+ const char *start_script;
+ struct sigaction sa;
+ int ch, modes;
+
+ client_port = NULL;
+ master_port = NULL;
+ stat_port = NULL;
+ start_script = NULL;
+ report_file = NULL;
+ modes = 0;
+
+ while ((ch = getopt(argc, argv, "I:b:c:m:r:s:v")) != -1) {
+ switch (ch) {
+ case 'I':
+ start_script = optarg;
+ break;
+ case 'b':
+ build_path = optarg;
+ break;
+ case 'c':
+ client_port = optarg;
+ ++modes;
+ break;
+ case 'm':
+ master_port = optarg;
+ ++modes;
+ break;
+ case 'r':
+ report_file = optarg;
+ break;
+ case 's':
+ stat_port = optarg;
+ ++modes;
+ break;
+ case 'v':
+ ++verbosity;
+ break;
+ default:
+ usage();
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (modes > 1) {
+ warnx("Only one of client mode, statistic mode or master mode can be active");
+ usage();
+ }
+
+ sa.sa_sigaction = NULL;
+ sa.sa_handler = SIG_IGN;
+ sa.sa_flags = 0;
+ (void)sigemptyset(&sa.sa_mask);
+ (void)sigaction(SIGPIPE, (struct sigaction *)&sa, NULL);
+
+ if (build_path == NULL && (modes == 0 || client_port != NULL))
+ usage();
+
+ if (stat_port != NULL) {
+ if (argc != 0)
+ usage();
+ stat_mode(stat_port);
+ }
+
+ if (master_mode == NULL) {
+ build_cmd = strrchr(build_path, '/');
+ if (build_cmd == NULL)
+ build_cmd = build_path;
+ else
+ ++build_cmd;
+ }
+
+ if (client_port != NULL) {
+ if (argc != 0)
+ usage();
+ client_mode(client_port);
+ }
+
+ if (argc != 3)
+ usage();
+
+ if (verbosity >= 2)
+ tzset();
+
+ init_jobs(argv[0], argv[1], argv[2]);
+
+ if (master_port != NULL)
+ master_mode(master_port, start_script);
+ else
+ standalone_mode();
+
+ if (report_file)
+ finish_build(report_file);
+
+ return 0;
+}
+
+int
+build_package(const char *build_info, size_t len)
+{
+ int input[2];
+ pid_t child;
+
+ if (pipe(input) == -1)
+ err(1, "Failed to create pipe");
+
+ child = vfork();
+ if (child == -1)
+ err(1, "Failed to create child");
+ if (child != 0) {
+ ssize_t bytes_written;
+ const char *begin;
+ size_t bytes_left;
+ int ret;
+
+ (void)close(input[0]);
+ begin = build_info;
+ bytes_left = len;
+ while (bytes_left > 0) {
+ bytes_written = write(input[1], begin, bytes_left);
+ if (bytes_written == -1 && errno == EPIPE)
+ break;
+ if (bytes_written <= 0) {
+ (void)close(input[1]);
+ (void)kill(child, SIGTERM);
+ (void)waitpid(child, &ret, 0);
+ return 1;
+ }
+ bytes_left -= bytes_written;
+ begin += bytes_written;
+ }
+ (void)close(input[1]);
+ (void)waitpid(child, &ret, 0);
+ return ret;
+ }
+
+ (void)close(input[1]);
+ if (dup2(input[0], 0) == -1) {
+ const char err_msg[] = "dup failed for stdin\n";
+
+ (void)write(STDERR_FILENO, err_msg, sizeof(err_msg) - 1);
+ _exit(255);
+ }
+
+ (void)execl(build_path, build_cmd, (char *)NULL);
+ _exit(255);
+}
+
+static void
+standalone_mode(void)
+{
+ struct build_job *job;
+
+ while ((job = get_job()) != NULL) {
+ if (build_package(job->begin, job->end - job->begin) == 0)
+ process_job(job, JOB_DONE, 1);
+ else
+ process_job(job, JOB_FAILED, 1);
+ }
+}
diff --git a/pkgtools/pbulk/files/pbulk/pbuild/pbuild.h b/pkgtools/pbulk/files/pbulk/pbuild/pbuild.h
new file mode 100644
index 00000000000..383c2082d05
--- /dev/null
+++ b/pkgtools/pbulk/files/pbulk/pbuild/pbuild.h
@@ -0,0 +1,92 @@
+/* $NetBSD: pbuild.h,v 1.1.1.1 2007/06/19 19:49:56 joerg Exp $ */
+
+/*-
+ * Copyright (c) 2007 Joerg Sonnenberger <joerg@NetBSD.org>.
+ * All rights reserved.
+ *
+ * This code was developed as part of Google's Summer of Code 2007 program.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/queue.h>
+#include <inttypes.h>
+
+enum job_state {
+ JOB_INIT,
+ JOB_OPEN,
+ JOB_IN_PROCESSING,
+ JOB_FAILED,
+ JOB_PREFAILED,
+ JOB_INDIRECT_FAILED,
+ JOB_INDIRECT_PREFAILED,
+ JOB_DONE
+};
+
+struct build_stat {
+ size_t open_jobs;
+ size_t in_processing;
+ size_t failed;
+ size_t prefailed;
+ size_t indirect_failed;
+ size_t indirect_prefailed;
+ size_t done;
+};
+
+struct build_job;
+
+struct dependency_list {
+ struct build_job *dependency;
+ SLIST_ENTRY(dependency_list) depends_link;
+};
+
+struct build_job {
+ char *pkgname;
+ const char *begin;
+ const char *end;
+ enum job_state state;
+ int pkg_depth;
+ int unrestricted_subset;
+ size_t open_depends;
+
+ SLIST_HEAD(, dependency_list) depending_pkgs;
+
+ TAILQ_ENTRY(build_job) build_link;
+ SLIST_ENTRY(build_job) hash_link;
+ SLIST_ENTRY(build_job) depth_tree_link;
+};
+
+extern int verbosity;
+
+void init_jobs(const char *, const char *, const char *);
+struct build_job *get_job(void);
+void process_job(struct build_job *, enum job_state, int);
+int build_package(const char *, size_t);
+void finish_build(const char *);
+void build_stats(struct build_stat *);
+
+void client_mode(const char *);
+void master_mode(const char *, const char *);
+void stat_mode(const char *);
diff --git a/pkgtools/pbulk/files/pbulk/pbuild/pbulk-build.1 b/pkgtools/pbulk/files/pbulk/pbuild/pbulk-build.1
new file mode 100644
index 00000000000..ccdc4c027ec
--- /dev/null
+++ b/pkgtools/pbulk/files/pbulk/pbuild/pbulk-build.1
@@ -0,0 +1,132 @@
+.\" $NetBSD: pbulk-build.1,v 1.1.1.1 2007/06/19 19:49:55 joerg Exp $
+.\"
+.\" Copyright (c) 2007 Thomas Klausner and Joerg Sonnenberger.
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+.\" ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+.\" TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+.\" PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+.\" BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+.\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+.\" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+.\" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+.\" CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+.\" POSSIBILITY OF SUCH DAMAGE.
+.\"
+.Dd June 11, 2007
+.Dt PBULK-BUILD 1
+.Os
+.Sh NAME
+.Nm pbulk-build
+.Nd build all packages specified in input file
+.Sh SYNOPSIS
+.Nm
+.Fl s Oo Ar ip: Oc Ns Ar port
+.Nm
+.Op Fl v
+.Fl c Oo Ar ip: Oc Ns Ar port
+.Fl b Ar build_script
+.Nm
+.Op Fl I Ar start_script
+.Op Fl r Ar report_file
+.Op Fl v
+.Fl m Oo Ar ip: Oc Ns Ar port
+.Ar input success error
+.Nm
+.Op Fl r Ar report_file
+.Op Fl v
+.Fl b Ar build_script
+.Ar input success error
+.Sh DESCRIPTION
+.Nm
+builds all packages specified in an input file.
+.Pp
+Supported options are:
+.Bl -tag -width 15n -offset indent
+.It Fl b Ar build_script
+Use
+.Ar build_script
+for building the packages.
+See
+.Sx BUILD SCRIPT FORMAT
+for details.
+.It Fl c Oo Ar ip: Oc Ns Ar port
+Obtain jobs from master running on the given
+.Ar port
+(or
+.Ar ip:port ) .
+If used with
+.Fl v ,
+print the name of the package to build to stdout.
+.It Fl I Ar start_script
+Run
+.Ar start_script
+after opening the socket and wait for completion before entering build loop.
+.It Fl m Oo Ar ip: Oc Ns Ar port
+Enter master mode.
+The master binds to
+.Ar port
+(or
+.Ar ip:port )
+and waits for clients to connect and build individual packages.
+.It Fl s Oo Ar ip: Oc Ns Ar port
+Query the master running on the given
+.Ar port
+(or
+.Ar ip:port )
+for the current number of successful, open, and failed builds.
+.It Fl r Ar report_file
+Write name of each package,
+the result of its build,
+whether the package belongs to the restricted subset
+and the size of the subtree
+to
+.Ar report_file
+at the end of the build.
+.It Fl v
+Be more verbose.
+Details depend on the other flags used with it.
+.El
+In normal mode (neither
+.Fl c ,
+.Fl m ,
+nor
+.Fl s
+specified) and master mode
+.Pq Fl m ,
+.Nm
+reads the resolved tree scan from
+.Ar input .
+It then writes successful builds to
+.Ar success
+and failing builds to
+.Ar error .
+If either
+.Ar success
+or
+.Ar error
+exists at start-up, they are read and the build continues where
+they left off.
+If
+.Fl v
+is specified once,
+.Nm
+prints the start of each build and the result.
+If
+.Fl v
+is specified twice, each begin and end message is prefixed with
+the current time.
+.Ss BUILD SCRIPT FORMAT
+XXX: to be documented
+.\" XXX: .Sh EXIT STATUS
diff --git a/pkgtools/pbulk/files/pbulk/pbuild/stat.c b/pkgtools/pbulk/files/pbulk/pbuild/stat.c
new file mode 100644
index 00000000000..fe542e3ecb8
--- /dev/null
+++ b/pkgtools/pbulk/files/pbulk/pbuild/stat.c
@@ -0,0 +1,108 @@
+/* $NetBSD: stat.c,v 1.1.1.1 2007/06/19 19:49:56 joerg Exp $ */
+
+/*-
+ * Copyright (c) 2007 Joerg Sonnenberger <joerg@NetBSD.org>.
+ * All rights reserved.
+ *
+ * This code was developed as part of Google's Summer of Code 2007 program.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/socket.h>
+#include <err.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <arpa/inet.h>
+
+#include "pbulk.h"
+#include "pbuild.h"
+
+void
+stat_mode(const char *client_port)
+{
+ struct sockaddr_in dst;
+ ssize_t recv_bytes, sent_bytes;
+ char buf[7 * 4];
+ struct build_stat st;
+ uint32_t tmp;
+ int fd;
+
+ if (parse_sockaddr_in(client_port, &dst))
+ errx(1, "Could not parse addr/port");
+
+ fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+ if (fd == -1)
+ err(1, "Could not create socket");
+ if (connect(fd, (struct sockaddr *)&dst, dst.sin_len) == -1)
+ err(1, "Could not connect socket");
+
+ sent_bytes = write(fd, "S", 1);
+ if (sent_bytes == -1)
+ err(1, "Could not write to socket");
+ if (sent_bytes == 0)
+ exit(0);
+ if (sent_bytes != 1)
+ errx(1, "Premature end of stream while writing to socket");
+
+ recv_bytes = atomic_read(fd, &buf, 7 * 4);
+ if (recv_bytes == 0 || (recv_bytes == -1 && errno == ECONNRESET))
+ exit(0);
+ if (recv_bytes == -1)
+ err(1, "Could not read from socket");
+ if (recv_bytes != 7 * 4)
+ errx(1, "Premature end while reading statistics from socket");
+
+ (void)memcpy(&tmp, buf, 4);
+ st.open_jobs = ntohl(tmp);
+ (void)memcpy(&tmp, buf + 4, 4);
+ st.in_processing = ntohl(tmp);
+ (void)memcpy(&tmp, buf + 8, 4);
+ st.failed = ntohl(tmp);
+ (void)memcpy(&tmp, buf + 12, 4);
+ st.prefailed = ntohl(tmp);
+ (void)memcpy(&tmp, buf + 16, 4);
+ st.indirect_failed = ntohl(tmp);
+ (void)memcpy(&tmp, buf + 20, 4);
+ st.indirect_prefailed = ntohl(tmp);
+ (void)memcpy(&tmp, buf + 24, 4);
+ st.done = ntohl(tmp);
+
+ (void)printf("Jobs not yet processed: %zu\n", st.open_jobs);
+ (void)printf("Jobs currently in processing: %zu\n", st.in_processing);
+ (void)printf("Successful builds: %zu\n", st.done);
+ (void)printf("Failing builds: %zu\n", st.failed + st.prefailed + st.indirect_failed + st.indirect_prefailed);
+ (void)printf(" Directly broken: %zu\n", st.failed);
+ (void)printf(" Broken due to a broken dependency: %zu\n", st.indirect_failed);
+ (void)printf(" Not build as explicitly marked broken: %zu\n", st.prefailed);
+ (void)printf(" Broken due to an explicitly broken dependency: %zu\n", st.indirect_prefailed);
+
+ exit(0);
+}
diff --git a/pkgtools/pbulk/files/pbulk/pbulk.conf b/pkgtools/pbulk/files/pbulk/pbulk.conf
new file mode 100644
index 00000000000..ecbe4b9a18d
--- /dev/null
+++ b/pkgtools/pbulk/files/pbulk/pbulk.conf
@@ -0,0 +1,69 @@
+# $NetBSD: pbulk.conf,v 1.1.1.1 2007/06/19 19:49:55 joerg Exp $
+
+base_url=http://www.pkgsrc-box.org/reports/current/DragonFly-1.8
+master_mode=yes
+
+master_ip=192.168.75.10
+scan_clients="192.168.75.21 192.168.75.22 192.168.75.23 192.168.75.24"
+build_clients="192.168.75.21 192.168.75.22 192.168.75.23 192.168.75.24"
+
+master_port_scan=${master_ip}:2001
+master_port_build=${master_ip}:2002
+
+pkg_rsync_args="-av --delete-excluded -e ssh"
+pkg_rsync_target="pkgsrc@192.168.75.1:/public/packages/current/DragonFly-1.8"
+report_rsync_args="-av --delete-excluded -e ssh"
+report_rsync_target="pkgsrc@192.168.75.1:/public/reports/current/DragonFly-1.8"
+bootstrapkit=/usr/pkgsrc/bootstrap/bootstrap.tar.gz
+
+report_graph_script_limit=512
+
+bulklog=/bulklog
+packages=/packages
+prefix=/usr/pkg
+pkgsrc=/usr/pkgsrc
+pkgdb=/var/db/pkg
+varbase=/var
+
+pkg_install_prefix=/usr/pkg
+# For pkg_install from base (NetBSD only):
+# pkg_install_prefix=/usr
+# external_pkg_info=/usr/sbin/pkg_info
+# For pkg_install from pkgsrc:
+# pkg_install_prefix=$prefix
+# Note that ${external_pkg_info} must be outside ${prefix}!
+external_pkg_info="@PKG_INFO@"
+pkg_add=${pkg_install_prefix}/sbin/pkg_add
+pkg_delete=${pkg_install_prefix}/sbin/pkg_delete
+bulkdir=/bulklog
+
+bzip2=@BZIP2@
+digest=@DIGEST@
+gzip="@GZIP_CMD@"
+ident=@IDENT@
+make=@MAKE@
+mail=@MAIL_CMD@
+rsync=@PREFIX@/bin/rsync
+
+loc=${bulklog}/meta
+
+pbuild=@PREFIX@/bin/pbulk-build
+presolve=@PREFIX@/bin/pbulk-resolve
+pscan=@PREFIX@/bin/pbulk-scan
+
+pkg_up_to_date_script=@PREFIX@/libexec/pbulk/pkg-up-to-date
+pbuild_script=@PREFIX@/libexec/pbulk/pkg-build
+pbuild_start_script=@PREFIX@/libexec/pbulk/build-client-start
+pscan_prepare=@PREFIX@/libexec/pbulk/client-clean
+pscan_start_script=@PREFIX@/libexec/pbulk/scan-client-start
+report_script=@PREFIX@/libexec/pbulk/create-report.awk
+report_html_script=@PREFIX@/libexec/pbulk/create-report-html.awk
+report_txt_script=@PREFIX@/libexec/pbulk/create-report-txt.awk
+report_graph_script=@PREFIX@/libexec/pbulk/create-broken-graph.awk
+packages_script=@PREFIX@/libexec/pbulk/compute-packages.awk
+
+script_phase_pre_build=@PREFIX@/libexec/pbulk/pre-build
+script_phase_build=@PREFIX@/libexec/pbulk/build
+script_phase_report=@PREFIX@/libexec/pbulk/report
+script_phase_scan=@PREFIX@/libexec/pbulk/scan
+script_phase_upload=@PREFIX@/libexec/pbulk/upload
diff --git a/pkgtools/pbulk/files/pbulk/presolve/Makefile b/pkgtools/pbulk/files/pbulk/presolve/Makefile
new file mode 100644
index 00000000000..b88c0439672
--- /dev/null
+++ b/pkgtools/pbulk/files/pbulk/presolve/Makefile
@@ -0,0 +1,8 @@
+# $NetBSD: Makefile,v 1.1.1.1 2007/06/19 19:49:57 joerg Exp $
+
+PROG= pbulk-resolve
+SRCS= presolve.c
+
+NO_LIBEVENT_NEEDED= # defined
+
+.include <bsd.prog.mk>
diff --git a/pkgtools/pbulk/files/pbulk/presolve/pbulk-resolve.1 b/pkgtools/pbulk/files/pbulk/presolve/pbulk-resolve.1
new file mode 100644
index 00000000000..b4803bf62d2
--- /dev/null
+++ b/pkgtools/pbulk/files/pbulk/presolve/pbulk-resolve.1
@@ -0,0 +1,77 @@
+.\" $NetBSD: pbulk-resolve.1,v 1.1.1.1 2007/06/19 19:49:57 joerg Exp $
+.\"
+.\" Copyright (c) 2007 Thomas Klausner and Joerg Sonnenberger.
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+.\" ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+.\" TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+.\" PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+.\" BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+.\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+.\" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+.\" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+.\" CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+.\" POSSIBILITY OF SUCH DAMAGE.
+.\"
+.Dd June 12, 2007
+.Dt PBULK-RESOLVE 1
+.Os
+.Sh NAME
+.Nm pbulk-resolve
+.Nd resolves dependencies in
+.Xr pbulk-scan 1
+output
+.Sh SYNOPSIS
+.Nm
+.Op Fl v
+.Op Fl i Ar missing
+.Ar input Op ...
+.Sh DESCRIPTION
+.Nm
+resolves dependencies based on the output of
+.Xr pbulk-scan 1
+saved in
+.Ar input .
+.Pp
+Supported options are:
+.Bl -tag -offset indent
+.It Fl i Ar missing
+Enter incremental mode.
+For incremental mode, the package in
+.Ar input
+and the packages used to fulfill dependencies from the other input
+files are written to standard output including the resolved
+dependencies.
+The location of each unresolved dependency is written to
+.Ar missing .
+In normal mode, unresolvable dependencies are printed and the
+program exits with an error.
+.It Fl v
+If
+.Fl v
+is specified once and incremental mode is not active, a warning is
+printed whenever the best matching package has a different location
+than recorded in the dependency.
+If
+.Fl v
+is specified twice, additionally a warning is printed whenever two
+different packages can be used to fulfill a dependency.
+.El
+.Sh EXIT STATUS
+.Nm
+exits with return value 1 if an error occurred, or 0 if all
+dependencies have been resolved successfully.
+In incremental mode,
+.Ar missing
+contains all correct, but unresolvable dependencies.
diff --git a/pkgtools/pbulk/files/pbulk/presolve/presolve.c b/pkgtools/pbulk/files/pbulk/presolve/presolve.c
new file mode 100644
index 00000000000..844af2a3344
--- /dev/null
+++ b/pkgtools/pbulk/files/pbulk/presolve/presolve.c
@@ -0,0 +1,426 @@
+/* $NetBSD: presolve.c,v 1.1.1.1 2007/06/19 19:49:57 joerg Exp $ */
+
+/*-
+ * Copyright (c) 2007 Joerg Sonnenberger <joerg@NetBSD.org>.
+ * All rights reserved.
+ *
+ * This code was developed as part of Google's Summer of Code 2007 program.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/queue.h>
+#include <sys/stat.h>
+#include <ctype.h>
+#include <err.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "pbulk.h"
+
+static int verbosity;
+static FILE *incremental = NULL;
+
+static void
+usage(void)
+{
+ (void)fprintf(stderr, "usage: pbulk-resolve [ -i <missing> ] [ -v ] <pscan output> [ ... ]\n");
+ exit(1);
+}
+
+SLIST_HEAD(pkg_entry_hash, pkg_entry);
+
+struct pkg_entry {
+ char *pkgname;
+ char *depends;
+ char *pkglocation;
+ int active;
+ const char *begin;
+ const char *end;
+ SLIST_ENTRY(pkg_entry) hash_link;
+} *pkgs;
+
+size_t len_pkgs, allocated_pkgs;
+
+static char *pkgname_dup(const char *);
+static const char *pbulk_item_end(const char *);
+static void read_entries(const char *, int);
+static int resolve_entry(struct pkg_entry *);
+static void write_entries(void);
+static void hash_entries(void);
+static struct pkg_entry_hash *get_hash_chain(const char *);
+
+int
+main(int argc, char **argv)
+{
+ size_t i;
+ int ch, ret;
+
+ while ((ch = getopt(argc, argv, "i:v")) != -1) {
+ switch (ch) {
+ case 'i':
+ if (incremental != NULL)
+ (void)fclose(incremental);
+ if ((incremental = fopen(optarg, "w")) == NULL)
+ err(1, "Cannot open output file");
+ break;
+ case 'v':
+ ++verbosity;
+ break;
+ default:
+ usage();
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (argc == 0 || (incremental == NULL && argc > 1))
+ usage();
+
+ read_entries(argv[0], 1);
+ while (--argc > 0)
+ read_entries(*++argv, 0);
+
+ hash_entries();
+
+ ret = 0;
+ for (i = 0; i < len_pkgs; ++i) {
+ if (resolve_entry(&pkgs[i]))
+ ret = 1;
+ }
+
+ if (ret == 0)
+ write_entries();
+
+ return ret;
+}
+
+static const char *
+find_content(struct pkg_entry *pkg, const char *prefix)
+{
+ size_t len = strlen(prefix);
+ const char *line;
+
+ for (line = pkg->begin; line != pkg->end; line = strchr(line, '\n') + 1) {
+ if (strncmp(line, prefix, len) == 0)
+ return line + len;
+ }
+ return NULL;
+}
+
+static void
+log_multi_match(const char *pattern, const char *pkgname, const char *match)
+{
+ if (verbosity < 2)
+ return;
+ warnx("Multiple matches for dependency %s of package %s: %s", pattern, pkgname, match);
+}
+
+static void
+find_match_iter(const char *pattern, const char *pkgname, struct pkg_entry **best, struct pkg_entry *cur, size_t *matches)
+{
+ if (pkg_match(pattern, cur->pkgname) == 0)
+ return;
+ if (*matches == 0) {
+ *best = cur;
+ ++(*matches);
+ return;
+ }
+
+ if (*matches == 1)
+ log_multi_match(pattern, pkgname, (*best)->pkgname);
+ log_multi_match(pattern, pkgname, (*best)->pkgname);
+
+ if (pkg_order((*best)->pkgname, cur->pkgname) == cur->pkgname)
+ *best = cur;
+ ++(*matches);
+}
+
+static void
+validate_best_match(struct pkg_entry *match, const char *location,
+ const char *pattern, const char *pkgname)
+{
+ if (verbosity < 1 || incremental != NULL)
+ return;
+ if (strcmp(match->pkglocation, location) != 0) {
+ warnx("Best matching %s differs from location %s for dependency %s of package %s",
+ match->pkgname, location, pattern, pkgname);
+ }
+}
+
+static struct pkg_entry *
+find_match(const char *pkgname, const char *pattern, const char *location)
+{
+ size_t matches;
+ struct pkg_entry *best;
+
+ best = NULL;
+ matches = 0;
+
+ if ((isalnum((unsigned char)pattern[0]) || pattern[0] == '-') &&
+ (isalnum((unsigned char)pattern[1]) || pattern[1] == '-') &&
+ (isalnum((unsigned char)pattern[2]) || pattern[2] == '-') &&
+ (isalnum((unsigned char)pattern[3]) || pattern[3] == '-') &&
+ strchr(pattern, '{') == NULL) {
+ struct pkg_entry *iter;
+
+ SLIST_FOREACH(iter, get_hash_chain(pattern), hash_link)
+ find_match_iter(pattern, pkgname, &best, iter, &matches);
+ } else {
+ size_t i;
+
+ for (i = 0; i < len_pkgs; ++i)
+ find_match_iter(pattern, pkgname, &best, &pkgs[i], &matches);
+ }
+
+ if (matches == 0) {
+ if (incremental != NULL)
+ (void)fprintf(incremental, "%s\n", location);
+ else
+ warnx("No match found for dependency %s of package %s", pattern, pkgname);
+ return NULL;
+ }
+ validate_best_match(best, location, pattern, pkgname);
+ return best;
+}
+
+static int
+resolve_entry(struct pkg_entry *pkg)
+{
+ const char *line, *pattern_begin, *pattern_end, *location_begin;
+ char *pattern, *location, *old_depends;
+ struct pkg_entry *best_match;
+ struct pkg_entry **depends_list;
+ size_t i;
+ int ret;
+
+ if (pkg->active == 0)
+ return 0;
+
+ if (find_content(pkg, "DEPENDS=") != NULL)
+ return 0;
+
+ ret = 0;
+
+ if ((line = find_content(pkg, "ALL_DEPENDS=")) == NULL)
+ errx(1, "No ALL_DEPENDS line for %s", pkg->pkgname);
+
+ depends_list = xmalloc(sizeof(struct pkg_entry *));
+ depends_list[0] = NULL;
+
+ line += strspn(line, " \t");
+
+ while (*line != '\n') {
+ pattern_begin = line;
+ pattern_end = location_begin = strchr(pattern_begin, ':');
+ if (location_begin == NULL ||
+ strncmp(location_begin, ":../../", 7) != 0) {
+ free(depends_list);
+ warnx("Incorrect dependency for %s, skipping", pkg->pkgname);
+ return 1;
+ }
+ location_begin += 7;
+ line = location_begin + strcspn(location_begin, " \t\n");
+ pattern = xstrndup(pattern_begin, pattern_end - pattern_begin);
+ location = xstrndup(location_begin, line - location_begin);
+ line += strspn(line, " \t");
+
+ for (i = 0; depends_list[i] != NULL; ++i) {
+ if (pkg_match(pattern, depends_list[i]->pkgname))
+ break;
+ }
+ if (depends_list[i] != NULL) {
+ /*
+ * If we already have a match, assume that it is most
+ * likely stricter than this one. The best match check
+ * was therefore already done.
+ */
+ if (strcmp(depends_list[i]->pkglocation, location) != 0)
+ validate_best_match(depends_list[i], location, pattern, pkg->pkgname);
+ best_match = NULL; /* XXX For GCC */
+ } else
+ best_match = find_match(pkg->pkgname, pattern, location);
+ free(pattern);
+ free(location);
+
+ if (depends_list[i] != NULL)
+ continue;
+
+ if (best_match == NULL) {
+ ret = 1;
+ continue;
+ }
+
+ depends_list = xrealloc(depends_list, (i + 2) * sizeof(struct pkg_entry *));
+ depends_list[i] = best_match;
+ depends_list[i + 1] = NULL;
+
+ if (pkg->depends == NULL)
+ pkg->depends = xstrdup(best_match->pkgname);
+ else {
+ old_depends = pkg->depends;
+ pkg->depends = xasprintf("%s %s", old_depends, best_match->pkgname);
+ free(old_depends);
+ }
+
+ best_match->active = 1;
+ }
+
+ free(depends_list);
+ if (ret == 1) {
+ free(pkg->depends);
+ pkg->depends = NULL;
+ if (incremental != NULL)
+ return 0;
+ else
+ return 1;
+ }
+ return 0;
+}
+
+static char *
+pkgname_dup(const char *line)
+{
+ const char *pkgname;
+ char *pkgname_end;
+ size_t pkgname_len;
+
+ if (strncmp(line, "PKGNAME=", 8) != 0)
+ return NULL;
+ pkgname = line + 8;
+ pkgname_end = strchr(pkgname, '\n');
+ pkgname_len = pkgname_end - pkgname;
+ if (pkgname_end == NULL || pkgname_len < 4 ||
+ strcspn(pkgname, " \t\n") != pkgname_len)
+ return NULL;
+ return xstrndup(pkgname, pkgname_len);
+}
+
+static const char *
+pbulk_item_end(const char *line)
+{
+ const char *line_end;
+
+ do {
+ line_end = strchr(line, '\n');
+ if (line_end == NULL)
+ return NULL;
+ line = line_end + 1;
+ if (strncmp(line, "PKGNAME=", 8) == 0)
+ return line;
+ } while (*line != '\0');
+
+ return line;
+}
+
+static void
+read_entries(const char *input_file, int def_active)
+{
+ char *input;
+ const char *input_iter;
+ const char *location_line, *location_line_end;
+ int fd;
+
+ if ((fd = open(input_file, O_RDONLY, 0)) == -1)
+ err(1, "Cannot open input");
+
+ input = read_from_file(fd);
+ (void)close(fd);
+
+ if (allocated_pkgs == 0) {
+ allocated_pkgs = 1024;
+ pkgs = xmalloc(allocated_pkgs * sizeof(*pkgs));
+ }
+
+ input_iter = input;
+ while ((pkgs[len_pkgs].pkgname = pkgname_dup(input_iter)) != NULL) {
+ pkgs[len_pkgs].active = def_active;
+ pkgs[len_pkgs].begin = input_iter;
+ pkgs[len_pkgs].end = pbulk_item_end(input_iter);
+ pkgs[len_pkgs].depends = NULL;
+ if (pkgs[len_pkgs].end == NULL)
+ errx(1, "Invalid input");
+ input_iter = pkgs[len_pkgs].end;
+ location_line = find_content(&pkgs[len_pkgs], "PKG_LOCATION=");
+ if (location_line == NULL)
+ errx(1, "Invalid input");
+ location_line_end = strchr(location_line, '\n');
+ if (location_line_end == NULL)
+ errx(1, "Invalid input");
+ pkgs[len_pkgs].pkglocation = xstrndup(location_line, location_line_end - location_line);
+ ++len_pkgs;
+ if (len_pkgs == allocated_pkgs) {
+ allocated_pkgs *= 2;
+ pkgs = xrealloc(pkgs, allocated_pkgs * sizeof(*pkgs));
+ }
+ }
+
+ if (*input_iter != '\0')
+ errx(1, "Invalid input");
+}
+
+static void
+write_entries(void)
+{
+ size_t i;
+
+ for (i = 0; i < len_pkgs; ++i) {
+ if (pkgs[i].active == 0)
+ continue;
+ (void)fwrite(pkgs[i].begin, 1, pkgs[i].end - pkgs[i].begin, stdout);
+ if (pkgs[i].depends != NULL)
+ (void)printf("DEPENDS=%s\n", pkgs[i].depends);
+ }
+}
+
+#define HASH_SIZE 4096
+#define HASH_ITEM(x) (((unsigned char)(x)[0] + (unsigned char)(x)[1] * 257 + (unsigned char)(x)[1] * 65537) & (HASH_SIZE - 1))
+
+static struct pkg_entry_hash hash_table[HASH_SIZE];
+
+static void
+hash_entries(void)
+{
+ size_t i, hash;
+
+ for (i = 0; i < HASH_SIZE; ++i)
+ SLIST_INIT(&hash_table[i]);
+
+ for (i = 0; i < len_pkgs; ++i) {
+ hash = HASH_ITEM(pkgs[i].pkgname);
+ SLIST_INSERT_HEAD(&hash_table[hash], &pkgs[i], hash_link);
+ }
+}
+
+static struct pkg_entry_hash *
+get_hash_chain(const char *pkgname)
+{
+ return &hash_table[HASH_ITEM(pkgname)];
+}
diff --git a/pkgtools/pbulk/files/pbulk/pscan/Makefile b/pkgtools/pbulk/files/pbulk/pscan/Makefile
new file mode 100644
index 00000000000..dbe5aa33eb1
--- /dev/null
+++ b/pkgtools/pbulk/files/pbulk/pscan/Makefile
@@ -0,0 +1,6 @@
+# $NetBSD: Makefile,v 1.1.1.1 2007/06/19 19:49:57 joerg Exp $
+
+PROG= pbulk-scan
+SRCS= pscan.c jobs.c master.c client.c
+
+.include <bsd.prog.mk>
diff --git a/pkgtools/pbulk/files/pbulk/pscan/client.c b/pkgtools/pbulk/files/pbulk/pscan/client.c
new file mode 100644
index 00000000000..c2dcad38cff
--- /dev/null
+++ b/pkgtools/pbulk/files/pbulk/pscan/client.c
@@ -0,0 +1,120 @@
+/* $NetBSD: client.c,v 1.1.1.1 2007/06/19 19:49:57 joerg Exp $ */
+
+/*-
+ * Copyright (c) 2007 Joerg Sonnenberger <joerg@NetBSD.org>.
+ * All rights reserved.
+ *
+ * This code was developed as part of Google's Summer of Code 2007 program.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/socket.h>
+#include <err.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <arpa/inet.h>
+
+#include "pbulk.h"
+#include "pscan.h"
+
+void
+client_mode(const char *client_port)
+{
+ struct sockaddr_in dst;
+ uint16_t path_len;
+ uint32_t net_output_len;
+ ssize_t recv_bytes, sent_bytes;
+ size_t output_len;
+ char *path, *output;
+ int fd;
+
+ if (parse_sockaddr_in(client_port, &dst))
+ errx(1, "Could not parse addr/port");
+
+ fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+ if (fd == -1)
+ err(1, "Could not create socket");
+ if (connect(fd, (struct sockaddr *)&dst, dst.sin_len) == -1)
+ err(1, "Could not connect socket");
+
+loop:
+ recv_bytes = atomic_read(fd, &path_len, 2);
+ if (recv_bytes == -1)
+ err(1, "Could not read from socket");
+ if (recv_bytes == 0)
+ exit(0);
+ if (recv_bytes != 2)
+ errx(1, "Premature end while reading path length from socket");
+ path_len = ntohs(path_len);
+ if (path_len < 3)
+ errx(1, "Invalid path length from master");
+
+ path = xmalloc(path_len + 1);
+ path[path_len] = '\0';
+ recv_bytes = atomic_read(fd, path, path_len);
+ if (recv_bytes == -1)
+ err(1, "Could not read from socket");
+ if (recv_bytes != path_len || strlen(path) != path_len)
+ errx(1, "Premature end of stream while reading path from socket");
+ if (path[0] == '/' ||
+ strchr(path, '/') == NULL ||
+ strchr(path, '/') != strrchr(path, '/') ||
+ memcmp(path, "../", 3) == 0 ||
+ memcmp(path + path_len - 3, "/..", 3) == 0)
+ errx(1, "Invalid path from master");
+
+ if (verbosity >= 1) {
+ (void)printf("Scanning %s\n", path);
+ (void)fflush(stdout);
+ }
+
+ output = scan_pkglocation(path);
+ free(path);
+ if (output != NULL)
+ output_len = strlen(output);
+ else
+ output_len = 0;
+ if (output_len > 0xfffffffful)
+ errx(1, "Output too large");
+ net_output_len = htonl((uint32_t)output_len);
+
+ sent_bytes = write(fd, &net_output_len, 4);
+ if (sent_bytes == -1)
+ err(1, "Could not write to socket");
+ if (sent_bytes != 4)
+ errx(1, "Premature end of stream while writing to socket");
+ sent_bytes = write(fd, output, output_len);
+ if (sent_bytes == -1)
+ err(1, "Could not write to socket");
+ if (sent_bytes != output_len)
+ errx(1, "Premature end of stream while writing to socket");
+ free(output);
+ goto loop;
+}
diff --git a/pkgtools/pbulk/files/pbulk/pscan/jobs.c b/pkgtools/pbulk/files/pbulk/pscan/jobs.c
new file mode 100644
index 00000000000..8d6097220c1
--- /dev/null
+++ b/pkgtools/pbulk/files/pbulk/pscan/jobs.c
@@ -0,0 +1,267 @@
+/* $NetBSD: jobs.c,v 1.1.1.1 2007/06/19 19:49:57 joerg Exp $ */
+
+/*-
+ * Copyright (c) 2007 Joerg Sonnenberger <joerg@NetBSD.org>.
+ * All rights reserved.
+ *
+ * This code was developed as part of Google's Summer of Code 2007 program.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/uio.h>
+#include <err.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "pbulk.h"
+#include "pscan.h"
+
+#define UNCONST(x) ((void *)(uintptr_t)(x))
+
+static struct scan_job *jobs;
+static size_t len_jobs, allocated_jobs, first_undone_job, done_jobs;
+
+struct pkgname_hash {
+ struct pkgname_hash *next;
+ char *pkgname;
+};
+
+void
+add_job(const char *cat, size_t cat_len, const char *dir, size_t dir_len)
+{
+ char *location;
+
+ location = xasprintf("%.*s/%.*s", (int)cat_len, cat, dir_len, dir);
+ add_job_full(location);
+ free(location);
+}
+
+void
+add_job_full(const char *location)
+{
+ if (len_jobs == allocated_jobs) {
+ if (allocated_jobs == 0) {
+ allocated_jobs = 1024;
+ jobs = xmalloc(sizeof(*jobs) * allocated_jobs);
+ } else {
+ allocated_jobs *= 2;
+ jobs = xrealloc(jobs, sizeof(*jobs) * allocated_jobs);
+ }
+ }
+ jobs[len_jobs].pkg_location = xstrdup(location);
+ jobs[len_jobs].scan_output = NULL;
+ jobs[len_jobs].state = JOB_OPEN;
+ ++len_jobs;
+}
+
+struct scan_job *
+get_job(void)
+{
+ size_t i;
+
+ for (i = first_undone_job; i < len_jobs; ++i) {
+ if (jobs[i].state == JOB_OPEN) {
+ jobs[i].state = JOB_IN_PROCESSING;
+ return &jobs[i];
+ }
+ }
+
+ return NULL;
+}
+
+void
+process_job(struct scan_job *job, enum job_state state)
+{
+ job->state = state;
+
+ for (; first_undone_job < len_jobs; ++first_undone_job) {
+ if (jobs[first_undone_job].state != JOB_DONE)
+ break;
+ }
+
+ if (state == JOB_DONE) {
+ ++done_jobs;
+ if (verbosity >= 1) {
+ if (done_jobs % 50)
+ (void)putchar('.');
+ else
+ (void)printf(". %zd/%zd\n", done_jobs, len_jobs);
+ (void)fflush(stdout);
+ }
+ }
+}
+
+static char *
+pkgname_dup(const char *line)
+{
+ const char *pkgname;
+ char *pkgname_end;
+ size_t pkgname_len;
+
+ if (strncmp(line, "PKGNAME=", 8) != 0)
+ return NULL;
+ pkgname = line + 8;
+ pkgname_end = strchr(pkgname, '\n');
+ pkgname_len = pkgname_end - pkgname;
+ if (pkgname_end == NULL || pkgname_len < 2 ||
+ strcspn(pkgname, " \t\n") != pkgname_len)
+ return NULL;
+ return xstrndup(pkgname, pkgname_len);
+}
+
+#define HASH_SIZE 1024
+#define HASH_ITEM(x) (((unsigned char)(x)[0] + (unsigned char)(x)[1] * 257) & (HASH_SIZE - 1))
+
+static struct pkgname_hash *pkgname_hash[HASH_SIZE];
+
+static int
+pkgname_in_hash(const char *pkgname)
+{
+ struct pkgname_hash *iter;
+
+ for (iter = pkgname_hash[HASH_ITEM(pkgname)]; iter != NULL;
+ iter = iter->next) {
+ if (strcmp(iter->pkgname, pkgname) == 0)
+ return 1;
+ }
+ return 0;
+}
+
+static const char *
+pbulk_item_end(const char *line)
+{
+ const char *line_end;
+
+ do {
+ line_end = strchr(line, '\n');
+ if (line_end == NULL)
+ return NULL;
+ line = line_end + 1;
+ if (strncmp(line, "PKGNAME=", 8) == 0)
+ return line;
+ } while (*line != '\0');
+
+ return line;
+}
+
+static void
+write_single_job(int fd, const char *begin_entry, struct scan_job *job)
+{
+ struct pkgname_hash *entry;
+ const char *end_entry, *end_line;
+ char *pkgname;
+ int skip_current;
+ struct iovec output[5];
+ const int iovcnt = 5;
+ ssize_t expected;
+
+ for (; begin_entry != NULL && begin_entry[0] != '\0';) {
+ pkgname = pkgname_dup(begin_entry);
+ if (pkgname == NULL) {
+ warnx("pbulk-index output not recognized for %s",
+ job->pkg_location);
+ break;
+ }
+
+ if (pkgname_in_hash(pkgname)) {
+ warnx("Duplicate package: %s", pkgname);
+ skip_current = 1;
+ } else {
+ skip_current = 0;
+ }
+
+ end_entry = pbulk_item_end(begin_entry);
+ if (end_entry == NULL) {
+ free(pkgname);
+ warnx("pbulk-index output not recognized for %s",
+ job->pkg_location);
+ break;
+ }
+
+ if (skip_current == 0) {
+ end_line = strchr(begin_entry, '\n');
+ if (end_line == NULL || end_line > end_entry)
+ errx(254, "internal error");
+
+ output[0].iov_base = UNCONST(begin_entry);
+ expected = output[0].iov_len =
+ end_line + 1 - begin_entry;
+ output[1].iov_base = UNCONST("PKG_LOCATION=");
+ output[1].iov_len = strlen(output[1].iov_base);
+ expected += output[1].iov_len;
+ output[2].iov_base = job->pkg_location;
+ output[2].iov_len = strlen(output[2].iov_base);
+ expected += output[2].iov_len;
+ output[3].iov_base = UNCONST("\n");
+ output[3].iov_len = 1;
+ expected += output[3].iov_len;
+ output[4].iov_base = UNCONST(end_line + 1);
+ output[4].iov_len = end_entry - end_line - 1;
+ expected += output[4].iov_len;
+
+ if (writev(fd, output, iovcnt) != expected)
+ err(1, "writev failed");
+
+ entry = xmalloc(sizeof(*entry));
+ entry->next = pkgname_hash[HASH_ITEM(pkgname)];
+ entry->pkgname = pkgname;
+ pkgname_hash[HASH_ITEM(pkgname)] = entry;
+ } else {
+ free(pkgname);
+ }
+ begin_entry = end_entry;
+ }
+}
+
+void
+write_jobs(const char *output_file)
+{
+ size_t i;
+ int fd;
+
+ if (verbosity >= 1) {
+ if (done_jobs % 50)
+ (void)printf(" %zd/%zd\n", done_jobs, len_jobs);
+ (void)fflush(stdout);
+ }
+
+ fd = open(output_file, O_WRONLY | O_CREAT | O_TRUNC, 0666);
+
+ for (i = 0; i < len_jobs; ++i) {
+ if (jobs[i].state != JOB_DONE) {
+ warnx("%s was not processed", jobs[i].pkg_location);
+ continue;
+ }
+ if (jobs[i].scan_output == NULL) {
+ warnx("Scan failed for %s", jobs[i].pkg_location);
+ continue;
+ }
+ write_single_job(fd, jobs[i].scan_output, jobs + i);
+ }
+}
diff --git a/pkgtools/pbulk/files/pbulk/pscan/master.c b/pkgtools/pbulk/files/pbulk/pscan/master.c
new file mode 100644
index 00000000000..241f537ae32
--- /dev/null
+++ b/pkgtools/pbulk/files/pbulk/pscan/master.c
@@ -0,0 +1,240 @@
+/* $NetBSD: master.c,v 1.1.1.1 2007/06/19 19:49:57 joerg Exp $ */
+
+/*-
+ * Copyright (c) 2007 Joerg Sonnenberger <joerg@NetBSD.org>.
+ * All rights reserved.
+ *
+ * This code was developed as part of Google's Summer of Code 2007 program.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/ioctl.h>
+#include <sys/queue.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <sys/wait.h>
+#include <err.h>
+#include <event.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "pbulk.h"
+#include "pscan.h"
+
+static LIST_HEAD(, scan_peer) active_peers, inactive_peers;
+
+struct scan_peer {
+ LIST_ENTRY(scan_peer) peer_link;
+
+ struct scan_job *job;
+
+ int fd;
+ char tmp_buf[4];
+
+ size_t output_len;
+};
+
+static void assign_job(struct scan_peer *);
+
+static void
+kill_peer(void *arg)
+{
+ struct scan_peer *peer = arg;
+
+ (void)close(peer->fd);
+ LIST_REMOVE(peer, peer_link);
+ free(peer->job->scan_output);
+ peer->job->scan_output = NULL;
+ process_job(peer->job, JOB_OPEN);
+ free(peer);
+
+ peer = LIST_FIRST(&inactive_peers);
+ if (peer == NULL)
+ return;
+ LIST_REMOVE(peer, peer_link);
+ assign_job(peer);
+}
+
+static void
+finish_job(void *arg)
+{
+ struct scan_peer *peer = arg;
+
+ if (strlen(peer->job->scan_output) != peer->output_len) {
+ warnx("Invalid output len received from peer");
+ kill_peer(peer);
+ return;
+ }
+ LIST_REMOVE(peer, peer_link);
+ process_job(peer->job, JOB_DONE);
+ assign_job(peer);
+}
+
+static void
+recv_output(void *arg)
+{
+ struct scan_peer *peer = arg;
+ uint32_t output_len;
+
+ (void)memcpy(&output_len, peer->tmp_buf, 4);
+ output_len = ntohl(output_len);
+ if (output_len == 0) {
+ LIST_REMOVE(peer, peer_link);
+ process_job(peer->job, JOB_DONE);
+ assign_job(peer);
+ return;
+ }
+ if (output_len == 0xffffff) {
+ warnx("Invalid output len received from peer");
+ kill_peer(peer);
+ return;
+ }
+ peer->job->scan_output = xmalloc(output_len + 1);
+ peer->job->scan_output[output_len] = '\0';
+ peer->output_len = output_len;
+ deferred_read(peer->fd, peer->job->scan_output, output_len, peer, finish_job, kill_peer);
+}
+
+static void
+recv_output_len(void *arg)
+{
+ struct scan_peer *peer = arg;
+
+ deferred_read(peer->fd, peer->tmp_buf, 4, peer, recv_output, kill_peer);
+}
+
+static void
+send_job_path(void *arg)
+{
+ struct scan_peer *peer = arg;
+
+ deferred_write(peer->fd, peer->job->pkg_location,
+ strlen(peer->job->pkg_location), peer, recv_output_len,
+ kill_peer);
+}
+
+static void
+assign_job(struct scan_peer *peer)
+{
+ size_t job_len;
+ uint16_t net_job_len;
+
+ peer->job = get_job();
+ if (peer->job == NULL) {
+ LIST_INSERT_HEAD(&inactive_peers, peer, peer_link);
+ if (LIST_EMPTY(&active_peers))
+ event_loopexit(NULL);
+ return;
+ }
+
+ LIST_INSERT_HEAD(&active_peers, peer, peer_link);
+
+ peer->job->scan_output = NULL;
+
+ job_len = strlen(peer->job->pkg_location);
+ if (job_len > 0xffff)
+ errx(1, "Location inside pkgsrc tree too long");
+ net_job_len = htons(job_len);
+ (void)memcpy(peer->tmp_buf, &net_job_len, 2);
+
+ deferred_write(peer->fd, peer->tmp_buf, 2, peer, send_job_path,
+ kill_peer);
+}
+
+static void
+listen_handler(int sock, short event, void *arg)
+{
+ struct scan_peer *peer;
+ struct sockaddr_in src;
+ socklen_t src_len;
+ int fd, ioctl_arg;
+
+ src_len = sizeof(src);
+ if ((fd = accept(sock, (struct sockaddr *)&src, &src_len)) == -1) {
+ warn("Could not accept connection");
+ return;
+ }
+ ioctl_arg = 1;
+ if (ioctl(fd, FIONBIO, &ioctl_arg) == -1) {
+ (void)close(fd);
+ warn("Could not set non-blocking IO");
+ return;
+ }
+
+ peer = xmalloc(sizeof(*peer));
+ peer->fd = fd;
+ assign_job(peer);
+}
+
+void
+master_mode(const char *master_port, const char *start_script)
+{
+ struct event listen_event;
+ struct sockaddr_in dst;
+ int fd;
+
+ LIST_INIT(&active_peers);
+ LIST_INIT(&inactive_peers);
+
+ event_init();
+
+ if (parse_sockaddr_in(master_port, &dst))
+ errx(1, "Could not parse addr/port");
+
+ fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+ if (fd == -1)
+ err(1, "Could not create socket");
+ if (ioctl(fd, FIOCLEX, NULL) == -1)
+ err(1, "Could not set close-on-exec flag");
+ if (bind(fd, (struct sockaddr *)&dst, dst.sin_len) == -1)
+ err(1, "Could not bind socket");
+ if (listen(fd, 5) == -1)
+ err(1, "Could not listen on socket");
+
+ if (start_script) {
+ pid_t child;
+ int status;
+
+ if ((child = vfork()) == 0) {
+ execlp(start_script, start_script, (char *)NULL);
+ _exit(255);
+ }
+ if (child == -1)
+ err(1, "Could not fork start script");
+ waitpid(child, &status, 0);
+ if (status != 0)
+ err(1, "Start script failed");
+ }
+
+ event_set(&listen_event, fd, EV_READ | EV_PERSIST, listen_handler, NULL);
+ event_add(&listen_event, NULL);
+
+ event_dispatch();
+
+ (void)close(fd);
+}
diff --git a/pkgtools/pbulk/files/pbulk/pscan/pbulk-scan.1 b/pkgtools/pbulk/files/pbulk/pscan/pbulk-scan.1
new file mode 100644
index 00000000000..cbcf8e13b9f
--- /dev/null
+++ b/pkgtools/pbulk/files/pbulk/pscan/pbulk-scan.1
@@ -0,0 +1,95 @@
+.\" $NetBSD: pbulk-scan.1,v 1.1.1.1 2007/06/19 19:49:57 joerg Exp $
+.\"
+.\" Copyright (c) 2007 Thomas Klausner and Joerg Sonnenberger.
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+.\" ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+.\" TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+.\" PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+.\" BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+.\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+.\" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+.\" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+.\" CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+.\" POSSIBILITY OF SUCH DAMAGE.
+.\"
+.Dd June 11, 2007
+.Dt PBULK-SCAN 1
+.Os
+.Sh NAME
+.Nm pbulk-scan
+.Nd extracts information for pbulk from a pkgsrc tree
+.Sh SYNOPSIS
+.Nm
+.Op Fl v
+.Fl c Oo Ar ip: Oc Ns Ar port
+.Fl M Ar make
+.Ar pkgsrc
+.Nm
+.Op Fl lv
+.Op Fl I Ar start_script
+.Op Fl m Oo Ar ip: Oc Ns Ar port
+.Fl M Ar make
+.Ar pkgsrc output
+.Sh DESCRIPTION
+.Nm
+extracts information for a pbulk bulk build from a pkgsrc tree.
+.Pp
+Supported options are:
+.Bl -tag -width 15n -offset indent
+.It Fl c Oo Ar ip: Oc Ns Ar port
+Connect to pbulk bulk build master process on
+.Ar port
+(or
+.Ar ip:port ) .
+.It Fl I Ar start_script
+Run
+.Ar start_script
+after opening the socket and wait for completion before entering scan loop.
+.It Fl l
+Read the list of locations to scan from stdin, one line per location.
+Otherwise the list of locations is built from the
+.Va SUBDIRS
+variable in the top-level Makefile and the
+.Va SUBDIRS
+variables in the category Makefiles.
+.It Fl M Ar make
+Use
+.Ar make
+to extract the data.
+Usually
+.Dq make
+or
+.Dq bmake .
+.It Fl m Oo Ar ip: Oc Ns Ar port
+Enter master mode.
+In this mode,
+.Nm
+waits for connections on
+.Ar port
+(or
+.Ar ip:port ) .
+.It Fl v
+Log each location to be scanned or other progress to stdout.
+.El
+.Ar pkgsrc
+is the path to the pkgsrc checkout and
+.Ar output
+is the output file.
+.Pp
+A warning is printed to stderr if the same package name occurs
+more than once in the scan output.
+.Sh EXIT STATUS
+.Nm
+exits with return value 1 on internal errors.
diff --git a/pkgtools/pbulk/files/pbulk/pscan/pscan.c b/pkgtools/pbulk/files/pbulk/pscan/pscan.c
new file mode 100644
index 00000000000..a078e2b01f8
--- /dev/null
+++ b/pkgtools/pbulk/files/pbulk/pscan/pscan.c
@@ -0,0 +1,243 @@
+/* $NetBSD: pscan.c,v 1.1.1.1 2007/06/19 19:49:57 joerg Exp $ */
+
+/*-
+ * Copyright (c) 2007 Joerg Sonnenberger <joerg@NetBSD.org>.
+ * All rights reserved.
+ *
+ * This code was developed as part of Google's Summer of Code 2007 program.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/uio.h>
+#include <err.h>
+#include <inttypes.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "pbulk.h"
+#include "pscan.h"
+
+int verbosity;
+
+static const char *bmake_path;
+static const char *bmake_cmd;
+static const char *output_file;
+static const char *pkgsrc_tree;
+
+static void find_full_tree(void);
+static void read_limited_list(void);
+static void standalone_mode(void);
+
+static void
+usage(void)
+{
+ (void)fprintf(stderr, "usage: pbulk-scan -c <master> [ -v ] -M <make> <pkgsrc tree>\n");
+ (void)fprintf(stderr, "usage: pbulk-scan [ -I <start> ] [ -l ] [ -v ] [ -m <port> ] -M <make> <pksgrc tree> <output file>\n");
+ exit(1);
+}
+
+int
+main(int argc, char **argv)
+{
+ const char *client_port = NULL, *master_port = NULL, *start_script = NULL;
+ int ch, limited_scan;
+ struct sigaction sa;
+
+ limited_scan = 0;
+
+ while ((ch = getopt(argc, argv, "I:M:lc:m:v")) != -1) {
+ switch (ch) {
+ case 'I':
+ start_script = optarg;
+ break;
+ case 'c':
+ client_port = optarg;
+ break;
+ case 'l':
+ limited_scan = 1;
+ break;
+ case 'm':
+ master_port = optarg;
+ break;
+ case 'M':
+ bmake_path = optarg;
+ break;
+ case 'v':
+ ++verbosity;
+ break;
+ default:
+ usage();
+ }
+ }
+
+ sa.sa_sigaction = NULL;
+ sa.sa_handler = SIG_IGN;
+ sa.sa_flags = 0;
+ (void)sigemptyset(&sa.sa_mask);
+ (void)sigaction(SIGPIPE, (struct sigaction *)&sa, NULL);
+
+ argc -= optind;
+ argv += optind;
+
+ if (bmake_path == NULL)
+ usage();
+
+ if (client_port != NULL && master_port != NULL) {
+ warnx("Only client mode or master mode can be active");
+ usage();
+ }
+
+ bmake_cmd = strrchr(bmake_path, '/');
+ if (bmake_cmd == NULL)
+ bmake_cmd = bmake_path;
+ else
+ ++bmake_cmd;
+
+ if (client_port) {
+ if (limited_scan != 0 || argc != 1)
+ usage();
+ pkgsrc_tree = argv[0];
+
+ client_mode(client_port);
+ }
+
+ if (argc != 2)
+ usage();
+
+ pkgsrc_tree = argv[0];
+ output_file = argv[1];
+
+ if (limited_scan == 0)
+ find_full_tree();
+ else
+ read_limited_list();
+
+ if (master_port != NULL)
+ master_mode(master_port, start_script);
+ else
+ standalone_mode();
+
+ write_jobs(output_file);
+
+ return 0;
+}
+
+char *
+scan_pkglocation(const char *pkg_location)
+{
+ const char * extract_pbulk_index[] = {
+ bmake_cmd,
+ "pbulk-index",
+ NULL
+ };
+ char *path, *buf;
+
+ path = xasprintf("%s/%s", pkgsrc_tree, pkg_location);
+ buf = read_from_child(path, bmake_path, extract_pbulk_index);
+ free(path);
+
+ return buf;
+}
+
+static void
+read_limited_list(void)
+{
+ char location[PATH_MAX], *eos;
+
+ while (fgets(location, PATH_MAX, stdin) != NULL) {
+ eos = strchr(location, '\n');
+ if (eos == NULL)
+ err(1, "Incomplete or too long input line");
+ *eos = '\0';
+ add_job_full(location);
+ }
+}
+
+static void
+find_full_tree(void)
+{
+ const char * extract_subdir[] = {
+ bmake_cmd,
+ "show-subdir-var",
+ "VARNAME=SUBDIR",
+ NULL
+ };
+ char *cat_path;
+ char *buf, *buf_orig, *cat, *cat_orig;
+ size_t buf_len, cat_len;
+
+ buf = read_from_child(pkgsrc_tree, bmake_path, extract_subdir);
+
+ if (buf == NULL)
+ err(1, "Cannot extract categories");
+
+ cat = cat_orig = buf;
+ for (;;) {
+ cat += strspn(cat, " \t\n");
+ cat_len = strcspn(cat, " \t\n");
+ if (cat_len == 0)
+ break;
+
+ cat_path = xasprintf("%s/%.*s", pkgsrc_tree, (int)cat_len, cat);
+ buf_orig = buf = read_from_child(cat_path, bmake_path, extract_subdir);
+ free(cat_path);
+ if (buf == NULL) {
+ warnx("Cannot extract subdirectories for %.*s", (int)cat_len, cat);
+ cat += cat_len;
+ continue;
+ }
+
+ for (;;) {
+ buf += strspn(buf, " \t\n");
+ buf_len = strcspn(buf, " \t\n");
+ if (buf_len == 0)
+ break;
+ add_job(cat, cat_len, buf, buf_len);
+ buf += buf_len;
+ }
+ free(buf_orig);
+
+ cat += cat_len;
+ }
+
+ free(cat_orig);
+}
+
+static void
+standalone_mode(void)
+{
+ struct scan_job *job;
+
+ while ((job = get_job()) != NULL) {
+ job->scan_output = scan_pkglocation(job->pkg_location);
+ process_job(job, JOB_DONE);
+ }
+}
diff --git a/pkgtools/pbulk/files/pbulk/pscan/pscan.h b/pkgtools/pbulk/files/pbulk/pscan/pscan.h
new file mode 100644
index 00000000000..988866c14f7
--- /dev/null
+++ b/pkgtools/pbulk/files/pbulk/pscan/pscan.h
@@ -0,0 +1,60 @@
+/* $NetBSD: pscan.h,v 1.1.1.1 2007/06/19 19:49:57 joerg Exp $ */
+
+/*-
+ * Copyright (c) 2007 Joerg Sonnenberger <joerg@NetBSD.org>.
+ * All rights reserved.
+ *
+ * This code was developed as part of Google's Summer of Code 2007 program.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <inttypes.h>
+
+enum job_state {
+ JOB_OPEN,
+ JOB_IN_PROCESSING,
+ JOB_DONE
+};
+
+struct scan_job {
+ char *pkg_location;
+ char *scan_output;
+ enum job_state state;
+};
+
+extern int verbosity;
+
+char *scan_pkglocation(const char *);
+
+void client_mode(const char *);
+void master_mode(const char *, const char *);
+
+void add_job(const char *, size_t, const char *, size_t);
+void add_job_full(const char *);
+struct scan_job *get_job(void);
+void process_job(struct scan_job *, enum job_state);
+void write_jobs(const char *);
+
diff --git a/pkgtools/pbulk/files/pbulk/scripts/Makefile b/pkgtools/pbulk/files/pbulk/scripts/Makefile
new file mode 100644
index 00000000000..0e5e993dcdc
--- /dev/null
+++ b/pkgtools/pbulk/files/pbulk/scripts/Makefile
@@ -0,0 +1,13 @@
+# $NetBSD: Makefile,v 1.1.1.1 2007/06/19 19:49:59 joerg Exp $
+
+SCRIPTS= build build-client-start bulkbuild client-clean \
+ pkg-build pkg-up-to-date pre-build report scan \
+ scan-client-start upload \
+ compute-packages.awk create-broken-graph.awk \
+ create-report-html.awk create-report-txt.awk \
+ create-report.awk
+
+SCRIPTSDIR= ${PREFIX}/libexec/pbulk
+SCRIPTSDIR_bulkbuild= ${PREFIX}/bin
+
+.include <bsd.prog.mk>
diff --git a/pkgtools/pbulk/files/pbulk/scripts/build b/pkgtools/pbulk/files/pbulk/scripts/build
new file mode 100755
index 00000000000..eef6d419971
--- /dev/null
+++ b/pkgtools/pbulk/files/pbulk/scripts/build
@@ -0,0 +1,64 @@
+#!@SH@
+# $NetBSD: build,v 1.1.1.1 2007/06/19 19:49:59 joerg Exp $
+#
+# Copyright (c) 2007 Joerg Sonnenberger <joerg@NetBSD.org>.
+# All rights reserved.
+#
+# This code was developed as part of Google's Summer of Code 2007 program.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+# COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
+# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+# OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+. @PBULK_CONFIG@
+
+set -e
+
+echo "Building..."
+case "${master_mode}" in
+[nN][oO])
+ ${pbuild} -r ${loc}/pbuild -v -b ${pbuild_script} ${loc}/presolve ${loc}/success ${loc}/error
+ ;;
+[yY][eE][sS])
+ ${pbuild} -r ${loc}/pbuild -I ${pbuild_start_script} -m ${master_port_build} -v ${loc}/presolve ${loc}/success ${loc}/error
+ ;;
+*)
+ echo "master_mode must be either yes or no."
+ exit 1
+ ;;
+esac
+
+echo "BUILD_END=`date +%s`" >> ${loc}/status
+
+echo "Building pkg_summary..."
+cd ${packages}/All
+sed 's/$/.tgz/' < ${loc}/success | sort | xargs ${external_pkg_info} -X | ${gzip} -c > pkg_summary.gz
+${gzip} -dc < pkg_summary.gz | ${bzip2} -c > pkg_summary.bz2
+
+echo "Building SHA512..."
+cd ${packages}
+{
+ echo "All/pkg_summary.bz2"
+ echo "All/pkg_summary.gz"
+ sed 's|^\(.*\)$|All/\1.tgz|' < ${loc}/success
+} | sort | xargs ${digest} SHA512 | ${bzip2} -c > SHA512.bz2
diff --git a/pkgtools/pbulk/files/pbulk/scripts/build-client-start b/pkgtools/pbulk/files/pbulk/scripts/build-client-start
new file mode 100755
index 00000000000..3cb3fe62c4e
--- /dev/null
+++ b/pkgtools/pbulk/files/pbulk/scripts/build-client-start
@@ -0,0 +1,10 @@
+#!@SH@
+# $NetBSD: build-client-start,v 1.1.1.1 2007/06/19 19:49:59 joerg Exp $
+
+. @PBULK_CONFIG@
+
+set -e
+
+for client in ${build_clients}; do
+ ssh $client "${pbuild} -c ${master_port_build} -b ${pbuild_script}" &
+done
diff --git a/pkgtools/pbulk/files/pbulk/scripts/bulkbuild b/pkgtools/pbulk/files/pbulk/scripts/bulkbuild
new file mode 100755
index 00000000000..4b0c233b76b
--- /dev/null
+++ b/pkgtools/pbulk/files/pbulk/scripts/bulkbuild
@@ -0,0 +1,12 @@
+#!@SH@
+# $NetBSD: bulkbuild,v 1.1.1.1 2007/06/19 19:49:59 joerg Exp $
+
+. @PBULK_CONFIG@
+
+set -e
+
+${script_phase_pre_build}
+${script_phase_scan}
+${script_phase_build}
+${script_phase_report}
+${script_phase_upload}
diff --git a/pkgtools/pbulk/files/pbulk/scripts/client-clean b/pkgtools/pbulk/files/pbulk/scripts/client-clean
new file mode 100755
index 00000000000..e2848c50055
--- /dev/null
+++ b/pkgtools/pbulk/files/pbulk/scripts/client-clean
@@ -0,0 +1,11 @@
+#!@SH@
+# $NetBSD: client-clean,v 1.1.1.1 2007/06/19 19:49:59 joerg Exp $
+
+. @PBULK_CONFIG@
+
+set -e
+
+# Clean build system first
+rm -rf ${prefix} ${pkgdb} ${varbase}/qmail 2> /dev/null || true
+# Install fresh bootstrap state
+[ -n "${bootstrapkit}" ] && tar xzf ${bootstrapkit} -C /
diff --git a/pkgtools/pbulk/files/pbulk/scripts/compute-packages.awk b/pkgtools/pbulk/files/pbulk/scripts/compute-packages.awk
new file mode 100755
index 00000000000..0e5414e6200
--- /dev/null
+++ b/pkgtools/pbulk/files/pbulk/scripts/compute-packages.awk
@@ -0,0 +1,73 @@
+#!@AWK@ -f -
+# $NetBSD: compute-packages.awk,v 1.1.1.1 2007/06/19 19:49:59 joerg Exp $
+#
+# Copyright (c) 2007 Joerg Sonnenberger <joerg@NetBSD.org>.
+# All rights reserved.
+#
+# This code was developed as part of Google's Summer of Code 2007 program.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+# COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
+# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+# OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+BEGIN {
+ meta_dir = ARGV[1]
+ report_file = meta_dir "/report"
+
+ while ((getline < report_file) > 0) {
+ if ($0 ~ "^PKGNAME=") {
+ cur = substr($0, 9)
+ pkgs[cur] = cur
+ }
+
+ if ($0 ~ "^CATEGORIES=")
+ categories[cur] = substr($0, 12)
+
+ if ($0 ~ "^BUILD_STATUS=")
+ status[cur] = substr($0, 14)
+
+ if ($0 ~ "^RESTRICTED_SUBSET=")
+ subset[cur] = substr($0, 19)
+ }
+ close(presolve_file)
+
+ for (pkg in pkgs) {
+ # skip failed build
+ if (status[pkg] != "done")
+ continue;
+ # skip restricted packages
+ if (subset[pkg] != "no")
+ continue;
+ # for the rest, build category/file list
+ split(categories[pkg], cats, "[ \t]+")
+ cats[0] = "All"
+ for (cat_idx in cats) {
+ cat = cats[cat_idx]
+ if (!(cat in printed_cats)) {
+ print "+ " cat "/"
+ printed_cats[cat] = cat
+ }
+ print "+ " cat "/" pkg ".tgz"
+ }
+ }
+}
diff --git a/pkgtools/pbulk/files/pbulk/scripts/create-broken-graph.awk b/pkgtools/pbulk/files/pbulk/scripts/create-broken-graph.awk
new file mode 100755
index 00000000000..dba6bd0af5a
--- /dev/null
+++ b/pkgtools/pbulk/files/pbulk/scripts/create-broken-graph.awk
@@ -0,0 +1,91 @@
+#!@AWK@ -f -
+# $NetBSD: create-broken-graph.awk,v 1.1.1.1 2007/06/19 19:49:59 joerg Exp $
+#
+# Copyright (c) 2007 Joerg Sonnenberger <joerg@NetBSD.org>.
+# All rights reserved.
+#
+# This code was developed as part of Google's Summer of Code 2007 program.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+# COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
+# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+# OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+BEGIN {
+ meta_dir = ARGV[1]
+ report_file = meta_dir "/report"
+ graph_file = meta_dir "/report.dot"
+
+ while ((getline < report_file) > 0) {
+ if ($0 ~ "^PKGNAME=")
+ cur = substr($0, 9)
+ else if ($0 ~ "^DEPENDS=")
+ depends[cur] = substr($0, 9)
+ else if ($0 ~ "^PKG_DEPTH=")
+ depth[cur] = substr($0, 11) - 1
+ else if ($0 ~ "^BUILD_STATUS=")
+ status[cur] = substr($0, 14)
+ }
+ close(report_file)
+
+ print "digraph \"Broken packages\" {" > graph_file
+ for (pkg in depends) {
+ split(depends[pkg], depend_list, "[ \t]+")
+
+ for (dep in depend_list) {
+ cur_pkg = depend_list[dep]
+ if (status[cur_pkg] != "failed" &&
+ status[cur_pkg] != "prefailed" &&
+ status[cur_pkg] != "indirect-failed" &&
+ status[cur_pkg] != "indirect-prefailed")
+ continue;
+ if (status[cur_pkg] == "failed")
+ color = "red"
+ else
+ color = "lightgray"
+ printf("\"%s\" -> \"%s\" [ color = \"%s\" ];\n", cur_pkg, pkg, color) > graph_file
+ drawn_pkgs[cur_pkg] = 1
+ drawn_pkgs[pkg] = 1
+ }
+ }
+ for (pkg in status) {
+ if (status[pkg] != "failed" &&
+ status[pkg] != "prefailed" &&
+ status[pkg] != "indirect-failed" &&
+ status[pkg] != "indirect-prefailed")
+ continue;
+
+ if (depth[pkg] == 0 && !(pkg in drawn_pkgs))
+ continue;
+
+ if (status[pkg] == "failed")
+ color = "red"
+ else if (status[pkg] == "prefailed")
+ color = "gray2"
+ else if (status[pkg] == "indirect-failed")
+ color = "orange"
+ else if (status[pkg] == "indirect-prefailed")
+ color = "lightgray"
+ printf("\"%s\" [ color = \"%s\", fontcolor = \"%s\", label=\"%s\" ];\n", pkg, color, color, pkg) > graph_file
+ }
+ print "}" > graph_file
+}
diff --git a/pkgtools/pbulk/files/pbulk/scripts/create-report-html.awk b/pkgtools/pbulk/files/pbulk/scripts/create-report-html.awk
new file mode 100755
index 00000000000..7cbe2e6fa67
--- /dev/null
+++ b/pkgtools/pbulk/files/pbulk/scripts/create-report-html.awk
@@ -0,0 +1,244 @@
+#!@AWK@ -f -
+# $NetBSD: create-report-html.awk,v 1.1.1.1 2007/06/19 19:49:59 joerg Exp $
+#
+# Copyright (c) 2007 Joerg Sonnenberger <joerg@NetBSD.org>.
+# All rights reserved.
+#
+# This code was developed as part of Google's Summer of Code 2007 program.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+# COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
+# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+# OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+function sort(ARRAY, INDICES, OPTIONS, i, idx, sort_cmd) {
+ sort_cmd = "sort " OPTIONS " > " tmp_sort
+
+ for (idx in ARRAY)
+ print idx | sort_cmd
+ close sort_cmd
+ i = 0
+ while ((getline < tmp_sort) > 0) {
+ INDICES[i] = $0
+ ++i
+ }
+ close tmp_sort
+ system("rm " tmp_sort)
+}
+
+function print_failed_log_line(PKGNAME, PHASE, VAR) {
+ if (VAR == 1)
+ printf("<td><a href=\"../%s/%s.log\">%s</a></td>\n",
+ PKGNAME, PHASE, PHASE) > html_report
+ else
+ print "<td>&nbsp;</td>" > html_report
+}
+
+function print_failed(PKGNAME, cmd, has_pre_clean, has_depends,
+ has_checksum,has_configure, has_build, has_install, has_package,
+ has_clean, has_deinstall) {
+ cmd = "ls " log_dir "/" PKGNAME " 2>/dev/null"
+ if (status[PKGNAME] == "failed") {
+ while ((cmd | getline) > 0) {
+ if ($0 == "pre-clean.log")
+ has_pre_clean = 1
+ else if ($0 == "depends.log")
+ has_depends = 1
+ else if ($0 == "checksum.log")
+ has_checksum = 1
+ else if ($0 == "configure.log")
+ has_configure = 1
+ else if ($0 == "build.log")
+ has_build = 1
+ else if ($0 == "install.log")
+ has_install = 1
+ else if ($0 == "package.log")
+ has_package = 1
+ else if ($0 == "clean.log")
+ has_clean = 1
+ else if ($0 == "deinstall.log")
+ has_deinstall = 1
+ }
+ close cmd
+ }
+ print "<tr class=\"" status[PKGNAME] "\">" > html_report
+ print "<td>" location[PKGNAME] "</td>" > html_report
+ print "<td>" PKGNAME "</td>" > html_report
+ if (depth[PKGNAME] == 0)
+ print "<td>&nbsp;</td>" > html_report
+ else
+ print "<td>" depth[PKGNAME] "</td>" > html_report
+ print "<td>" maintainer[PKGNAME] "</td>" > html_report
+ print "<td>" status[PKGNAME] "</td>" > html_report
+ print_failed_log_line(PKGNAME, "pre-clean", has_pre_clean)
+ print_failed_log_line(PKGNAME, "depends", has_depends)
+ print_failed_log_line(PKGNAME, "checksum", has_checksum)
+ print_failed_log_line(PKGNAME, "configure", has_configure)
+ print_failed_log_line(PKGNAME, "build", has_build)
+ print_failed_log_line(PKGNAME, "install", has_install)
+ print_failed_log_line(PKGNAME, "package", has_package)
+ print_failed_log_line(PKGNAME, "clean", has_clean)
+ print_failed_log_line(PKGNAME, "deinstall", has_deinstall)
+ print "</tr>" > html_report
+}
+
+function format_time(FORMAT, TIME, format_cmd) {
+ format_cmd = sprintf("date -r %d \"+%s\"", TIME, FORMAT)
+ format_cmd | getline
+ close format_cmd
+ return $0
+}
+
+BEGIN {
+ meta_dir = ARGV[1]
+ log_dir = ARGV[2]
+ report_file = meta_dir "/report"
+ html_report = meta_dir "/report.html"
+ status_file = meta_dir "/status"
+ tmp_sort = meta_dir "/tmp_sort"
+
+ pkgs_done = 0
+ pkgs_failed = 0
+ pkgs_prefailed = 0
+ pkgs_indirect_failed = 0
+ pkgs_indirect_prefailed = 0
+
+ while ((getline < status_file) > 0) {
+ if ($0 ~ "^PLATFORM=")
+ pkgsrc_platform = substr($0, 10)
+ else if ($0 ~ "^COMPILER=")
+ pkgsrc_compiler = substr($0, 10)
+ else if ($0 ~ "^BUILD_START=")
+ pkgsrc_build_start = substr($0, 13)
+ else if ($0 ~ "^BUILD_END=")
+ pkgsrc_build_end = substr($0, 11)
+ else if ($0 ~ "^BASE_URL=")
+ pkgsrc_base_url = substr($0, 10)
+ }
+ close status_file
+
+ while ((getline < report_file) > 0) {
+ if ($0 ~ "^PKGNAME=")
+ cur = substr($0, 9)
+ else if ($0 ~ "^MAINTAINER=")
+ maintainer[cur] = substr($0, 12)
+ else if ($0 ~ "^PKG_LOCATION=")
+ location[cur] = substr($0, 14)
+ else if ($0 ~ "^PKG_DEPTH=")
+ depth[cur] = substr($0, 11) - 1
+ else if ($0 ~ "^BUILD_STATUS=") {
+ status[cur] = substr($0, 14)
+ }
+ }
+ close(report_file)
+
+ for (pkg in status) {
+ if (status[pkg] == "done")
+ ++pkgs_done
+ else if (status[pkg] == "failed")
+ ++pkgs_failed
+ else if (status[pkg] == "prefailed")
+ ++pkgs_prefailed
+ else if (status[pkg] == "indirect-failed")
+ ++pkgs_indirect_failed
+ else if (status[pkg] == "indirect-prefailed")
+ ++pkgs_indirect_prefailed
+ }
+
+ print "<html>" > html_report
+ print " <head>" > html_report
+ printf(" <title> pkgsrc bulk build for %s from %s</title>\n",
+ pkgsrc_platform, format_time("%F %R")) > html_report
+ print " </head>" > html_report
+ print " <body>" > html_report
+ printf(" <h1> pkgsrc bulk build for %s</h1>\n", pkgsrc_platform) > html_report
+ printf(" <h2> Build start: %s</h2>\n", format_time("%F %R", pkgsrc_build_start)) > html_report
+ printf(" <h2> Build end: %s</h2>\n", format_time("%F %R", pkgsrc_build_end)) > html_report
+ print " <hr />" > html_report
+
+ all_pkgs = pkgs_done + pkgs_failed + pkgs_prefailed + pkgs_indirect_failed + pkgs_indirect_prefailed
+
+ print " <table>" > html_report
+ printf(" <tr><td>Total number of packages:</td><td>%d</td></tr>\n", all_pkgs) > html_report
+ printf(" <tr><td>Successfully built:</td><td>%d</td></tr>\n", pkgs_done) > html_report
+ printf(" <tr><td>Failed build:</td><td>%d</td></tr>\n", pkgs_failed) > html_report
+ printf(" <tr><td>Depending on failed package:</td><td>%d</td></tr>\n", pkgs_indirect_failed) > html_report
+ printf(" <tr><td>Explicitly broken or masked:</td><td>%d</td></tr>\n", pkgs_prefailed) > html_report
+ printf(" <tr><td>Depending on masked package:</td><td>%d</td></tr>\n", pkgs_indirect_prefailed) > html_report
+ print " </table>" > html_report
+ print " <hr />" > html_report
+
+ for (pkg in status) {
+ if (depth[pkg] == 0 || status[pkg] != "failed")
+ continue
+ top_count[depth[pkg] " " pkg] = pkg
+ }
+ sort(top_count, sorted_top_count, "-rn")
+ if (sorted_top_count[0]) {
+ print " <h2> Most offending build failures </h2>" > html_report
+ print " <table>" > html_report
+ print " <tr>" > html_report
+ print " <th> Location </th>" > html_report
+ print " <th> Package </th>" > html_report
+ print " <th> Breaks </th>" > html_report
+ print " <th> Maintainer </th>" > html_report
+ print " <th> Status </th>" > html_report
+ print " <th colspan=\"9\"> Build log </th>" > html_report
+ print " </tr>" > html_report
+
+ for (i = 0; i < 10 && sorted_top_count[i] != ""; ++i) {
+ pkg = top_count[sorted_top_count[i]]
+ print_failed(pkg)
+ }
+
+ print " </table>" > html_report
+ print " <hr />" > html_report
+ }
+
+ print " <h2> All unsuccesful builds </h2>" > html_report
+
+ print " <table>" > html_report
+ print " <tr>" > html_report
+ print " <th> Location </th>" > html_report
+ print " <th> Package </th>" > html_report
+ print " <th> Breaks </th>" > html_report
+ print " <th> Maintainer </th>" > html_report
+ print " <th> Status </th>" > html_report
+ print " <th colspan=\"9\"> Build log </th>" > html_report
+ print " </tr>" > html_report
+
+ for (pkg in status)
+ loc_pkg_array[location[pkg] " " pkg] = pkg
+
+ sort(loc_pkg_array, sorted_loc_pkg_array, "")
+
+ for (i = 0; sorted_loc_pkg_array[i] != ""; ++i) {
+ pkg = loc_pkg_array[sorted_loc_pkg_array[i]]
+ if (status[pkg] == "done")
+ continue
+ print_failed(pkg)
+ }
+
+ print " </table>" > html_report
+ print " </body>" > html_report
+ print "</html>" > html_report
+}
diff --git a/pkgtools/pbulk/files/pbulk/scripts/create-report-txt.awk b/pkgtools/pbulk/files/pbulk/scripts/create-report-txt.awk
new file mode 100755
index 00000000000..94d51245271
--- /dev/null
+++ b/pkgtools/pbulk/files/pbulk/scripts/create-report-txt.awk
@@ -0,0 +1,197 @@
+#!@AWK@ -f -
+# $NetBSD: create-report-txt.awk,v 1.1.1.1 2007/06/19 19:49:59 joerg Exp $
+#
+# Copyright (c) 2007 Joerg Sonnenberger <joerg@NetBSD.org>.
+# All rights reserved.
+#
+# This code was developed as part of Google's Summer of Code 2007 program.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+# COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
+# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+# OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+function sort(ARRAY, INDICES, OPTIONS, i, idx, sort_cmd) {
+ sort_cmd = "sort " OPTIONS " > " tmp_sort
+
+ for (idx in ARRAY)
+ print idx | sort_cmd
+ close sort_cmd
+ i = 0
+ while ((getline < tmp_sort) > 0) {
+ INDICES[i] = $0
+ ++i
+ }
+ close tmp_sort
+ system("rm " tmp_sort)
+}
+
+function format_time(FORMAT, TIME, format_cmd) {
+ format_cmd = sprintf("date -r %d \"+%s\"", TIME, FORMAT)
+ format_cmd | getline
+ close format_cmd
+ return $0
+}
+
+BEGIN {
+ meta_dir = ARGV[1]
+ report_file = meta_dir "/report"
+ txt_report = meta_dir "/report.txt"
+ html_report = meta_dir "/report.html"
+ status_file = meta_dir "/status"
+ tmp_sort = meta_dir "/tmp_sort"
+
+ pkgs_done = 0
+ pkgs_failed = 0
+ pkgs_prefailed = 0
+ pkgs_indirect_failed = 0
+ pkgs_indirect_prefailed = 0
+
+ while ((getline < status_file) > 0) {
+ if ($0 ~ "^PLATFORM=")
+ pkgsrc_platform = substr($0, 10)
+ else if ($0 ~ "^COMPILER=")
+ pkgsrc_compiler = substr($0, 10)
+ else if ($0 ~ "^BUILD_START=")
+ pkgsrc_build_start = substr($0, 13)
+ else if ($0 ~ "^BUILD_END=")
+ pkgsrc_build_end = substr($0, 11)
+ else if ($0 ~ "^BASE_URL=")
+ pkgsrc_base_url = substr($0, 10)
+ }
+ close status_file
+
+ while ((getline < report_file) > 0) {
+ if ($0 ~ "^PKGNAME=")
+ cur = substr($0, 9)
+ else if ($0 ~ "^MAINTAINER=")
+ maintainer[cur] = substr($0, 12)
+ else if ($0 ~ "^PKG_LOCATION=")
+ location[cur] = substr($0, 14)
+ else if ($0 ~ "^PKG_DEPTH=")
+ depth[cur] = substr($0, 11) - 1
+ else if ($0 ~ "^BUILD_STATUS=") {
+ status[cur] = substr($0, 14)
+ }
+ }
+ close(report_file)
+
+ for (pkg in status) {
+ loc = location[pkg]
+ if (status[pkg] == "failed") {
+ broken_location[loc] += depth[pkg]
+ pkg_location[loc] = pkg
+ if (location_status[loc] == "")
+ location_status[loc] = "failed"
+ else if (location_status[loc] == "ignore")
+ location_status[loc] = "mixed"
+ } else {
+ if (location_status[loc] == "failed")
+ location_status[loc] = "mixed"
+ else if (location_status[loc] == "")
+ location_status[loc] = "ignore"
+ }
+
+ if (status[pkg] == "done")
+ ++pkgs_done
+ else if (status[pkg] == "failed")
+ ++pkgs_failed
+ else if (status[pkg] == "prefailed")
+ ++pkgs_prefailed
+ else if (status[pkg] == "indirect-failed")
+ ++pkgs_indirect_failed
+ else if (status[pkg] == "indirect-prefailed")
+ ++pkgs_indirect_prefailed
+ }
+
+ print "pkgsrc bulk build report" > txt_report
+ print "========================" > txt_report
+ print "" > txt_report
+ print pkgsrc_platform > txt_report
+ print "Compiler: " pkgsrc_compiler > txt_report
+ print "" > txt_report
+ print "Build start: " format_time("%F %R", pkgsrc_build_start) > txt_report
+ print "Build end: " format_time("%F %R", pkgsrc_build_end) > txt_report
+ print "" > txt_report
+ report_base_url = pkgsrc_base_url format_time("/%Y%m%d.%H%M", pkgsrc_build_start)
+ print "Full report: " report_base_url "/meta/report.html" > txt_report
+ print "Machine readable version: " report_base_url "/meta/report.bz2" > txt_report
+ print "" > txt_report
+ all_pkgs = pkgs_done + pkgs_failed + pkgs_prefailed + pkgs_indirect_failed + pkgs_indirect_prefailed
+ printf "Total number of packages: %5d\n", all_pkgs > txt_report
+ printf " Succesfully built: %5d\n", pkgs_done > txt_report
+ printf " Failed to build: %5d\n", pkgs_failed > txt_report
+ printf " Depending on failed package: %5d\n", pkgs_indirect_failed > txt_report
+ printf " Explictly broken or masked: %5d\n", pkgs_prefailed > txt_report
+ printf " Depending on masked package: %5d\n", pkgs_indirect_prefailed > txt_report
+ print "" > txt_report
+
+ for (loc in location_status) {
+ if (broken_location[loc] != "" && broken_location[loc] != 0)
+ top_count[broken_location[loc] " " loc] = loc
+ }
+ sort(top_count, sorted_top_count, "-rn")
+ if (sorted_top_count[0]) {
+ print "Most offending build failures" > txt_report
+ print "" > txt_report
+ print "Package Breaks Maintainer" > txt_report
+ print "-------------------------------------------------------------------------" > txt_report
+ for (i = 0; i < 10 && sorted_top_count[i] != ""; ++i) {
+ loc = top_count[sorted_top_count[i]]
+ printf "%- 37s % 6d %s\n", loc, broken_location[loc],
+ maintainer[pkg_location[loc]] > txt_report
+ }
+ print "" > txt_report
+ }
+ print "Build failures" > txt_report
+ print "" > txt_report
+ print "Package Breaks Maintainer" > txt_report
+ print "-------------------------------------------------------------------------" > txt_report
+
+ sort(location_status, sorted_loc, "")
+
+ for (i = 0; sorted_loc[i] != ""; ++i) {
+ loc = sorted_loc[i]
+
+ if (location_status[loc] == "ignore")
+ continue
+
+ if (broken_location[loc] == 0)
+ printf "%- 44s %s\n", loc,
+ maintainer[pkg_location[loc]] > txt_report
+ else
+ printf "%- 37s % 6d %s\n", loc, broken_location[loc],
+ maintainer[pkg_location[loc]] > txt_report
+
+ if (location_status[loc] != "mixed")
+ continue
+ for (p in status) {
+ if (location[p] != loc || status[p] != "failed")
+ continue
+ if (depth[p] == 0)
+ printf " %- 40s %s\n", p, maintainer[p] > txt_report
+ else
+ printf " %- 33s % 6d %s\n", p, depth[p], maintainer[p] > txt_report
+ }
+ }
+ close txt_report
+}
diff --git a/pkgtools/pbulk/files/pbulk/scripts/create-report.awk b/pkgtools/pbulk/files/pbulk/scripts/create-report.awk
new file mode 100755
index 00000000000..663d0c6e059
--- /dev/null
+++ b/pkgtools/pbulk/files/pbulk/scripts/create-report.awk
@@ -0,0 +1,93 @@
+#!@AWK@ -f -
+# $NetBSD: create-report.awk,v 1.1.1.1 2007/06/19 19:49:59 joerg Exp $
+#
+# Copyright (c) 2007 Joerg Sonnenberger <joerg@NetBSD.org>.
+# All rights reserved.
+#
+# This code was developed as part of Google's Summer of Code 2007 program.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+# COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
+# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+# OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+BEGIN {
+ meta_dir = ARGV[1]
+ pbuild_file = meta_dir "/pbuild"
+ presolve_file = meta_dir "/presolve"
+ full_pbuild_file = meta_dir "/report"
+
+ FS = "|"
+ while ((getline < pbuild_file) > 0) {
+ status[$1] = $2
+ restricted[$1] = $3
+ depth[$1] = $4
+ }
+ close(pbuild_file)
+
+ FS = ""
+
+ while ((getline < presolve_file) > 0) {
+ if ($0 ~ "^PKGNAME=") {
+ cur = substr($0, 9)
+ pkg[cur] = $0
+ } else {
+ pkg[cur] = pkg[cur] "\n" $0
+ }
+
+ if ($0 ~ "^MAINTAINER=")
+ maintainer[cur] = substr($0, 12)
+
+ if ($0 ~ "^PKG_LOCATION=") {
+ loc = substr($0, 14)
+ location[cur] = loc
+
+ if (status[cur] == "failed") {
+ broken_location[loc] += depth[cur]
+ pkg_location[loc] = cur
+ if (location_status[loc] == "")
+ location_status[loc] = "failed"
+ else if (location_status[loc] == "ignore")
+ location_status[loc] = "mixed"
+ } else {
+ if (location_status[loc] == "failed")
+ location_status[loc] = "mixed"
+ else if (location_status[loc] == "")
+ location_status[loc] = "ignore"
+ }
+ }
+ }
+ close(presolve_file)
+
+ printf "" > full_pbuild_file
+
+ for (p in pkg) {
+ print pkg[p] > full_pbuild_file
+ print "PKG_DEPTH=" depth[p] > full_pbuild_file
+ if (restricted[p] == "restricted")
+ print "RESTRICTED_SUBSET=yes" > full_pbuild_file
+ else
+ print "RESTRICTED_SUBSET=no" > full_pbuild_file
+ print "BUILD_STATUS=" status[p] > full_pbuild_file
+ }
+ close full_pbuild_file
+}
diff --git a/pkgtools/pbulk/files/pbulk/scripts/pkg-build b/pkgtools/pbulk/files/pbulk/scripts/pkg-build
new file mode 100755
index 00000000000..597860decff
--- /dev/null
+++ b/pkgtools/pbulk/files/pbulk/scripts/pkg-build
@@ -0,0 +1,102 @@
+#!@SH@
+# $NetBSD: pkg-build,v 1.1.1.1 2007/06/19 19:49:59 joerg Exp $
+#
+# Copyright (c) 2007 Joerg Sonnenberger <joerg@NetBSD.org>.
+# All rights reserved.
+#
+# This code was developed as part of Google's Summer of Code 2007 program.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+# COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
+# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+# OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+. @PBULK_CONFIG@
+
+cleanup() {
+ ${make} clean > /dev/null 2>&1 || true
+ exit 1
+}
+
+run_make() {
+ ${make} $1 \
+ BATCH=1 \
+ DEPENDS_TARGET=/nonexistent \
+ ${MAKE_FLAGS} \
+ WRKLOG=${bulkdir}/${pkgname}/work.log
+
+}
+
+while read build_info_line; do
+ case "${build_info_line}" in
+ PKGNAME=*)
+ pkgname=${build_info_line#PKGNAME=}
+ ;;
+ PKG_LOCATION=*)
+ pkgdir=${build_info_line#PKG_LOCATION=}
+ ;;
+ DEPENDS=*)
+ dependencies=${build_info_line#DEPENDS=}
+ ;;
+ MULTI_VERSION=*)
+ MAKE_FLAGS=${build_info_line#MULTI_VERSION=*}
+ ;;
+ esac
+done
+
+${pkg_up_to_date_script} ${pkgname} ${dependencies} && exit 0
+
+set -e
+
+# Clean build system first
+rm -rf ${prefix} ${pkgdb} ${varbase}/qmail 2> /dev/null || true
+# Install fresh bootstrap state
+[ -n "${bootstrapkit}" ] && tar xzf ${bootstrapkit} -C /
+
+# Output directory
+mkdir -p ${bulkdir}/${pkgname}
+
+# Go to target directory
+cd ${pkgsrc}/${pkgdir}
+# Clean build area, just in case
+${make} clean > ${bulkdir}/${pkgname}/pre-clean.log 2>&1
+# Install all dependencies the package said it would need
+if [ ! -z "$dependencies" ]; then
+ PKG_PATH=${packages}/All ${pkg_add} $dependencies > ${bulkdir}/${pkgname}/depends.log 2>&1
+fi
+# Build package, create a separate log file for each major phase
+run_make checksum > ${bulkdir}/${pkgname}/checksum.log 2>&1 || cleanup
+run_make configure > ${bulkdir}/${pkgname}/configure.log 2>&1 || cleanup
+run_make all > ${bulkdir}/${pkgname}/build.log 2>&1 || cleanup
+run_make install > ${bulkdir}/${pkgname}/install.log 2>&1 || cleanup
+run_make package > ${bulkdir}/${pkgname}/package.log 2>&1 || cleanup
+# Clean build area
+${make} clean > ${bulkdir}/${pkgname}/clean.log 2>&1
+# Test uninstall rules
+${pkg_delete} ${pkgname} > ${bulkdir}/${pkgname}/deinstall.log 2>&1
+
+# Comment the following out if you want to test all deinstall scripts.
+# This is quite expensive and mostly redundant, so it is disabled by default.
+#${pkg_delete} -r \* > /dev/null 2>&1 || true
+
+# Cleanup build logs on success
+rm -R ${bulkdir}/${pkgname}
diff --git a/pkgtools/pbulk/files/pbulk/scripts/pkg-up-to-date b/pkgtools/pbulk/files/pbulk/scripts/pkg-up-to-date
new file mode 100755
index 00000000000..b0e14f6869c
--- /dev/null
+++ b/pkgtools/pbulk/files/pbulk/scripts/pkg-up-to-date
@@ -0,0 +1,64 @@
+#!@SH@
+# $NetBSD: pkg-up-to-date,v 1.1.1.1 2007/06/19 19:49:59 joerg Exp $
+#
+# Copyright (c) 2007 Joerg Sonnenberger <joerg@NetBSD.org>.
+# All rights reserved.
+#
+# This code was developed as part of Google's Summer of Code 2007 program.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+# COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
+# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+# OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+. @PBULK_CONFIG@
+
+set -e
+
+pkg="${packages}/All/$1.tgz"
+[ -f ${pkg} ]
+
+${external_pkg_info} -qb ${pkg} | sed 's/:/ /' | while read file file_id; do
+ [ -z "$file" ] && continue
+
+ id=`${ident} ${pkgsrc}/${file} 2> /dev/null | head -n 2 | sed -e 1d -e 's/^ //'`
+ [ "$id" = "$file_id" ]
+done
+
+# TODO: compare build options
+
+# Remove current package, so that only dependencies are in $* now.
+shift
+
+${external_pkg_info} -qN ${pkg} | while read dep; do
+ # pkg_info prints a trailing newline, ignore that
+ [ -z "${dep}" ] && continue
+ found=0
+ for dep2 in $*; do
+ if [ $dep = $dep2 ]; then
+ found=1
+ break
+ fi
+ done
+ [ $found = 1 ]
+ [ "${packages}/All/${dep}.tgz" -nt "${pkg}" ] && exit 1
+done
diff --git a/pkgtools/pbulk/files/pbulk/scripts/pre-build b/pkgtools/pbulk/files/pbulk/scripts/pre-build
new file mode 100755
index 00000000000..f66ba5300fb
--- /dev/null
+++ b/pkgtools/pbulk/files/pbulk/scripts/pre-build
@@ -0,0 +1,55 @@
+#!@SH@
+# $NetBSD: pre-build,v 1.1.1.1 2007/06/19 19:49:59 joerg Exp $
+#
+# Copyright (c) 2007 Joerg Sonnenberger <joerg@NetBSD.org>.
+# All rights reserved.
+#
+# This code was developed as part of Google's Summer of Code 2007 program.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+# COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
+# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+# OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+. @PBULK_CONFIG@
+
+set -e
+
+rm -rf "${bulklog}" || true
+mkdir -p "${bulklog}" "${loc}"
+
+# Clean build system first
+rm -rf ${prefix} ${pkgdb} ${varbase}/qmail 2> /dev/null || true
+# Install fresh bootstrap state
+[ -n "${bootstrapkit}" ] && tar xzf ${bootstrapkit} -C /
+
+# Log common settings...
+opsys=`cd ${pkgsrc}/pkgtools/pkg_install && ${make} show-var VARNAME=OPSYS`
+opver=`cd ${pkgsrc}/pkgtools/pkg_install && ${make} show-var VARNAME=OS_VERSION`
+platform=`cd ${pkgsrc}/pkgtools/pkg_install && ${make} show-var VARNAME=MACHINE_ARCH`
+compiler=`cd ${pkgsrc}/pkgtools/pkg_install && ${make} show-var VARNAME=PKGSRC_COMPILER`
+
+echo "PLATFORM=${opsys} ${opver}/${platform}" > ${loc}/status
+echo "COMPILER=${compiler}" >> ${loc}/status
+build_start=`date +%s`
+echo "BUILD_START=${build_start}" >> ${loc}/status
+echo "BASE_URL=${base_url}" >> ${loc}/status
diff --git a/pkgtools/pbulk/files/pbulk/scripts/report b/pkgtools/pbulk/files/pbulk/scripts/report
new file mode 100755
index 00000000000..aa51769c243
--- /dev/null
+++ b/pkgtools/pbulk/files/pbulk/scripts/report
@@ -0,0 +1,103 @@
+#!@SH@
+# $NetBSD: report,v 1.1.1.1 2007/06/19 19:49:59 joerg Exp $
+#
+# Copyright (c) 2007 Joerg Sonnenberger <joerg@NetBSD.org>.
+# All rights reserved.
+#
+# This code was developed as part of Google's Summer of Code 2007 program.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+# COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
+# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+# OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+. @PBULK_CONFIG@
+
+set -e
+
+echo "Build reports..."
+${report_script} ${loc}
+${bzip2} -zc ${loc}/report > ${loc}/report.bz2
+${report_html_script} ${loc} ${bulklog}
+${report_txt_script} ${loc}
+
+${report_graph_script} ${loc}
+if [ "`grep -- "->" < ${loc}/report.dot | wc -l`" -lt ${report_graph_script_limit} ]; then
+ ${pkg_add} ${packages}/All/graphviz
+ ${prefix}/bin/neato -Tsvg -Goverlap=ortho -Gsplines=true \
+ -o ${loc}/report.svg ${loc}/report.dot
+else
+ rm -f ${loc}/report.svg
+fi
+
+while read line; do
+ case "${line}" in
+ BUILD_START=*)
+ build_start=${line#BUILD_START=}
+ ;;
+ PLATFORM=*)
+ platform=${line#PLATFORM=}
+ esac
+done < ${loc}/status
+
+if [ -z "${build_start}" ]; then
+ echo "Could not find start time of build."
+ exit 1
+fi
+
+echo "Sending report mail..."
+formatted_start=`date -r ${build_start} "+%F %R"`
+report_dir=`date -r ${build_start} "+%Y%m%d.%H%M"`
+cat ${loc}/report.txt | ${mail} -s "pkgsrc ${platform} ${formatted_start}" joerg@bec.de
+
+cd ${bulklog}
+echo "Uploading report..."
+{
+ echo "meta/report.bz2"
+ echo "meta/report.txt"
+ echo "meta/report.html"
+ echo "meta/report.dot"
+ echo "meta/report.svg"
+ echo "meta/status"
+ while read pkg; do
+ echo "${pkg}/pre-clean.log"
+ echo "${pkg}/depends.log"
+ echo "${pkg}/checksum.log"
+ echo "${pkg}/configure.log"
+ echo "${pkg}/build.log"
+ echo "${pkg}/install.log"
+ echo "${pkg}/package.log"
+ echo "${pkg}/clean.log"
+ echo "${pkg}/deinstall.log"
+ echo "${pkg}/work.log"
+ done < ${loc}/error
+} | {
+ echo "+ meta/"
+ while read pkg; do
+ echo "+ ${pkg}/"
+ done < ${loc}/error
+
+ while read file; do
+ [ -f "$file" ] && echo "+ $file"
+ done
+ echo "- *"
+} | ${rsync} --exclude-from=- ${report_rsync_args} . ${report_rsync_target}/${report_dir}
diff --git a/pkgtools/pbulk/files/pbulk/scripts/scan b/pkgtools/pbulk/files/pbulk/scripts/scan
new file mode 100755
index 00000000000..e7b27b36cd9
--- /dev/null
+++ b/pkgtools/pbulk/files/pbulk/scripts/scan
@@ -0,0 +1,88 @@
+#!@SH@
+# $NetBSD: scan,v 1.1.1.1 2007/06/19 19:49:59 joerg Exp $
+#
+# Copyright (c) 2007 Joerg Sonnenberger <joerg@NetBSD.org>.
+# All rights reserved.
+#
+# This code was developed as part of Google's Summer of Code 2007 program.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+# COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
+# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+# OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+. @PBULK_CONFIG@
+
+set -e
+
+if [ -z "${limited_list}" ]; then
+ echo "Scanning..."
+ case "${master_mode}" in
+ [nN][oO])
+ ${pscan} -v -M ${make} ${pkgsrc} ${loc}/pscan
+ ;;
+ [yY][eE][sS])
+ ${pscan} -v -I ${pscan_start_script} -m ${master_port_scan} -M ${make} ${pkgsrc} ${loc}/pscan
+ ;;
+ *)
+ echo "master_mode must be either yes or no."
+ exit 1
+ ;;
+ esac
+ echo "Resolving..."
+ ${presolve} -v ${loc}/pscan > ${loc}/presolve 2> ${loc}/presolve.log
+else
+ initial=1
+ cp "${limited_list}" ${loc}/missing
+
+ while [ -s ${loc}/missing ]; do
+ sort -u ${loc}/missing > ${loc}/missing.s
+ echo "Scanning..."
+ case "${master_mode}" in
+ [nN][oO])
+ ${pscan} -v -l -M ${make} ${pkgsrc} ${loc}/pscan < ${loc}/missing.s
+ ;;
+ [yY][eE][sS])
+ ${pscan} -v -l -I ${pscan_start_script} -m ${master_port_scan} -M ${make} ${pkgsrc} ${loc}/pscan < ${loc}/missing.s
+ ;;
+ *)
+ echo "master_mode must be either yes or no."
+ exit 1
+ ;;
+ esac
+ echo "Resolving..."
+ if [ "$initial" = 1 ]; then
+ ${presolve} -i ${loc}/missing -v ${loc}/pscan > ${loc}/presolve
+ initial=0
+ else
+ ${presolve} -i ${loc}/missing -v ${loc}/presolve ${loc}/pscan > ${loc}/presolve.new
+ cmp -s ${loc}/presolve.new ${loc}/presolve && break
+ mv ${loc}/presolve.new ${loc}/presolve
+ fi
+ done
+
+ if [ -s ${loc}/missing ]; then
+ echo "Unresolvable dependencies found, exiting:"
+ cat ${loc}/missing
+ exit 1
+ fi
+fi
diff --git a/pkgtools/pbulk/files/pbulk/scripts/scan-client-start b/pkgtools/pbulk/files/pbulk/scripts/scan-client-start
new file mode 100755
index 00000000000..d9057f14882
--- /dev/null
+++ b/pkgtools/pbulk/files/pbulk/scripts/scan-client-start
@@ -0,0 +1,10 @@
+#!@SH@
+# $NetBSD: scan-client-start,v 1.1.1.1 2007/06/19 19:49:59 joerg Exp $
+
+. @PBULK_CONFIG@
+
+set -e
+
+for client in ${scan_clients}; do
+ ssh $client "${pscan_prepare} && ${pscan} -c ${master_port_scan} -M ${make} ${pkgsrc}" &
+done
diff --git a/pkgtools/pbulk/files/pbulk/scripts/upload b/pkgtools/pbulk/files/pbulk/scripts/upload
new file mode 100755
index 00000000000..cf6ff1136a8
--- /dev/null
+++ b/pkgtools/pbulk/files/pbulk/scripts/upload
@@ -0,0 +1,45 @@
+#!@SH@
+# $NetBSD: upload,v 1.1.1.1 2007/06/19 19:49:59 joerg Exp $
+#
+# Copyright (c) 2007 Joerg Sonnenberger <joerg@NetBSD.org>.
+# All rights reserved.
+#
+# This code was developed as part of Google's Summer of Code 2007 program.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+# COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
+# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+# OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+. @PBULK_CONFIG@
+
+set -e
+
+echo "Uploading packages..."
+cd ${packages}
+{
+ echo "+ SHA512.bz2"
+ echo "+ All/pkg_summary.bz2"
+ echo "+ All/pkg_summary.gz"
+ ${packages_script} ${loc}
+ echo "- *"
+} | sort | ${rsync} --exclude-from=- ${pkg_rsync_args} . ${pkg_rsync_target}