summaryrefslogtreecommitdiff
path: root/procfs_dir.c
blob: dfe30a2628da1272e3d172875cc8b4814592f974 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
#include <stdlib.h>
#include <string.h>
#include "procfs.h"
#include "procfs_dir.h"

struct procfs_dir_node
{
  const struct procfs_dir_ops *ops;
  void *hook;
};

static int
entry_exists (struct procfs_dir_node *dir, const struct procfs_dir_entry *ent)
{
  if (ent->ops.exists)
    return ent->ops.exists (dir->hook, ent->hook);
  if (dir->ops->entry_ops.exists)
    return dir->ops->entry_ops.exists (dir->hook, ent->hook);

  return 1;
}

static error_t
procfs_dir_get_contents (void *hook, char **contents, ssize_t *contents_len)
{
  static const char dot_dotdot[] = ".\0..";
  struct procfs_dir_node *dir = hook;
  const struct procfs_dir_entry *ent;
  int pos;

  /* Evaluate how much space is needed.  Note that we include the hidden
     entries, just in case their status changes between now and then.  */
  pos = sizeof dot_dotdot;
  for (ent = dir->ops->entries; ent->name; ent++)
    pos += strlen (ent->name) + 1;

  *contents = malloc (pos);
  if (! *contents)
    return ENOMEM;

  memcpy (*contents, dot_dotdot, sizeof dot_dotdot);
  pos = sizeof dot_dotdot;
  for (ent = dir->ops->entries; ent->name; ent++)
    {
      if (! entry_exists (dir, ent))
	continue;

      strcpy (*contents + pos, ent->name);
      pos += strlen (ent->name) + 1;
    }

  *contents_len = pos;
  return 0;
}

static error_t
procfs_dir_lookup (void *hook, const char *name, struct node **np)
{
  struct procfs_dir_node *dir = hook;
  const struct procfs_dir_entry *ent;

  for (ent = dir->ops->entries; ent->name && strcmp (name, ent->name); ent++);
  if (! ent->name)
    return ENOENT;

  if (ent->ops.make_node)
    *np = ent->ops.make_node (dir->hook, ent->hook);
  else if (dir->ops->entry_ops.make_node)
    *np = dir->ops->entry_ops.make_node (dir->hook, ent->hook);
  else
    return EGRATUITOUS;

  if (! *np)
    return ENOMEM;

  return 0;
}

static void
procfs_dir_cleanup (void *hook)
{
  struct procfs_dir_node *dir = hook;

  if (dir->ops->cleanup)
    dir->ops->cleanup (dir->hook);

  free (dir);
}

struct node *
procfs_dir_make_node (const struct procfs_dir_ops *dir_ops, void *dir_hook)
{
  static const struct procfs_node_ops ops = {
    .get_contents = procfs_dir_get_contents,
    .lookup = procfs_dir_lookup,
    .cleanup_contents = procfs_cleanup_contents_with_free,
    .cleanup = procfs_dir_cleanup,
  };
  struct procfs_dir_node *dir;

  dir = malloc (sizeof *dir);
  if (! dir)
    {
      if (dir_ops->cleanup)
	dir_ops->cleanup (dir_hook);

      return NULL;
    }

  dir->ops = dir_ops;
  dir->hook = dir_hook;

  return procfs_make_node (&ops, dir);
}