<commit_before>/*
 *  ipc-9p.c
 *  einit
 *
 *  Created by <NAME> on 20/01/2008.
 *  Copyright 2008 <NAME>. All rights reserved.
 *
 */

/*
Copyright (c) 2008, <NAME>
All rights reserved.

Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:

    * Redistributions of source code must retain the above copyright notice,
	  this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright notice,
	  this list of conditions and the following disclaimer in the documentation
	  and/or other materials provided with the distribution.
    * Neither the name of the project nor the names of its contributors may be
	  used to endorse or promote products derived from this software without
	  specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <einit/config.h>
#include <einit/module.h>
#include <einit/utility.h>
#include <einit/event.h>
#include <einit/bitch.h>
#include <pthread.h>
#include <string.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/stat.h>

#include <einit-modules/ipc.h>

#include <ixp_local.h>

#ifdef estrdup
#undef estrdup
#endif
#ifdef emalloc
#undef emalloc
#endif
#ifdef ecalloc
#undef ecalloc
#endif

#include <regex.h>

#define EXPECTED_EIV 1

#if EXPECTED_EIV != EINIT_VERSION
#warning "This module was developed for a different version of eINIT, you might experience problems"
#endif

int einit_ipc_9p_configure (struct lmodule *);

#if defined(EINIT_MODULE) || defined(EINIT_MODULE_HEADER)

const struct smodule einit_ipc_9p_self = {
 .eiversion = EINIT_VERSION,
 .eibuild   = BUILDNUMBER,
 .version   = 1,
 .mode      = 0,
 .name      = "eINIT IPC module (9p)",
 .rid       = "einit-ipc-9p",
 .si        = {
  .provides = NULL,
  .requires = NULL,
  .after    = NULL,
  .before   = NULL
 },
 .configure = einit_ipc_9p_configure
};

module_register(einit_ipc_9p_self);

#endif

pthread_t einit_ipc_9p_thread;
char einit_ipc_9p_running = 0;

gid_t einit_ipc_9p_einitgid = 0;

void einit_ipc_9p_boot_event_handler_root_device_ok (struct einit_event *);
void einit_ipc_9p_power_event_handler (struct einit_event *);

pthread_mutex_t
 einit_ipc_9p_event_queue_mutex = PTHREAD_MUTEX_INITIALIZER,
 einit_ipc_9p_event_update_listeners_mutex = PTHREAD_MUTEX_INITIALIZER;

enum ipc_9p_filetype {
 i9_dir,
 i9_file,
 i9_events
};

struct msg_event_queue {
 char *event;
 struct msg_event_queue *next;
 struct msg_event_queue *previous;
};

struct msg_event_queue *einit_ipc_9p_event_queue = NULL;

Ixp9Req **einit_ipc_9p_event_update_listeners = NULL;

struct ipc_9p_filedata {
 char *data;
 enum ipc_9p_filetype type;
 struct stree *cur;
 struct ipc_fs_node **files;
 int c;
 char is_writable;
 struct msg_event_queue *event;
};

struct ipc_9p_fidaux {
 char **path;
 struct ipc_9p_filedata *fd;
};


struct ipc_9p_filedata *ipc_9p_filedata_dup (struct ipc_9p_filedata *d) {
 if (!d) return NULL;

 struct ipc_9p_filedata *fd = emalloc (sizeof (struct ipc_9p_filedata));

 fd->data = d->data ? (char *)str_stabilise (d->data) : NULL;
 fd->type = d->type;
 fd->cur = d->cur;
 fd->files = (struct ipc_fs_node **)(d->files ? set_fix_dup ((const void **)d->files, sizeof(struct ipc_fs_node)) : NULL);
 fd->c = d->c;
 fd->is_writable = d->is_writable;
 fd->event = d->event;

 return fd;
}

struct ipc_9p_fidaux *einit_ipc_9p_fidaux_dup (struct ipc_9p_fidaux *d) {
 struct ipc_9p_fidaux *fa = emalloc (sizeof (struct ipc_9p_fidaux));

 fa->path = (char **)(d->path ? set_str_dup_stable (d->path) : NULL);
 fa->fd = ipc_9p_filedata_dup(d->fd);

 return fa;
}


void einit_ipc_9p_fs_open_spawn (Ixp9Req *r) {
// notice (1, "einit_ipc_9p_fs_open()");
 struct ipc_9p_fidaux *fa = r->fid->aux;

 if (r->ifcall.mode == P9_OREAD) {
  struct einit_event ev = evstaticinit(einit_ipc_read);

  ev.para = fa->path;

  event_emit(&ev, einit_event_flag_broadcast);

  if (ev.stringset) {
   struct ipc_9p_filedata *fd = ecalloc (1, sizeof (struct ipc_9p_filedata));
   ev.stringset = set_str_add (ev.stringset, "");
   char *r = set2str ('\n', (const char **)ev.stringset);

   fd->data = (char *)str_stabilise(r);
   if (r) efree (r);

   fd->type = i9_file;
   fd->cur = NULL;

   fa->fd = fd;

   efree (ev.stringset);
   ev.stringset = NULL;
  } else if (ev.set) {
   struct ipc_9p_filedata *fd = ecalloc (1, sizeof (struct ipc_9p_filedata));

   struct ipc_fs_node n = { .name = (char *)str_stabilise (".."), .is_file = 0 };
   ev.set = set_fix_add (ev.set, &n, sizeof (n));
   n.name = (char *)str_stabilise (".");
   ev.set = set_fix_add (ev.set, &n, sizeof (n));

   fd->data = (char *)str_stabilise ("unknown");
   fd->type = i9_dir;
   fd->files = (struct ipc_fs_node **)ev.set;
   fd->c = 0;

   fa->fd = fd;
  } else {
   evstaticdestroy (ev);
   respond (r, "File not found.");
   return;
  }

  evstaticdestroy (ev);

  respond(r, nil);
 } else /*if ((r->ifcall.mode == P9_OWRITE) || (r->ifcall.mode == (P9_OWRITE | P9_OTRUNC))) */{
  struct ipc_9p_filedata *fd = ecalloc (1, sizeof (struct ipc_9p_filedata));
  fa->fd = fd;

  fd->is_writable = 1;
//  notice (1, "opened file for writing");

  respond(r, nil);
 }/* else {
  respond (r, "Access Mode not supported.");
 }*/
}

