]> mj.ucw.cz Git - eval.git/commitdiff
Submitting works, implemented STATUS command.
authorMartin Mares <mj@ucw.cz>
Mon, 4 Jun 2007 12:16:18 +0000 (14:16 +0200)
committerMartin Mares <mj@ucw.cz>
Mon, 4 Jun 2007 12:16:18 +0000 (14:16 +0200)
submit/PROTOCOL
submit/commands.c
submit/submitd.c
submit/submitd.h
submit/tasks.c
submit/test.pl

index cbb55682910a83223e51f3351ef724326aca57cc..37953b8248212a1487ced56a3fc1e032f42314cf 100644 (file)
@@ -30,7 +30,9 @@ Submit request:
 
        !SUBMIT
        Ttask
 
        !SUBMIT
        Ttask
+       Ppart                   <-- optional part ID
        Ssize
        Ssize
+       Xextension
        (after the request is ACK-ed, the client sends raw data and then server sends a 2nd reply)
 
 Submit reply:
        (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)
 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
+               )
+       )
+)
index 6abd935f0f30cd9f3bbf4ed2d491b74df1e4ae16..93f5b83509f6edf08c86046612afd1d8c4e61ed4 100644 (file)
@@ -6,9 +6,13 @@
 
 #include "lib/lib.h"
 #include "lib/mempool.h"
 
 #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 "sherlock/object.h"
 #include "sherlock/objread.h"
 
+#include <time.h>
+
 #include "submitd.h"
 
 /*** REQUESTS AND REPLIES ***/
 #include "submitd.h"
 
 /*** REQUESTS AND REPLIES ***/
@@ -83,6 +87,44 @@ err(struct conn *c, byte *msg)
   obj_set_attr(c->reply, '-', 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 *
 /*** SUBMIT ***/
 
 static struct fastbuf *
@@ -132,19 +174,69 @@ cmd_submit(struct conn *c)
       err(c, "No such task");
       return;
     }
       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
   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);
 
   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);
   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 ***/
 }
 
 /*** COMMAND MUX ***/
@@ -162,6 +254,8 @@ execute_command(struct conn *c)
     log(L_DEBUG, "<< %s", cmd);
   if (!strcasecmp(cmd, "SUBMIT"))
     cmd_submit(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");
 }
   else
     err(c, "Unknown command");
 }
index 05d23166f369206b8005d77d2b66338f5e423028..b3c39280614d0e12ae8399b40ae0dd83dbace72e 100644 (file)
@@ -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 = {
 static struct cf_section submitd_conf = {
-  CF_INIT(config_init),
   CF_ITEMS {
     CF_UNS("Port", &port),
     CF_UNS("DHBits", &dh_bits),
   CF_ITEMS {
     CF_UNS("Port", &port),
     CF_UNS("DHBits", &dh_bits),
index 4030bc31e67f7c731490aa365a9f1df148775633..13524229603b5c5fcb902c78ac79dc6b9277f6be 100644 (file)
@@ -60,16 +60,27 @@ int process_command(struct conn *c);
 struct task {
   cnode n;
   byte *name;
 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);
 };
 
 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 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_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
 
 #endif
index 4a6930319799983dae873420da09c8ed5d879174..1ca515a86588e1711be1ab97506417992c5036ae 100644 (file)
@@ -8,20 +8,40 @@
 #include "lib/conf.h"
 #include "lib/fastbuf.h"
 #include "lib/stkstring.h"
 #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 <sys/stat.h>
 #include <fcntl.h>
 #include <unistd.h>
 #include "sherlock/object.h"
 
 #include <sys/stat.h>
 #include <fcntl.h>
 #include <unistd.h>
+#include <errno.h>
 
 #include "submitd.h"
 
 clist task_list;
 
 #include "submitd.h"
 
 clist task_list;
+static clist extensions;
+static clist open_data_extensions;
 
 static byte *
 
 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;
 }
 
   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_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_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_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
   }
 };
     CF_END
   }
 };
@@ -50,12 +73,42 @@ task_find(byte *name)
   return NULL;
 }
 
   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)
 {
 int
 user_exists_p(byte *user)
 {
-  byte *fn = stk_printf("solutions/%s/status", user);
+  byte *fn = stk_printf("solutions/%s", user);
   struct stat st;
   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
 }
 
 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");
   };
   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);
 }
 
 void
 task_unlock_status(struct conn *c, uns write_back)
 {
   ASSERT(c->task_lock_fd);
-  ASSERT(c->task_status);
 
   if (write_back)
     {
 
   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;
   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 *
 }
 
 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');
 {
   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 (!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;
 }
 
   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);
 }
 }
index 9948a7083a8f18944029f402d6f1da0c464dd66b..0cc8be90444d1820bbffc73b8a44398067d82083 100755 (executable)
@@ -29,7 +29,44 @@ if ($z =~ /TLS/) {
        ) or die "Cannot establish TLS connection: " . IO::Socket::SSL::errstr() . "\n";
 }
 
        ) 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;
 close $sk;