#include <syslog.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdio.h>
#include <sys/param.h>
#include "devfsd.h"


/* The following header is for struct devfsd_notify_struct */
#include <linux/devfs_fs.h>

static int realsymlink(char *arg1, char *arg2)
{
  char src[MAXPATHLEN + 1], dst[MAXPATHLEN + 1];
  struct stat buf;

  snprintf(src, sizeof(src), "/dev/%s", arg2);
  src[MAXPATHLEN] = '\0';
  snprintf(dst, sizeof(dst), "/dev/%s", arg1);
  dst[MAXPATHLEN] = '\0';
  if(lstat(dst, &buf))
  {
    SYSLOG(LOG_ERR, "Link destination \"%s\" doesn't exist.", dst);
    return -1;
  }
  if(symlink(dst, src))
  {
    SYSLOG(LOG_ERR, "Link \"%s\" to \"%s\" gives error %m.", src, dst);
    return -1;
  }
  SYSLOG(LOG_INFO, "Link \"%s\" to \"%s\" success!", src, dst);
  return 0;
}

int mysymlink(int argc, char **argv)
{
  if(argc != 3)
  {
    SYSLOG(LOG_ERR, "Need two params for link not %d.", argc);
    return -1;
  }
  return realsymlink(argv[1], argv[2]);
}

static int realunlink(const char *arg)
{
  struct stat buf;
  char name[MAXPATHLEN + 1];

  snprintf(name, sizeof(name), "/dev/%s", arg);
  name[MAXPATHLEN] = '\0';
  if(lstat(name, &buf))
  {
    SYSLOG(LOG_ERR, "Can't stat \"%s\" for unlink.", name);
    return -1;
  }
  if(!S_ISLNK(buf.st_mode))
  {
    SYSLOG(LOG_ERR, "Trying to unlink non-link \"%s\".", name);
    return -1;
  }
  if(unlink(name))
  {
    SYSLOG(LOG_ERR, "Unlink of \"%s\" gives error %m.", name);
    return -1;
  }
  SYSLOG(LOG_INFO, "Unlink of \"%s\" success!", name);
  return 0;
}

int myunlink(int argc, char **argv)
{
  if(argc != 2)
  {
    SYSLOG(LOG_ERR, "Need one param for unlink not %d.", argc);
    return -1;
  }
  return realunlink(argv[1]);
}

int mfunctest(int argc, char **argv)
{
  char buf[1024];
  int i;

  if(argv[1])
    strcpy(buf, argv[1]);
  for(i = 2; argv[i]; i++)
  {
    strcat(buf, ":");
    strcat(buf, argv[i]);
  }
  SYSLOG(LOG_ERR, "mfunctest:%s", buf);
  return 0;
}

static int isevent(void *arg)
{
  struct devfsd_notify_struct *e;

  if(!arg)
    return 0;
  e = arg;
  if(e->type > DEVFSD_NOTIFY_MAX || e->type < 0
    || e->namelen >= DEVFS_PATHLEN || e->namelen <= 0)
  {
    /* Must be a string not an event */
    return 0;
  }
  return 1;
}

int cfdolink(void *arg1, void *arg2, void *arg3, void *arg4, void *arg5)
{
  char src[MAXPATHLEN + 1], dst[MAXPATHLEN + 1];
  struct stat buf;
  struct devfsd_notify_struct *e;
  char *args[4];
  int num_args, rc = 0;

  if(!isevent(arg1))
  {
    SYSLOG(LOG_ERR, "cfdolink() first param must be event.");
    return -1;
  }
  e = arg1;
  args[0] = arg2;
  args[1] = arg3;
  args[2] = arg4;
  args[3] = arg5;
  for(num_args = 0; num_args < 4 && args[num_args]; num_args++)
  {
    if(isevent(args[num_args]))
    {
      SYSLOG(LOG_ERR, "Only the first cfdolink() param should be an event.");
      return -1;
    }
  }
  if(num_args == 0)
  {
    SYSLOG(LOG_ERR, "Must specify at least one link name for cfdolink().");
    return -1;
  }
  switch(e->type)
  {
    case DEVFSD_NOTIFY_REGISTERED:
      for(num_args--; num_args >= 0; num_args--)
        rc |= realsymlink(e->devname, args[num_args]);
    break;
    case DEVFSD_NOTIFY_UNREGISTERED:
      for(num_args--; num_args >= 0; num_args--)
        rc |= realunlink(args[num_args]);
    break;
    default:
    break;
  }

  return rc;
}

static void log_event(void *arg, char *name)
{
  const char *tname;
  struct devfsd_notify_struct *e;

  if(!isevent(arg))
  {
    /* Must be a string not an event */
    SYSLOG(LOG_INFO, "event %s, val:%s", name, arg);
    return;
  }
  e = arg;
  switch(e->type)
  {
    case DEVFSD_NOTIFY_REGISTERED:
      tname = "registered";
    break;
    case DEVFSD_NOTIFY_UNREGISTERED:
      tname = "unregistered";
    break;
    case DEVFSD_NOTIFY_ASYNC_OPEN:
      tname = "async_open";
    break;
    case DEVFSD_NOTIFY_CLOSE:
      tname = "close";
    break;
    case DEVFSD_NOTIFY_LOOKUP:
      tname = "lookup";
    break;
    case DEVFSD_NOTIFY_CHANGE:
      tname = "change";
    break;
    case DEVFSD_NOTIFY_CREATE:
      tname = "create";
    break;
    default:
      tname = "unknown";
  }
  if(e->overrun_count)
    SYSLOG(LOG_INFO, "devfsd lost %d events", e->overrun_count);
  SYSLOG(LOG_INFO
       , "event %s, type:%s, mode:%o, maj/min:%d:%d, uid:%d, gid:%d, name:%s"
       , name, tname, e->mode, e->major, e->minor, e->uid, e->gid, e->devname);
}

int cfunctest(void *arg1, void *arg2, void *arg3, void *arg4, void *arg5)
{
  if(arg1)
  {
    log_event(arg1, "arg1");
    if(arg2)
    {
      log_event(arg2, "arg2");
      if(arg3)
      {
        log_event(arg3, "arg3");
        if(arg4)
        {
          log_event(arg4, "arg4");
          if(arg5)
            log_event(arg5, "arg5");
        }
      }
    }
  }

  return 0;
}