void einit_ipc_9p_fs_open (Ixp9Req *r) {
 struct ipc_9p_fidaux *fa = r->fid->aux;

 if (fa && fa->path && fa->path[0] && strmatch (fa->path[0], "events")) {
  if (r->ifcall.mode == P9_OREAD) {
   struct ipc_9p_filedata *fd = ecalloc (1, sizeof (struct ipc_9p_filedata));

   fd->type = i9_events;
   fd->event = einit_ipc_9p_event_queue;

   fa->fd = fd;

   respond(r, nil);
  }
 } else
//  ethread_spawn_detached_run ((void *(*)(void *))einit_ipc_9p_fs_open_spawn, r);
  einit_ipc_9p_fs_open_spawn(r);
}

void einit_ipc_9p_fs_walk(Ixp9Req *r) {
// notice (1, "einit_ipc_9p_fs_walk()");
 int i = 0;
 struct ipc_9p_fidaux *fa = r->fid->aux;

 r->newfid->aux = einit_ipc_9p_fidaux_dup (fa);
 fa = r->newfid->aux;

 for(; i < r->ifcall.nwname; i++) {
//  notice (1, "new path element: %s", r->ifcall.wname[i]);
  if (strmatch (r->ifcall.wname[i], "..")) {
   if (fa->path) {
    int y = 0;

    for (; fa->path[y]; y++);
    y--;
    if (fa->path[y])
     fa->path[y] = 0;

    if (!fa->path[0]) {
     efree (fa->path);
     fa->path = NULL;
    }
   }
  } else {
   fa->path = set_str_add_stable (fa->path, r->ifcall.wname[i]);
  }

  r->ofcall.wqid[i].type = P9_QTDIR;
  r->ofcall.wqid[i].path = 0;
//  r->ofcall.wqid[i].mode = 0660;
 }

 r->ofcall.wqid[i-1].type = P9_QTAPPEND;

 r->ofcall.nwqid = i;
 respond(r, nil);
}

void einit_ipc_9p_fs_reply_event (Ixp9Req *r) {
 struct ipc_9p_fidaux *fa = r->fid->aux;
 struct ipc_9p_filedata *fd = fa->fd;

 if (fd->event->next != einit_ipc_9p_event_queue) {
//  fprintf (stdout, "printing event\n");
//  fflush (stderr);

  r->ofcall.data = estrdup (fd->event->event);
  r->ofcall.count = strlen (r->ofcall.data);

  fd->event = fd->event->next;

  respond(r, nil);
 } else {
//  fprintf (stdout, "no more events right now\n");
//  fflush (stdout);

  emutex_lock(&einit_ipc_9p_event_update_listeners_mutex);
  einit_ipc_9p_event_update_listeners = (Ixp9Req **)set_noa_add ((void **)einit_ipc_9p_event_update_listeners, r);
  emutex_unlock(&einit_ipc_9p_event_update_listeners_mutex);
 }
}

void einit_ipc_9p_fs_read (Ixp9Req *r) {
// notice (1, "einit_ipc_9p_fs_read()");
 struct ipc_9p_fidaux *fa = r->fid->aux;
 struct ipc_9p_filedata *fd = fa->fd;

 if (fd->type == i9_events) {
  einit_ipc_9p_fs_reply_event (r);
 } else if (fd->type == i9_file) {
  if (fd->data) {
   fflush (stderr);

   size_t size = r->ifcall.count;
   r->ofcall.data = ecalloc(1, size+1);

   int y = 0;
   ssize_t tsize = strlen (fd->data);
   ssize_t offs = r->ifcall.offset;

   if (tsize >= offs) {
    for (y = 0; ((offs+y) < tsize) && (y <= size); y++) {
     r->ofcall.data[y] = fd->data[offs+y];
    }

    if (y > size) {
     y = size;
    }

    r->ofcall.count = y;

    respond(r, nil);
   } else {
    r->ofcall.count = 0;
    respond(r, nil);
   }
  } else {
   r->ofcall.count = 0;
   respond(r, nil);
  }
 } else if (fd->type == i9_dir) {
  if (fd->files && (fd->files[fd->c])) {
   Stat s;
   memset (&s, 0, sizeof (Stat));

//   notice (1, "submitting: %s", fd->files[fd->c]->name);

   s.name = fd->files[fd->c]->name;

   s.uid = "root";
   s.gid = "einit";
   s.muid = "unknown";

   s.extension = "";

   if (!einit_ipc_9p_einitgid) {
    lookupuidgid (NULL, &einit_ipc_9p_einitgid, NULL, "einit");
   }

   s.mode = 0666;

   if (!(fd->files[fd->c]->is_file)) {
    s.mode |= 0770 | P9_DMDIR;
    s.qid.type |= QTDIR; 
   }

   size_t size = r->ifcall.count;
   void *buf = ecalloc(1, size);
   IxpMsg m = ixp_message((uchar*)buf, size, MsgPack); 
   if (r->dotu)
    ixp_pstat_dotu(&m, &s);
   else
    ixp_pstat(&m, &s);

   r->ofcall.count = r->dotu ? ixp_sizeof_stat_dotu(&s) : ixp_sizeof_stat(&s);
   r->ofcall.data = (char*)m.data;

   fd->c++;
   respond(r, nil);
  } else {
   r->ofcall.count = 0;
   respond(r, nil);
  }
 }
}

