diff --git a/Makefile b/Makefile
index b6b0e805..21c95c5b 100644
--- a/Makefile
+++ b/Makefile
@@ -1,6 +1,6 @@
 MXQ_VERSION_MAJOR = 0
-MXQ_VERSION_MINOR = 17
-MXQ_VERSION_PATCH = 0
+MXQ_VERSION_MINOR = 18
+MXQ_VERSION_PATCH = 2
 MXQ_VERSION_EXTRA = "beta"
 MXQ_VERSIONDATE = 2015
 
@@ -25,6 +25,7 @@ LIBEXECDIR = ${EPREFIX}/libexec
 DATADIR    = ${PREFIX}/share
 MANDIR     = ${DATADIR}/man
 SYSCONFDIR = ${PREFIX}/etc
+LOCALSTATEDIR = ${PREFIX}/var
 
 DESTDIR=
 
@@ -43,6 +44,11 @@ ifneq (, $(filter /usr /usr/local, ${PREFIX}))
     SYSCONFDIR = /etc
 endif
 
+### set localstatedir /var if prefix /usr || /usr/local
+ifneq (, $(filter /usr /usr/local, ${PREFIX}))
+    LOCALSTATEDIR = /var
+endif
+
 ########################################################################
 
 ### strip /mxq from SYSCONFDIR if set
@@ -125,6 +131,7 @@ CFLAGS += -DLIBEXECDIR=\"${LIBEXECDIR}\"
 CFLAGS += -DDATADIR=\"${DATADIR}\"
 CFLAGS += -DMANDIR=\"${MANDIR}\"
 CFLAGS += -DSYSCONFDIR=\"${SYSCONFDIR}\"
+CFLAGS += -DLOCALSTATEDIR=\"${LOCALSTATEDIR}\"
 CFLAGS += $(EXTRA_CFLAGS)
 
 ########################################################################
@@ -248,6 +255,7 @@ install::
 	$(call quiet-installdir,0755,${DESTDIR}${SYSCONFDIR}/mxq)
 	$(call quiet-installdir,0755,${DESTDIR}${MAN1DIR})
 	$(call quiet-installdir,0755,${DESTDIR}${CGIDIR})
+	$(call quiet-installdir,0755,${DESTDIR}${LOCALSTATEDIR}/log)
 
 ########################################################################
 
diff --git a/mx_util.c b/mx_util.c
index f783ed38..e2b488ce 100644
--- a/mx_util.c
+++ b/mx_util.c
@@ -856,6 +856,47 @@ int mx_strscan_ll(char **str, long long int *to)
     return res;
 }
 
+char *_mx_strconcat_do(char *first, ...)
+{
+    va_list ap;
+    char *join = NULL;
+    char *str;
+    char *ptr;
+    size_t len;
+
+    if (!first) {
+        join = strdup("");
+        return join;
+    }
+
+    len = strlen(first);
+
+    va_start(ap, first);
+        do {
+            str = va_arg(ap, char *);
+            if (!str)
+                break;
+            len += strlen(str);
+        } while(1);
+    va_end(ap);
+
+    join = malloc(len+1);
+    if (!join)
+        return NULL;
+
+    ptr = stpcpy(join, first);
+    va_start(ap, first);
+        do {
+            str = va_arg(ap, char *);
+            if (!str)
+                break;
+            ptr = stpcpy(ptr, str);
+        } while(1);
+    va_end(ap);
+
+    return join;
+}
+
 int mx_sleep(unsigned int seconds)
 {
     if (seconds)
diff --git a/mx_util.h b/mx_util.h
index 508476c6..92614fe5 100644
--- a/mx_util.h
+++ b/mx_util.h
@@ -136,6 +136,9 @@ 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);
 
+char *_mx_strconcat_do(char *first, ...);
+#define mx_strconcat(s, ...) _mx_strconcat_do((s), ##__VA_ARGS__, NULL)
+
 int mx_sleep(unsigned int seconds);
 int mx_sleep_nofail(unsigned int seconds);
 
diff --git a/mxq.h b/mxq.h
index 54b073f9..aac59fe1 100644
--- a/mxq.h
+++ b/mxq.h
@@ -46,6 +46,12 @@
 #endif
 #define MXQ_MYSQL_DEFAULT_GROUP_STR MXQ_MYSQL_DEFAULT_GROUP
 
