Goto sanos source index

//
// procfs.c
//
// Kernel Information Filesystem
//
// Copyright (C) 2002 Michael Ringgaard. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// 
// 1. Redistributions of source code must retain the above copyright 
//    notice, this list of conditions and the following disclaimer.  
// 2. 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.  
// 3. 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 <os/krnl.h>

struct proc_inode *proc_list_head;
struct proc_inode *proc_list_tail;
ino_t next_procino = PROC_ROOT_INODE + 1;

int procfs_open(struct file *filp, char *name);
int procfs_close(struct file *filp);

int procfs_read(struct file *filp, void *data, size_t size, off64_t pos);

off64_t procfs_tell(struct file *filp);
off64_t procfs_lseek(struct file *filp, off64_t offset, int origin);

int procfs_fstat(struct file *filp, struct stat64 *buffer);
int procfs_stat(struct fs *fs, char *name, struct stat64 *buffer);

int procfs_opendir(struct file *filp, char *name);
int procfs_readdir(struct file *filp, struct direntry *dirp, int count);

struct fsops procfsops = {
  FSOP_OPEN | FSOP_CLOSE | FSOP_READ | FSOP_TELL | FSOP_LSEEK | FSOP_STAT | FSOP_FSTAT | FSOP_OPENDIR | FSOP_READDIR,

  NULL,
  NULL,

  NULL,
  NULL,
  NULL,

  NULL,

  procfs_open,
  procfs_close,
  NULL,
  NULL,

  procfs_read,
  NULL,
  NULL,

  procfs_tell,
  procfs_lseek,
  NULL,

  NULL,
  NULL,

  procfs_fstat,
  procfs_stat,

  NULL,

  NULL,
  NULL,
  NULL,
  NULL,

  NULL,
  NULL,

  NULL,
  NULL,
  NULL,

  procfs_opendir,
  procfs_readdir
};

static struct proc_inode *find_proc(char *name) {
  struct proc_inode *inode = proc_list_head;
  int namelen = strlen(name);

  while (inode) {
    if (fnmatch(name, namelen, inode->name, inode->namelen)) return inode;
    inode = inode->next;
  }

  return NULL;
}

static void free_proc_file(struct proc_file *pf) {
  struct proc_blk *blk;
  struct proc_blk *next;

  if (!pf) return;
  blk = pf->blkhead;
  while (blk) {
    next = blk->next;
    kfree(blk);
    blk = next;
  }

  kfree(pf);
}

void init_procfs() {
  register_filesystem("procfs", &procfsops);
}

int register_proc_inode(char *name, proc_t proc, void *arg) {
  struct proc_inode *inode;

  inode = (struct proc_inode *) kmalloc(sizeof(struct proc_inode));
  if (!inode) return -ENOMEM;

  inode->ino = next_procino++;
  inode->name = name;
  inode->namelen = strlen(name);
  inode->proc = proc;
  inode->arg = arg;
  inode->size = 0;

  if (proc_list_tail) {
    proc_list_tail->next = inode;
    proc_list_tail = inode;
  } else {
    proc_list_head = proc_list_tail = inode;
  }

  return 0;
}

int proc_write(struct proc_file *pf, void *buffer, size_t size) {
  char *ptr = (char *) buffer;
  size_t left = size;
  struct proc_blk *blk;
  size_t count;

  while (left > 0) {
    if (!pf->blktail || pf->blktail->size == PROC_BLKSIZE) {
      blk = (struct proc_blk *) kmalloc(sizeof(struct proc_blk));
      if (!blk) return -ENOMEM;

      blk->next = NULL;
      blk->size = 0;

      if (pf->blktail) {
        pf->blktail->next = blk;
        pf->blktail = blk;
      } else {
        pf->blkhead = pf->blktail = blk;
      }
    } else {
      blk = pf->blktail;
    }

    count = blk->size + left > PROC_BLKSIZE ? (size_t) (PROC_BLKSIZE - blk->size) : left;

    memcpy(blk->data + blk->size, ptr, count);
    blk->size += count;
    ptr += count;
    left -= count;
    pf->size += count;
  }

  return size;
}

int pprintf(struct proc_file *pf, const char *fmt, ...) {
  va_list args;
  char buffer[1024];
  int len;

  va_start(args, fmt);
  len = vsprintf(buffer, fmt, args);
  va_end(args);
    
  return proc_write(pf, buffer, len);
}

int procfs_open(struct file *filp, char *name) {
  struct proc_inode *inode;
  struct proc_file *pf;
  int rc;

  if (*name == PS1 || *name == PS2) name++;
  inode = find_proc(name);
  if (!inode) return -ENOENT;

  pf = (struct proc_file *) kmalloc(sizeof(struct proc_file));
  if (!pf) return -ENOMEM;

  pf->inode = inode;
  pf->blkhead = pf->blktail = NULL;
  pf->size = 0;

  rc = inode->proc(pf, inode->arg);
  if (rc < 0) {
    free_proc_file(pf);
    return rc;
  }

  inode->size = pf->size;
  filp->data = pf;
  filp->mode = S_IFCHR | S_IRUSR | S_IRGRP | S_IROTH;
  return 0;
}