void einit_ipc_9p_fs_stat_spawn (Ixp9Req *r) {
 struct ipc_9p_fidaux *fa = r->fid->aux;
 char *path = set2str ('/', (const char **)fa->path);

 struct einit_event ev = evstaticinit(einit_ipc_stat);

 ev.para = fa->path;
 event_emit(&ev, einit_event_flag_broadcast);

 char is_file = ev.flag;

 evstaticdestroy (ev);

 if (!path) {
  path = estrdup ("/");
 }

 Stat s;
 memset (&s, 0, sizeof (Stat));

 s.mode = P9_OAPPEND | 0660;

 if (!is_file) {
//  notice (1, "directory: %s", path);
  s.mode = 0770 | P9_DMDIR;
  s.qid.type |= QTDIR; 
 }

 s.name = path;

 s.uid = "root";
 s.gid = "einit";
 s.muid = "unknown";

 s.extension = "";

 if (!einit_ipc_9p_einitgid) {
  lookupuidgid (NULL, &einit_ipc_9p_einitgid, NULL, "einit");
 }
 s.n_gid = einit_ipc_9p_einitgid;

 r->fid->qid = s.qid;
 size_t size = r->dotu ? ixp_sizeof_stat_dotu(&s) : ixp_sizeof_stat(&s);
 r->ofcall.nstat = size;

 void *buf = ecalloc (1, size);

 IxpMsg m = ixp_message(buf, size, MsgPack);
 if (r->dotu)
  ixp_pstat_dotu(&m, &s);
 else
  ixp_pstat(&m, &s);

 r->ofcall.stat = m.data; 

 efree (path);

 respond(r, nil);
}

void einit_ipc_9p_fs_stat (Ixp9Req *r) {
 einit_ipc_9p_fs_stat_spawn(r);
// ethread_spawn_detached_run ((void *(*)(void *))einit_ipc_9p_fs_stat_spawn, r);
}

void einit_ipc_9p_fs_write (Ixp9Req *r) {
 struct ipc_9p_fidaux *fa = r->fid->aux;

// notice (1, "einit_ipc_9p_fs_write(%i, %i)", r->ifcall.count, r->ofcall.count);

 if (r->ifcall.count == 0) {
//  notice (1, "einit_ipc_9p_fs_write()");

  respond(r, nil);
  return;
 } else {
  struct ipc_9p_filedata *fd = fa->fd;
  int len = 1 + (fd->data ? strlen (fd->data) : 0);

  r->ofcall.count = r->ifcall.count;
  fd->data = fd->data ? erealloc (fd->data, r->ifcall.count + len) : emalloc (r->ifcall.count + len);

  memcpy (fd->data + (len-1), r->ifcall.data, r->ifcall.count);
  fd->data[r->ifcall.count + len -1] = 0;

//  notice (1, "einit_ipc_9p_fs_write(%s)", fd->data);

  respond(r, nil);
 }
}

void einit_ipc_9p_fs_clunk_spawn (Ixp9Req *r) {
// notice (1, "einit_ipc_9p_fs_clunk()");
 struct ipc_9p_fidaux *fa = r->fid->aux;

 if (fa && fa->fd) {
  emutex_lock (&einit_ipc_9p_event_update_listeners_mutex);
  repeat:
  if (einit_ipc_9p_event_update_listeners) {
   int i = 0;
   for (; einit_ipc_9p_event_update_listeners[i]; i++) {
    struct ipc_9p_fidaux *sfa = einit_ipc_9p_event_update_listeners[i]->fid->aux;
    if (sfa->fd == fa->fd) {
     einit_ipc_9p_event_update_listeners = (Ixp9Req **)setdel ((void **)einit_ipc_9p_event_update_listeners, einit_ipc_9p_event_update_listeners[i]);
     goto repeat;
    }
   }
  }
  emutex_unlock (&einit_ipc_9p_event_update_listeners_mutex);

  struct ipc_9p_filedata *fd = fa->fd;

  if (fd->is_writable && fd->data) {
   strtrim (fd->data);

   if (fd->data[0]) {
    struct einit_event ev = evstaticinit(einit_ipc_write);

    ev.para = fa->path;
    ev.set = set_noa_add (ev.set, fd->data);
    event_emit(&ev, einit_event_flag_broadcast);

    evstaticdestroy (ev);
   }
  }
 }

 respond(r, nil);
}

