2 * The Submit Daemon: High-Level Part of the Protocol
4 * (c) 2007 Martin Mares <mj@ucw.cz>
8 #include "lib/mempool.h"
9 #include "lib/simple-lists.h"
10 #include "lib/stkstring.h"
11 #include "lib/fastbuf.h"
12 #include "sherlock/object.h"
13 #include "sherlock/objread.h"
20 /*** REQUESTS AND REPLIES ***/
23 read_error_cb(struct obj_read_state *st UNUSED, char *msg)
25 client_error("Request parse error: %s", msg);
29 read_request(struct conn *c)
34 c->pool = mp_new(1024);
35 c->request = obj_new(c->pool);
36 c->reply = obj_new(c->pool);
38 struct obj_read_state st;
39 obj_read_start(&st, c->request);
40 st.error_callback = read_error_cb;
45 int l = bgets_nodie(&c->rx_fb, line, sizeof(line));
47 client_error("Request line too long");
53 client_error("Truncated request");
58 if (size >= max_request_size)
59 client_error("Request too long");
60 obj_read_attr(&st, line[0], line+1);
67 write_reply(struct conn *c)
69 if (!obj_find_attr(c->reply, '-') && !obj_find_attr(c->reply, '+'))
70 obj_set_attr(c->reply, '+', "OK");
74 if (m = obj_find_aval(c->reply, '-'))
75 msg(L_DEBUG, ">> -%s", m);
76 else if (m = obj_find_aval(c->reply, '+'))
77 msg(L_DEBUG, ">> +%s", m);
79 msg(L_DEBUG, ">> ???");
81 obj_write(&c->tx_fb, c->reply, BUCKET_TYPE_PLAIN);
82 bputc(&c->tx_fb, '\n');
87 err(struct conn *c, byte *msg)
89 obj_set_attr(c->reply, '-', msg);
95 copy_attrs(struct odes *dest, struct odes *src)
97 for (struct oattr *a = src->attrs ; a; a=a->next)
98 if (a->attr < OBJ_ATTR_SON)
99 for (struct oattr *aa = a; aa; aa=aa->same)
100 obj_add_attr(dest, aa->attr, aa->val);
104 cmd_status(struct conn *c)
106 uns verbose = obj_find_anum(c->request, 'V', 0);
109 CLIST_FOR_EACH(struct task *, t, task_list)
111 struct odes *to = task_status_find_task(c, t, 1);
112 struct odes *tr = obj_add_son(c->reply, 'T' + OBJ_ATTR_SON);
114 CLIST_FOR_EACH(simp_node *, x, *t->extensions)
115 obj_add_attr(tr, 'A', x->s);
116 CLIST_FOR_EACH(simp_node *, p, t->parts)
118 struct odes *po = task_status_find_part(to, p->s, 1);
119 struct odes *pr = obj_add_son(tr, 'P' + OBJ_ATTR_SON);
121 uns current_ver = obj_find_anum(po, 'V', 0);
122 for (struct oattr *v = obj_find_attr(po, 'V' + OBJ_ATTR_SON); v; v=v->same)
124 struct odes *vo = v->son;
125 uns ver = obj_find_anum(vo, 'V', 0);
126 if (ver == current_ver || verbose)
127 obj_add_son_ref(pr, 'V' + OBJ_ATTR_SON, vo);
133 /*** Contest timeout checks ***/
136 load_time_limit(char *name)
138 struct fastbuf *f = bopen_try(name, O_RDONLY, 1024);
143 if (bgets_nodie(f, buf, sizeof(buf)) < 0 ||
144 sscanf(buf, "%d:%d", &h, &m) != 2 ||
145 h < 0 || h > 23 || m < 0 || m > 59)
147 msg(L_ERROR, "Invalid timeout in %s", name);
156 contest_over(struct conn *c)
158 time_t now = time(NULL);
159 struct tm *tm = localtime(&now);
160 int tstamp = tm->tm_hour*100 + tm->tm_min;
161 int local_limit = load_time_limit(stk_printf("solutions/%s/TIMEOUT", c->user));
162 int global_limit = load_time_limit("solutions/TIMEOUT");
163 if (trace_commands > 1)
164 msg(L_DEBUG, "Time check: current %d, global limit %d, user limit %d", tstamp, global_limit, local_limit);
165 if (local_limit >= 0)
166 return (tstamp >= local_limit);
167 return (global_limit >= 0 && tstamp >= global_limit);
172 static struct fastbuf *
173 read_attachment(struct conn *c, uns max_size)
175 uns size = obj_find_anum(c->request, 'S', 0);
178 err(c, "Submission too large");
181 obj_set_attr(c->reply, '+', "Go on");
183 obj_set_attr(c->reply, '+', NULL);
185 // This is less efficient than bbcopy(), but we want our own error handling.
186 struct fastbuf *fb = bopen_tmp(4096);
191 uns cnt = bread(&c->rx_fb, buf, MIN(remains, (uns)sizeof(buf)));
195 client_error("Truncated attachment");
197 bwrite(fb, buf, cnt);
205 cmd_submit(struct conn *c)
209 err(c, "The contest is over, no more submits allowed");
213 byte *tname = obj_find_aval(c->request, 'T');
216 err(c, "No task specified");
219 struct task *task = task_find(tname);
222 err(c, "No such task");
226 byte *pname = obj_find_aval(c->request, 'P');
229 simp_node *s = clist_head(&task->parts);
233 else if (!part_exists_p(task, pname))
235 err(c, "No such task part");
239 byte *ext = obj_find_aval(c->request, 'X');
240 if (!ext || !ext_exists_p(task, ext))
242 err(c, "Missing or invalid extension");
246 uns max_size = task->max_size ? : max_attachment_size;
247 struct fastbuf *fb = read_attachment(c, max_size);
252 struct odes *tasko = task_status_find_task(c, task, 1);
253 struct odes *parto = task_status_find_part(tasko, pname, 1);
254 uns current_ver = obj_find_anum(parto, 'V', 0);
255 if (current_ver >= max_versions)
257 err(c, "Maximum number of submits of this task exceeded");
259 task_unlock_status(c, 0);
263 uns replaced_ver = 0;
264 for (struct oattr *a = obj_find_attr(parto, 'V' + OBJ_ATTR_SON); a; a=a->same)
266 uns ver = obj_find_anum(a->son, 'V', 0);
267 byte *ext = obj_find_aval(a->son, 'X');
269 last_ver = MAX(last_ver, ver);
270 if (ver == current_ver)
272 task_delete_part(c->user, tname, pname, ext, ver);
273 obj_set_attr(a->son, 'S', "replaced");
274 replaced_ver = current_ver;
277 struct odes *vero = obj_add_son(parto, 'V' + OBJ_ATTR_SON);
278 obj_set_attr_num(vero, 'V', ++last_ver);
279 obj_set_attr_num(vero, 'T', time(NULL));
280 obj_set_attr_num(vero, 'L', obj_find_anum(c->request, 'S', 0));
281 obj_set_attr(vero, 'S', "submitted");
282 obj_set_attr(vero, 'X', ext);
283 task_submit_part(c->user, tname, pname, ext, last_ver, fb);
284 obj_set_attr_num(parto, 'V', last_ver);
285 task_unlock_status(c, 1);
287 msg(L_INFO, "User %s submitted task %s%s (version %d%s)",
289 (strcmp(tname, pname) ? stk_printf("/%s", pname) : ""),
291 (replaced_ver ? stk_printf(", replaced %d", replaced_ver) : ""));
294 /*** COMMAND MUX ***/
297 execute_command(struct conn *c)
299 byte *cmd = obj_find_aval(c->request, '!');
302 err(c, "Missing command");
306 msg(L_DEBUG, "<< %s", cmd);
307 if (!strcasecmp(cmd, "SUBMIT"))
309 else if (!strcasecmp(cmd, "STATUS"))
312 err(c, "Unknown command");
316 process_command(struct conn *c)
318 if (!read_request(c))
325 /*** INITIAL HANDSHAKE ***/
328 execute_init(struct conn *c)
330 byte *user = obj_find_aval(c->request, 'U');
333 err(c, "Missing user");
337 !strcmp(user, c->cert_name) ||
338 c->rule->allow_admin && !strcmp(c->cert_name, "admin"))
340 if (!user_exists_p(user))
342 err(c, "Unknown user");
345 msg(L_INFO, "Logged in %s", user);
349 err(c, "Permission denied");
350 msg(L_ERROR, "Unauthorized attempt to log in as %s", user);
353 c->user = xstrdup(user);
357 process_init(struct conn *c)
359 if (!read_request(c))
363 return !obj_find_attr(c->reply, '-');