int procfs_close(struct file *filp) {
  if (!(filp->flags & F_DIR)) {
    struct proc_file *pf = filp->data;
    free_proc_file(pf);
  }

  return 0;
}

int procfs_read(struct file *filp, void *data, size_t size, off64_t pos) {
  struct proc_file *pf = filp->data;
  struct proc_blk *blk;
  size_t start = 0;
  char *ptr = (char *) data;
  int left = size;
  int count;
  int offset;

  if (filp->flags & F_DIR) return -EINVAL;
  if (!size) return 0;

  blk = pf->blkhead;
  while (blk && start + blk->size <= pos) {
    start += blk->size;
    blk = blk->next;
  }

  if (blk) {
    offset = (int) pos - start;

    if (offset < 0 || offset >= PROC_BLKSIZE) {
      kprintf("invalid proc blk offset %d\n", offset);
      dbg_break();
    }

    if (left < blk->size - offset) {
      count = left;
    } else {
      count = blk->size - offset;
    }

    memcpy(ptr, blk->data + offset, count);
    ptr += count;
    left -= count;
    blk = blk->next;
  }

  while (left > 0 && blk) {
    if (left < blk->size) {
      count = left;
    } else {
      count = blk->size;
    }

    memcpy(ptr, blk->data, count);
    ptr += count;
    left -= count;
    blk = blk->next;
  }

  return size - left;
}

off64_t procfs_tell(struct file *filp) {
  return filp->pos;
}

off64_t procfs_lseek(struct file *filp, off64_t offset, int origin) {
  struct proc_file *pf = filp->data;

  if (filp->flags & F_DIR) return -EINVAL;

  switch (origin) {
    case SEEK_END:
      offset += pf->size;
      break;

    case SEEK_CUR:
      offset += filp->pos;
  }

  if (offset < 0 || offset > pf->size) return -EINVAL;

  filp->pos = offset;
  return offset;
}

int procfs_fstat(struct file *filp, struct stat64 *buffer) {
  struct proc_file *pf = filp->data;

  if (filp->flags & F_DIR) {
    if (buffer) {
      memset(buffer, 0, sizeof(struct stat64));
      buffer->st_mode = S_IFDIR | S_IRUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH;
      buffer->st_ino = PROC_ROOT_INODE;
      buffer->st_nlink = 1;
      buffer->st_dev = NODEV;
      buffer->st_atime = buffer->st_mtime = buffer->st_ctime = get_time();
      buffer->st_size = 0;
    }

    return 0;
  }

  if (buffer) {
    memset(buffer, 0, sizeof(struct stat64));
    buffer->st_mode = S_IFCHR | S_IRUSR | S_IRGRP | S_IROTH;

    buffer->st_ino = pf->inode->ino;
    buffer->st_nlink = 1;
    buffer->st_dev = NODEV;

    buffer->st_atime = buffer->st_mtime = buffer->st_ctime = get_time();
    buffer->st_size = pf->size;
  }

  return pf->size;
}

int procfs_stat(struct fs *fs, char *name, struct stat64 *buffer) {
  struct proc_inode *inode;

  if (*name == PS1 || *name == PS2) name++;

  if (!*name) {
    if (buffer) {
      memset(buffer, 0, sizeof(struct stat64));
      buffer->st_mode = S_IFDIR | S_IRUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH;
      buffer->st_ino = PROC_ROOT_INODE;
      buffer->st_nlink = 1;
      buffer->st_dev = NODEV;
      buffer->st_atime = buffer->st_mtime = buffer->st_ctime = get_time();
      buffer->st_size = 0;
    }

    return 0;
  }

  inode = find_proc(name);
  if (!inode) return -ENOENT;

  if (buffer) {
    memset(buffer, 0, sizeof(struct stat64));
    buffer->st_mode = S_IFCHR | S_IRUSR | S_IRGRP | S_IROTH;

    buffer->st_ino = inode->ino;
    buffer->st_nlink = 1;
    buffer->st_dev = NODEV;

    buffer->st_atime = buffer->st_mtime = buffer->st_ctime = get_time();
    buffer->st_size = inode->size;
  }

  return inode->size;
}

int procfs_opendir(struct file *filp, char *name) {
  if (*name == PS1 || *name == PS2) name++;
  if (*name) return -ENOENT;

  filp->data = proc_list_head;
  return 0;
}

int procfs_readdir(struct file *filp, struct direntry *dirp, int count) {
  struct proc_inode *inode = filp->data;

  if (!inode) return 0;

  dirp->ino = inode->ino;
  dirp->namelen = inode->namelen;
  dirp->reclen = sizeof(struct direntry) + dirp->namelen + 1;
  memcpy(dirp->name, inode->name, inode->namelen + 1);

  filp->pos++;
  filp->data = inode->next;
  return 1;
}