void einit_ipc_9p_fs_clunk (Ixp9Req *r) {
 einit_ipc_9p_fs_clunk_spawn(r);
// ethread_spawn_detached_run ((void *(*)(void *))einit_ipc_9p_fs_clunk_spawn, r);
}

void einit_ipc_9p_fs_flush(Ixp9Req *r) {
 notice (1, "einit_ipc_9p_fs_flush()");
// respond (r, nil);
 emutex_lock (&einit_ipc_9p_event_update_listeners_mutex);
 repeat:
  if (einit_ipc_9p_event_update_listeners) {
  int i = 0;
  for (; einit_ipc_9p_event_update_listeners[i]; i++) {
   if (r->ifcall.oldtag == einit_ipc_9p_event_update_listeners[i]->ifcall.tag) {
    einit_ipc_9p_event_update_listeners = (Ixp9Req **)setdel ((void **)einit_ipc_9p_event_update_listeners, einit_ipc_9p_event_update_listeners[i]);
    goto repeat;
   }
  }
 }
 emutex_unlock (&einit_ipc_9p_event_update_listeners_mutex);

 respond (r, nil);
}

void einit_ipc_9p_fs_attach(Ixp9Req *r) {
 r->fid->qid.type = QTDIR;
 r->fid->qid.path = (uintptr_t)r->fid;
 r->fid->aux = ecalloc (1, sizeof (struct ipc_9p_fidaux));

 r->ofcall.qid = r->fid->qid;
 respond(r, nil);
}

void einit_ipc_9p_fs_create(Ixp9Req *r) {
 notice (1, "einit_ipc_9p_fs_create()");
 respond (r, "not implemented.");
}

void einit_ipc_9p_fs_remove(Ixp9Req *r) {
 notice (1, "einit_ipc_9p_fs_remove()");
 respond (r, "not implemented.");
}

void einit_ipc_9p_fs_freefid(Fid *f) {
 if (f->aux) {
  struct ipc_9p_fidaux *fa = f->aux;

  if (fa->fd) {
   efree (fa->fd);
  }

  efree (f->aux);
 }
}

Ixp9Srv einit_ipc_9p_srv = {
 .open    = einit_ipc_9p_fs_open,
 .walk    = einit_ipc_9p_fs_walk,
 .read    = einit_ipc_9p_fs_read,
 .stat    = einit_ipc_9p_fs_stat,
 .write   = einit_ipc_9p_fs_write,
 .clunk   = einit_ipc_9p_fs_clunk,
 .flush   = einit_ipc_9p_fs_flush,
 .attach  = einit_ipc_9p_fs_attach,
 .create  = einit_ipc_9p_fs_create,
 .remove  = einit_ipc_9p_fs_remove,
 .freefid = einit_ipc_9p_fs_freefid
};

static IxpServer einit_ipc_9p_server; 

void *einit_ipc_9p_listen (void *param) {
 intptr_t fdp = (intptr_t)param;
 int fd = fdp;

 fcntl (fd, F_SETFD, FD_CLOEXEC);

 IxpConn* connection = ixp_listen(&einit_ipc_9p_server, fd, &einit_ipc_9p_srv, serve_9pcon, NULL); 

 if (connection) {
// ixp_pthread_init();

  notice (1, "9p server initialised");
  /* server loop nao */

  ixp_serverloop(&einit_ipc_9p_server);

  notice (1, "9p server loop has terminated: %s", ixp_errbuf());

  einit_ipc_9p_running = 0;
 } else {
  notice (1, "could not initialise 9p server");
 }

 return NULL;
}

