chiark / gitweb /
admin: Implement job table infrastructure.
[tripe] / server / admin.c
index eb48039887679aa529393b945dd4e9c2855b9d8a..5030dc28d6b770e4d2690a7276cf543b5a4d3c37 100644 (file)
@@ -732,6 +732,195 @@ static int a_bgadd(admin *a, admin_bgop *bg, const char *tag,
   return (0);
 }
 
+/*----- Job table manipulation --------------------------------------------*/
+
+#define JOB_SHIFT 16
+#define JOB_INDEXMASK ((1ul << JOB_SHIFT) - 1)
+#define JOB_SEQMASK ((1ul << (32 - JOB_SHIFT)) - 1)
+
+#define JOB_END 0xfffffffful
+
+static unsigned long a_joboffset;
+
+/* --- @a_jobidencode@ --- *
+ *
+ * Arguments:  @admin_svcop *svc@ = pointer to a service operation
+ *
+ * Returns:    A jobid for this job, in an internal static buffer.
+ *
+ * Use:                Constructs a jobid.  In order to dissuade people from
+ *             predicting jobids, we obfuscate them.
+ *
+ *             A `raw' jobid consists of two 16-bit fields.  The low 16 bits
+ *             are an index into a big array.  The high 16 bits are a
+ *             sequence number recording how many times that slot has been
+ *             reused.
+ *
+ *             This `raw' jobid is then obfuscated by adding a randomly-
+ *             generated offset, and multiplying (mod %$2^{32}$%) by a fixed
+ *             odd constant.
+ */
+
+static const char *a_jobidencode(admin_svcop *svc)
+{
+  admin_jobtable *j = &svc->prov->j;
+  static char buf[10];
+  unsigned long pre;
+  unsigned seq;
+
+  assert(svc->index <= JOB_INDEXMASK);
+  seq = j->v[svc->index].seq;
+  assert(seq <= JOB_SEQMASK);
+  pre = (unsigned long)svc->index | ((unsigned long)seq << JOB_SHIFT);
+  sprintf(buf, "J%08lx", ((pre + a_joboffset) * 0x0f87a7a3ul) & 0xffffffff);
+  return (buf);
+}
+
+/* --- @a_jobiddecode@ --- *
+ *
+ * Arguments:  @admin_jobtable *j@ = pointer to a job table
+ *             @const char *jid@ = pointer to a jobid string
+ *
+ * Returns:    A pointer to the job's @svcop@ structure.
+ */
+
+static admin_svcop *a_jobiddecode(admin_jobtable *j, const char *jid)
+{
+  unsigned i;
+  unsigned long pre;
+
+  if (jid[0] != 'J')
+    return (0);
+  for (i = 1; i < 9; i++) {
+    if (!isxdigit((unsigned char)jid[i]))
+      return (0);
+  }
+  if (jid[9] != 0)
+    return (0);
+  pre = strtoul(jid + 1, 0, 16);
+  pre = ((pre * 0xbd11c40bul) - a_joboffset) & 0xffffffff;
+  i = pre & JOB_INDEXMASK;
+  if (i >= j->n || j->v[i].seq != (pre >> JOB_SHIFT))
+    return (0);
+  return (j->v[i].u.op);
+}
+
+/* --- @a_jobcreate@ --- *
+ *
+ * Arguments:  @admin *a@ = pointer to administration client
+ *
+ * Returns:    A pointer to a freshly-allocated @svcop@, or null.
+ *
+ * Use:                Allocates a fresh @svcop@ and links it into a job table.
+ */
+
+static admin_svcop *a_jobcreate(admin *a)
+{
+  admin_svcop *svc;
+  unsigned i;
+  unsigned sz;
+  admin_jobtable *j = &a->j;
+
+  if (j->free != JOB_END) {
+    i = j->free;
+    j->free = j->v[i].u.next;
+  } else {
+    if (j->n == j->sz) {
+      if (j->sz > JOB_INDEXMASK)
+       return (0);
+      sz = j->sz;
+      if (!sz) {
+       j->sz = 16;
+       j->v = xmalloc(j->sz * sizeof(*j->v));
+      } else {
+       j->sz = sz << 1;
+       j->v = xrealloc(j->v, j->sz * sizeof(*j->v), sz * sizeof(*j->v));
+      }
+    }
+    i = j->n++;
+    j->v[i].seq = 0;
+  }
+  svc = xmalloc(sizeof(*svc));
+  svc->index = i;
+  svc->prov = a;
+  svc->next = j->active;
+  svc->prev = 0;
+  if (j->active) j->active->prev = svc;
+  j->active = svc;
+  j->v[i].u.op = svc;
+  IF_TRACING(T_ADMIN, {
+    trace(T_ADMIN, "admin: created job %s (%u)", a_jobidencode(svc), i);
+  })
+  return (svc);
+}
+
+/* --- @a_jobdestroy@ --- *
+ *
+ * Arguments:  @admin_svcop *svc@ = pointer to job block
+ *
+ * Returns:    ---
+ *
+ * Use:                Frees up a completed (or cancelled) job.
+ */
+
+static void a_jobdestroy(admin_svcop *svc)
+{
+  admin *a = svc->prov;
+  admin_jobtable *j = &a->j;
+  unsigned i = svc->index;
+
+  IF_TRACING(T_ADMIN, {
+    trace(T_ADMIN, "admin: destroying job %s (%u)", a_jobidencode(svc), i);
+  })
+  assert(j->v[i].u.op = svc);
+  j->v[i].u.next = j->free;
+  j->v[i].seq++;
+  j->free = i;
+  if (svc->next) svc->next->prev = svc->prev;
+  if (svc->prev) svc->prev->next = svc->next;
+  else j->active = svc->next;
+}
+
+/* --- @a_jobtableinit@ --- *
+ *
+ * Arguments:  @admin_jobtable *j@ = pointer to job table
+ *
+ * Returns:    ---
+ *
+ * Use:                Initializes a job table.
+ */
+
+static void a_jobtableinit(admin_jobtable *j)
+{
+  if (!a_joboffset)
+    a_joboffset = GR_RANGE(&rand_global, 0xffffffff) + 1;
+  j->n = j->sz = 0;
+  j->active = 0;
+  j->free = JOB_END;
+  j->v = 0;
+}
+
+/* --- @a_jobtablefinal@ --- *
+ *
+ * Arguments:  @admin_jobtable *j@ = pointer to job table
+ *
+ * Returns:    ---
+ *
+ * Use:                Closes down a job table.
+ */
+
+static void a_jobtablefinal(admin_jobtable *j)
+{
+  admin_svcop *svc, *ssvc;
+
+  for (svc = j->active; svc; svc = ssvc) {
+    ssvc = svc->next;
+    a_bgfail(&svc->bg, "provider-failed", A_END);
+    a_bgrelease(&svc->bg);
+  }
+  if (j->v) xfree(j->v);
+}
+
 /*----- Services infrastructure -------------------------------------------*/
 
 /* --- @a_svcfind@ --- *
@@ -1683,12 +1872,13 @@ static void a_destroypending(void)
       xfree(bg);
     }
 
-    /* --- Release services I hold --- */
+    /* --- Release services I hold, and abort pending jobs --- */
 
     for (svc = a->svcs; svc; svc = ssvc) {
       ssvc = svc->next;
       a_svcrelease(svc);
     }
+    a_jobtablefinal(&a->j);
 
     /* --- Close file descriptors and selectory --- */
 
@@ -1816,6 +2006,7 @@ void a_create(int fd_in, int fd_out, unsigned f)
   a->bg = 0;
   a->ref = 0;
   a->svcs = 0;
+  a_jobtableinit(&a->j);
   a->f = f;
   if (fd_in == STDIN_FILENO) a_stdin = a;
   fdflags(fd_in, O_NONBLOCK, O_NONBLOCK, FD_CLOEXEC, FD_CLOEXEC);