diff --git a/Makefile b/Makefile index a5ffcad..645109e 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ MXQ_VERSION_MAJOR = 0 -MXQ_VERSION_MINOR = 13 +MXQ_VERSION_MINOR = 16 MXQ_VERSION_PATCH = 0 MXQ_VERSION_EXTRA = "beta" MXQ_VERSIONDATE = 2013-2015 @@ -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})) @@ -57,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 @@ -69,8 +76,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 + MXQ_INITIAL_TMPDIR := /scratch/local endif ######################################################################## @@ -373,6 +380,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 @@ -506,7 +514,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) ######################################################################## diff --git a/mx_util.c b/mx_util.c index 199bcd5..efde13f 100644 --- a/mx_util.c +++ b/mx_util.c @@ -752,6 +752,194 @@ 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_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_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 d805aef..f79d77f 100644 --- a/mx_util.h +++ b/mx_util.h @@ -5,9 +5,57 @@ #include <stdint.h> #include <stdarg.h> #include <string.h> +#include <stdio.h> #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 <assert.h> # define mx_assert_return_minus_errno(test, eno) \ @@ -46,9 +94,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 +162,15 @@ 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_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/mxq_group.c b/mxq_group.c index f9ec96a..65f7757 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)); @@ -97,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 8ab438d..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,19 +17,18 @@ 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; uint32_t job_time; + uint16_t job_max_per_node; + uint64_t group_jobs; uint64_t group_jobs_inq; uint64_t group_jobs_running; diff --git a/mxq_job.c b/mxq_job.c index 16a64af..68fc709 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)); @@ -154,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; @@ -171,16 +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; } @@ -391,19 +380,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); @@ -662,7 +653,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; @@ -673,6 +664,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); @@ -697,6 +690,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 5d78ba1..9e7ba35 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,13 +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; @@ -107,6 +101,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 633f711..9ab4fda 100644 --- a/mxqd.c +++ b/mxqd.c @@ -34,43 +34,31 @@ #include "mxq_job.h" #include "mx_mysql.h" #include "mxqd.h" +#include "mxq.h" -#ifndef MXQ_VERSION -#define MXQ_VERSION "0.00" -#endif +#define MYSQL_DEFAULT_FILE MXQ_MYSQL_DEFAULT_FILE +#define MYSQL_DEFAULT_GROUP "mxqd" -#ifndef MXQ_VERSIONFULL -#define MXQ_VERSIONFULL "MXQ v0.00 super alpha 0" +#ifndef MXQ_INITIAL_PATH +# define MXQ_INITIAL_PATH "/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin" #endif -#ifndef MXQ_VERSIONDATE -#define MXQ_VERSIONDATE "2015" +#ifndef MXQ_INITIAL_TMPDIR +# define MXQ_INITIAL_TMPDIR "/tmp" #endif -#define MYSQL_DEFAULT_FILE MXQ_MYSQL_DEFAULT_FILE -#define MYSQL_DEFAULT_GROUP "mxqd" - volatile sig_atomic_t global_sigint_cnt=0; 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 <tolzmann@molgen.mpg.de> " 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 <slots> default: 1\n" @@ -85,18 +73,26 @@ static void print_usage(void) " --no-log default: write a logfile\n" " --debug default: info log level\n" "\n" + " --initial-path <path> default: %s\n" + " --initial-tmpdir <directory> default: %s\n" + "\n" " -V, --version\n" " -h, --help\n" "\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_INITIAL_PATH, + MXQ_INITIAL_TMPDIR, + MXQ_MYSQL_DEFAULT_FILE_STR, + MXQ_MYSQL_DEFAULT_GROUP_STR ); } @@ -203,13 +199,17 @@ 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; + 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[] = { @@ -219,6 +219,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'), @@ -232,6 +234,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; @@ -272,7 +277,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': @@ -318,6 +323,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; @@ -351,6 +364,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) { @@ -396,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; @@ -455,6 +484,10 @@ void group_init(struct mxq_group_list *group) } jobs_max /= g->job_threads; + /* limit maximum number of jobs on user/group request */ + 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; memory_max = jobs_max * g->job_memory; @@ -778,7 +811,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("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); @@ -789,7 +823,9 @@ 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); fh = open("/proc/self/loginuid", O_WRONLY|O_TRUNC); if (fh == -1) { @@ -983,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; @@ -1331,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) @@ -1673,6 +1711,7 @@ int main(int argc, char *argv[]) mx_log_info(" by Marius Tolzmann <tolzmann@molgen.mpg.de> " 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 5a9ca86..c4db22d 100644 --- a/mxqd.h +++ b/mxqd.h @@ -70,12 +70,18 @@ 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; char *pidfilename; struct mx_flock *flock; + char *initial_path; + char *initial_tmpdir; + int is_running; }; diff --git a/mxqsub.c b/mxqsub.c index 6cb34d6..87a476b 100644 --- a/mxqsub.c +++ b/mxqsub.c @@ -82,15 +82,18 @@ 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" " -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" @@ -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; diff --git a/mysql/alter_tables_0.15.0.sql b/mysql/alter_tables_0.15.0.sql new file mode 100644 index 0000000..95faa89 --- /dev/null +++ b/mysql/alter_tables_0.15.0.sql @@ -0,0 +1,11 @@ +ALTER TABLE mxq_group + ADD COLUMN + 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 30365ce..c203fa2 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, @@ -81,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 != ""; diff --git a/test_mx_util.c b/test_mx_util.c index 66dd776..5700e80 100644 --- a/test_mx_util.c +++ b/test_mx_util.c @@ -1,7 +1,11 @@ +#define _GNU_SOURCE + #include <assert.h> #include <errno.h> #include <string.h> +#include <sys/types.h> +#include <unistd.h> #include "mx_util.h" @@ -44,6 +48,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 +276,87 @@ static void test_mx_strtobytes(void) assert(mx_strtobytes("test", &l) == -EINVAL); } +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); + 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_strtoll(str, &l); + 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; + _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; + + 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")); + + 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(); @@ -284,5 +370,7 @@ int main(int argc, char *argv[]) test_mx_strtoseconds(); test_mx_strtominutes(); test_mx_strtobytes(); + test_mx_read_first_line_from_file(); + test_mx_strscan(); return 0; } 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@" diff --git a/web/pages/mxq/mxq.in b/web/pages/mxq/mxq.in index 5e75c6a..d90ca06 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"; <pre> -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,11 +247,13 @@ 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} +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} @@ -315,9 +320,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"; <pre> job_status : $job_status_text @@ -326,16 +337,17 @@ job_priority : $o{job_priority} group_id : $link_group_id -job_workdir : $o{job_workdir} -job_argc ; $o{job_argc} -job_argv ; $argv -job_stdout : $o{job_stdout} -job_stderr : $o{job_stderr} +job_workdir : $job_workdir +job_argc : $o{job_argc} +job_argv : $job_argv +job_stdout : $job_stdout +job_stderr : $job_stderr 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} @@ -343,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} @@ -351,9 +363,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}