void einit_ipc_9p_generic_event_handler (struct einit_event *ev) {
 struct msg_event_queue *e = emalloc (sizeof (struct msg_event_queue));

 char **data = NULL;
 char buffer[BUFFERSIZE];

 esprintf (buffer, BUFFERSIZE, "event=%i", ev->seqid);
 data = set_str_add (data, buffer);

 esprintf (buffer, BUFFERSIZE, "type=%s", event_code_to_string(ev->type));
 data = set_str_add (data, buffer);

 if (ev->integer) {
  esprintf (buffer, BUFFERSIZE, "integer=%i", ev->integer);
  data = set_str_add (data, buffer);
 }

 if (ev->task) {
  esprintf (buffer, BUFFERSIZE, "task=%i", ev->task);
  data = set_str_add (data, buffer);
 }

 if (ev->status) {
  esprintf (buffer, BUFFERSIZE, "status=%i", ev->status);
  data = set_str_add (data, buffer);
 }

 if (ev->flag) {
  esprintf (buffer, BUFFERSIZE, "flag=%i", ev->flag);
  data = set_str_add (data, buffer);
 }

 if (ev->string) {
  char *msg_string;
  size_t i = strlen (ev->string) + 1 + 9; /* "string=\n"*/
  msg_string = emalloc (i);
  esprintf (msg_string, i, "string=%s", ev->string);

  data = set_str_add (data, msg_string);
 }<commit_msg>add module= to /events<commit_after>/*
 *  ipc-9p.c
 *  einit
 *
 *  Created by <NAME> on 20/01/2008.
 *  Copyright 2008 <NAME>. All rights reserved.
 *
 */

/*
Copyright (c) 2008, <NAME>
All rights reserved.

Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:

    * Redistributions of source code must retain the above copyright notice,
	  this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright notice,
	  this list of conditions and the following disclaimer in the documentation
	  and/or other materials provided with the distribution.
    * Neither the name of the project nor the names of its contributors may be
	  used to endorse or promote products derived from this software without
	  specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <einit/config.h>
#include <einit/module.h>
#include <einit/utility.h>
#include <einit/event.h>
#include <einit/bitch.h>
#include <pthread.h>
#include <string.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/stat.h>

#include <einit-modules/ipc.h>

#include <ixp_local.h>

#ifdef estrdup
#undef estrdup
#endif
#ifdef emalloc
#undef emalloc
#endif
#ifdef ecalloc
#undef ecalloc
#endif

#include <regex.h>

#define EXPECTED_EIV 1

#if EXPECTED_EIV != EINIT_VERSION
#warning "This module was developed for a different version of eINIT, you might experience problems"
#endif

int einit_ipc_9p_configure (struct lmodule *);

#if defined(EINIT_MODULE) || defined(EINIT_MODULE_HEADER)

const struct smodule einit_ipc_9p_self = {
 .eiversion = EINIT_VERSION,
 .eibuild   = BUILDNUMBER,
 .version   = 1,
 .mode      = 0,
 .name      = "eINIT IPC module (9p)",
 .rid       = "einit-ipc-9p",
 .si        = {
  .provides = NULL,
  .requires = NULL,
  .after    = NULL,
  .before   = NULL
 },
 .configure = einit_ipc_9p_configure
};

module_register(einit_ipc_9p_self);

#endif

pthread_t einit_ipc_9p_thread;
char einit_ipc_9p_running = 0;

gid_t einit_ipc_9p_einitgid = 0;

void einit_ipc_9p_boot_event_handler_root_device_ok (struct einit_event *);
void einit_ipc_9p_power_event_handler (struct einit_event *);

pthread_mutex_t
 einit_ipc_9p_event_queue_mutex = PTHREAD_MUTEX_INITIALIZER,
 einit_ipc_9p_event_update_listeners_mutex = PTHREAD_MUTEX_INITIALIZER;

enum ipc_9p_filetype {
 i9_dir,
 i9_file,
 i9_events
};

struct msg_event_queue {
 char *event;
 struct msg_event_queue *next;
 struct msg_event_queue *previous;
};

struct msg_event_queue *einit_ipc_9p_event_queue = NULL;

Ixp9Req **einit_ipc_9p_event_update_listeners = NULL;

struct ipc_9p_filedata {
 char *data;
 enum ipc_9p_filetype type;
 struct stree *cur;
 struct ipc_fs_node **files;
 int c;
 char is_writable;
 struct msg_event_queue *event;
};

struct ipc_9p_fidaux {
 char **path;
 struct ipc_9p_filedata *fd;
};


struct ipc_9p_filedata *ipc_9p_filedata_dup (struct ipc_9p_filedata *d) {
 if (!d) return NULL;

 struct ipc_9p_filedata *fd = emalloc (sizeof (struct ipc_9p_filedata));

 fd->data = d->data ? (char *)str_stabilise (d->data) : NULL;
 fd->type = d->type;
 fd->cur = d->cur;
 fd->files = (struct ipc_fs_node **)(d->files ? set_fix_dup ((const void **)d->files, sizeof(struct ipc_fs_node)) : NULL);
 fd->c = d->c;
 fd->is_writable = d->is_writable;
 fd->event = d->event;

 return fd;
}

struct ipc_9p_fidaux *einit_ipc_9p_fidaux_dup (struct ipc_9p_fidaux *d) {
 struct ipc_9p_fidaux *fa = emalloc (sizeof (struct ipc_9p_fidaux));

 fa->path = (char **)(d->path ? set_str_dup_stable (d->path) : NULL);
 fa->fd = ipc_9p_filedata_dup(d->fd);

 return fa;
}


void einit_ipc_9p_fs_open_spawn (Ixp9Req *r) {
// notice (1, "einit_ipc_9p_fs_open()");
 struct ipc_9p_fidaux *fa = r->fid->aux;

 if (r->ifcall.mode == P9_OREAD) {
  struct einit_event ev = evstaticinit(einit_ipc_read);

  ev.para = fa->path;

  event_emit(&ev, einit_event_flag_broadcast);

  if (ev.stringset) {
   struct ipc_9p_filedata *fd = ecalloc (1, sizeof (struct ipc_9p_filedata));
   ev.stringset = set_str_add (ev.stringset, "");
   char *r = set2str ('\n', (const char **)ev.stringset);

   fd->data = (char *)str_stabilise(r);
   if (r) efree (r);

   fd->type = i9_file;
   fd->cur = NULL;

   fa->fd = fd;

   efree (ev.stringset);
   ev.stringset = NULL;
  } else if (ev.set) {
   struct ipc_9p_filedata *fd = ecalloc (1, sizeof (struct ipc_9p_filedata));

   struct ipc_fs_node n = { .name = (char *)str_stabilise (".."), .is_file = 0 };
   ev.set = set_fix_add (ev.set, &n, sizeof (n));
   n.name = (char *)str_stabilise (".");
   ev.set = set_fix_add (ev.set, &n, sizeof (n));

   fd->data = (char *)str_stabilise ("unknown");
   fd->type = i9_dir;
   fd->files = (struct ipc_fs_node **)ev.set;
   fd->c = 0;

   fa->fd = fd;
  } else {
   evstaticdestroy (ev);
   respond (r, "File not found.");
   return;
  }

  evstaticdestroy (ev);

  respond(r, nil);
 } else /*if ((r->ifcall.mode == P9_OWRITE) || (r->ifcall.mode == (P9_OWRITE | P9_OTRUNC))) */{
  struct ipc_9p_filedata *fd = ecalloc (1, sizeof (struct ipc_9p_filedata));
  fa->fd = fd;

  fd->is_writable = 1;
//  notice (1, "opened file for writing");

  respond(r, nil);
 }/* else {
  respond (r, "Access Mode not supported.");
 }*/
}

