From 2c3e101dcd66d9237c09653395350bafe5884b86 Mon Sep 17 00:00:00 2001 From: Martin Mares Date: Mon, 4 Jun 2007 14:16:18 +0200 Subject: [PATCH] Submitting works, implemented STATUS command. --- submit/PROTOCOL | 20 ++++++++ submit/commands.c | 102 +++++++++++++++++++++++++++++++++++++-- submit/submitd.c | 8 ---- submit/submitd.h | 15 +++++- submit/tasks.c | 120 ++++++++++++++++++++++++++++++++++++++-------- submit/test.pl | 43 +++++++++++++++-- 6 files changed, 271 insertions(+), 37 deletions(-) diff --git a/submit/PROTOCOL b/submit/PROTOCOL index cbb5568..37953b8 100644 --- a/submit/PROTOCOL +++ b/submit/PROTOCOL @@ -30,7 +30,9 @@ Submit request: !SUBMIT Ttask + Ppart <-- optional part ID Ssize + Xextension (after the request is ACK-ed, the client sends raw data and then server sends a 2nd reply) Submit reply: @@ -40,3 +42,21 @@ Submit reply: Submit reply after data: (only status) + + +Task status objects +~~~~~~~~~~~~~~~~~~~ +(T <-- task + Tname + (P <-- task part + Pname + Vid <-- which version is current + (V <-- submitted version + Vid + Tsubmit_time + Sstatus + Hhash + Xextension + ) + ) +) diff --git a/submit/commands.c b/submit/commands.c index 6abd935..93f5b83 100644 --- a/submit/commands.c +++ b/submit/commands.c @@ -6,9 +6,13 @@ #include "lib/lib.h" #include "lib/mempool.h" +#include "lib/simple-lists.h" +#include "lib/stkstring.h" #include "sherlock/object.h" #include "sherlock/objread.h" +#include + #include "submitd.h" /*** REQUESTS AND REPLIES ***/ @@ -83,6 +87,44 @@ err(struct conn *c, byte *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) + 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 *, 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); + } + } + } +} + /*** SUBMIT ***/ static struct fastbuf * @@ -132,19 +174,69 @@ cmd_submit(struct conn *c) err(c, "No such task"); return; } + + byte *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; + } + + byte *ext = obj_find_aval(c->request, 'X'); + if (!ext || !ext_exists_p(task, ext)) + { + err(c, "Missing or invalid extension"); + return; + } + struct fastbuf *fb = read_attachment(c); 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); + 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); + byte *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(vero, 'S', "submitted"); + obj_set_attr(vero, 'X', ext); + // FIXME: hash + // FIXME: remove old versions from the status file? + task_submit_part(c->user, tname, pname, ext, last_ver, fb); + obj_set_attr_num(parto, 'V', last_ver); task_unlock_status(c, 1); + + log(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 ***/ @@ -162,6 +254,8 @@ execute_command(struct conn *c) log(L_DEBUG, "<< %s", cmd); if (!strcasecmp(cmd, "SUBMIT")) cmd_submit(c); + else if (!strcasecmp(cmd, "STATUS")) + cmd_status(c); else err(c, "Unknown command"); } diff --git a/submit/submitd.c b/submit/submitd.c index 05d2316..b3c3928 100644 --- a/submit/submitd.c +++ b/submit/submitd.c @@ -55,15 +55,7 @@ static struct cf_section access_conf = { } }; -static byte * -config_init(void) -{ - clist_init(&access_rules); - return NULL; -} - static struct cf_section submitd_conf = { - CF_INIT(config_init), CF_ITEMS { CF_UNS("Port", &port), CF_UNS("DHBits", &dh_bits), diff --git a/submit/submitd.h b/submit/submitd.h index 4030bc3..1352422 100644 --- a/submit/submitd.h +++ b/submit/submitd.h @@ -60,16 +60,27 @@ int process_command(struct conn *c); struct task { cnode n; byte *name; + uns open_data; // Number of parts for open-data tasks + clist parts; // List of parts of this task (simp_nodes) + clist *extensions; // List of allowed extensions for this task (simp_nodes) }; extern clist task_list; extern struct cf_section tasks_conf; struct task *task_find(byte *name); +int part_exists_p(struct task *t, byte *name); int user_exists_p(byte *user); +int ext_exists_p(struct task *t, byte *ext); + void task_lock_status(struct conn *c); void task_unlock_status(struct conn *c, uns write_back); -void task_submit(struct conn *c, struct task *t, struct fastbuf *fb, byte *filename); -struct odes *task_status_find_task(struct conn *c, struct task *t); +void task_load_status(struct conn *c); + +struct odes *task_status_find_task(struct conn *c, struct task *t, uns create); +struct odes *task_status_find_part(struct odes *t, byte *part, uns create); + +void task_submit_part(byte *user, byte *task, byte *part, byte *ext, uns version, struct fastbuf *fb); +void task_delete_part(byte *user, byte *task, byte *part, byte *ext, uns version); #endif diff --git a/submit/tasks.c b/submit/tasks.c index 4a69303..1ca515a 100644 --- a/submit/tasks.c +++ b/submit/tasks.c @@ -8,20 +8,40 @@ #include "lib/conf.h" #include "lib/fastbuf.h" #include "lib/stkstring.h" +#include "lib/simple-lists.h" +#include "lib/mempool.h" #include "sherlock/object.h" #include #include #include +#include #include "submitd.h" clist task_list; +static clist extensions; +static clist open_data_extensions; static byte * -tasks_conf_init(void) +tasks_conf_commit(void *p UNUSED) { - clist_init(&task_list); + // We do not do any journaling here as we do not switch config files on the fly + CLIST_FOR_EACH(struct task *, t, task_list) + { + clist_init(&t->parts); + if (t->open_data) + { + for (uns i=1; i<=t->open_data; i++) + simp_append(cf_pool, &t->parts)->s = mp_printf(cf_pool, "%d", i); + t->extensions = &open_data_extensions; + } + else + { + simp_append(cf_pool, &t->parts)->s = t->name; + t->extensions = &extensions; + } + } return NULL; } @@ -29,14 +49,17 @@ static struct cf_section task_conf = { CF_TYPE(struct task), CF_ITEMS { CF_STRING("Name", PTR_TO(struct task, name)), + CF_UNS("OpenData", PTR_TO(struct task, open_data)), CF_END } }; struct cf_section tasks_conf = { - CF_INIT(tasks_conf_init), + CF_COMMIT(tasks_conf_commit), CF_ITEMS { CF_LIST("Task", &task_list, &task_conf), + CF_LIST("Extension", &extensions, &cf_string_list_config), + CF_LIST("OpenDataExt", &open_data_extensions, &cf_string_list_config), CF_END } }; @@ -50,12 +73,42 @@ task_find(byte *name) return NULL; } +int +part_exists_p(struct task *t, byte *name) +{ + CLIST_FOR_EACH(simp_node *, p, t->parts) + if (!strcmp(p->s, name)) + return 1; + return 0; +} + +int +ext_exists_p(struct task *t, byte *ext) +{ + CLIST_FOR_EACH(simp_node *, x, *t->extensions) + if (!strcmp(x->s, ext)) + return 1; + return 0; +} + int user_exists_p(byte *user) { - byte *fn = stk_printf("solutions/%s/status", user); + byte *fn = stk_printf("solutions/%s", user); struct stat st; - return !stat(fn, &st) && S_ISREG(st.st_mode); + return !stat(fn, &st) && S_ISDIR(st.st_mode); +} + +void +task_load_status(struct conn *c) +{ + struct fastbuf *fb = bopen_try(stk_printf("solutions/%s/status", c->user), O_RDONLY, 4096); + c->task_status = obj_new(c->pool); + if (fb) + { + obj_read(fb, c->task_status); + bclose(fb); + } } void @@ -72,21 +125,13 @@ task_lock_status(struct conn *c) }; if (fcntl(c->task_lock_fd, F_SETLKW, &fl) < 0) die("Cannot lock status file: %m"); - - struct fastbuf *fb = bopen_try(stk_printf("solutions/%s/status", c->user), O_RDONLY, 4096); - c->task_status = obj_new(c->pool); - if (fb) - { - obj_read(fb, c->task_status); - bclose(fb); - } + task_load_status(c); } void task_unlock_status(struct conn *c, uns write_back) { ASSERT(c->task_lock_fd); - ASSERT(c->task_status); if (write_back) { @@ -109,27 +154,62 @@ task_unlock_status(struct conn *c, uns write_back) if (fcntl(c->task_lock_fd, F_SETLKW, &fl) < 0) die("Cannot unlock status file: %m"); c->task_lock_fd = 0; - c->task_status = NULL; } struct odes * -task_status_find_task(struct conn *c, struct task *t) +task_status_find_task(struct conn *c, struct task *t, uns create) { for (struct oattr *a = obj_find_attr(c->task_status, 'T' + OBJ_ATTR_SON); a; a=a->same) { struct odes *o = a->son; byte *name = obj_find_aval(o, 'T'); + ASSERT(name); if (!strcmp(name, t->name)) return o; } + if (!create) + return NULL; struct odes *o = obj_add_son(c->task_status, 'T' + OBJ_ATTR_SON); obj_set_attr(o, 'T', t->name); return o; } -void -task_submit(struct conn *c, struct task *t, struct fastbuf *fb, byte *filename) +struct odes * +task_status_find_part(struct odes *to, byte *part, uns create) +{ + for (struct oattr *a = obj_find_attr(to, 'P' + OBJ_ATTR_SON); a; a=a->same) + { + struct odes *o = a->son; + byte *name = obj_find_aval(o, 'P'); + ASSERT(name); + if (!strcmp(name, part)) + return o; + } + if (!create) + return NULL; + struct odes *o = obj_add_son(to, 'P' + OBJ_ATTR_SON); + obj_set_attr(o, 'P', part); + return o; +} + +void task_submit_part(byte *user, byte *task, byte *part, byte *ext, uns version UNUSED, struct fastbuf *fb) +{ + byte *dir = stk_printf("solutions/%s/%s", user, task); + byte *name = stk_printf("%s/%s.%s", dir, part, ext); + + struct stat st; + if (stat(dir, &st) < 0 && errno == ENOENT && mkdir(dir, 0777) < 0) + die("Cannot create %s: %m", dir); + + bconfig(fb, BCONFIG_IS_TEMP_FILE, 0); + if (rename(fb->name, name) < 0) + die("Cannot rename %s to %s: %m", fb->name, name); +} + +void task_delete_part(byte *user, byte *task, byte *part, byte *ext, uns version UNUSED) { - byte *dir = stk_printf("solutions/%s/%s", c->user, t->name); - byte *name = stk_printf("%s/%s", dir, filename); + byte *dir = stk_printf("solutions/%s/%s", user, task); + byte *name = stk_printf("%s/%s.%s", dir, part, ext); + if (unlink(name) < 0) + log(L_ERROR, "Cannot delete %s: %m", name); } diff --git a/submit/test.pl b/submit/test.pl index 9948a70..0cc8be9 100755 --- a/submit/test.pl +++ b/submit/test.pl @@ -29,7 +29,44 @@ if ($z =~ /TLS/) { ) or die "Cannot establish TLS connection: " . IO::Socket::SSL::errstr() . "\n"; } -print $sk "Hello, world!\n"; -my $y = <$sk>; -print $y; +sub sendobj($) { + my ($h) = @_; + foreach my $x (keys %{$h}) { + print $sk $x, $h->{$x}, "\n"; + } + print $sk "\n"; + # FIXME: flush +}; + +sub recvobj() { + my $h = {}; + while (<$sk>) { + chomp; + /^(.)(.*)$/ || last; + $h->{$1} = $2; + } + if (defined $h->{'-'}) { die "-" . $h->{'-'} . "\n"; } + return $h; +} + +sub printobj($) { + my ($h) = @_; + foreach my $x (keys %{$h}) { + print $x, $h->{$x}, "\n"; + } +} + +sendobj({ 'U' => 'testuser' }); +recvobj(); + +#sendobj({ '!' => 'SUBMIT', 'T' => 'plans', 'S' => 100, 'X' => 'c' }); +#recvobj(); +#print $sk "<"; +#foreach my $x (1..98) { print $sk "."; } +#print $sk ">"; +#recvobj(); + +sendobj({ '!' => 'STATUS' }); +printobj(recvobj()); + close $sk; -- 2.39.2