X-Git-Url: http://mj.ucw.cz/gitweb/?a=blobdiff_plain;f=submit%2Fcommands.c;h=3c3e3aa530548bd2f71df3ba4f69a89c795e91f5;hb=4b4a4ee2d962681c449a6cf62e75e72016d51cf4;hp=6abd935f0f30cd9f3bbf4ed2d491b74df1e4ae16;hpb=92811b1aa5a791bc776b58afb241a7d93e737604;p=moe.git diff --git a/submit/commands.c b/submit/commands.c index 6abd935..3c3e3aa 100644 --- a/submit/commands.c +++ b/submit/commands.c @@ -6,15 +6,21 @@ #include "lib/lib.h" #include "lib/mempool.h" +#include "lib/simple-lists.h" +#include "lib/stkstring.h" +#include "lib/fastbuf.h" #include "sherlock/object.h" #include "sherlock/objread.h" +#include +#include + #include "submitd.h" /*** REQUESTS AND REPLIES ***/ static void NONRET -read_error_cb(struct obj_read_state *st UNUSED, byte *msg) +read_error_cb(struct obj_read_state *st UNUSED, char *msg) { client_error("Request parse error: %s", msg); } @@ -32,7 +38,7 @@ read_request(struct conn *c) struct obj_read_state st; obj_read_start(&st, c->request); st.error_callback = read_error_cb; - byte line[1024]; + char line[1024]; uns size = 0; for (;;) { @@ -64,13 +70,13 @@ write_reply(struct conn *c) obj_set_attr(c->reply, '+', "OK"); if (trace_commands) { - byte *msg; - if (msg = obj_find_aval(c->reply, '-')) - log(L_DEBUG, ">> -%s", msg); - else if (msg = obj_find_aval(c->reply, '+')) - log(L_DEBUG, ">> +%s", msg); + char *m; + if (m = obj_find_aval(c->reply, '-')) + msg(L_DEBUG, ">> -%s", m); + else if (m = obj_find_aval(c->reply, '+')) + msg(L_DEBUG, ">> +%s", m); else - log(L_DEBUG, ">> ???"); + msg(L_DEBUG, ">> ???"); } obj_write(&c->tx_fb, c->reply, BUCKET_TYPE_PLAIN); bputc(&c->tx_fb, '\n'); @@ -78,18 +84,96 @@ write_reply(struct conn *c) } static void -err(struct conn *c, byte *msg) +err(struct conn *c, char *msg) { obj_set_attr(c->reply, '-', msg); } +/*** STATUS ***/ + +static void +copy_attrs(struct odes *dest, struct odes *src) +{ + for (struct oattr *a = src->attrs ; a; a=a->next) + if (a->attr < OBJ_ATTR_SON) + for (struct oattr *aa = a; aa; aa=aa->same) + obj_add_attr(dest, aa->attr, aa->val); +} + +static void +cmd_status(struct conn *c) +{ + uns verbose = obj_find_anum(c->request, 'V', 0); + task_load_status(c); + + CLIST_FOR_EACH(struct task *, t, task_list) + { + struct odes *to = task_status_find_task(c, t, 1); + struct odes *tr = obj_add_son(c->reply, 'T' + OBJ_ATTR_SON); + copy_attrs(tr, to); + CLIST_FOR_EACH(simp_node *, x, *t->extensions) + obj_add_attr(tr, 'A', x->s); + CLIST_FOR_EACH(simp_node *, p, t->parts) + { + struct odes *po = task_status_find_part(to, p->s, 1); + struct odes *pr = obj_add_son(tr, 'P' + OBJ_ATTR_SON); + copy_attrs(pr, po); + uns current_ver = obj_find_anum(po, 'V', 0); + for (struct oattr *v = obj_find_attr(po, 'V' + OBJ_ATTR_SON); v; v=v->same) + { + struct odes *vo = v->son; + uns ver = obj_find_anum(vo, 'V', 0); + if (ver == current_ver || verbose) + obj_add_son_ref(pr, 'V' + OBJ_ATTR_SON, vo); + } + } + } +} + +/*** Contest timeout checks ***/ + +static int +load_time_limit(char *name) +{ + struct fastbuf *f = bopen_try(name, O_RDONLY, 1024); + if (!f) + return -1; + char buf[256]; + int h, m; + if (bgets_nodie(f, buf, sizeof(buf)) < 0 || + sscanf(buf, "%d:%d", &h, &m) != 2 || + h < 0 || h > 23 || m < 0 || m > 59) + { + msg(L_ERROR, "Invalid timeout in %s", name); + bclose(f); + return -1; + } + bclose(f); + return 100*h + m; +} + +static int +contest_over(struct conn *c) +{ + time_t now = time(NULL); + struct tm *tm = localtime(&now); + int tstamp = tm->tm_hour*100 + tm->tm_min; + int local_limit = load_time_limit(stk_printf("solutions/%s/TIMEOUT", c->user)); + int global_limit = load_time_limit("solutions/TIMEOUT"); + if (trace_commands > 1) + msg(L_DEBUG, "Time check: current %d, global limit %d, user limit %d", tstamp, global_limit, local_limit); + if (local_limit >= 0) + return (tstamp >= local_limit); + return (global_limit >= 0 && tstamp >= global_limit); +} + /*** SUBMIT ***/ static struct fastbuf * -read_attachment(struct conn *c) +read_attachment(struct conn *c, uns max_size) { uns size = obj_find_anum(c->request, 'S', 0); - if (size > max_attachment_size) + if (size > max_size) { err(c, "Submission too large"); return NULL; @@ -100,7 +184,7 @@ read_attachment(struct conn *c) // This is less efficient than bbcopy(), but we want our own error handling. struct fastbuf *fb = bopen_tmp(4096); - byte buf[4096]; + char buf[4096]; uns remains = size; while (remains) { @@ -120,7 +204,13 @@ read_attachment(struct conn *c) static void cmd_submit(struct conn *c) { - byte *tname = obj_find_aval(c->request, 'T'); + if (contest_over(c)) + { + err(c, "The contest is over, no more submits allowed"); + return; + } + + char *tname = obj_find_aval(c->request, 'T'); if (!tname) { err(c, "No task specified"); @@ -132,19 +222,73 @@ cmd_submit(struct conn *c) err(c, "No such task"); return; } - struct fastbuf *fb = read_attachment(c); + + char *pname = obj_find_aval(c->request, 'P'); + if (!pname) + { + simp_node *s = clist_head(&task->parts); + ASSERT(s); + pname = s->s; + } + else if (!part_exists_p(task, pname)) + { + err(c, "No such task part"); + return; + } + + char *ext = obj_find_aval(c->request, 'X'); + if (!ext || !ext_exists_p(task, ext)) + { + err(c, "Missing or invalid extension"); + return; + } + + uns max_size = task->max_size ? : max_attachment_size; + struct fastbuf *fb = read_attachment(c, max_size); if (!fb) return; - // FIXME: Check contest time - // FIXME: Keep history of submitted tasks - // FIXME: File names - task_lock_status(c); - struct odes *o = task_status_find_task(c, task); - task_submit(c, task, fb, task->name); - log(L_INFO, "User %s submitted task %s", c->user, task->name); + struct odes *tasko = task_status_find_task(c, task, 1); + struct odes *parto = task_status_find_part(tasko, pname, 1); + uns current_ver = obj_find_anum(parto, 'V', 0); + if (current_ver >= max_versions) + { + err(c, "Maximum number of submits of this task exceeded"); + bclose(fb); + task_unlock_status(c, 0); + return; + } + uns last_ver = 0; + uns replaced_ver = 0; + for (struct oattr *a = obj_find_attr(parto, 'V' + OBJ_ATTR_SON); a; a=a->same) + { + uns ver = obj_find_anum(a->son, 'V', 0); + char *ext = obj_find_aval(a->son, 'X'); + ASSERT(ver && ext); + last_ver = MAX(last_ver, ver); + if (ver == current_ver) + { + task_delete_part(c->user, tname, pname, ext, ver); + obj_set_attr(a->son, 'S', "replaced"); + replaced_ver = current_ver; + } + } + struct odes *vero = obj_add_son(parto, 'V' + OBJ_ATTR_SON); + obj_set_attr_num(vero, 'V', ++last_ver); + obj_set_attr_num(vero, 'T', time(NULL)); + obj_set_attr_num(vero, 'L', obj_find_anum(c->request, 'S', 0)); + obj_set_attr(vero, 'S', "submitted"); + obj_set_attr(vero, 'X', ext); + task_submit_part(c->user, tname, pname, ext, last_ver, fb); + obj_set_attr_num(parto, 'V', last_ver); task_unlock_status(c, 1); + + msg(L_INFO, "User %s submitted task %s%s (version %d%s)", + c->user, tname, + (strcmp(tname, pname) ? stk_printf("/%s", pname) : ""), + last_ver, + (replaced_ver ? stk_printf(", replaced %d", replaced_ver) : "")); } /*** COMMAND MUX ***/ @@ -152,16 +296,20 @@ cmd_submit(struct conn *c) static void execute_command(struct conn *c) { - byte *cmd = obj_find_aval(c->request, '!'); + char *cmd = obj_find_aval(c->request, '!'); if (!cmd) { err(c, "Missing command"); return; } if (trace_commands) - log(L_DEBUG, "<< %s", cmd); + msg(L_DEBUG, "<< %s", cmd); if (!strcasecmp(cmd, "SUBMIT")) cmd_submit(c); + else if (!strcasecmp(cmd, "STATUS")) + cmd_status(c); + else if (!strcasecmp(cmd, "NOP")) + ; else err(c, "Unknown command"); } @@ -181,7 +329,7 @@ process_command(struct conn *c) static void execute_init(struct conn *c) { - byte *user = obj_find_aval(c->request, 'U'); + char *user = obj_find_aval(c->request, 'U'); if (!user) { err(c, "Missing user"); @@ -196,12 +344,12 @@ execute_init(struct conn *c) err(c, "Unknown user"); return; } - log(L_INFO, "Logged in %s", user); + msg(L_INFO, "Logged in %s", user); } else { err(c, "Permission denied"); - log(L_ERROR, "Unauthorized attempt to log in as %s", user); + msg(L_ERROR, "Unauthorized attempt to log in as %s", user); return; } c->user = xstrdup(user);