void einit_ipc_9p_fs_open (Ixp9Req *r) {
 struct ipc_9p_fidaux *fa = r->fid->aux;

 if (fa && fa->path && fa->path[0] && strmatch (fa->path[0], "events")) {
  if (r->ifcall.mode == P9_OREAD) {
   struct ipc_9p_filedata *fd = ecalloc (1, sizeof (struct ipc_9p_filedata));

   fd->type = i9_events;
   fd->event = einit_ipc_9p_event_queue;

   fa->fd = fd;

   respond(r, nil);
  }
 } else
//  ethread_spawn_detached_run ((void *(*)(void *))einit_ipc_9p_fs_open_spawn, r);
  einit_ipc_9p_fs_open_spawn(r);
}

void einit_ipc_9p_fs_walk(Ixp9Req *r) {
// notice (1, "einit_ipc_9p_fs_walk()");
 int i = 0;
 struct ipc_9p_fidaux *fa = r->fid->aux;

 r->newfid->aux = einit_ipc_9p_fidaux_dup (fa);
 fa = r->newfid->aux;

 for(; i < r->ifcall.nwname; i++) {
//  notice (1, "new path element: %s", r->ifcall.wname[i]);
  if (strmatch (r->ifcall.wname[i], "..")) {
   if (fa->path) {
    int y = 0;

    for (; fa->path[y]; y++);
    y--;
    if (fa->path[y])
     fa->path[y] = 0;

    if (!fa->path[0]) {
     efree (fa->path);
     fa->path = NULL;
    }
   }
  } else {
   fa->path = set_str_add_stable (fa->path, r->ifcall.wname[i]);
  }

  r->ofcall.wqid[i].type = P9_QTDIR;
  r->ofcall.wqid[i].path = 0;
//  r->ofcall.wqid[i].mode = 0660;
 }

 r->ofcall.wqid[i-1].type = P9_QTAPPEND;

 r->ofcall.nwqid = i;
 respond(r, nil);
}

void einit_ipc_9p_fs_reply_event (Ixp9Req *r) {
 struct ipc_9p_fidaux *fa = r->fid->aux;
 struct ipc_9p_filedata *fd = fa->fd;

 if (fd->event->next != einit_ipc_9p_event_queue) {
//  fprintf (stdout, "printing event\n");
//  fflush (stderr);

  r->ofcall.data = estrdup (fd->event->event);
  r->ofcall.count = strlen (r->ofcall.data);

  fd->event = fd->event->next;

  respond(r, nil);
 } else {
//  fprintf (stdout, "no more events right now\n");
//  fflush (stdout);

  emutex_lock(&einit_ipc_9p_event_update_listeners_mutex);
  einit_ipc_9p_event_update_listeners = (Ixp9Req **)set_noa_add ((void **)einit_ipc_9p_event_update_listeners, r);
  emutex_unlock(&einit_ipc_9p_event_update_listeners_mutex);
 }
}

void einit_ipc_9p_fs_read (Ixp9Req *r) {
// notice (1, "einit_ipc_9p_fs_read()");
 struct ipc_9p_fidaux *fa = r->fid->aux;
 struct ipc_9p_filedata *fd = fa->fd;

 if (fd->type == i9_events) {
  einit_ipc_9p_fs_reply_event (r);
 } else if (fd->type == i9_file) {
  if (fd->data) {
   fflush (stderr);

   size_t size = r->ifcall.count;
   r->ofcall.data = ecalloc(1, size+1);

   int y = 0;
   ssize_t tsize = strlen (fd->data);
   ssize_t offs = r->ifcall.offset;

   if (tsize >= offs) {
    for (y = 0; ((offs+y) < tsize) && (y <= size); y++) {
     r->ofcall.data[y] = fd->data[offs+y];
    }

    if (y > size) {
     y = size;
    }

    r->ofcall.count = y;

    respond(r, nil);
   } else {
    r->ofcall.count = 0;
    respond(r, nil);
   }
  } else {
   r->ofcall.count = 0;
   respond(r, nil);
  }
 } else if (fd->type == i9_dir) {
  if (fd->files && (fd->files[fd->c])) {
   Stat s;
   memset (&s, 0, sizeof (Stat));

//   notice (1, "submitting: %s", fd->files[fd->c]->name);

   s.name = fd->files[fd->c]->name;

   s.uid = "root";
   s.gid = "einit";
   s.muid = "unknown";

   s.extension = "";

   if (!einit_ipc_9p_einitgid) {
    lookupuidgid (NULL, &einit_ipc_9p_einitgid, NULL, "einit");
   }

   s.mode = 0666;

   if (!(fd->files[fd->c]->is_file)) {
    s.mode |= 0770 | P9_DMDIR;
    s.qid.type |= QTDIR; 
   }

   size_t size = r->ifcall.count;
   void *buf = ecalloc(1, size);
   IxpMsg m = ixp_message((uchar*)buf, size, MsgPack); 
   if (r->dotu)
    ixp_pstat_dotu(&m, &s);
   else
    ixp_pstat(&m, &s);

   r->ofcall.count = r->dotu ? ixp_sizeof_stat_dotu(&s) : ixp_sizeof_stat(&s);
   r->ofcall.data = (char*)m.data;

   fd->c++;
   respond(r, nil);
  } else {
   r->ofcall.count = 0;
   respond(r, nil);
  }
 }
}

