vm: change NO_MEM to a more impossible value
[minix.git] / servers / procfs / tree.c
blobf4dfa49ce1e5f4b542f24bb9849f3655ad027c46
1 /* ProcFS - tree.c - by Alen Stojanov and David van Moolenbroek */
3 #include "inc.h"
5 struct proc proc[NR_PROCS + NR_TASKS];
6 struct mproc mproc[NR_PROCS];
7 struct fproc fproc[NR_PROCS];
9 static int nr_pid_entries;
11 /*===========================================================================*
12 * slot_in_use *
13 *===========================================================================*/
14 static int slot_in_use(int slot)
16 /* Return whether the given slot is in use by a process.
19 /* For kernel tasks, check only the kernel slot. Tasks do not have a
20 * PM/VFS process slot.
22 if (slot < NR_TASKS)
23 return (proc[slot].p_rts_flags != RTS_SLOT_FREE);
25 /* For regular processes, check only the PM slot. Do not check the
26 * kernel slot, because that would skip zombie processes. The PID check
27 * should be redundant, but if it fails, procfs could crash.
29 return ((mproc[slot - NR_TASKS].mp_flags & IN_USE) &&
30 mproc[slot - NR_TASKS].mp_pid != 0);
33 /*===========================================================================*
34 * check_owner *
35 *===========================================================================*/
36 static int check_owner(struct inode *node, int slot)
38 /* Check if the owner user and group ID of the inode are still in sync
39 * the current effective user and group ID of the given process.
41 struct inode_stat stat;
43 if (slot < NR_TASKS) return TRUE;
45 get_inode_stat(node, &stat);
47 return (stat.uid == mproc[slot - NR_TASKS].mp_effuid &&
48 stat.gid == mproc[slot - NR_TASKS].mp_effgid);
51 /*===========================================================================*
52 * make_stat *
53 *===========================================================================*/
54 static void make_stat(struct inode_stat *stat, int slot, int index)
56 /* Fill in an inode_stat structure for the given process slot and
57 * per-pid file index (or NO_INDEX for the process subdirectory root).
60 if (index == NO_INDEX)
61 stat->mode = DIR_ALL_MODE;
62 else
63 stat->mode = pid_files[index].mode;
65 if (slot < NR_TASKS) {
66 stat->uid = SUPER_USER;
67 stat->gid = SUPER_USER;
68 } else {
69 stat->uid = mproc[slot - NR_TASKS].mp_effuid;
70 stat->gid = mproc[slot - NR_TASKS].mp_effgid;
73 stat->size = 0;
74 stat->dev = NO_DEV;
77 /*===========================================================================*
78 * dir_is_pid *
79 *===========================================================================*/
80 static int dir_is_pid(struct inode *node)
82 /* Return whether the given node is a PID directory.
85 return (get_parent_inode(node) == get_root_inode() &&
86 get_inode_index(node) != NO_INDEX);
89 /*===========================================================================*
90 * update_proc_table *
91 *===========================================================================*/
92 static int update_proc_table(void)
94 /* Get the process table from the kernel.
95 * Check the magic number in the table entries.
97 int r, slot;
99 if ((r = sys_getproctab(proc)) != OK) return r;
101 for (slot = 0; slot < NR_PROCS + NR_TASKS; slot++) {
102 if (proc[slot].p_magic != PMAGIC) {
103 printf("PROCFS: system version mismatch!\n");
105 return EINVAL;
109 return OK;
112 /*===========================================================================*
113 * update_mproc_table *
114 *===========================================================================*/
115 static int update_mproc_table(void)
117 /* Get the process table from PM.
118 * Check the magic number in the table entries.
120 int r, slot;
122 r = getsysinfo(PM_PROC_NR, SI_PROC_TAB, mproc, sizeof(mproc));
123 if (r != OK) return r;
125 for (slot = 0; slot < NR_PROCS; slot++) {
126 if (mproc[slot].mp_magic != MP_MAGIC) {
127 printf("PROCFS: PM version mismatch!\n");
129 return EINVAL;
133 return OK;
136 /*===========================================================================*
137 * update_fproc_table *
138 *===========================================================================*/
139 static int update_fproc_table(void)
141 /* Get the process table from VFS.
144 return getsysinfo(VFS_PROC_NR, SI_PROC_TAB, fproc, sizeof(fproc));
147 /*===========================================================================*
148 * update_tables *
149 *===========================================================================*/
150 static int update_tables(void)
152 /* Get the process tables from the kernel, PM, and VFS.
154 int r;
156 if ((r = update_proc_table()) != OK) return r;
158 if ((r = update_mproc_table()) != OK) return r;
160 if ((r = update_fproc_table()) != OK) return r;
162 return OK;
165 /*===========================================================================*
166 * init_tree *
167 *===========================================================================*/
168 int init_tree(void)
170 /* Initialize this module, before VTreeFS is started. As part of the
171 * process, check if we're not compiled against a kernel different from
172 * the one that is running at the moment.
174 int i, r;
176 if ((r = update_tables()) != OK)
177 return r;
179 /* Get the maximum number of entries that we may add to each PID's
180 * directory. We could just default to a large value, but why not get
181 * it right?
183 for (i = 0; pid_files[i].name != NULL; i++);
185 nr_pid_entries = i;
187 return OK;
190 /*===========================================================================*
191 * out_of_inodes *
192 *===========================================================================*/
193 static void out_of_inodes(void)
195 /* Out of inodes - the NR_INODES value is set too low. We can not do
196 * much, but we might be able to continue with degraded functionality,
197 * so do not panic. If the NR_INODES value is not below the *crucial*
198 * minimum, the symptom of this case will be an incomplete listing of
199 * the main proc directory.
201 static int warned = FALSE;
203 if (warned == FALSE) {
204 printf("PROCFS: out of inodes!\n");
206 warned = TRUE;
210 /*===========================================================================*
211 * construct_pid_dirs *
212 *===========================================================================*/
213 static void construct_pid_dirs(void)
215 /* Regenerate the set of PID directories in the root directory of the
216 * file system. Add new directories and delete old directories as
217 * appropriate; leave unchanged those that should remain the same.
219 * We have to make two passes. Otherwise, we would trigger a vtreefs
220 * assert when we add an entry for a PID before deleting the previous
221 * entry for that PID. While rare, such rapid PID reuse does occur in
222 * practice.
224 struct inode *root, *node;
225 struct inode_stat stat;
226 char name[PNAME_MAX+1];
227 pid_t pid;
228 int i;
230 root = get_root_inode();
232 /* First pass: delete old entries. */
233 for (i = 0; i < NR_PROCS + NR_TASKS; i++) {
234 /* Do we already have an inode associated with this slot? */
235 node = get_inode_by_index(root, i);
236 if (node == NULL)
237 continue;
239 /* If the process slot is not in use, delete the associated
240 * inode.
242 if (!slot_in_use(i)) {
243 delete_inode(node);
245 continue;
248 /* Otherwise, get the process ID. */
249 if (i < NR_TASKS)
250 pid = (pid_t) (i - NR_TASKS);
251 else
252 pid = mproc[i - NR_TASKS].mp_pid;
254 /* If there is an old entry, see if the pid matches the current
255 * entry, and the owner is still the same. Otherwise, delete
256 * the old entry first. We reconstruct the entire subtree even
257 * if only the owner changed, for security reasons: if a
258 * process could keep open a file or directory across the owner
259 * change, it might be able to access information it shouldn't.
261 if (pid != (pid_t) get_inode_cbdata(node) ||
262 !check_owner(node, i))
263 delete_inode(node);
266 /* Second pass: add new entries. */
267 for (i = 0; i < NR_PROCS + NR_TASKS; i++) {
268 /* If the process slot is not in use, skip this slot. */
269 if (!slot_in_use(i))
270 continue;
272 /* If we have an inode associated with this slot, we have
273 * already checked it to be up-to-date above.
275 if (get_inode_by_index(root, i) != NULL)
276 continue;
278 /* Get the process ID. */
279 if (i < NR_TASKS)
280 pid = (pid_t) (i - NR_TASKS);
281 else
282 pid = mproc[i - NR_TASKS].mp_pid;
284 /* Add the entry for the process slot. */
285 snprintf(name, PNAME_MAX + 1, "%d", pid);
287 make_stat(&stat, i, NO_INDEX);
289 node = add_inode(root, name, i, &stat, nr_pid_entries,
290 (cbdata_t) pid);
292 if (node == NULL)
293 out_of_inodes();
297 /*===========================================================================*
298 * make_one_pid_entry *
299 *===========================================================================*/
300 static void make_one_pid_entry(struct inode *parent, char *name, int slot)
302 /* Construct one file in a PID directory, if a file with the given name
303 * should exist at all.
305 struct inode *node;
306 struct inode_stat stat;
307 int i;
309 /* Don't readd if it is already there. */
310 node = get_inode_by_name(parent, name);
311 if (node != NULL)
312 return;
314 /* Only add the file if it is a known, registered name. */
315 for (i = 0; pid_files[i].name != NULL; i++) {
316 if (!strcmp(name, pid_files[i].name)) {
317 make_stat(&stat, slot, i);
319 node = add_inode(parent, name, i, &stat,
320 (index_t) 0, (cbdata_t) 0);
322 if (node == NULL)
323 out_of_inodes();
325 break;
330 /*===========================================================================*
331 * make_all_pid_entries *
332 *===========================================================================*/
333 static void make_all_pid_entries(struct inode *parent, int slot)
335 /* Construct all files in a PID directory.
337 struct inode *node;
338 struct inode_stat stat;
339 int i;
341 for (i = 0; pid_files[i].name != NULL; i++) {
342 node = get_inode_by_index(parent, i);
343 if (node != NULL)
344 continue;
346 make_stat(&stat, slot, i);
348 node = add_inode(parent, pid_files[i].name, i, &stat,
349 (index_t) 0, (cbdata_t) 0);
351 if (node == NULL)
352 out_of_inodes();
356 /*===========================================================================*
357 * construct_pid_entries *
358 *===========================================================================*/
359 static void construct_pid_entries(struct inode *parent, char *name)
361 /* Construct one requested file entry, or all file entries, in a PID
362 * directory.
364 int slot;
366 slot = get_inode_index(parent);
367 assert(slot >= 0 && slot < NR_TASKS + NR_PROCS);
369 /* If this process is already gone, delete the directory now. */
370 if (!slot_in_use(slot)) {
371 delete_inode(parent);
373 return;
376 /* If a specific file name is being looked up, see if we have to add
377 * an inode for that file. If the directory contents are being
378 * retrieved, add all files that have not yet been added.
380 if (name != NULL)
381 make_one_pid_entry(parent, name, slot);
382 else
383 make_all_pid_entries(parent, slot);
386 /*===========================================================================*
387 * pid_read *
388 *===========================================================================*/
389 static void pid_read(struct inode *node)
391 /* Data is requested from one of the files in a PID directory. Call the
392 * function that is responsible for generating the data for that file.
394 struct inode *parent;
395 int slot, index;
397 /* Get the slot number of the process. Note that this currently will
398 * not work for files not in the top-level pid subdirectory.
400 parent = get_parent_inode(node);
402 slot = get_inode_index(parent);
404 /* Get this file's index number. */
405 index = get_inode_index(node);
407 /* Call the handler procedure for the file. */
408 ((void (*) (int)) pid_files[index].data)(slot);
411 /*===========================================================================*
412 * pid_link *
413 *===========================================================================*/
414 static int pid_link(struct inode *UNUSED(node), char *ptr, int max)
416 /* The contents of a symbolic link in a PID directory are requested.
417 * This function is a placeholder for future use.
420 /* Nothing yet. */
421 strlcpy(ptr, "", max);
423 return OK;
426 /*===========================================================================*
427 * lookup_hook *
428 *===========================================================================*/
429 int lookup_hook(struct inode *parent, char *name,
430 cbdata_t UNUSED(cbdata))
432 /* Path name resolution hook, for a specific parent and name pair.
433 * If needed, update our own view of the system first; after that,
434 * determine whether we need to (re)generate certain files.
436 static clock_t last_update = 0;
437 clock_t now;
438 int r;
440 /* Update lazily for lookups, as this gets too expensive otherwise.
441 * Alternative: pull in only PM's table?
443 if ((r = getuptime(&now)) != OK)
444 panic(__FILE__, "unable to get uptime", r);
446 if (last_update != now) {
447 update_tables();
449 last_update = now;
452 /* If the parent is the root directory, we must now reconstruct all
453 * entries, because some of them might have been garbage collected.
454 * We must update the entire tree at once; if we update individual
455 * entries, we risk name collisions.
457 if (parent == get_root_inode()) {
458 construct_pid_dirs();
460 /* If the parent is a process directory, we may need to (re)construct
461 * the entry being looked up.
463 else if (dir_is_pid(parent)) {
464 /* We might now have deleted our current containing directory;
465 * construct_pid_entries() will take care of this case.
467 construct_pid_entries(parent, name);
470 return OK;
473 /*===========================================================================*
474 * getdents_hook *
475 *===========================================================================*/
476 int getdents_hook(struct inode *node, cbdata_t UNUSED(cbdata))
478 /* Directory entry retrieval hook, for potentially all files in a
479 * directory. Make sure that all files that are supposed to be
480 * returned, are actually part of the virtual tree.
483 if (node == get_root_inode()) {
484 update_tables();
486 construct_pid_dirs();
487 } else if (dir_is_pid(node)) {
488 construct_pid_entries(node, NULL /*name*/);
491 return OK;
494 /*===========================================================================*
495 * read_hook *
496 *===========================================================================*/
497 int read_hook(struct inode *node, off_t off, char **ptr,
498 size_t *len, cbdata_t cbdata)
500 /* Regular file read hook. Call the appropriate callback function to
501 * generate and return the data.
504 buf_init(off, *len);
506 /* Populate the buffer with the proper content. */
507 if (get_inode_index(node) != NO_INDEX) {
508 pid_read(node);
509 } else {
510 ((void (*) (void)) cbdata)();
513 *len = buf_get(ptr);
515 return OK;
518 /*===========================================================================*
519 * rdlink_hook *
520 *===========================================================================*/
521 int rdlink_hook(struct inode *node, char *ptr, size_t max,
522 cbdata_t UNUSED(cbdata))
524 /* Symbolic link resolution hook. Not used yet.
526 struct inode *parent;
528 /* Get the parent inode. */
529 parent = get_parent_inode(node);
531 /* If the parent inode is a pid directory, call the pid handler.
533 if (parent != NULL && dir_is_pid(parent))
534 pid_link(node, ptr, max);
536 return OK;