From 2172543ccc6cc4b87a3928e4227571c454682487 Mon Sep 17 00:00:00 2001 From: Marius Tolzmann Date: Thu, 27 Aug 2015 14:55:00 +0200 Subject: [PATCH 01/30] Makefile: Install lighttpd.conf to SYSCONFDIR/mxq --- Makefile | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index a5ffcad..71f7c3c 100644 --- a/Makefile +++ b/Makefile @@ -45,6 +45,11 @@ endif ######################################################################## +### strip /mxq from SYSCONFDIR if set +ifeq ($(notdir ${SYSCONFDIR}),mxq) + override SYSCONFDIR := $(patsubst %/,%,$(dir ${SYSCONFDIR})) +endif + ### strip /mxq from LIBEXECDIR if set ifeq ($(notdir ${LIBEXECDIR}),mxq) override LIBEXECDIR := $(patsubst %/,%,$(dir ${LIBEXECDIR})) @@ -506,7 +511,7 @@ install:: web/pages/mxq/mxq $(call quiet-install,0755,$^,${DESTDIR}${CGIDIR}/mxq) install:: web/lighttpd.conf - $(call quiet-install,0644,$^,${DESTDIR}${LIBEXECDIR}/mxq/lighttpd.conf) + $(call quiet-install,0644,$^,${DESTDIR}${SYSCONFDIR}/mxq/lighttpd.conf) ######################################################################## From 4418e56c76f67d5ecb28ba0a4e274365e1ab1c99 Mon Sep 17 00:00:00 2001 From: Marius Tolzmann Date: Fri, 28 Aug 2015 13:20:27 +0200 Subject: [PATCH 02/30] MXQ bump version --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 71f7c3c..b93cce9 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ MXQ_VERSION_MAJOR = 0 -MXQ_VERSION_MINOR = 13 +MXQ_VERSION_MINOR = 14 MXQ_VERSION_PATCH = 0 MXQ_VERSION_EXTRA = "beta" MXQ_VERSIONDATE = 2013-2015 From 169ae7a21484cc6120a2e070228ca679c6415f4d Mon Sep 17 00:00:00 2001 From: Donald Buczek Date: Thu, 3 Sep 2015 08:25:42 +0200 Subject: [PATCH 03/30] mxqsub: fix usage information (default restart mode) --- mxqsub.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mxqsub.c b/mxqsub.c index 6cb34d6..00bd144 100644 --- a/mxqsub.c +++ b/mxqsub.c @@ -88,9 +88,9 @@ static void print_usage(void) " -r, --restart[=MODE] restart job on system failure (default: 'never')\n" "\n" " available restart [MODE]s:\n" - " 'never' do not restart\n" + " 'never' do not restart. (default)\n" " 'samehost' only restart if running on the same host.\n" - " 'always' always restart or requeue. (default)\n" + " 'always' always restart or requeue.\n" "\n" "Job grouping:\n" " Grouping is done by default based on the jobs resource\n" From 0156b38b4f4d6662d79a920c981265e37762bea0 Mon Sep 17 00:00:00 2001 From: Donald Buczek Date: Thu, 3 Sep 2015 09:24:38 +0200 Subject: [PATCH 04/30] web: fix usage comment of lighttpd.conf to install location --- web/lighttpd.conf.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/lighttpd.conf.in b/web/lighttpd.conf.in index 7013003..6ac7210 100644 --- a/web/lighttpd.conf.in +++ b/web/lighttpd.conf.in @@ -1,7 +1,7 @@ # lighttpd.conf # start with: -# lighttpd [-D] -f @LIBEXECDIR@/mxq/lighttpd.conf +# lighttpd [-D] -f @SYSCONFDIR@/mxq/lighttpd.conf server.document-root = "@CGIDIR@" From 5232ca4e4b804f86169bb04e9501cab64f5978c0 Mon Sep 17 00:00:00 2001 From: Donald Buczek Date: Thu, 3 Sep 2015 11:44:56 +0200 Subject: [PATCH 05/30] web: fix typos --- web/pages/mxq/mxq.in | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/web/pages/mxq/mxq.in b/web/pages/mxq/mxq.in index 5e75c6a..4ec1d5d 100755 --- a/web/pages/mxq/mxq.in +++ b/web/pages/mxq/mxq.in @@ -327,8 +327,8 @@ job_priority : $o{job_priority} group_id : $link_group_id job_workdir : $o{job_workdir} -job_argc ; $o{job_argc} -job_argv ; $argv +job_argc : $o{job_argc} +job_argv : $argv job_stdout : $o{job_stdout} job_stderr : $o{job_stderr} job_umask: : $job_umask_text @@ -351,9 +351,9 @@ job_id_first : $o{job_id_first} stats_status : $o{stats_status} -stats_utim_sec : $o{stats_utime_sec} +stats_utime_sec : $o{stats_utime_sec} stats_utime_usec : $o{stats_utime_usec} -stats_stim_sec : $o{stats_stime_sec} +stats_stime_sec : $o{stats_stime_sec} stats_stime_usec : $o{stats_stime_usec} stats_real_sec : $o{stats_real_sec} stats_real_usec : $o{stats_real_usec} From fdb3dbbd8fb903282ac712a7bda6937f91c220cd Mon Sep 17 00:00:00 2001 From: Donald Buczek Date: Thu, 3 Sep 2015 11:57:38 +0200 Subject: [PATCH 06/30] web: escape user-chosen strings in html output --- web/pages/mxq/mxq.in | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/web/pages/mxq/mxq.in b/web/pages/mxq/mxq.in index 4ec1d5d..4c4f531 100755 --- a/web/pages/mxq/mxq.in +++ b/web/pages/mxq/mxq.in @@ -232,9 +232,12 @@ sub group_detail { my $group_status_text=group_status($o{'group_status'}); + my $group_name=escapeHTML($o{group_name}); + my $job_command=escapeHTML($o{job_command}); + $out.=<<"EOF";
-group_name     : $o{group_name}
+group_name     : $group_name
 group_status   : $group_status_text
 group_flags    : $o{group_flags}
 group_priority : $o{group_priority}
@@ -244,7 +247,7 @@ user_name      : $o{user_name}
 user_gid       : $o{user_gid}
 user_group     : $o{user_group}
 
-job_command    : $o{job_command}
+job_command    : $job_command
 job_threads    : $o{job_threads}
 job_memory     : $o{job_memory}
 job_time       : $o{job_time}