void einit_ipc_9p_fs_stat_spawn (Ixp9Req *r) {
 struct ipc_9p_fidaux *fa = r->fid->aux;
 char *path = set2str ('/', (const char **)fa->path);

 struct einit_event ev = evstaticinit(einit_ipc_stat);

 ev.para = fa->path;
 event_emit(&ev, einit_event_flag_broadcast);

 char is_file = ev.flag;

 evstaticdestroy (ev);

 if (!path) {
  path = estrdup ("/");
 }

 Stat s;
 memset (&s, 0, sizeof (Stat));

 s.mode = P9_OAPPEND | 0660;

 if (!is_file) {
//  notice (1, "directory: %s", path);
  s.mode = 0770 | P9_DMDIR;
  s.qid.type |= QTDIR; 
 }

 s.name = path;

 s.uid = "root";
 s.gid = "einit";
 s.muid = "unknown";

 s.extension = "";

 if (!einit_ipc_9p_einitgid) {
  lookupuidgid (NULL, &einit_ipc_9p_einitgid, NULL, "einit");
 }
 s.n_gid = einit_ipc_9p_einitgid;

 r->fid->qid = s.qid;
 size_t size = r->dotu ? ixp_sizeof_stat_dotu(&s) : ixp_sizeof_stat(&s);
 r->ofcall.nstat = size;

 void *buf = ecalloc (1, size);

 IxpMsg m = ixp_message(buf, size, MsgPack);
 if (r->dotu)
  ixp_pstat_dotu(&m, &s);
 else
  ixp_pstat(&m, &s);

 r->ofcall.stat = m.data; 

 efree (path);

 respond(r, nil);
}

void einit_ipc_9p_fs_stat (Ixp9Req *r) {
 einit_ipc_9p_fs_stat_spawn(r);
// ethread_spawn_detached_run ((void *(*)(void *))einit_ipc_9p_fs_stat_spawn, r);
}

void einit_ipc_9p_fs_write (Ixp9Req *r) {
 struct ipc_9p_fidaux *fa = r->fid->aux;

// notice (1, "einit_ipc_9p_fs_write(%i, %i)", r->ifcall.count, r->ofcall.count);

 if (r->ifcall.count == 0) {
//  notice (1, "einit_ipc_9p_fs_write()");

  respond(r, nil);
  return;
 } else {
  struct ipc_9p_filedata *fd = fa->fd;
  int len = 1 + (fd->data ? strlen (fd->data) : 0);

  r->ofcall.count = r->ifcall.count;
  fd->data = fd->data ? erealloc (fd->data, r->ifcall.count + len) : emalloc (r->ifcall.count + len);

  memcpy (fd->data + (len-1), r->ifcall.data, r->ifcall.count);
  fd->data[r->ifcall.count + len -1] = 0;

//  notice (1, "einit_ipc_9p_fs_write(%s)", fd->data);

  respond(r, nil);
 }
}

void einit_ipc_9p_fs_clunk_spawn (Ixp9Req *r) {
// notice (1, "einit_ipc_9p_fs_clunk()");
 struct ipc_9p_fidaux *fa = r->fid->aux;

 if (fa && fa->fd) {
  emutex_lock (&einit_ipc_9p_event_update_listeners_mutex);
  repeat:
  if (einit_ipc_9p_event_update_listeners) {
   int i = 0;
   for (; einit_ipc_9p_event_update_listeners[i]; i++) {
    struct ipc_9p_fidaux *sfa = einit_ipc_9p_event_update_listeners[i]->fid->aux;
    if (sfa->fd == fa->fd) {
     einit_ipc_9p_event_update_listeners = (Ixp9Req **)setdel ((void **)einit_ipc_9p_event_update_listeners, einit_ipc_9p_event_update_listeners[i]);
     goto repeat;
    }
   }
  }
  emutex_unlock (&einit_ipc_9p_event_update_listeners_mutex);

  struct ipc_9p_filedata *fd = fa->fd;

  if (fd->is_writable && fd->data) {
   strtrim (fd->data);

   if (fd->data[0]) {
    struct einit_event ev = evstaticinit(einit_ipc_write);

    ev.para = fa->path;
    ev.set = set_noa_add (ev.set, fd->data);
    event_emit(&ev, einit_event_flag_broadcast);

    evstaticdestroy (ev);
   }
  }
 }

 respond(r, nil);
}

void einit_ipc_9p_fs_clunk (Ixp9Req *r) {
 einit_ipc_9p_fs_clunk_spawn(r);
// ethread_spawn_detached_run ((void *(*)(void *))einit_ipc_9p_fs_clunk_spawn, r);
}