+#if defined (LOCALSTATEDIR)
+#   define MXQ_LOGDIR LOCALSTATEDIR "/log"
+#else
+#   define MXQ_LOGDIR "/var/log"
+#endif
+
 static void mxq_print_generic_version(void)
 {
     printf(
diff --git a/mxq_group.c b/mxq_group.c
index 2887411d..e9c0ac4d 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 30
+#define GROUP_FIELDS_CNT 31
 #define GROUP_FIELDS \
             " group_id," \
             " group_name," \
@@ -37,6 +37,7 @@
             " group_jobs_unknown," \
             " group_jobs_restarted," \
             " group_slots_running," \
+            " stats_max_sumrss," \
             " stats_max_maxrss," \
             " stats_max_utime_sec," \
             " stats_max_stime_sec," \
@@ -85,6 +86,7 @@ static int bind_result_group_fields(struct mx_mysql_bind *result, struct mxq_gro
 
     res += mx_mysql_bind_var(result, idx++, uint64, &(g->group_slots_running));
 
+    res += mx_mysql_bind_var(result, idx++, uint64, &(g->stats_max_sumrss));
     res += mx_mysql_bind_var(result, idx++, uint64, &(g->stats_max_maxrss));
     res += mx_mysql_bind_var(result, idx++, int64, &(g->stats_max_utime.tv_sec));
     res += mx_mysql_bind_var(result, idx++, int64, &(g->stats_max_stime.tv_sec));
diff --git a/mxq_group.h b/mxq_group.h
index 46a352b2..7286a5a0 100644
--- a/mxq_group.h
+++ b/mxq_group.h
@@ -41,6 +41,7 @@ struct mxq_group {
 
    uint64_t  group_slots_running;
 
+   uint64_t  stats_max_sumrss;
    uint64_t  stats_max_maxrss;
 
    struct timeval stats_max_utime;
diff --git a/mxq_job.c b/mxq_job.c
index 68fc709d..4b33fda3 100644
--- a/mxq_job.c
+++ b/mxq_job.c
@@ -16,44 +16,39 @@
 #include "mxq_group.h"
 #include "mxq_job.h"
 
-#define JOB_FIELDS_CNT 35
+#define JOB_FIELDS_CNT 36
 #define JOB_FIELDS \
                 " job_id, " \
                 " job_status, " \
                 " job_flags, " \
                 " job_priority, " \
                 " group_id, " \
-                \
                 " job_workdir, " \
                 " job_argc, " \
                 " job_argv, " \
                 " job_stdout, " \
                 " job_stderr, " \
-                \
                 " job_umask, " \
                 " host_submit, " \
                 " host_id, " \
                 " server_id, " \
                 " host_hostname, " \
-                \
                 " 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_max_sumrss, " \
                 " stats_status, " \
                 " stats_utime_sec, " \
                 " stats_utime_usec, " \
                 " stats_stime_sec, " \
                 " stats_stime_usec, " \
-                \
                 " stats_real_sec, " \
                 " stats_real_usec, " \
                 " stats_maxrss, " \
                 " stats_minflt, " \
                 " stats_majflt, " \
-                \
                 " stats_nswap, " \
                 " stats_inblock, " \
                 " stats_oublock, " \
@@ -73,37 +68,32 @@ static int bind_result_job_fields(struct mx_mysql_bind *result, struct mxq_job *
     res += mx_mysql_bind_var(result, idx++, uint64, &(j->job_flags));
     res += mx_mysql_bind_var(result, idx++, uint16, &(j->job_priority));
     res += mx_mysql_bind_var(result, idx++, uint64, &(j->group_id));
-
     res += mx_mysql_bind_var(result, idx++, string, &(j->job_workdir));
     res += mx_mysql_bind_var(result, idx++, uint16, &(j->job_argc));
     res += mx_mysql_bind_var(result, idx++, string, &(j->job_argv_str));
     res += mx_mysql_bind_var(result, idx++, string, &(j->job_stdout));
     res += mx_mysql_bind_var(result, idx++, string, &(j->job_stderr));
-
     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_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++, uint64, &(j->stats_max_sumrss));
     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_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_inblock));
     res += mx_mysql_bind_var(result, idx++,  int64, &(j->stats_rusage.ru_oublock));
@@ -457,6 +447,7 @@ int mxq_set_job_status_running(struct mx_mysql *mysql, struct mxq_job *job)
 int mxq_set_job_status_exited(struct mx_mysql *mysql, struct mxq_job *job)
 {
     int res;
+    int idx;
     uint16_t newstatus;
     struct mx_mysql_bind param = {0};
 
@@ -485,6 +476,7 @@ int mxq_set_job_status_exited(struct mx_mysql *mysql, struct mxq_job *job)
             "UPDATE mxq_job SET"
             " job_status = ?,"
             " date_end = NULL,"
+            " stats_max_sumrss = ?, "
             " stats_status = ?, "
             " stats_utime_sec = ?, "
             " stats_utime_usec = ?, "
@@ -506,30 +498,32 @@ int mxq_set_job_status_exited(struct mx_mysql *mysql, struct mxq_job *job)
             " AND server_id = ?"
             " AND host_pid = ?";
 
-    res = mx_mysql_bind_init_param(&param, 20);
+    res = mx_mysql_bind_init_param(&param, 21);
     assert(res == 0);
 
+    idx = 0;
     res = 0;
-    res += mx_mysql_bind_var(&param, 0, uint16, &(newstatus));
-    res += mx_mysql_bind_var(&param, 1, int32,  &(job->stats_status));
-    res += mx_mysql_bind_var(&param, 2, int64, &(job->stats_rusage.ru_utime.tv_sec));
-    res += mx_mysql_bind_var(&param, 3, int64, &(job->stats_rusage.ru_utime.tv_usec));
-    res += mx_mysql_bind_var(&param, 4, int64, &(job->stats_rusage.ru_stime.tv_sec));
-    res += mx_mysql_bind_var(&param, 5, int64, &(job->stats_rusage.ru_stime.tv_usec));
-    res += mx_mysql_bind_var(&param, 6, int64, &(job->stats_realtime.tv_sec));
-    res += mx_mysql_bind_var(&param, 7, int64, &(job->stats_realtime.tv_usec));
-    res += mx_mysql_bind_var(&param, 8, int64, &(job->stats_rusage.ru_maxrss));
-    res += mx_mysql_bind_var(&param, 9, int64, &(job->stats_rusage.ru_minflt));
-    res += mx_mysql_bind_var(&param, 10, int64, &(job->stats_rusage.ru_majflt));
-    res += mx_mysql_bind_var(&param, 11, int64, &(job->stats_rusage.ru_nswap));
-    res += mx_mysql_bind_var(&param, 12, int64, &(job->stats_rusage.ru_inblock));
-    res += mx_mysql_bind_var(&param, 13, int64, &(job->stats_rusage.ru_oublock));
-    res += mx_mysql_bind_var(&param, 14, int64, &(job->stats_rusage.ru_nvcsw));
-    res += mx_mysql_bind_var(&param, 15, int64, &(job->stats_rusage.ru_nivcsw));
-    res += mx_mysql_bind_var(&param, 16, uint64, &(job->job_id));
-    res += mx_mysql_bind_var(&param, 17, string, &(job->host_hostname));
-    res += mx_mysql_bind_var(&param, 18, string, &(job->server_id));
-    res += mx_mysql_bind_var(&param, 19, uint32, &(job->host_pid));
+    res += mx_mysql_bind_var(&param, idx++, uint16, &(newstatus));
+    res += mx_mysql_bind_var(&param, idx++, uint64, &(job->stats_max_sumrss));
+    res += mx_mysql_bind_var(&param, idx++,  int32, &(job->stats_status));
+    res += mx_mysql_bind_var(&param, idx++,  int64, &(job->stats_rusage.ru_utime.tv_sec));
+    res += mx_mysql_bind_var(&param, idx++,  int64, &(job->stats_rusage.ru_utime.tv_usec));
+    res += mx_mysql_bind_var(&param, idx++,  int64, &(job->stats_rusage.ru_stime.tv_sec));
+    res += mx_mysql_bind_var(&param, idx++,  int64, &(job->stats_rusage.ru_stime.tv_usec));
+    res += mx_mysql_bind_var(&param, idx++,  int64, &(job->stats_realtime.tv_sec));
+    res += mx_mysql_bind_var(&param, idx++,  int64, &(job->stats_realtime.tv_usec));
+    res += mx_mysql_bind_var(&param, idx++,  int64, &(job->stats_rusage.ru_maxrss));
+    res += mx_mysql_bind_var(&param, idx++,  int64, &(job->stats_rusage.ru_minflt));
+    res += mx_mysql_bind_var(&param, idx++,  int64, &(job->stats_rusage.ru_majflt));
+    res += mx_mysql_bind_var(&param, idx++,  int64, &(job->stats_rusage.ru_nswap));
+    res += mx_mysql_bind_var(&param, idx++,  int64, &(job->stats_rusage.ru_inblock));
+    res += mx_mysql_bind_var(&param, idx++,  int64, &(job->stats_rusage.ru_oublock));
+    res += mx_mysql_bind_var(&param, idx++,  int64, &(job->stats_rusage.ru_nvcsw));
+    res += mx_mysql_bind_var(&param, idx++,  int64, &(job->stats_rusage.ru_nivcsw));
+    res += mx_mysql_bind_var(&param, idx++, uint64, &(job->job_id));
+    res += mx_mysql_bind_var(&param, idx++, string, &(job->host_hostname));
+    res += mx_mysql_bind_var(&param, idx++, string, &(job->server_id));
+    res += mx_mysql_bind_var(&param, idx++, uint32, &(job->host_pid));
     assert(res == 0);
 
     res = mx_mysql_do_statement_noresult_retry_on_fail(mysql, query, &param);
@@ -607,7 +601,7 @@ int mxq_job_set_tmpfilenames(struct mxq_group *g, struct mxq_job *j)
     return 1;
 }
 
-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_assigned_to_server(struct mx_mysql *mysql, struct mxq_job **mxq_jobs, uint64_t group_id, char *hostname, char *server_id)
 {
     int res;
     struct mxq_job *jobs = NULL;
@@ -630,14 +624,16 @@ int mxq_load_job_assigned_to_server(struct mx_mysql *mysql, struct mxq_job **mxq
             " WHERE job_status = " status_str(MXQ_JOB_STATUS_ASSIGNED)
             " AND host_hostname = ?"
             " AND server_id  = ?"
+            " AND group_id = ?"
             " LIMIT 1";
 
-    res = mx_mysql_bind_init_param(&param, 2);
+    res = mx_mysql_bind_init_param(&param, 3);
     assert(res == 0);
 
     res = 0;
     res += mx_mysql_bind_var(&param, 0, string, &hostname);
     res += mx_mysql_bind_var(&param, 1, string, &server_id);
+    res += mx_mysql_bind_var(&param, 2, uint64, &group_id);
     assert(res == 0);
 
     res = bind_result_job_fields(&result, &j);
@@ -668,14 +664,15 @@ int mxq_load_job_from_group_for_server(struct mx_mysql *mysql, struct mxq_job *m
     assert(*host_id);
 
     do {
-        res = mxq_load_job_assigned_to_server(mysql, &jobs, hostname, server_id);
+        res = mxq_load_job_from_group_assigned_to_server(mysql, &jobs, group_id, hostname, server_id);
 
         if(res < 0) {
-            mx_log_err("  group_id=%lu :: mxq_load_job_assigned_to_server: %m", group_id);
+            mx_log_err("  group_id=%lu :: mxq_load_job_from_group_assigned_to_server: %m", group_id);
             return 0;
         }
         if(res == 1) {
             memcpy(mxqjob, &jobs[0], sizeof(*mxqjob));
+            free(jobs);
             break;
         }
 
diff --git a/mxq_job.h b/mxq_job.h
index 42b2d81d..3bf62bbf 100644
--- a/mxq_job.h
+++ b/mxq_job.h
@@ -52,6 +52,8 @@ struct mxq_job {
 
     struct timeval stats_starttime;
 
+    uint64_t   stats_max_sumrss;
+
     int32_t        stats_status;
     struct timeval stats_realtime;
     struct rusage  stats_rusage;
@@ -103,7 +105,7 @@ int mxq_set_job_status_running(struct mx_mysql *mysql, struct mxq_job *job);
 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_assigned_to_server(struct mx_mysql *mysql, struct mxq_job **mxq_jobs, 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 43afc3d8..15fa51df 100644
--- a/mxqd.c
+++ b/mxqd.c
@@ -78,7 +78,9 @@ static void print_usage(void)
 #else
     "      --no-log                      default: write a logfile\n"
 #endif
+    "      --log-directory <logdir>      default: " MXQ_LOGDIR "\n"
     "      --debug                       default: info log level\n"
+    "\n"
     "      --recover-only  (recover from crash and exit)\n"
     "\n"
     "      --initial-path <path>         default: %s\n"
@@ -92,6 +94,9 @@ static void print_usage(void)
     "  -M, --mysql-default-file [mysql-file]    default: %s\n"
     "  -S, --mysql-default-group [mysql-group]  default: %s\n"
     "\n"
+    "Directories:\n"
+    "    LOGDIR      " MXQ_LOGDIR "\n"
+    "\n"
     "Environment:\n"
     "  MXQ_MYSQL_DEFAULT_FILE   change default for [mysql-file]\n"
     "  MXQ_MYSQL_DEFAULT_GROUP  change default for [mysql-group]\n"
@@ -135,11 +140,26 @@ static void cpuset_clear_running(cpu_set_t *running,cpu_set_t *job) {
 }
 
 /**********************************************************************/
-int setup_cronolog(char *cronolog, char *link, char *format)
+int setup_cronolog(char *cronolog, char *logdir, char *rellink, char *relformat)
 {
     int res;
     int pipe_fd[2];
     int pid;
+    _mx_cleanup_free_ char *link = NULL;
+    _mx_cleanup_free_ char *format = NULL;
+
+    if (logdir) {
+        link   = mx_strconcat(logdir, "/", rellink);
+        format = mx_strconcat(logdir, "/", relformat);
+    } else {
+        link   = strdup(rellink);
+        format = strdup(relformat);
+    }
+
+    if (!link || !format) {
+        mx_log_err("can't allocate filenames: (%m)");
+        return 0;
+    }
 
     res = pipe(pipe_fd);
     if (res == -1) {
@@ -276,6 +296,7 @@ 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_logdir = NULL;
     char *arg_initial_path;
     char *arg_initial_tmpdir;
     char arg_daemonize = 0;
@@ -296,7 +317,8 @@ int server_init(struct mxq_server *server, int argc, char *argv[])
                 MX_OPTION_NO_ARG("version",            'V'),
                 MX_OPTION_NO_ARG("daemonize",            1),
                 MX_OPTION_NO_ARG("no-log",               3),
-                MX_OPTION_NO_ARG("log",                  4),
+                MX_OPTION_OPTIONAL_ARG("log",            4),
+                MX_OPTION_REQUIRED_ARG("log-directory",  4),
                 MX_OPTION_NO_ARG("debug",                5),
                 MX_OPTION_NO_ARG("recover-only",         9),
                 MX_OPTION_REQUIRED_ARG("pid-file",       2),
@@ -354,6 +376,7 @@ int server_init(struct mxq_server *server, int argc, char *argv[])
 
             case 4:
                 arg_nolog = 0;
+                arg_logdir = optctl.optarg;
                 break;
 
             case 5:
@@ -440,13 +463,6 @@ int server_init(struct mxq_server *server, int argc, char *argv[])
 
     memset(server, 0, sizeof(*server));
 
-    res = mx_mysql_initialize(&(server->mysql));
-    assert(res == 0);
-
-    mx_mysql_option_set_default_file(server->mysql,  arg_mysql_default_file);
-    mx_mysql_option_set_default_group(server->mysql, arg_mysql_default_group);
-    mx_mysql_option_set_reconnect(server->mysql, 1);
-
     server->hostname = arg_hostname;
     server->server_id = arg_server_id;
     server->initial_path = arg_initial_path;
@@ -490,25 +506,33 @@ int server_init(struct mxq_server *server, int argc, char *argv[])
     setup_stdin("/dev/null");
 
     if (!arg_nolog) {
-        if (access("/var/log",R_OK|W_OK|X_OK)) {
-            mx_log_err("MAIN: cant write to /var/log: %m");
+        if (!arg_logdir)
+            arg_logdir = MXQ_LOGDIR;
+
+        if (access(arg_logdir, R_OK|W_OK|X_OK)) {
+            if (!RUNNING_AS_ROOT)
+                mx_log_warning("Running mxqd as non-root user.");
+            mx_log_err("MAIN: can't write to '%s': %m", arg_logdir);
             exit(EX_IOERR);
         }
-        res = setup_cronolog("/usr/sbin/cronolog", "/var/log/mxqd_log", "/var/log/%Y/mxqd_log-%Y-%m");
+        res = setup_cronolog("/usr/sbin/cronolog", arg_logdir, "mxqd_log", "%Y/mxqd_log-%Y-%m");
         if (!res) {
+            if (!RUNNING_AS_ROOT)
+                mx_log_warning("Running mxqd as non-root user.");
             mx_log_err("MAIN: cronolog setup failed. exiting.");
             exit(EX_IOERR);
         }
     }
 
-    if (!RUNNING_AS_ROOT) {
-#if defined(MXQ_DEVELOPMENT) || defined(RUNASNORMALUSER)
-        mx_log_notice("Running mxqd as non-root user.");
-#else
-        mx_log_err("Running mxqd as non-root user is not supported at the moment.");
-        exit(EX_USAGE);
-#endif
-    }
+    if (!RUNNING_AS_ROOT)
+        mx_log_warning("Running mxqd as non-root user.");
+
+    res = mx_mysql_initialize(&(server->mysql));
+    assert(res == 0);
+
+    mx_mysql_option_set_default_file(server->mysql,  arg_mysql_default_file);
+    mx_mysql_option_set_default_group(server->mysql, arg_mysql_default_group);
+    mx_mysql_option_set_reconnect(server->mysql, 1);
 
     res = mx_read_first_line_from_file("/proc/sys/kernel/random/boot_id", &str_bootid);
     assert(res == 36);
@@ -1143,7 +1167,7 @@ unsigned long start_job(struct mxq_group_list *group)
             group->group.user_name, group->group.user_uid, group->group.group_id, mxqjob.job_id);
 
     cpuset_init_job(&mxqjob.host_cpu_set,&server->cpu_set_available,&server->cpu_set_running,group->slots_per_job);
-    cpuset_log(" job assgined cpus: ",&mxqjob.host_cpu_set);
+    cpuset_log(" job assigned cpus: ",&mxqjob.host_cpu_set);
 
     mx_mysql_disconnect(server->mysql);
 
@@ -1638,19 +1662,19 @@ int killall_over_memory(struct mxq_server *server)
                     continue;
                 }
 
-                memory = pinfo->sum_rss * pagesize / 1024 / 1024;
+                memory = pinfo->sum_rss * pagesize / 1024;
 
                 if (job->max_sum_rss < memory)
                     job->max_sum_rss = memory;
 
-                if (memory <= group->group.job_memory)
+                if (memory/1024 <= group->group.job_memory)
                     continue;
 
-                mx_log_info("killall_over_memory(): used(%llu) > requested(%llu): Sending signal=KILL to job=%s(%d):%lu:%lu pgrp=%d",
-                    memory, group->group.job_memory,
+                mx_log_info("killall_over_memory(): used(%lluMiB) > requested(%lluMiB): Sending signal=TERM to job=%s(%d):%lu:%lu pid=%d",
+                    memory/1024, group->group.job_memory,
                     group->group.user_name, group->group.user_uid, group->group.group_id, job->job.job_id, pid);
 
-                kill(-pid, SIGKILL);
+                kill(pid, SIGTERM);
             }
         }
     }
@@ -1762,7 +1786,7 @@ int catchall(struct mxq_server *server) {
         g = &job->group->group;
 
         timersub(&now, &j->stats_starttime, &j->stats_realtime);
-
+        j->stats_max_sumrss = job->max_sum_rss;
         j->stats_status = status;
         j->stats_rusage = rusage;
 
diff --git a/mxqdump.c b/mxqdump.c
index 2cac9c13..8b0b9665 100644
--- a/mxqdump.c
+++ b/mxqdump.c
@@ -128,6 +128,7 @@ static int print_group(struct mxq_group *g)
         " max_utime=%lu"
         " max_real=%lu"
         " max_memory=%lukiB"
+        " max_rss=%lukiB"
         " wait_sec=%lu"
         " run_sec=%lu"
         " idle_sec=%lu"
@@ -150,10 +151,11 @@ static int print_group(struct mxq_group *g)
         g->job_threads,
         g->job_memory*1024,
         g->job_time*60,
-        (100UL*(uint64_t)g->stats_max_maxrss/1024UL/g->job_memory),
+        (100UL*(uint64_t)g->stats_max_sumrss/1024UL/g->job_memory),
         (100UL*(uint64_t)g->stats_max_real.tv_sec/60UL/g->job_time),
         g->stats_max_utime.tv_sec,
         g->stats_max_real.tv_sec,
+        g->stats_max_sumrss,
         g->stats_max_maxrss,
         g->stats_wait_sec,
         g->stats_run_sec,
@@ -219,6 +221,7 @@ static int print_job(struct mxq_group *g, struct mxq_job *j)
         " runtime_requested=%us"
         " time_load=%lu%%"
         " memory_requested=%lukiB"
+        " max_memory=%lukiB"
         " max_rss=%lukiB"
         " memory_load=%lu%%"
         " threads=%d"
@@ -244,8 +247,9 @@ static int print_job(struct mxq_group *g, struct mxq_job *j)
         g->job_time*60,
         (100UL*(run_sec)/60UL/g->job_time),
         g->job_memory*1024,
+        j->stats_max_sumrss,
         j->stats_rusage.ru_maxrss,
-        (100UL*j->stats_rusage.ru_maxrss/1024UL/g->job_memory),
+        (100UL*j->stats_max_sumrss/1024UL/g->job_memory),
         g->job_threads,
         j->host_slots,
         mxq_job_status_to_name(j->job_status),
diff --git a/mysql/alter_tables_0.17.0.sql b/mysql/alter_tables_0.17.0.sql
new file mode 100644
index 00000000..b9daa722
--- /dev/null
+++ b/mysql/alter_tables_0.17.0.sql
@@ -0,0 +1,11 @@
+ALTER TABLE mxq_group
+    ADD COLUMN
+        stats_max_sumrss  INT8 UNSIGNED NOT NULL DEFAULT 0
+    AFTER
+        group_date_end;
+
+ALTER TABLE mxq_job
+    ADD COLUMN
+        stats_max_sumrss  INT8 UNSIGNED NOT NULL DEFAULT 0
+    AFTER
+        job_id_first;
diff --git a/mysql/create_tables.sql b/mysql/create_tables.sql
index 978c679f..f2566cc6 100644
--- a/mysql/create_tables.sql
+++ b/mysql/create_tables.sql
@@ -34,6 +34,8 @@ CREATE TABLE IF NOT EXISTS mxq_group (
 
    group_date_end    TIMESTAMP NOT NULL DEFAULT 0,
 
+   stats_max_sumrss  INT8 UNSIGNED NOT NULL DEFAULT 0,
+
    stats_max_maxrss     INT8 UNSIGNED NOT NULL DEFAULT 0,
    stats_max_utime_sec  INT8 UNSIGNED NOT NULL DEFAULT 0,
    stats_max_stime_sec  INT8 UNSIGNED NOT NULL DEFAULT 0,
@@ -94,6 +96,8 @@ CREATE TABLE IF NOT EXISTS mxq_job (
    job_id_old     INT8 UNSIGNED   NULL DEFAULT NULL,
    job_id_first   INT8 UNSIGNED   NULL DEFAULT NULL,
 
+   stats_max_sumrss  INT8 UNSIGNED NOT NULL DEFAULT 0,
+
    stats_status   INT4 UNSIGNED NOT NULL DEFAULT 0,
 
    stats_utime_sec  INT8 UNSIGNED NOT NULL DEFAULT 0,
diff --git a/mysql/create_trigger.sql b/mysql/create_trigger.sql
index ccd2af6e..c4269acf 100644
--- a/mysql/create_trigger.sql
+++ b/mysql/create_trigger.sql
@@ -64,6 +64,7 @@ CREATE TRIGGER mxq_update_job BEFORE UPDATE ON mxq_job
                    group_slots_running=group_slots_running-NEW.host_slots,
                    group_jobs_running=group_jobs_running-1,
                    group_jobs_failed=group_jobs_failed+1,
+                   stats_max_sumrss=GREATEST(stats_max_sumrss, NEW.stats_max_sumrss),
                    stats_max_maxrss=GREATEST(stats_max_maxrss, NEW.stats_maxrss),
                    stats_max_utime_sec=GREATEST(stats_max_utime_sec, NEW.stats_utime_sec),
                    stats_max_stime_sec=GREATEST(stats_max_stime_sec, NEW.stats_stime_sec),
@@ -99,6 +100,7 @@ CREATE TRIGGER mxq_update_job BEFORE UPDATE ON mxq_job
                    group_slots_running=group_slots_running-NEW.host_slots,
                    group_jobs_running=group_jobs_running-1,
                    group_jobs_finished=group_jobs_finished+1,
+                   stats_max_sumrss=GREATEST(stats_max_sumrss, NEW.stats_max_sumrss),
                    stats_max_maxrss=GREATEST(stats_max_maxrss, NEW.stats_maxrss),
                    stats_max_utime_sec=GREATEST(stats_max_utime_sec, NEW.stats_utime_sec),
                    stats_max_stime_sec=GREATEST(stats_max_stime_sec, NEW.stats_stime_sec),
@@ -113,6 +115,7 @@ CREATE TRIGGER mxq_update_job BEFORE UPDATE ON mxq_job
                 WHERE group_id=NEW.group_id;
             ELSEIF NEW.job_status NOT IN (399, 755, 989, 990) THEN
                 UPDATE mxq_group SET
+                   stats_max_sumrss=GREATEST(stats_max_sumrss, NEW.stats_max_sumrss),
                    stats_max_maxrss=GREATEST(stats_max_maxrss, NEW.stats_maxrss),
                    stats_max_utime_sec=GREATEST(stats_max_utime_sec, NEW.stats_utime_sec),
                    stats_max_stime_sec=GREATEST(stats_max_stime_sec, NEW.stats_stime_sec),
diff --git a/test_mx_util.c b/test_mx_util.c
index 06cfe728..2f6be8f7 100644
--- a/test_mx_util.c
+++ b/test_mx_util.c
@@ -384,6 +384,27 @@ static void test_mx_strvec() {
     mx_strvec_free(strvec);
 }
 
+static void test_mx_strcat() {
+    char *str;
+    char *str2;
+
+    assert(str = mx_strconcat(NULL));
+    assert(mx_streq(str, ""));
+    mx_free_null(str);
+
+    assert(str = mx_strconcat("abc", "123"));
+    assert(mx_streq(str, "abc123"));
+
+    assert(str2 = mx_strconcat(str, "def", str, "456"));
+    assert(mx_streq(str2, "abc123defabc123456"));
+    mx_free_null(str2);
+
+    assert(str2 = mx_strconcat(str));
+    assert(mx_streq(str, str2));
+    mx_free_null(str);
+    mx_free_null(str2);
+}
+
 static void test_mx_cpuset(void)
 {
     cpu_set_t cpuset;
@@ -421,6 +442,7 @@ int main(int argc, char *argv[])
     test_mx_read_first_line_from_file();
     test_mx_strscan();
     test_mx_strvec();
+    test_mx_strcat();
     test_mx_cpuset();
     return 0;
 }
diff --git a/web/pages/mxq/mxq.in b/web/pages/mxq/mxq.in
index 6a4f8b2b..422c14b4 100755
--- a/web/pages/mxq/mxq.in
+++ b/web/pages/mxq/mxq.in
@@ -269,6 +269,7 @@ group_mtime           : $o{group_mtime}
 
 group_date_end        : $o{group_date_end}
 
+stats_max_sumrss                 : $o{stats_max_sumrss} kiB
 stats_max_maxrss                 : $o{stats_max_maxrss}
 stats_max_utime_sec              : $o{stats_max_utime_sec}
 stats_max_stime_sec              : $o{stats_max_stime_sec}
@@ -311,7 +312,7 @@ sub job {
 
 	$dbh or db_init();
 
-	my $sth=$dbh->prepare('SELECT *,timestampdiff(MINUTE,date_start,now()) as t FROM mxq_job WHERE job_id=? LIMIT 1',undef);
+	my $sth=$dbh->prepare('SELECT *,timestampdiff(MINUTE,date_start,now()) as t,timestampdiff(MINUTE,date_start,date_end) as t2 FROM mxq_job WHERE job_id=? LIMIT 1',undef);
 	$sth->execute($job_id);
 	my %o=%{$sth->fetchrow_hashref('NAME_lc')};
 
@@ -325,8 +326,8 @@ sub job {
 	my $job_stdout=escapeHTML($o{job_stdout});
 	my $job_stderr=escapeHTML($o{job_stderr});
 
-
-	my $ago = defined $o{t} ? "($o{t} minutes ago)" : '';
+	my $ago = $job_status_text eq 'RUNNING' && defined $o{t} ? "($o{t} minutes ago)" : '';
+	my $rt = defined $o{t2} ? "($o{t2} minutes runtime)" : '';
 
 	defined $_ or $_='&lt;null&gt;' for values %o;
 
@@ -358,12 +359,14 @@ host_slots       : $o{host_slots}
 
 date_submit      : $o{date_submit}
 date_start       : $o{date_start} $ago
-date_end         : $o{date_end}
+date_end         : $o{date_end} $rt
 
 job_id_new       : $o{job_id_new}
 job_id_old       : $o{job_id_old}
 job_id_first     : $o{job_id_first}
 
+stats_max_sumrss : $o{stats_max_sumrss} kiB
+
 stats_status     : $o{stats_status}
 
 stats_utime_sec  : $o{stats_utime_sec}
@@ -394,17 +397,17 @@ sub group_table_rows {
 	$out.=$q->Tr($q->th($head));
 
 	while (my $row=$sth->fetchrow_arrayref()) {
-		my ($group_id,$group_name,$user_name,$group_mtime,$group_status,
+		my ($group_id,$group_name,$job_threads,$user_name,$group_mtime,$group_status,
 			$group_jobs,$group_jobs_inq,$group_jobs_running,$group_jobs_finished,$group_jobs_failed,$group_jobs_cancelled,$group_jobs_unknown
 		)=@$row;
 
 		$out.=$q->Tr(
 			$q->td({class=>'number'},$q->a({href=>selfurl("/group/$group_id")},$group_id)),
 			$q->td($group_name),
+			$q->td({class=>'number'},$job_threads),
 			$q->td($user_name),
 			$q->td($group_mtime),
 			$q->td(group_status($group_status)),
-
 			$q->td({class=>'number'},$group_jobs),
 			$q->td({class=>'number'},$group_jobs_inq),
 			$q->td({class=>'number'},$group_jobs_running),
@@ -426,11 +429,11 @@ sub group_table {
 	my $out;
 
 	my @cols=qw(
-		group_id group_name user_name group_mtime group_status
+		group_id group_name job_threads user_name group_mtime group_status
 		group_jobs group_jobs_inq group_jobs_running group_jobs_finished group_jobs_failed group_jobs_cancelled group_jobs_unknown
 	);
 	my @head=qw(
-		group_id group_name user_name group_mtime group_status
+		group_id group_name threads user_name group_mtime group_status
 		jobs inq running finished failed cancelled unknown
 	);
 
@@ -444,6 +447,7 @@ sub group_table {
 	@cols=qw(
         group_id
         group_name
+        job_threads
         user_name
         group_date_end
         group_status
@@ -457,6 +461,7 @@ sub group_table {
 	@head=qw(
         group_id
         group_name
+        threads
         user_name
         date_end
         group_status