@@ -315,9 +318,15 @@ sub job {
 	my $job_status_text=job_status($o{'job_status'});
 	my $job_umask_text=sprintf('%03O',$o{job_umask});
 	my $link_group_id=a({href=>selfurl("/group/$o{group_id}")},$o{group_id});
-	my $argv=split_cmd($o{job_argv});
+	my $job_argv=escapeHTML(split_cmd($o{job_argv}));
+	my $job_workdir=escapeHTML($o{job_workdir});
+	my $job_stdout=escapeHTML($o{job_stdout});
+	my $job_stderr=escapeHTML($o{job_stderr});
+
+	defined $_ or $_='<null>' for values %o;
 
 	$out.=h2("Job Details $o{job_id}");
+
 	$out.=<<"EOF";
 
 job_status       : $job_status_text
@@ -326,11 +335,11 @@ job_priority     : $o{job_priority}
 
 group_id         : $link_group_id
 
-job_workdir      : $o{job_workdir}
+job_workdir      : $job_workdir
 job_argc         : $o{job_argc}
-job_argv         : $argv
-job_stdout       : $o{job_stdout}
-job_stderr       : $o{job_stderr}
+job_argv         : $job_argv
+job_stdout       : $job_stdout
+job_stderr       : $job_stderr
 job_umask:       : $job_umask_text
 
 host_submit      : $o{host_submit}

From becb9faff6c8dd8d6715965b0200c7916c1efacc Mon Sep 17 00:00:00 2001
From: Donald Buczek 
Date: Thu, 3 Sep 2015 14:18:30 +0200
Subject: [PATCH 07/30] mxqd: set TMPDIR

---
 Makefile | 5 +++--
 mxqd.c   | 3 +++
 2 files changed, 6 insertions(+), 2 deletions(-)

diff --git a/Makefile b/Makefile
index b93cce9..9f12067 100644
--- a/Makefile
+++ b/Makefile
@@ -74,8 +74,8 @@ OS_RELEASE = $(shell ./os-release)
 
 # special defaults for mariux64
 ifeq (${OS_RELEASE}, mariux64)
-   MXQ_INITIAL_PATH := ${MXQ_INITIAL_PATH}:/usr/local/package/bin
-
+   MXQ_INITIAL_PATH   := ${MXQ_INITIAL_PATH}:/usr/local/package/bin
+   CFLAGS_MXQ_INITIAL_TMPDIR      = -DMXQ_INITIAL_TMPDIR=\"/scratch/local\"
 endif
 
 ########################################################################
@@ -378,6 +378,7 @@ mxqd.o: $(mxq_job.h)
 mxqd.o: $(mx_mysql.h)
 mxqd.o: CFLAGS += $(CFLAGS_MYSQL)
 mxqd.o: CFLAGS += $(CFLAGS_MXQ_INITIAL_PATH)
+mxqd.o: CFLAGS += $(CFLAGS_MXQ_INITIAL_TMPDIR)
 mxqd.o: CFLAGS += -Wno-unused-but-set-variable
 
 clean: CLEAN += mxqd.o
diff --git a/mxqd.c b/mxqd.c
index 633f711..5a8eaf0 100644
--- a/mxqd.c
+++ b/mxqd.c
@@ -779,6 +779,9 @@ static int init_child_process(struct mxq_group_list *group, struct mxq_job *j)
     mx_setenv_forever("USERNAME", g->user_name);
     mx_setenv_forever("LOGNAME",  g->user_name);
     mx_setenv_forever("PATH",     MXQ_INITIAL_PATH);
+#ifdef MXQ_INITIAL_TMPDIR
+    mx_setenv_forever("TMPDIR", MXQ_INITIAL_TMPDIR);
+#endif
     mx_setenv_forever("PWD",      j->job_workdir);
     mx_setenv_forever("HOME",     passwd->pw_dir);
     mx_setenv_forever("SHELL",    passwd->pw_shell);

From faf268d2361b5bfd8d6ced59a31f4a32b7db92ca Mon Sep 17 00:00:00 2001
From: Donald Buczek 
Date: Thu, 3 Sep 2015 14:36:31 +0200
Subject: [PATCH 08/30] MXQ bump version

---
 Makefile | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Makefile b/Makefile
index 9f12067..b692e42 100644
--- a/Makefile
+++ b/Makefile
@@ -1,5 +1,5 @@
 MXQ_VERSION_MAJOR = 0
-MXQ_VERSION_MINOR = 14
+MXQ_VERSION_MINOR = 15
 MXQ_VERSION_PATCH = 0
 MXQ_VERSION_EXTRA = "beta"
 MXQ_VERSIONDATE = 2013-2015

From 90ef2645ae64e4edeb401430483281e4954a5a1f Mon Sep 17 00:00:00 2001
From: Marius Tolzmann 
Date: Fri, 18 Sep 2015 18:27:19 +0200
Subject: [PATCH 09/30] mxqd: Clean up setting initial TMPDIR

clean up becb9faff6c8dd8d6715965b0200c7916c1efacc
---
 Makefile | 6 ++++--
 mxqd.c   | 8 +++++---
 2 files changed, 9 insertions(+), 5 deletions(-)

diff --git a/Makefile b/Makefile
index b692e42..696a558 100644
--- a/Makefile
+++ b/Makefile
@@ -62,11 +62,13 @@ CGIDIR = ${LIBEXECDIR}/mxq/cgi
 MXQ_MYSQL_DEFAULT_FILE  = ${SYSCONFDIR}/mxq/mysql.cnf
 MXQ_MYSQL_DEFAULT_GROUP = mxqclient
 
-MXQ_INITIAL_PATH = /sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin
+MXQ_INITIAL_PATH   = /sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin
+MXQ_INITIAL_TMPDIR = /tmp
 
 CFLAGS_MXQ_MYSQL_DEFAULT_FILE  = -DMXQ_MYSQL_DEFAULT_FILE=\"$(MXQ_MYSQL_DEFAULT_FILE)\"
 CFLAGS_MXQ_MYSQL_DEFAULT_GROUP = -DMXQ_MYSQL_DEFAULT_GROUP=\"$(MXQ_MYSQL_DEFAULT_GROUP)\"
 CFLAGS_MXQ_INITIAL_PATH        = -DMXQ_INITIAL_PATH=\"$(MXQ_INITIAL_PATH)\"
+CFLAGS_MXQ_INITIAL_TMPDIR      = -DMXQ_INITIAL_TMPDIR=\"$(MXQ_INITIAL_TMPDIR)\"
 
 MYSQL_CONFIG = mysql_config
 
@@ -75,7 +77,7 @@ OS_RELEASE = $(shell ./os-release)
 # special defaults for mariux64
 ifeq (${OS_RELEASE}, mariux64)
    MXQ_INITIAL_PATH   := ${MXQ_INITIAL_PATH}:/usr/local/package/bin
-   CFLAGS_MXQ_INITIAL_TMPDIR      = -DMXQ_INITIAL_TMPDIR=\"/scratch/local\"
+   MXQ_INITIAL_TMPDIR := /scratch/local
 endif
 
 ########################################################################
diff --git a/mxqd.c b/mxqd.c
index 5a8eaf0..53c07ba 100644
--- a/mxqd.c
+++ b/mxqd.c
@@ -50,6 +50,10 @@
 #define MYSQL_DEFAULT_FILE     MXQ_MYSQL_DEFAULT_FILE
 #define MYSQL_DEFAULT_GROUP    "mxqd"
 
+#ifndef MXQ_INITIAL_TMPDIR
+#  define MXQ_INITIAL_TMPDIR    "/tmp"
+#endif
+
 volatile sig_atomic_t global_sigint_cnt=0;
 volatile sig_atomic_t global_sigterm_cnt=0;
 
@@ -779,9 +783,7 @@ static int init_child_process(struct mxq_group_list *group, struct mxq_job *j)
     mx_setenv_forever("USERNAME", g->user_name);
     mx_setenv_forever("LOGNAME",  g->user_name);
     mx_setenv_forever("PATH",     MXQ_INITIAL_PATH);
-#ifdef MXQ_INITIAL_TMPDIR
-    mx_setenv_forever("TMPDIR", MXQ_INITIAL_TMPDIR);
-#endif
+    mx_setenv_forever("TMPDIR",   MXQ_INITIAL_TMPDIR);
     mx_setenv_forever("PWD",      j->job_workdir);
     mx_setenv_forever("HOME",     passwd->pw_dir);
     mx_setenv_forever("SHELL",    passwd->pw_shell);

From ec090afddfd020bc72d7869d1492f002a53bde32 Mon Sep 17 00:00:00 2001
From: Marius Tolzmann 
Date: Mon, 21 Sep 2015 10:21:35 +0200
Subject: [PATCH 10/30] mxqd: Define default for MXQ_INITIAL_PATH in mxqd.c

---
 mxqd.c | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/mxqd.c b/mxqd.c
index 53c07ba..c2b572d 100644
--- a/mxqd.c
+++ b/mxqd.c
@@ -50,6 +50,10 @@
 #define MYSQL_DEFAULT_FILE     MXQ_MYSQL_DEFAULT_FILE
 #define MYSQL_DEFAULT_GROUP    "mxqd"
 
+#ifndef MXQ_INITIAL_PATH
+#  define MXQ_INITIAL_PATH      "/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin"
+#endif
+
 #ifndef MXQ_INITIAL_TMPDIR
 #  define MXQ_INITIAL_TMPDIR    "/tmp"
 #endif

From e974e79ea5e74fab7c549b210f9c3af12d0074d6 Mon Sep 17 00:00:00 2001
From: Marius Tolzmann 
Date: Mon, 21 Sep 2015 10:33:57 +0200
Subject: [PATCH 11/30] mxqd: Use mysql defaults from mxq.h

---
 mxqd.c | 37 ++++++++++---------------------------
 1 file changed, 10 insertions(+), 27 deletions(-)

diff --git a/mxqd.c b/mxqd.c
index c2b572d..6d4c340 100644
--- a/mxqd.c
+++ b/mxqd.c
@@ -34,18 +34,7 @@
 #include "mxq_job.h"
 #include "mx_mysql.h"
 #include "mxqd.h"
-
-#ifndef MXQ_VERSION
-#define MXQ_VERSION "0.00"
-#endif
-
-#ifndef MXQ_VERSIONFULL
-#define MXQ_VERSIONFULL "MXQ v0.00 super alpha 0"
-#endif
-
-#ifndef MXQ_VERSIONDATE
-#define MXQ_VERSIONDATE "2015"
-#endif
+#include "mxq.h"
 
 #define MYSQL_DEFAULT_FILE     MXQ_MYSQL_DEFAULT_FILE
 #define MYSQL_DEFAULT_GROUP    "mxqd"
@@ -63,22 +52,13 @@ volatile sig_atomic_t global_sigterm_cnt=0;
 
 int mxq_redirect_output(char *stdout_fname, char *stderr_fname);
 
-static void print_version(void)
-{
-    printf(
-    "mxqd - " MXQ_VERSIONFULL "\n"
-    "  by Marius Tolzmann  " MXQ_VERSIONDATE "\n"
-    "  Max Planck Institute for Molecular Genetics - Berlin Dahlem\n"
-    );
-}
-
 static void print_usage(void)
 {
-    print_version();
+    mxq_print_generic_version();
     printf(
     "\n"
     "Usage:\n"
-    "  mxqd [options]\n"
+    "  %s [options]\n"
     "\n"
     "options:\n"
     "  -j, --slots                default: 1\n"
@@ -98,13 +78,16 @@ static void print_usage(void)
     "\n"
     "Change how to connect to the mysql server:\n"
     "\n"
-    "  -M, --mysql-default-file [mysql-file]    default: " MYSQL_DEFAULT_FILE "\n"
-    "  -S, --mysql-default-group [mysql-group]  default: " MYSQL_DEFAULT_GROUP "\n"
+    "  -M, --mysql-default-file [mysql-file]    default: %s\n"
+    "  -S, --mysql-default-group [mysql-group]  default: %s\n"
     "\n"
     "Environment:\n"
     "  MXQ_MYSQL_DEFAULT_FILE   change default for [mysql-file]\n"
     "  MXQ_MYSQL_DEFAULT_GROUP  change default for [mysql-group]\n"
-    "\n"
+    "\n",
+    program_invocation_short_name,
+    MXQ_MYSQL_DEFAULT_FILE_STR,
+    MXQ_MYSQL_DEFAULT_GROUP_STR
     );
 }
 
@@ -280,7 +263,7 @@ int server_init(struct mxq_server *server, int argc, char *argv[])
                 break;
 
             case 'V':
-                print_version();
+                mxq_print_generic_version();
                 exit(EX_USAGE);
 
             case 'h':

From 586f295272e878e7f305d9cba477845d5c2aa47d Mon Sep 17 00:00:00 2001
From: Marius Tolzmann 
Date: Tue, 22 Sep 2015 14:12:23 +0200
Subject: [PATCH 12/30] mysql: Add new column job_max_per_node to mxq_group
 table

---
 mysql/alter_tables_0.15.0.sql | 5 +++++
 mysql/create_tables.sql       | 2 ++
 2 files changed, 7 insertions(+)
 create mode 100644 mysql/alter_tables_0.15.0.sql

diff --git a/mysql/alter_tables_0.15.0.sql b/mysql/alter_tables_0.15.0.sql
new file mode 100644
index 0000000..b1e8d15
--- /dev/null
+++ b/mysql/alter_tables_0.15.0.sql
@@ -0,0 +1,5 @@
+ALTER TABLE mxq_group
+    ADD COLUMN
+        job_max_per_node  INT2 UNSIGNED NOT NULL DEFAULT 0
+    AFTER
+        job_time;
diff --git a/mysql/create_tables.sql b/mysql/create_tables.sql
index 30365ce..cee2cd0 100644
--- a/mysql/create_tables.sql
+++ b/mysql/create_tables.sql
@@ -18,6 +18,8 @@ CREATE TABLE IF NOT EXISTS mxq_group (
    job_memory     INT8 UNSIGNED NOT NULL DEFAULT 1024,
    job_time       INT4 UNSIGNED NOT NULL DEFAULT 15,
 
+   job_max_per_node     INT2 UNSIGNED NOT NULL DEFAULT 0,
+
    group_jobs           INT8 UNSIGNED NOT NULL DEFAULT 0,
    group_jobs_inq       INT8 UNSIGNED NOT NULL DEFAULT 0,
    group_jobs_running   INT8 UNSIGNED NOT NULL DEFAULT 0,

From 9a9c9f600fe58c9822b90c571f3b4c78936880a1 Mon Sep 17 00:00:00 2001
From: Marius Tolzmann 
Date: Tue, 22 Sep 2015 14:14:09 +0200
Subject: [PATCH 13/30] mxq_group: Load new field job_max_per_node from
 mxq_group

---
 mxq_group.c | 5 ++++-
 mxq_group.h | 2 ++
 2 files changed, 6 insertions(+), 1 deletion(-)

diff --git a/mxq_group.c b/mxq_group.c
index f9ec96a..558fabd 100644
--- a/mxq_group.c
+++ b/mxq_group.c
@@ -12,7 +12,7 @@
 #include "mx_util.h"
 #include "mx_mysql.h"
 
-#define GROUP_FIELDS_CNT 29
+#define GROUP_FIELDS_CNT 30
 #define GROUP_FIELDS \
             " group_id," \
             " group_name," \
@@ -27,6 +27,7 @@
             " job_threads," \
             " job_memory," \
             " job_time," \
+            " job_max_per_node," \
             " group_jobs," \
             " group_jobs_inq," \
             " group_jobs_running," \
@@ -70,6 +71,8 @@ static int bind_result_group_fields(struct mx_mysql_bind *result, struct mxq_gro
     res += mx_mysql_bind_var(result, idx++, uint64, &(g->job_memory));
     res += mx_mysql_bind_var(result, idx++, uint32, &(g->job_time));
 
+    res += mx_mysql_bind_var(result, idx++, uint16, &(g->job_max_per_node));
+
     res += mx_mysql_bind_var(result, idx++, uint64, &(g->group_jobs));
     res += mx_mysql_bind_var(result, idx++, uint64, &(g->group_jobs_inq));
     res += mx_mysql_bind_var(result, idx++, uint64, &(g->group_jobs_running));
diff --git a/mxq_group.h b/mxq_group.h
index 8ab438d..db6b441 100644
--- a/mxq_group.h
+++ b/mxq_group.h
@@ -31,6 +31,8 @@ struct mxq_group {
    uint64_t  job_memory;
    uint32_t  job_time;
 
+   uint16_t  job_max_per_node;
+
    uint64_t  group_jobs;
    uint64_t  group_jobs_inq;
    uint64_t  group_jobs_running;

From 78065a2701b1a4fe928d50d099a95c5ab8924e3f Mon Sep 17 00:00:00 2001
From: Marius Tolzmann 
Date: Tue, 22 Sep 2015 14:14:50 +0200
Subject: [PATCH 14/30] mxqsub: Add option --max-jobs-per-node=N

---
 mxqsub.c | 40 ++++++++++++++++++++++++++++++++--------
 1 file changed, 32 insertions(+), 8 deletions(-)

diff --git a/mxqsub.c b/mxqsub.c
index 00bd144..87a476b 100644
--- a/mxqsub.c
+++ b/mxqsub.c
@@ -82,6 +82,9 @@ static void print_usage(void)
     "               to specify years, weeks, days, hours and minutes\n"
     "               Defaults to minutes if no suffix is set.\n"
     "\n"
+    "      --max-jobs-per-node=NUMBER  limit the number of jobs executed on each cluster node\n"
+    "                                  (default: 0 [limited by the server])\n"
+    "\n"
     "Job handling:\n"
     "  Define what to do if something bad happens:\n"
     "\n"
@@ -170,6 +173,7 @@ static int load_group_id(struct mx_mysql *mysql, struct mxq_group *g)
                 " AND job_threads = ?"
                 " AND job_memory = ?"
                 " AND job_time = ?"
+                " AND job_max_per_node = ?"
                 " AND group_priority = ?"
                 " AND group_status = 0"
                 " AND group_flags & ? = 0 "
@@ -189,8 +193,9 @@ static int load_group_id(struct mx_mysql *mysql, struct mxq_group *g)
     res += mx_mysql_statement_param_bind(stmt, 6, uint16, &(g->job_threads));
     res += mx_mysql_statement_param_bind(stmt, 7, uint64, &(g->job_memory));
     res += mx_mysql_statement_param_bind(stmt, 8, uint32, &(g->job_time));
-    res += mx_mysql_statement_param_bind(stmt, 9, uint16, &(g->group_priority));
-    res += mx_mysql_statement_param_bind(stmt, 10, uint64, &(flags));
+    res += mx_mysql_statement_param_bind(stmt, 9, uint16, &(g->job_max_per_node));
+    res += mx_mysql_statement_param_bind(stmt, 10, uint16, &(g->group_priority));
+    res += mx_mysql_statement_param_bind(stmt, 11, uint64, &(flags));
     assert(res == 0);
 
     res = mx_mysql_statement_execute(stmt, &num_rows);
@@ -250,6 +255,7 @@ static int load_group_id_by_group_id(struct mx_mysql *mysql, struct mxq_group *g
                 " AND job_threads = ?"
                 " AND job_memory = ?"
                 " AND job_time = ?"
+                " AND job_max_per_node = ?"
                 " AND group_priority = ?"
                 " AND group_status = 0"
                 " AND group_id = ?"
@@ -270,9 +276,10 @@ static int load_group_id_by_group_id(struct mx_mysql *mysql, struct mxq_group *g
     res += mx_mysql_statement_param_bind(stmt,  6, uint16, &(g->job_threads));
     res += mx_mysql_statement_param_bind(stmt,  7, uint64, &(g->job_memory));
     res += mx_mysql_statement_param_bind(stmt,  8, uint32, &(g->job_time));
-    res += mx_mysql_statement_param_bind(stmt,  9, uint16, &(g->group_priority));
-    res += mx_mysql_statement_param_bind(stmt, 10, uint64, &(g->group_id));
-    res += mx_mysql_statement_param_bind(stmt, 11, uint64, &(flags));
+    res += mx_mysql_statement_param_bind(stmt,  9, uint16, &(g->job_max_per_node));
+    res += mx_mysql_statement_param_bind(stmt, 10, uint16, &(g->group_priority));
+    res += mx_mysql_statement_param_bind(stmt, 11, uint64, &(g->group_id));
+    res += mx_mysql_statement_param_bind(stmt, 12, uint64, &(flags));
     assert(res == 0);
 
     res = mx_mysql_statement_execute(stmt, &num_rows);
@@ -325,6 +332,7 @@ static int load_group_id_run_or_wait(struct mx_mysql *mysql, struct mxq_group *g
                 " AND job_threads = ?"
                 " AND job_memory = ?"
                 " AND job_time = ?"
+                " AND job_max_per_node = ?"
                 " AND group_priority = ?"
                 " AND group_status = 0"
                 " AND ("
@@ -349,8 +357,9 @@ static int load_group_id_run_or_wait(struct mx_mysql *mysql, struct mxq_group *g
     res += mx_mysql_statement_param_bind(stmt, 6, uint16, &(g->job_threads));
     res += mx_mysql_statement_param_bind(stmt, 7, uint64, &(g->job_memory));
     res += mx_mysql_statement_param_bind(stmt, 8, uint32, &(g->job_time));
-    res += mx_mysql_statement_param_bind(stmt, 9, uint16, &(g->group_priority));
-    res += mx_mysql_statement_param_bind(stmt, 10, uint64, &(flags));
+    res += mx_mysql_statement_param_bind(stmt, 9, uint16, &(g->job_max_per_node));
+    res += mx_mysql_statement_param_bind(stmt, 10, uint16, &(g->group_priority));
+    res += mx_mysql_statement_param_bind(stmt, 11, uint64, &(flags));
     assert(res == 0);
 
     res = mx_mysql_statement_execute(stmt, &num_rows);
@@ -405,6 +414,7 @@ static int add_group(struct mx_mysql *mysql, struct mxq_group *g)
                 " job_threads = ?,"
                 " job_memory = ?,"
                 " job_time = ?,"
+                " job_max_per_node = ?,"
                 " group_priority = ?");
     if (!stmt) {
         mx_log_err("mx_mysql_statement_prepare(): %m");
@@ -420,7 +430,8 @@ static int add_group(struct mx_mysql *mysql, struct mxq_group *g)
     res += mx_mysql_statement_param_bind(stmt, 6, uint16, &(g->job_threads));
     res += mx_mysql_statement_param_bind(stmt, 7, uint64, &(g->job_memory));
     res += mx_mysql_statement_param_bind(stmt, 8, uint32, &(g->job_time));
-    res += mx_mysql_statement_param_bind(stmt, 9, uint16, &(g->group_priority));
+    res += mx_mysql_statement_param_bind(stmt, 9, uint16, &(g->job_max_per_node));
+    res += mx_mysql_statement_param_bind(stmt, 10, uint16, &(g->group_priority));
     assert(res == 0);
 
     res = mx_mysql_statement_execute(stmt, &num_rows);
@@ -585,6 +596,7 @@ int main(int argc, char *argv[])
     u_int16_t  arg_threads;
     u_int64_t  arg_memory;
     u_int32_t  arg_time;
+    u_int16_t  arg_max_per_node;
     u_int64_t  arg_groupid;
     char      *arg_workdir;
     char      *arg_stdout;
@@ -646,6 +658,8 @@ int main(int argc, char *argv[])
                 MX_OPTION_REQUIRED_ARG("memory",       'm'),
                 MX_OPTION_REQUIRED_ARG("runtime",      't'),
 
+                MX_OPTION_REQUIRED_ARG("max-jobs-per-node", 6),
+
                 MX_OPTION_REQUIRED_ARG("define",       'D'),
 
                 MX_OPTION_OPTIONAL_ARG("mysql-default-file",  'M'),
@@ -666,6 +680,7 @@ int main(int argc, char *argv[])
     arg_threads        = 1;
     arg_memory         = 2048;
     arg_time           = 0;
+    arg_max_per_node   = 0;
     arg_workdir        = current_workdir;
     arg_stdout         = "/dev/null";
     arg_stderr         = "stdout";
@@ -829,6 +844,13 @@ int main(int argc, char *argv[])
                 }
                 break;
 
+            case 6:
+                if (mx_strtou16(optctl.optarg, &arg_max_per_node) < 0) {
+                    mx_log_crit("--max-jobs-per-node '%s': %m", optctl.optarg);
+                    exit(EX_CONFIG);
+                }
+                break;
+
             case 'w':
                 if (!(*optctl.optarg)) {
                     mx_log_crit("--workdir '%s': String is empty.", optctl.optarg);
@@ -944,6 +966,8 @@ int main(int argc, char *argv[])
     group.job_memory     = arg_memory;
     group.job_time       = arg_time;
 
+    group.job_max_per_node = arg_max_per_node;
+
     job.job_flags      = arg_jobflags;
     job.job_priority   = arg_priority;
     job.job_workdir    = arg_workdir;

From 603a8c833944612bbbb45a1267f38846bae52c97 Mon Sep 17 00:00:00 2001
From: Marius Tolzmann 
Date: Tue, 22 Sep 2015 14:18:46 +0200
Subject: [PATCH 15/30] mxqd: Limit maximum number of jobs when requested by
 user

this finally implements https://github.molgen.mpg.de/mariux64/mxq/issues/28
---
 mxqd.c | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/mxqd.c b/mxqd.c
index 6d4c340..e923b06 100644
--- a/mxqd.c
+++ b/mxqd.c
@@ -446,6 +446,10 @@ void group_init(struct mxq_group_list *group)
     }
     jobs_max /= g->job_threads;
 
+    /* limit maximum number of jobs on user/group request */
+    if (jobs_max > g->job_max_per_node)
+        jobs_max = g->job_max_per_node;
+
     slots_max = jobs_max * slots_per_job;
     memory_max = jobs_max * g->job_memory;
 

From 168c5656ce126d0eac1bc4d47323581bf9968b65 Mon Sep 17 00:00:00 2001
From: Marius Tolzmann 
Date: Tue, 22 Sep 2015 15:46:17 +0200
Subject: [PATCH 16/30] web: Display new field job_max_per_node in group view

---
 web/pages/mxq/mxq.in | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/web/pages/mxq/mxq.in b/web/pages/mxq/mxq.in
index 4c4f531..0e85317 100755
--- a/web/pages/mxq/mxq.in
+++ b/web/pages/mxq/mxq.in
@@ -252,6 +252,8 @@ job_threads    : $o{job_threads}
 job_memory     : $o{job_memory}
 job_time       : $o{job_time}
 
+job_max_per_node      : $o{job_max_per_node}
+
 group_jobs            : $o{group_jobs}
 group_jobs_inq        : $o{group_jobs_inq}
 group_jobs_running    : $o{group_jobs_running}

From 7ba4434ec3643d810ea7021cd79de1169691c93a Mon Sep 17 00:00:00 2001
From: Marius Tolzmann 
Date: Wed, 23 Sep 2015 10:12:37 +0200
Subject: [PATCH 17/30] mxqd: Add options --initial-path and --initial-tmpdir

resolves https://github.molgen.mpg.de/mariux64/mxq/issues/29
---
 mxqd.c | 26 ++++++++++++++++++++++++--
 mxqd.h |  3 +++
 2 files changed, 27 insertions(+), 2 deletions(-)

diff --git a/mxqd.c b/mxqd.c
index 6d4c340..30a2698 100644
--- a/mxqd.c
+++ b/mxqd.c
@@ -73,6 +73,9 @@ static void print_usage(void)
     "      --no-log                      default: write a logfile\n"
     "      --debug                       default: info log level\n"
     "\n"
+    "      --initial-path          default: %s\n"
+    "      --initial-tmpdir   default: %s\n"
+    "\n"
     "  -V, --version\n"
     "  -h, --help\n"
     "\n"
@@ -86,6 +89,8 @@ static void print_usage(void)
     "  MXQ_MYSQL_DEFAULT_GROUP  change default for [mysql-group]\n"
     "\n",
     program_invocation_short_name,
+    MXQ_INITIAL_PATH,
+    MXQ_INITIAL_TMPDIR,
     MXQ_MYSQL_DEFAULT_FILE_STR,
     MXQ_MYSQL_DEFAULT_GROUP_STR
     );
@@ -194,6 +199,8 @@ int server_init(struct mxq_server *server, int argc, char *argv[])
     char *arg_mysql_default_group;
     char *arg_mysql_default_file;
     char *arg_pidfile = NULL;
+    char *arg_initial_path;
+    char *arg_initial_tmpdir;
     char arg_daemonize = 0;
     char arg_nolog = 0;
     int opt;
@@ -210,6 +217,8 @@ int server_init(struct mxq_server *server, int argc, char *argv[])
                 MX_OPTION_NO_ARG("no-log",               3),
                 MX_OPTION_NO_ARG("debug",                5),
                 MX_OPTION_REQUIRED_ARG("pid-file",       2),
+                MX_OPTION_REQUIRED_ARG("initial-path",   7),
+                MX_OPTION_REQUIRED_ARG("initial-tmpdir", 8),
                 MX_OPTION_REQUIRED_ARG("slots",        'j'),
                 MX_OPTION_REQUIRED_ARG("memory",       'm'),
                 MX_OPTION_REQUIRED_ARG("max-memory-per-slot", 'x'),
@@ -223,6 +232,9 @@ int server_init(struct mxq_server *server, int argc, char *argv[])
     arg_server_id = "main";
     arg_hostname  = mx_hostname();
 
+    arg_initial_path = MXQ_INITIAL_PATH;
+    arg_initial_tmpdir = MXQ_INITIAL_TMPDIR;
+
     arg_mysql_default_group = getenv("MXQ_MYSQL_DEFAULT_GROUP");
     if (!arg_mysql_default_group)
         arg_mysql_default_group = MYSQL_DEFAULT_GROUP;
@@ -309,6 +321,14 @@ int server_init(struct mxq_server *server, int argc, char *argv[])
                 arg_server_id = optctl.optarg;
                 break;
 
+            case 7:
+                arg_initial_path = optctl.optarg;
+                break;
+
+            case 8:
+                arg_initial_tmpdir = optctl.optarg;
+                break;
+
             case 'M':
                 arg_mysql_default_file = optctl.optarg;
                 break;
@@ -342,6 +362,8 @@ int server_init(struct mxq_server *server, int argc, char *argv[])
 
     server->hostname = arg_hostname;
     server->server_id = arg_server_id;
+    server->initial_path = arg_initial_path;
+    server->initial_tmpdir = arg_initial_tmpdir;
 
     server->flock = mx_flock(LOCK_EX, "/dev/shm/mxqd.%s.%s.lck", server->hostname, server->server_id);
     if (!server->flock) {
@@ -769,8 +791,8 @@ static int init_child_process(struct mxq_group_list *group, struct mxq_job *j)
     mx_setenv_forever("USER",     g->user_name);
     mx_setenv_forever("USERNAME", g->user_name);
     mx_setenv_forever("LOGNAME",  g->user_name);
-    mx_setenv_forever("PATH",     MXQ_INITIAL_PATH);
-    mx_setenv_forever("TMPDIR",   MXQ_INITIAL_TMPDIR);
+    mx_setenv_forever("PATH",     s->initial_path);
+    mx_setenv_forever("TMPDIR",   s->initial_tmpdir);
     mx_setenv_forever("PWD",      j->job_workdir);
     mx_setenv_forever("HOME",     passwd->pw_dir);
     mx_setenv_forever("SHELL",    passwd->pw_shell);
diff --git a/mxqd.h b/mxqd.h
index 5a9ca86..57c9b4f 100644
--- a/mxqd.h
+++ b/mxqd.h
@@ -76,6 +76,9 @@ struct mxq_server {
     char *pidfilename;
     struct mx_flock *flock;
 
+    char *initial_path;
+    char *initial_tmpdir;
+
     int is_running;
 };
 

From f42d526383ad005af180072a8e54b35656a6d42e Mon Sep 17 00:00:00 2001
From: Marius Tolzmann 
Date: Wed, 23 Sep 2015 12:56:58 +0200
Subject: [PATCH 18/30] mx_util: Add mx_read_first_line_from_file()

---
 mx_util.c      | 28 ++++++++++++++++++++++++++++
 mx_util.h      | 11 +++++++++++
 test_mx_util.c | 22 ++++++++++++++++++++++
 3 files changed, 61 insertions(+)

diff --git a/mx_util.c b/mx_util.c
index 199bcd5..612e9fe 100644
--- a/mx_util.c
+++ b/mx_util.c
@@ -752,6 +752,34 @@ int mx_open_newfile(char *fname)
     return fh;
 }
 
+int mx_read_first_line_from_file(char *fname, char **line)
+{
+    _mx_cleanup_fclose_ FILE *fp;
+    char *buf = NULL;
+    size_t n = 0;
+    ssize_t res;
+
+    fp = fopen(fname, "r");
+    if (!fp)
+        return -errno;
+
+    res = getline(&buf, &n, fp);
+    if (res == -1)
+        return -errno;
+
+    *line = buf;
+
+    if (!res)
+        return res;
+
+    res--;
+
+    if (buf[res] == '\n')
+        buf[res] = 0;
+
+    return res;
+}
+
 int mx_sleep(unsigned int seconds)
 {
     if (seconds)
diff --git a/mx_util.h b/mx_util.h
index d805aef..7cd10d0 100644
--- a/mx_util.h
+++ b/mx_util.h
@@ -5,6 +5,7 @@
 #include 
 #include 
 #include 
+#include 
 
 #include "mx_log.h"
 
@@ -46,9 +47,17 @@ static inline void __mx_free(void *ptr) {
     free(*(void **)ptr);
 }
 
+static inline void __mx_fclose(FILE **ptr) {
+    if (*ptr)
+        fclose(*ptr);
+}
+
 #undef _mx_cleanup_free_
 #define _mx_cleanup_free_ _mx_cleanup_(__mx_free)
 
+#undef _mx_cleanup_fclose_
+#define _mx_cleanup_fclose_ _mx_cleanup_(__mx_fclose)
+
 #undef likely
 #define likely(x)       __builtin_expect((x),1)
 
@@ -106,6 +115,8 @@ int mx_setenvf_forever(const char *name, char *fmt, ...) __attribute__ ((format(
 
 int mx_open_newfile(char *fname);
 
+int mx_read_first_line_from_file(char *fname, char **line);
+
 int mx_sleep(unsigned int seconds);
 int mx_sleep_nofail(unsigned int seconds);
 
diff --git a/test_mx_util.c b/test_mx_util.c
index 66dd776..6e956f3 100644
--- a/test_mx_util.c
+++ b/test_mx_util.c
@@ -44,6 +44,7 @@ static void test_mx_strtoul(void)
     assert(mx_strtoul("-1", &l) == -ERANGE);
     assert(mx_strtoul(" -1", &l) == -ERANGE);
 
+    assert(mx_strtoul("123 123", &l) == -EINVAL);
     assert(mx_strtoul("123s", &l) == -EINVAL);
     assert(mx_strtoul("0888", &l) == -EINVAL);
     assert(mx_strtoul("1.2", &l)  == -EINVAL);
@@ -271,6 +272,26 @@ static void test_mx_strtobytes(void)
     assert(mx_strtobytes("test", &l) == -EINVAL);
 }
 
+static void test_mx_read_first_line_from_file(void)
+{
+    char *str;
+
+    assert(mx_read_first_line_from_file("/proc/sys/kernel/random/boot_id", &str) == 36);
+    assert(str);
+    mx_free_null(str);
+
+    assert(mx_read_first_line_from_file("/proc/sys/kernel/random/uuid", &str) == 36);
+    assert(str);
+    mx_free_null(str);
+
+    assert(mx_read_first_line_from_file("/proc/no_such_file", &str) == -ENOENT);
+    assert(str == NULL);
+
+    assert(mx_read_first_line_from_file("/proc/self/stat", &str) > 0);
+    assert(str);
+    mx_free_null(str);
+}
+
 int main(int argc, char *argv[])
 {
     test_mx_strskipwhitespaces();
@@ -284,5 +305,6 @@ int main(int argc, char *argv[])
     test_mx_strtoseconds();
     test_mx_strtominutes();
     test_mx_strtobytes();
+    test_mx_read_first_line_from_file();
     return 0;
 }

From bbc2071df957e3f791129914611a4fc786e7da38 Mon Sep 17 00:00:00 2001
From: Marius Tolzmann 
Date: Wed, 23 Sep 2015 16:40:06 +0200
Subject: [PATCH 19/30] mx_util: Add mx_strscan_ull() and mx_strscan_ll()

---
 mx_util.c      | 62 ++++++++++++++++++++++++++++++++++++++++++++++++++
 mx_util.h      |  3 +++
 test_mx_util.c | 42 ++++++++++++++++++++++++++++++++++
 3 files changed, 107 insertions(+)

diff --git a/mx_util.c b/mx_util.c
index 612e9fe..7c42334 100644
--- a/mx_util.c
+++ b/mx_util.c
@@ -780,6 +780,68 @@ int mx_read_first_line_from_file(char *fname, char **line)
     return res;
 }
 
+int mx_strscan_ull(char **str, unsigned long long int *to)
+{
+    unsigned long long int l;
+    char *s;
+    char *p;
+    char o = 0;
+    int res;
+
+    s = *str;
+
+    p = strchr(s, ' ');
+    if (p) {
+        o = *p;
+        *p = 0;
+        p++;
+    } else {
+        p = s + strlen(s);
+    }
+
+    res = mx_strtoull(s, &l);
+    if (o)
+        *(p-1) = o;
+
+    if (res == 0) {
+        *to = l;
+        *str = p;
+    }
+
+    return res;
+}
+
+int mx_strscan_ll(char **str, long long int *to)
+{
+    long long int l;
+    char *s;
+    char *p;
+    char o = 0;
+    int res;
+
+    s = *str;
+
+    p = strchr(s, ' ');
+    if (p) {
+        o = *p;
+        *p = 0;
+        p++;
+    } else {
+        p = s + strlen(s);
+    }
+
+    res = mx_strtoll(s, &l);
+    if (o)
+        *(p-1) = o;
+
+    if (res == 0) {
+        *to = l;
+        *str = p;
+    }
+
+    return res;
+}
+
 int mx_sleep(unsigned int seconds)
 {
     if (seconds)
diff --git a/mx_util.h b/mx_util.h
index 7cd10d0..e6787db 100644
--- a/mx_util.h
+++ b/mx_util.h
@@ -117,6 +117,9 @@ int mx_open_newfile(char *fname);
 
 int mx_read_first_line_from_file(char *fname, char **line);
 
+int mx_strscan_ull(char **str, unsigned long long int *to);
+int mx_strscan_ll(char **str, long long int *to);
+
 int mx_sleep(unsigned int seconds);
 int mx_sleep_nofail(unsigned int seconds);
 
diff --git a/test_mx_util.c b/test_mx_util.c
index 6e956f3..6263acf 100644
--- a/test_mx_util.c
+++ b/test_mx_util.c
@@ -292,6 +292,47 @@ static void test_mx_read_first_line_from_file(void)
     mx_free_null(str);
 }
 
+static void test_mx_strscan(void)
+{
+    _mx_cleanup_free_ char *s = NULL;
+    char *str;
+    unsigned long long int ull;
+    long long int ll;
+
+    assert(s = strdup("123 456 -789 246 abc"));
+    str = s;
+
+    assert(mx_strscan_ull(&str, &ull) == 0);
+    assert(ull == 123);
+
+    assert(mx_strscan_ull(&str, &ull) == 0);
+    assert(ull == 456);
+
+    assert(mx_strscan_ull(&str, &ull) == -ERANGE);
+    assert(mx_streq(str, "-789 246 abc"));
+
+    assert(mx_strscan_ll(&str, &ll) == 0);
+    assert(ll == -789);
+    assert(mx_streq(str, "246 abc"));
+
+    assert(mx_strscan_ll(&str, &ll) == 0);
+    assert(ll == 246);
+    assert(mx_streq(str, "abc"));
+
+    assert(mx_strscan_ull(&str, &ull) == -EINVAL);
+    assert(mx_streq(str, "abc"));
+    assert(mx_streq(s, "123 456 -789 246 abc"));
+    mx_free_null(s);
+
+    assert(s = strdup("123"));
+    str = s;
+    assert(mx_strscan_ull(&str, &ull) == 0);
+    assert(ull == 123);
+    assert(mx_streq(str, ""));
+    assert(mx_streq(s, "123"));
+
+}
+
 int main(int argc, char *argv[])
 {
     test_mx_strskipwhitespaces();
@@ -306,5 +347,6 @@ int main(int argc, char *argv[])
     test_mx_strtominutes();
     test_mx_strtobytes();
     test_mx_read_first_line_from_file();
+    test_mx_strscan();
     return 0;
 }

From 426ffedb34b61153766e46a3c399d463ea92554c Mon Sep 17 00:00:00 2001
From: Marius Tolzmann 
Date: Wed, 23 Sep 2015 16:41:23 +0200
Subject: [PATCH 20/30] mx_util: Add mx_proc_pid_stat()

---
 mx_util.c      | 98 ++++++++++++++++++++++++++++++++++++++++++++++++++
 mx_util.h      | 51 ++++++++++++++++++++++++++
 test_mx_util.c | 24 +++++++++++++
 3 files changed, 173 insertions(+)

diff --git a/mx_util.c b/mx_util.c
index 7c42334..efde13f 100644
--- a/mx_util.c
+++ b/mx_util.c
@@ -842,6 +842,104 @@ int mx_strscan_ll(char **str, long long int *to)
     return res;
 }
 
+int mx_strscan_proc_pid_stat(char *str, struct proc_pid_stat *pps)
+{
+    size_t res = 0;
+    char *p;
+    char *s;
+
+    pps->comm = NULL;
+
+    s = str;
+
+    res += mx_strscan_ll(&s, &(pps->pid));
+
+    p = strrchr(s, ')');
+    if (!p)
+        return -(errno=EINVAL);
+
+    *p = 0;
+    s++;
+
+    pps->comm = mx_strdup_forever(s);
+    s = p + 2;
+
+    pps->state = *s;
+    res += !(*(s+1) == ' ');
+    s += 2;
+
+    res += mx_strscan_ll(&s, &(pps->ppid));
+    res += mx_strscan_ll(&s, &(pps->pgrp));
+    res += mx_strscan_ll(&s, &(pps->session));
+    res += mx_strscan_ll(&s, &(pps->tty_nr));
+    res += mx_strscan_ll(&s, &(pps->tpgid));
+    res += mx_strscan_ull(&s, &(pps->flags));
+    res += mx_strscan_ull(&s, &(pps->minflt));
+    res += mx_strscan_ull(&s, &(pps->cminflt));
+    res += mx_strscan_ull(&s, &(pps->majflt));
+    res += mx_strscan_ull(&s, &(pps->cmajflt));
+    res += mx_strscan_ull(&s, &(pps->utime));
+    res += mx_strscan_ull(&s, &(pps->stime));
+    res += mx_strscan_ll(&s, &(pps->cutime));
+    res += mx_strscan_ll(&s, &(pps->cstime));
+    res += mx_strscan_ll(&s, &(pps->priority));
+    res += mx_strscan_ll(&s, &(pps->nice));
+    res += mx_strscan_ll(&s, &(pps->num_threads));
+    res += mx_strscan_ll(&s, &(pps->itrealvalue));
+    res += mx_strscan_ull(&s, &(pps->starttime));
+    res += mx_strscan_ull(&s, &(pps->vsize));
+    res += mx_strscan_ll(&s, &(pps->rss));
+    res += mx_strscan_ull(&s, &(pps->rsslim));
+    res += mx_strscan_ull(&s, &(pps->startcode));
+    res += mx_strscan_ull(&s, &(pps->endcode));
+    res += mx_strscan_ull(&s, &(pps->startstack));
+    res += mx_strscan_ull(&s, &(pps->kstkesp));
+    res += mx_strscan_ull(&s, &(pps->kstkeip));
+    res += mx_strscan_ull(&s, &(pps->signal));
+    res += mx_strscan_ull(&s, &(pps->blocked));
+    res += mx_strscan_ull(&s, &(pps->sigignore));
+    res += mx_strscan_ull(&s, &(pps->sigcatch));
+    res += mx_strscan_ull(&s, &(pps->wchan));
+    res += mx_strscan_ull(&s, &(pps->nswap));
+    res += mx_strscan_ull(&s, &(pps->cnswap));
+    res += mx_strscan_ll(&s, &(pps->exit_signal));
+    res += mx_strscan_ll(&s, &(pps->processor));
+    res += mx_strscan_ull(&s, &(pps->rt_priority));
+    res += mx_strscan_ull(&s, &(pps->policy));
+    res += mx_strscan_ull(&s, &(pps->delayacct_blkio_ticks));
+    res += mx_strscan_ull(&s, &(pps->guest_time));
+    res += mx_strscan_ll(&s, &(pps->cguest_time));
+
+    if (res != 0)
+        return -(errno=EINVAL);
+
+    return 0;
+}
+
+int mx_proc_pid_stat(struct proc_pid_stat *pps, pid_t pid)
+{
+    _mx_cleanup_free_ char *fname = NULL;
+    _mx_cleanup_free_ char *line = NULL;
+    int res;
+
+    mx_asprintf_forever(&fname, "/proc/%d/stat", pid);
+
+    res = mx_read_first_line_from_file(fname, &line);
+    if (res < 0)
+        return res;
+
+    res = mx_strscan_proc_pid_stat(line, pps);
+    if (res < 0)
+        return res;
+
+    return 0;
+}
+
+void mx_proc_pid_stat_free(struct proc_pid_stat *pps)
+{
+    mx_free_null(pps->comm);
+}
+
 int mx_sleep(unsigned int seconds)
 {
     if (seconds)
diff --git a/mx_util.h b/mx_util.h
index e6787db..f79d77f 100644
--- a/mx_util.h
+++ b/mx_util.h
@@ -9,6 +9,53 @@
 
 #include "mx_log.h"
 
+struct proc_pid_stat {
+    long long int pid;     /* 1 */
+    char *comm;            /* 2 (comm) */
+    char state;            /* 3 "RSDZTW" */
+    long long int ppid;    /* 4 */
+    long long int pgrp;    /* 5 */
+    long long int session; /* 6 */
+    long long int tty_nr;  /* 7 */
+    long long int tpgid;   /* 8 */
+    unsigned long long int flags;   /*  9 */
+    unsigned long long int minflt;  /* 10 */
+    unsigned long long int cminflt; /* 11 */
+    unsigned long long int majflt;  /* 12 */
+    unsigned long long int cmajflt; /* 13 */
+    unsigned long long int utime;   /* 14 */
+    unsigned long long int stime;   /* 15 */
+    long long int cutime;   /* 16 */
+    long long int cstime;   /* 17 */
+    long long int priority; /* 18 */
+    long long int nice;     /* 19 */
+    long long int num_threads;          /* 20 */
+    long long int itrealvalue;          /* 21 */
+    unsigned long long int starttime;   /* 22 */
+    unsigned long long int vsize;       /* 23 */
+    long long int rss;                  /* 24 */
+    unsigned long long int rsslim;      /* 25 */
+    unsigned long long int startcode;   /* 26 */
+    unsigned long long int endcode;     /* 27 */
+    unsigned long long int startstack;  /* 28 */
+    unsigned long long int kstkesp;     /* 29 */
+    unsigned long long int kstkeip;     /* 30 */
+    unsigned long long int signal;      /* 31 */
+    unsigned long long int blocked;     /* 32 */
+    unsigned long long int sigignore;   /* 33 */
+    unsigned long long int sigcatch;    /* 34 */
+    unsigned long long int wchan;       /* 35 */
+    unsigned long long int nswap;       /* 36 */
+    unsigned long long int cnswap;      /* 37 */
+    long long int exit_signal;          /* 38 */
+    long long int processor;            /* 39 */
+    unsigned long long int rt_priority; /* 40 */
+    unsigned long long int policy;      /* 41 */
+    unsigned long long int delayacct_blkio_ticks; /* 42 */
+    unsigned long long int guest_time;  /* 43 */
+    long long int cguest_time;          /* 44 */
+};
+
 #ifdef MX_NDEBUG
 #   include 
 #   define mx_assert_return_minus_errno(test, eno) \
@@ -119,6 +166,10 @@ int mx_read_first_line_from_file(char *fname, char **line);
 
 int mx_strscan_ull(char **str, unsigned long long int *to);
 int mx_strscan_ll(char **str, long long int *to);
+int mx_strscan_proc_pid_stat(char *str, struct proc_pid_stat *pps);
+
+int mx_proc_pid_stat(struct proc_pid_stat *pps, pid_t pid);
+void mx_proc_pid_stat_free(struct proc_pid_stat *pps);
 
 int mx_sleep(unsigned int seconds);
 int mx_sleep_nofail(unsigned int seconds);
diff --git a/test_mx_util.c b/test_mx_util.c
index 6263acf..5700e80 100644
--- a/test_mx_util.c
+++ b/test_mx_util.c
@@ -1,7 +1,11 @@
 
+#define _GNU_SOURCE
+
 #include 
 #include 
 #include 
+#include 
+#include 
 
 #include "mx_util.h"
 
@@ -275,6 +279,7 @@ static void test_mx_strtobytes(void)
 static void test_mx_read_first_line_from_file(void)
 {
     char *str;
+    long long int l;
 
     assert(mx_read_first_line_from_file("/proc/sys/kernel/random/boot_id", &str) == 36);
     assert(str);
@@ -289,6 +294,7 @@ static void test_mx_read_first_line_from_file(void)
 
     assert(mx_read_first_line_from_file("/proc/self/stat", &str) > 0);
     assert(str);
+    mx_strtoll(str, &l);
     mx_free_null(str);
 }
 
@@ -298,6 +304,9 @@ static void test_mx_strscan(void)
     char *str;
     unsigned long long int ull;
     long long int ll;
+    _mx_cleanup_free_ char *line = NULL;
+    struct proc_pid_stat pps = {0};
+    struct proc_pid_stat pps2 = {0};
 
     assert(s = strdup("123 456 -789 246 abc"));
     str = s;
@@ -331,8 +340,23 @@ static void test_mx_strscan(void)
     assert(mx_streq(str, ""));
     assert(mx_streq(s, "123"));
 
+    assert(mx_read_first_line_from_file("/proc/self/stat", &line) > 0);
+    assert(mx_strscan_proc_pid_stat(line, &pps) == 0);
+    assert(pps.pid == getpid());
+    assert(pps.ppid == getppid());
+    assert(pps.state == 'R');
+    assert(mx_streq(pps.comm, program_invocation_short_name) || mx_streq(pps.comm, "memcheck-amd64-"));
+    mx_proc_pid_stat_free(&pps);
+
+    assert(mx_proc_pid_stat(&pps2, getpid()) == 0);
+    assert(pps2.pid == getpid());
+    assert(pps2.ppid == getppid());
+    assert(pps2.state == 'R');
+    assert(mx_streq(pps2.comm, program_invocation_short_name) || mx_streq(pps2.comm, "memcheck-amd64-"));
+    mx_proc_pid_stat_free(&pps2);
 }
 
+
 int main(int argc, char *argv[])
 {
     test_mx_strskipwhitespaces();

From 6057a124d448885b5c98b35dbe2a9d3785d0aac4 Mon Sep 17 00:00:00 2001
From: Marius Tolzmann 
Date: Wed, 23 Sep 2015 21:46:33 +0200
Subject: [PATCH 21/30] mxqd: set MXQ_HOSTNAME and MXQ_SERVERID

---
 mxqd.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/mxqd.c b/mxqd.c
index 73fef9a..552af71 100644
--- a/mxqd.c
+++ b/mxqd.c
@@ -808,6 +808,8 @@ static int init_child_process(struct mxq_group_list *group, struct mxq_job *j)
     mx_setenvf_forever("MXQ_MEMORY",  "%lu",    g->job_memory);
     mx_setenvf_forever("MXQ_TIME",    "%d",     g->job_time);
     mx_setenvf_forever("MXQ_HOSTID",  "%s::%s", s->hostname, s->server_id);
+    mx_setenv_forever("MXQ_HOSTNAME", s->hostname);
+    mx_setenv_forever("MXQ_SERVERID", s->server_id);
 
     fh = open("/proc/self/loginuid", O_WRONLY|O_TRUNC);
     if (fh == -1) {

From 4f2859e17aeee74e878133f4ccece884431c142c Mon Sep 17 00:00:00 2001
From: Marius Tolzmann 
Date: Thu, 24 Sep 2015 10:40:06 +0200
Subject: [PATCH 22/30] mxqd: Set MXQ_HOSTID to bootid-hex(starttime)-pid

---
 mxqd.c | 21 ++++++++++++++++++++-
 mxqd.h |  3 +++
 2 files changed, 23 insertions(+), 1 deletion(-)

diff --git a/mxqd.c b/mxqd.c
index 552af71..d69017b 100644
--- a/mxqd.c
+++ b/mxqd.c
@@ -203,11 +203,13 @@ int server_init(struct mxq_server *server, int argc, char *argv[])
     char *arg_initial_tmpdir;
     char arg_daemonize = 0;
     char arg_nolog = 0;
+    char *str_bootid;
     int opt;
     unsigned long threads_total = 1;
     unsigned long memory_total = 2048;
     unsigned long memory_max   = 0;
     int i;
+    struct proc_pid_stat pps = {0};
 
     struct mx_getopt_ctl optctl;
     struct mx_option opts[] = {
@@ -409,6 +411,20 @@ int server_init(struct mxq_server *server, int argc, char *argv[])
         }
     }
 
+    res = mx_read_first_line_from_file("/proc/sys/kernel/random/boot_id", &str_bootid);
+    assert(res == 36);
+    assert(str_bootid);
+
+    server->boot_id = str_bootid;
+
+    res = mx_proc_pid_stat(&pps, getpid());
+    assert(res == 0);
+
+    server->starttime = pps.starttime;
+    mx_proc_pid_stat_free(&pps);
+
+    mx_asprintf_forever(&server->host_id, "%s-%llx-%x", server->boot_id, server->starttime, getpid());
+
     server->slots = threads_total;;
     server->memory_total = memory_total;
     server->memory_max_per_slot = memory_max;
@@ -807,7 +823,7 @@ static int init_child_process(struct mxq_group_list *group, struct mxq_job *j)
     mx_setenvf_forever("MXQ_SLOTS",   "%lu",    group->slots_per_job);
     mx_setenvf_forever("MXQ_MEMORY",  "%lu",    g->job_memory);
     mx_setenvf_forever("MXQ_TIME",    "%d",     g->job_time);
-    mx_setenvf_forever("MXQ_HOSTID",  "%s::%s", s->hostname, s->server_id);
+    mx_setenv_forever("MXQ_HOSTID",   s->host_id);
     mx_setenv_forever("MXQ_HOSTNAME", s->hostname);
     mx_setenv_forever("MXQ_SERVERID", s->server_id);
 
@@ -1351,6 +1367,8 @@ void server_close(struct mxq_server *server)
         unlink(server->pidfilename);
 
     mx_funlock(server->flock);
+
+    mx_free_null(server->boot_id);
 }
 
 int killall(struct mxq_server *server, int sig, unsigned int pgrp)
@@ -1693,6 +1711,7 @@ int main(int argc, char *argv[])
     mx_log_info("  by Marius Tolzmann  " MXQ_VERSIONDATE);
     mx_log_info("  Max Planck Institute for Molecular Genetics - Berlin Dahlem");
     mx_log_info("hostname=%s server_id=%s :: MXQ server started.", server.hostname, server.server_id);
+    mx_log_info("  host_id=%s", server.host_id);
     mx_log_info("slots=%lu memory_total=%lu memory_avg_per_slot=%.0Lf memory_max_per_slot=%ld :: server initialized.",
                   server.slots, server.memory_total, server.memory_avg_per_slot, server.memory_max_per_slot);
 
diff --git a/mxqd.h b/mxqd.h
index 57c9b4f..c4db22d 100644
--- a/mxqd.h
+++ b/mxqd.h
@@ -70,6 +70,9 @@ struct mxq_server {
 
     struct mx_mysql *mysql;
 
+    char *boot_id;
+    unsigned long long int starttime;
+    char *host_id;
     char *hostname;
     char *server_id;
     char *lockfilename;

From 7e1dd39d47b92d4d5eefad788f8d34b9474472fa Mon Sep 17 00:00:00 2001
From: Marius Tolzmann 
Date: Thu, 24 Sep 2015 14:59:16 +0200
Subject: [PATCH 23/30] mysql: Add new field host_id to table mxq_job

---
 mysql/alter_tables_0.15.0.sql | 6 ++++++
 mysql/create_tables.sql       | 1 +
 mysql/fix_host_id.sql         | 7 +++++++
 3 files changed, 14 insertions(+)
 create mode 100644 mysql/fix_host_id.sql

diff --git a/mysql/alter_tables_0.15.0.sql b/mysql/alter_tables_0.15.0.sql
index b1e8d15..95faa89 100644
--- a/mysql/alter_tables_0.15.0.sql
+++ b/mysql/alter_tables_0.15.0.sql
@@ -3,3 +3,9 @@ ALTER TABLE mxq_group
         job_max_per_node  INT2 UNSIGNED NOT NULL DEFAULT 0
     AFTER
         job_time;
+
+ALTER TABLE mxq_job
+    ADD COLUMN
+        host_id VARCHAR(1023) NOT NULL DEFAULT ""
+    AFTER
+        server_id;
diff --git a/mysql/create_tables.sql b/mysql/create_tables.sql
index cee2cd0..c203fa2 100644
--- a/mysql/create_tables.sql
+++ b/mysql/create_tables.sql
@@ -83,6 +83,7 @@ CREATE TABLE IF NOT EXISTS mxq_job (
    host_submit    VARCHAR(64)     NOT NULL DEFAULT "localhost",
 
    server_id      VARCHAR(1023)   NOT NULL DEFAULT "",
+   host_id        VARCHAR(1023)   NOT NULL DEFAULT "",
 
    host_hostname  VARCHAR(64)     NOT NULL DEFAULT "",
    host_pid       INT4 UNSIGNED   NOT NULL DEFAULT 0,
diff --git a/mysql/fix_host_id.sql b/mysql/fix_host_id.sql
new file mode 100644
index 0000000..c3c6fa3
--- /dev/null
+++ b/mysql/fix_host_id.sql
@@ -0,0 +1,7 @@
+UPDATE mxq_job
+    SET
+        host_id = CONCAT(host_hostname, '::', server_id)
+    WHERE
+        host_id = ""
+    AND server_id != ""
+    AND host_hostname != "";

From 19458bd19e4054d9f0de46d8308e7a7d400550b4 Mon Sep 17 00:00:00 2001
From: Marius Tolzmann 
Date: Thu, 24 Sep 2015 14:59:50 +0200
Subject: [PATCH 24/30] web: Display host_id in job view

---
 web/pages/mxq/mxq.in | 1 +
 1 file changed, 1 insertion(+)

diff --git a/web/pages/mxq/mxq.in b/web/pages/mxq/mxq.in
index 0e85317..9794b9b 100755
--- a/web/pages/mxq/mxq.in
+++ b/web/pages/mxq/mxq.in
@@ -347,6 +347,7 @@ job_umask:       : $job_umask_text
 host_submit      : $o{host_submit}
 
 server_id        : $o{server_id}
+host_id          : $o{host_id}
 
 host_hostname    : $o{host_hostname}
 host_pid         : $o{host_pid}

From 8f9edb250ecf6aaefdbc3d800a76e1114f13ea8f Mon Sep 17 00:00:00 2001
From: Marius Tolzmann 
Date: Thu, 24 Sep 2015 15:00:43 +0200
Subject: [PATCH 25/30] mxq_job: Load field host_id from table mxq_job

---
 mxq_job.c | 22 +++++++++++++---------
 mxq_job.h |  1 +
 2 files changed, 14 insertions(+), 9 deletions(-)

diff --git a/mxq_job.c b/mxq_job.c
index 16a64af..a0789f8 100644
--- a/mxq_job.c
+++ b/mxq_job.c
@@ -16,7 +16,7 @@
 #include "mxq_group.h"
 #include "mxq_job.h"
 
-#define JOB_FIELDS_CNT 34
+#define JOB_FIELDS_CNT 35
 #define JOB_FIELDS \
                 " job_id, " \
                 " job_status, " \
@@ -32,28 +32,29 @@
                 \
                 " job_umask, " \
                 " host_submit, " \
+                " host_id, " \
                 " server_id, " \
                 " host_hostname, " \
-                " host_pid, " \
                 \
+                " host_pid, " \
                 " host_slots, " \
                 " UNIX_TIMESTAMP(date_submit) as date_submit, " \
                 " UNIX_TIMESTAMP(date_start) as date_start, " \
                 " UNIX_TIMESTAMP(date_end) as date_end, " \
-                " stats_status, " \
                 \
+                " stats_status, " \
                 " stats_utime_sec, " \
                 " stats_utime_usec, " \
                 " stats_stime_sec, " \
                 " stats_stime_usec, " \
-                " stats_real_sec, " \
                 \
+                " stats_real_sec, " \
                 " stats_real_usec, " \
                 " stats_maxrss, " \
                 " stats_minflt, " \
                 " stats_majflt, " \
-                " stats_nswap, " \
                 \
+                " stats_nswap, " \
                 " stats_inblock, " \
                 " stats_oublock, " \
                 " stats_nvcsw, " \
@@ -81,28 +82,29 @@ static int bind_result_job_fields(struct mx_mysql_bind *result, struct mxq_job *
 
     res += mx_mysql_bind_var(result, idx++, uint32, &(j->job_umask));
     res += mx_mysql_bind_var(result, idx++, string, &(j->host_submit));
+    res += mx_mysql_bind_var(result, idx++, string, &(j->host_id));
     res += mx_mysql_bind_var(result, idx++, string, &(j->server_id));
     res += mx_mysql_bind_var(result, idx++, string, &(j->host_hostname));
-    res += mx_mysql_bind_var(result, idx++, uint32, &(j->host_pid));
 
+    res += mx_mysql_bind_var(result, idx++, uint32, &(j->host_pid));
     res += mx_mysql_bind_var(result, idx++, uint32, &(j->host_slots));
     res += mx_mysql_bind_var(result, idx++,  int64, &(j->date_submit));
     res += mx_mysql_bind_var(result, idx++,  int64, &(j->date_start));
     res += mx_mysql_bind_var(result, idx++,  int64, &(j->date_end));
-    res += mx_mysql_bind_var(result, idx++,  int32, &(j->stats_status));
 
+    res += mx_mysql_bind_var(result, idx++,  int32, &(j->stats_status));
     res += mx_mysql_bind_var(result, idx++,  int64, &(j->stats_rusage.ru_utime.tv_sec));
     res += mx_mysql_bind_var(result, idx++,  int64, &(j->stats_rusage.ru_utime.tv_usec));
     res += mx_mysql_bind_var(result, idx++,  int64, &(j->stats_rusage.ru_stime.tv_sec));
     res += mx_mysql_bind_var(result, idx++,  int64, &(j->stats_rusage.ru_stime.tv_usec));
-    res += mx_mysql_bind_var(result, idx++,  int64, &(j->stats_realtime.tv_sec));
 
+    res += mx_mysql_bind_var(result, idx++,  int64, &(j->stats_realtime.tv_sec));
     res += mx_mysql_bind_var(result, idx++,  int64, &(j->stats_realtime.tv_usec));
     res += mx_mysql_bind_var(result, idx++,  int64, &(j->stats_rusage.ru_maxrss));
     res += mx_mysql_bind_var(result, idx++,  int64, &(j->stats_rusage.ru_minflt));
     res += mx_mysql_bind_var(result, idx++,  int64, &(j->stats_rusage.ru_majflt));
-    res += mx_mysql_bind_var(result, idx++,  int64, &(j->stats_rusage.ru_nswap));
 
+    res += mx_mysql_bind_var(result, idx++,  int64, &(j->stats_rusage.ru_nswap));
     res += mx_mysql_bind_var(result, idx++,  int64, &(j->stats_rusage.ru_inblock));
     res += mx_mysql_bind_var(result, idx++,  int64, &(j->stats_rusage.ru_oublock));
     res += mx_mysql_bind_var(result, idx++,  int64, &(j->stats_rusage.ru_nvcsw));
@@ -175,6 +177,8 @@ void mxq_job_free_content(struct mxq_job *j)
         mx_free_null(j->host_submit);
         j->_host_submit_length = 0;
 
+        mx_free_null(j->host_id);
+
         mx_free_null(j->server_id);
         j->_server_id_length = 0;
 
diff --git a/mxq_job.h b/mxq_job.h
index 5d78ba1..872010e 100644
--- a/mxq_job.h
+++ b/mxq_job.h
@@ -40,6 +40,7 @@ struct mxq_job {
     char *     host_submit;
     unsigned long _host_submit_length;
 
+    char *     host_id;
     char *     server_id;
     unsigned long _server_id_length;
 

From 9c739de4d3a69e624e24251d04682b089ceab2cb Mon Sep 17 00:00:00 2001
From: Marius Tolzmann 
Date: Thu, 24 Sep 2015 15:17:27 +0200
Subject: [PATCH 26/30] mxq_group/mxq_job: Cleanup old mxq_mysql fields not
 needed anymore

---
 mxq_group.c |  7 -------
 mxq_group.h |  4 ----
 mxq_job.c   | 15 ---------------
 mxq_job.h   |  7 -------
 4 files changed, 33 deletions(-)

diff --git a/mxq_group.c b/mxq_group.c
index 558fabd..65f7757 100644
--- a/mxq_group.c
+++ b/mxq_group.c
@@ -100,16 +100,9 @@ static int bind_result_group_fields(struct mx_mysql_bind *result, struct mxq_gro
 void mxq_group_free_content(struct mxq_group *g)
 {
         mx_free_null(g->group_name);
-        g->_group_name_length = 0;
-
         mx_free_null(g->user_name);
-        g->_user_name_length = 0;
-
         mx_free_null(g->user_group);
-        g->_user_group_length = 0;
-
         mx_free_null(g->job_command);
-        g->_job_command_length = 0;
 }
 
 
diff --git a/mxq_group.h b/mxq_group.h
index db6b441..e678cb3 100644
--- a/mxq_group.h
+++ b/mxq_group.h
@@ -10,7 +10,6 @@ struct mxq_group {
    uint64_t  group_id;
 
    char *    group_name;
-   unsigned long  _group_name_length;
 
    uint8_t   group_status;
    uint64_t  group_flags;
@@ -18,14 +17,11 @@ struct mxq_group {
 
    uint32_t  user_uid;
    char *    user_name;
-   unsigned long  _user_name_length;
 
    uint32_t  user_gid;
    char *    user_group;
-   unsigned long  _user_group_length;
 
    char *    job_command;
-   unsigned long  _job_command_length;
 
    uint16_t  job_threads;
    uint64_t  job_memory;
diff --git a/mxq_job.c b/mxq_job.c
index a0789f8..1deabdb 100644
--- a/mxq_job.c
+++ b/mxq_job.c
@@ -156,16 +156,9 @@ char *mxq_job_status_to_name(uint64_t status)
 void mxq_job_free_content(struct mxq_job *j)
 {
         mx_free_null(j->job_workdir);
-        j->_job_workdir_length = 0;
-
         mx_free_null(j->job_argv_str);
-        j->_job_argv_str_length = 0;
-
         mx_free_null(j->job_stdout);
-        j->_job_stdout_length = 0;
-
         mx_free_null(j->job_stderr);
-        j->_job_stderr_length = 0;
 
         if (j->tmp_stderr == j->tmp_stdout) {
             j->tmp_stdout = NULL;
@@ -173,18 +166,10 @@ void mxq_job_free_content(struct mxq_job *j)
             mx_free_null(j->tmp_stdout);
         }
         mx_free_null(j->tmp_stderr);
-
         mx_free_null(j->host_submit);
-        j->_host_submit_length = 0;
-
         mx_free_null(j->host_id);
-
         mx_free_null(j->server_id);
-        j->_server_id_length = 0;
-
         mx_free_null(j->host_hostname);
-        j->_host_hostname_length = 0;
-
         mx_free_null(j->job_argv);
         j->job_argv = NULL;
 }
diff --git a/mxq_job.h b/mxq_job.h
index 872010e..506849c 100644
--- a/mxq_job.h
+++ b/mxq_job.h
@@ -19,18 +19,14 @@ struct mxq_job {
     struct mxq_group * group_ptr;
 
     char *     job_workdir;
-    unsigned long _job_workdir_length;
 
     uint16_t   job_argc;
 
     char **    job_argv;
     char *     job_argv_str;
-    unsigned long _job_argv_str_length;
 
     char *     job_stdout;
-    unsigned long _job_stdout_length;
     char *     job_stderr;
-    unsigned long _job_stderr_length;
 
     char *     tmp_stdout;
     char *     tmp_stderr;
@@ -38,14 +34,11 @@ struct mxq_job {
     uint32_t   job_umask;
 
     char *     host_submit;
-    unsigned long _host_submit_length;
 
     char *     host_id;
     char *     server_id;
-    unsigned long _server_id_length;
 
     char *     host_hostname;
-    unsigned long _host_hostname_length;
 
     uint32_t   host_pid;
     uint32_t   host_slots;

From 4834dda44afefe67c4337bd2c78e048a58790fd4 Mon Sep 17 00:00:00 2001
From: Marius Tolzmann 
Date: Thu, 24 Sep 2015 15:40:43 +0200
Subject: [PATCH 27/30] mxqd: Fix max job per node limit

fixes 603a8c8
---
 mxqd.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/mxqd.c b/mxqd.c
index e923b06..17ba877 100644
--- a/mxqd.c
+++ b/mxqd.c
@@ -447,7 +447,7 @@ void group_init(struct mxq_group_list *group)
     jobs_max /= g->job_threads;
 
     /* limit maximum number of jobs on user/group request */
-    if (jobs_max > g->job_max_per_node)
+    if (g->job_max_per_node && jobs_max > g->job_max_per_node)
         jobs_max = g->job_max_per_node;
 
     slots_max = jobs_max * slots_per_job;

From 1db234809267ef6328fd783fe7d9ed7aa4fc81d5 Mon Sep 17 00:00:00 2001
From: Marius Tolzmann 
Date: Thu, 24 Sep 2015 15:03:31 +0200
Subject: [PATCH 28/30] mxqd: Set host_id for job after loading job from
 database

---
 mxq_job.c | 17 ++++++++++++-----
 mxq_job.h |  2 +-
 mxqd.c    |  2 +-
 3 files changed, 14 insertions(+), 7 deletions(-)

diff --git a/mxq_job.c b/mxq_job.c
index a0789f8..0a129ce 100644
--- a/mxq_job.c
+++ b/mxq_job.c
@@ -395,19 +395,21 @@ int mxq_set_job_status_loaded_on_server(struct mx_mysql *mysql, struct mxq_job *
     char *query =
             "UPDATE mxq_job SET"
             " job_status = " status_str(MXQ_JOB_STATUS_LOADED)
+            ", host_id = ?"
             " WHERE job_id = ?"
             " AND job_status = " status_str(MXQ_JOB_STATUS_ASSIGNED)
             " AND host_hostname = ?"
             " AND server_id = ?"
             " AND host_pid = 0";
 
-    res = mx_mysql_bind_init_param(¶m, 3);
+    res = mx_mysql_bind_init_param(¶m, 4);
     assert(res == 0);
 
     res = 0;
-    res += mx_mysql_bind_var(¶m, 0, uint64, &(job->job_id));
-    res += mx_mysql_bind_var(¶m, 1, string, &(job->host_hostname));
-    res += mx_mysql_bind_var(¶m, 2, string, &(job->server_id));
+    res += mx_mysql_bind_var(¶m, 0, string, &(job->host_id));
+    res += mx_mysql_bind_var(¶m, 1, uint64, &(job->job_id));
+    res += mx_mysql_bind_var(¶m, 2, string, &(job->host_hostname));
+    res += mx_mysql_bind_var(¶m, 3, string, &(job->server_id));
     assert(res == 0);
 
     res = mx_mysql_do_statement_noresult_retry_on_fail(mysql, query, ¶m);
@@ -666,7 +668,7 @@ int mxq_load_job_assigned_to_server(struct mx_mysql *mysql, struct mxq_job **mxq
     return res;
 }
 
-int mxq_load_job_from_group_for_server(struct mx_mysql *mysql, struct mxq_job *mxqjob, uint64_t group_id, char *hostname, char *server_id)
+int mxq_load_job_from_group_for_server(struct mx_mysql *mysql, struct mxq_job *mxqjob, uint64_t group_id, char *hostname, char *server_id, char *host_id)
 {
     int res;
     struct mxq_job *jobs   = NULL;
@@ -677,6 +679,8 @@ int mxq_load_job_from_group_for_server(struct mx_mysql *mysql, struct mxq_job *m
     assert(*hostname);
     assert(server_id);
     assert(*server_id);
+    assert(host_id);
+    assert(*host_id);
 
     do {
         res = mxq_load_job_assigned_to_server(mysql, &jobs, hostname, server_id);
@@ -701,6 +705,9 @@ int mxq_load_job_from_group_for_server(struct mx_mysql *mysql, struct mxq_job *m
         }
     } while (1);
 
+    mx_free_null(mxqjob->host_id);
+    mxqjob->host_id = mx_strdup_forever(host_id);
+
     res = mxq_set_job_status_loaded_on_server(mysql, mxqjob);
     if (res < 0) {
         mx_log_err("  group_id=%lu job_id=%lu :: mxq_set_job_status_loaded_on_server(): %m", group_id, mxqjob->job_id);
diff --git a/mxq_job.h b/mxq_job.h
index 872010e..4b09259 100644
--- a/mxq_job.h
+++ b/mxq_job.h
@@ -108,6 +108,6 @@ int mxq_set_job_status_exited(struct mx_mysql *mysql, struct mxq_job *job);
 int mxq_set_job_status_unknown_for_server(struct mx_mysql *mysql, char *hostname, char *server_id);
 int mxq_job_set_tmpfilenames(struct mxq_group *g, struct mxq_job *j);
 int mxq_load_job_assigned_to_server(struct mx_mysql *mysql, struct mxq_job **mxq_jobs, char *hostname, char *server_id);
-int mxq_load_job_from_group_for_server(struct mx_mysql *mysql, struct mxq_job *mxqjob, uint64_t group_id, char *hostname, char *server_id);
+int mxq_load_job_from_group_for_server(struct mx_mysql *mysql, struct mxq_job *mxqjob, uint64_t group_id, char *hostname, char *server_id, char *host_id);
 
 #endif
diff --git a/mxqd.c b/mxqd.c
index d69017b..54cca2a 100644
--- a/mxqd.c
+++ b/mxqd.c
@@ -1019,7 +1019,7 @@ unsigned long start_job(struct mxq_group_list *group)
 
     server = group->user->server;
 
-    res = mxq_load_job_from_group_for_server(server->mysql, &mxqjob, group->group.group_id, server->hostname, server->server_id);
+    res = mxq_load_job_from_group_for_server(server->mysql, &mxqjob, group->group.group_id, server->hostname, server->server_id, server->host_id);
 
     if (!res) {
         return 0;

From 78b44b66f9ad456fc3175faba420bd15e1dbe320 Mon Sep 17 00:00:00 2001
From: Marius Tolzmann 
Date: Thu, 24 Sep 2015 16:31:28 +0200
Subject: [PATCH 29/30] MXQ bump version

---
 Makefile | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Makefile b/Makefile
index 696a558..645109e 100644
--- a/Makefile
+++ b/Makefile
@@ -1,5 +1,5 @@
 MXQ_VERSION_MAJOR = 0
-MXQ_VERSION_MINOR = 15
+MXQ_VERSION_MINOR = 16
 MXQ_VERSION_PATCH = 0
 MXQ_VERSION_EXTRA = "beta"
 MXQ_VERSIONDATE = 2013-2015

From 2db1214bd2f37776d88d2e72f6c26140f15c296b Mon Sep 17 00:00:00 2001
From: Marius Tolzmann 
Date: Thu, 24 Sep 2015 16:42:09 +0200
Subject: [PATCH 30/30] web: Fix date_end in job view

---
 web/pages/mxq/mxq.in | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/web/pages/mxq/mxq.in b/web/pages/mxq/mxq.in
index 9794b9b..d90ca06 100755
--- a/web/pages/mxq/mxq.in
+++ b/web/pages/mxq/mxq.in
@@ -355,7 +355,7 @@ host_slots       : $o{host_slots}
 
 date_submit      : $o{date_submit}
 date_start       : $o{date_start}
-date_end         : $o{date_start}
+date_end         : $o{date_end}
 
 job_id_new       : $o{job_id_new}
 job_id_old       : $o{job_id_old}