void einit_ipc_9p_fs_flush(Ixp9Req *r) {
 notice (1, "einit_ipc_9p_fs_flush()");
// respond (r, nil);
 emutex_lock (&einit_ipc_9p_event_update_listeners_mutex);
 repeat:
  if (einit_ipc_9p_event_update_listeners) {
  int i = 0;
  for (; einit_ipc_9p_event_update_listeners[i]; i++) {
   if (r->ifcall.oldtag == einit_ipc_9p_event_update_listeners[i]->ifcall.tag) {
    einit_ipc_9p_event_update_listeners = (Ixp9Req **)setdel ((void **)einit_ipc_9p_event_update_listeners, einit_ipc_9p_event_update_listeners[i]);
    goto repeat;
   }
  }
 }
 emutex_unlock (&einit_ipc_9p_event_update_listeners_mutex);

 respond (r, nil);
}

void einit_ipc_9p_fs_attach(Ixp9Req *r) {
 r->fid->qid.type = QTDIR;
 r->fid->qid.path = (uintptr_t)r->fid;
 r->fid->aux = ecalloc (1, sizeof (struct ipc_9p_fidaux));

 r->ofcall.qid = r->fid->qid;
 respond(r, nil);
}

void einit_ipc_9p_fs_create(Ixp9Req *r) {
 notice (1, "einit_ipc_9p_fs_create()");
 respond (r, "not implemented.");
}

void einit_ipc_9p_fs_remove(Ixp9Req *r) {
 notice (1, "einit_ipc_9p_fs_remove()");
 respond (r, "not implemented.");
}

void einit_ipc_9p_fs_freefid(Fid *f) {
 if (f->aux) {
  struct ipc_9p_fidaux *fa = f->aux;

  if (fa->fd) {
   efree (fa->fd);
  }

  efree (f->aux);
 }
}

Ixp9Srv einit_ipc_9p_srv = {
 .open    = einit_ipc_9p_fs_open,
 .walk    = einit_ipc_9p_fs_walk,
 .read    = einit_ipc_9p_fs_read,
 .stat    = einit_ipc_9p_fs_stat,
 .write   = einit_ipc_9p_fs_write,
 .clunk   = einit_ipc_9p_fs_clunk,
 .flush   = einit_ipc_9p_fs_flush,
 .attach  = einit_ipc_9p_fs_attach,
 .create  = einit_ipc_9p_fs_create,
 .remove  = einit_ipc_9p_fs_remove,
 .freefid = einit_ipc_9p_fs_freefid
};

static IxpServer einit_ipc_9p_server; 

void *einit_ipc_9p_listen (void *param) {
 intptr_t fdp = (intptr_t)param;
 int fd = fdp;

 fcntl (fd, F_SETFD, FD_CLOEXEC);

 IxpConn* connection = ixp_listen(&einit_ipc_9p_server, fd, &einit_ipc_9p_srv, serve_9pcon, NULL); 

 if (connection) {
// ixp_pthread_init();

  notice (1, "9p server initialised");
  /* server loop nao */

  ixp_serverloop(&einit_ipc_9p_server);

  notice (1, "9p server loop has terminated: %s", ixp_errbuf());

  einit_ipc_9p_running = 0;
 } else {
  notice (1, "could not initialise 9p server");
 }

 return NULL;
}

void einit_ipc_9p_generic_event_handler (struct einit_event *ev) {
 struct msg_event_queue *e = emalloc (sizeof (struct msg_event_queue));

 char **data = NULL;
 char buffer[BUFFERSIZE];

 esprintf (buffer, BUFFERSIZE, "event=%i", ev->seqid);
 data = set_str_add (data, buffer);

 esprintf (buffer, BUFFERSIZE, "type=%s", event_code_to_string(ev->type));
 data = set_str_add (data, buffer);

 if (ev->integer) {
  esprintf (buffer, BUFFERSIZE, "integer=%i", ev->integer);
  data = set_str_add (data, buffer);
 }

 if (ev->task) {
  esprintf (buffer, BUFFERSIZE, "task=%i", ev->task);
  data = set_str_add (data, buffer);
 }

 if (ev->status) {
  esprintf (buffer, BUFFERSIZE, "status=%i", ev->status);
  data = set_str_add (data, buffer);
 }

 if (ev->flag) {
  esprintf (buffer, BUFFERSIZE, "flag=%i", ev->flag);
  data = set_str_add (data, buffer);
 }

 if ((ev->type == einit_feedback_module_status) && ev->para) {
  struct lmodule *m = (struct lmodule *)ev->para;
  if (m->module->rid) {
   char *msg_string;
   size_t i = strlen (m->module->rid) + 1 + 9; /* "module=\n"*/
   msg_string = emalloc (i);
   esprintf (msg_string, i, "module=%s", m->module->rid);

   data = set_str_add (data, msg_string);
  }
 }

 if (ev->string) {
  char *msg_string;
  size_t i = strlen (ev->string) + 1 + 9; /* "string=\n"*/
  msg_string = emalloc (i);
  esprintf (msg_string, i, "string=%s", ev->string);

  data = set_str_add (data, msg_string);
 }
{'fraction_non_alphanumeric': 0.15454956994114985, 'fraction_numerical': 0.016659121774558625, 'mean_word_length': 3.9427207637231505, 'pattern_counts': {'":': 0, '<': 49, '<?xml version=': 0, '>': 444, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6678231', 'n_tokens_mistral': 14452, 'n_tokens_neox': 13946, 'n_words': 2978}