1 /*
2 * Copyright (c) 2006-2023, RT-Thread Development Team
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 *
6 * Change Logs:
7 * Date Author Notes
8 */
9
10 #include "proc.h"
11 #include "procfs.h"
12
13 #include <rthw.h>
14 #include <rtdbg.h>
15
16 #include <fcntl.h>
17 #include <errno.h>
18
19 /*
20 * This is the root in the proc tree..
21 */
22 static struct proc_dentry _proc_root = {
23 .mode = S_IFDIR | (S_IRUSR | S_IRGRP | S_IROTH) | (S_IXUSR | S_IXGRP | S_IXOTH),
24 .ref_count = 1,
25
26 .parent = &_proc_root,
27 .node.sibling = RT_LIST_OBJECT_INIT(_proc_root.node.sibling),
28 .node.subnode = RT_LIST_OBJECT_INIT(_proc_root.node.subnode),
29
30 .fops = RT_NULL,
31
32 .name = "/",
33 .data = RT_NULL,
34 };
35
_proc_find(struct proc_dentry ** parent,const char * name)36 static int _proc_find(struct proc_dentry **parent, const char *name)
37 {
38 struct proc_dentry *dentry = RT_NULL, *tmp;
39
40 dfs_vfs_for_each_subnode(dentry, tmp, (*parent), node)
41 {
42 if (dentry == RT_NULL)
43 {
44 break;
45 }
46
47 if (rt_strcmp(dentry->name, name) == 0)
48 {
49 *parent = dentry;
50 return 0;
51 }
52 }
53
54 return -1;
55 }
56
proc_find(struct proc_dentry ** parent,const char ** name,rt_bool_t force_lookup)57 static int proc_find(struct proc_dentry **parent, const char **name, rt_bool_t force_lookup)
58 {
59 int ret = 0;
60 char *tmp = RT_NULL;
61
62 if (!(*parent))
63 {
64 *parent = &_proc_root;
65 }
66
67 tmp = rt_strdup(*name);
68 if (tmp)
69 {
70 char *begin = tmp, *end = RT_NULL;
71 if (*begin == '/')
72 {
73 begin++;
74 if (*begin == '\0')
75 {
76 rt_free(tmp);
77 *parent = proc_acquire(*parent);
78 return ret;
79 }
80 }
81
82 while (1)
83 {
84 end = rt_strstr(begin, "/");
85 if (end)
86 {
87 *end = '\0';
88 ret = _proc_find(parent, begin);
89 if (ret < 0 || !S_ISDIR((*parent)->mode))
90 {
91 *parent = RT_NULL;
92 ret = -1;
93 break;
94 }
95 begin = end + 1;
96 }
97 else if (force_lookup)
98 {
99 ret = _proc_find(parent, begin);
100 if (ret < 0)
101 {
102 if ((*parent)->ops && (*parent)->ops->lookup)
103 {
104 *parent = (*parent)->ops->lookup(*parent, begin);
105 if (*parent == RT_NULL)
106 {
107 ret = -1;
108 }
109 }
110 else
111 {
112 *parent = RT_NULL;
113 }
114 }
115 else
116 {
117 *parent = proc_acquire(*parent);
118 }
119 break;
120 }
121 else
122 {
123 *parent = proc_acquire(*parent);
124 break;
125 }
126 }
127
128 *name = *name + (begin - tmp);
129
130 rt_free(tmp);
131 }
132
133 return ret;
134 }
135
single_start(struct dfs_seq_file * seq,off_t * index)136 static void *single_start(struct dfs_seq_file *seq, off_t *index)
137 {
138 return NULL + (*index == 0);
139 }
140
single_next(struct dfs_seq_file * seq,void * data,off_t * index)141 static void *single_next(struct dfs_seq_file *seq, void *data, off_t *index)
142 {
143 ++*index;
144 return NULL;
145 }
146
single_stop(struct dfs_seq_file * seq,void * data)147 static void single_stop(struct dfs_seq_file *seq, void *data)
148 {
149 }
150
proc_open(struct dfs_file * file)151 static int proc_open(struct dfs_file *file)
152 {
153 struct proc_dentry *entry = (struct proc_dentry *)file->vnode->data;
154
155 if (entry->single_show)
156 {
157 struct dfs_seq_ops *seq_ops = (struct dfs_seq_ops *)rt_calloc(1, sizeof(struct dfs_seq_ops));
158 if (seq_ops)
159 {
160 int ret = 0;
161
162 seq_ops->start = single_start;
163 seq_ops->next = single_next;
164 seq_ops->stop = single_stop;
165 seq_ops->show = entry->single_show;
166
167 ret = dfs_seq_open(file, seq_ops);
168 if (ret != 0)
169 {
170 rt_free(seq_ops);
171 }
172 return ret;
173 }
174 }
175
176 return dfs_seq_open(file, entry->seq_ops);
177 }
178
proc_close(struct dfs_file * file)179 static int proc_close(struct dfs_file *file)
180 {
181 struct dfs_seq_file *seq = file->data;
182 struct proc_dentry *entry = (struct proc_dentry *)file->vnode->data;
183
184 if (seq && entry->single_show && seq->ops)
185 {
186 rt_free((void *)seq->ops);
187 seq->ops = RT_NULL;
188 }
189
190 return dfs_seq_release(file);
191 }
192
193 static const struct dfs_file_ops proc_file_ops = {
194 .open = proc_open,
195 .read = dfs_seq_read,
196 .lseek = dfs_seq_lseek,
197 .close = proc_close,
198 };
199
proc_create(struct proc_dentry ** parent,const char * name,mode_t mode)200 static struct proc_dentry *proc_create(struct proc_dentry **parent, const char *name, mode_t mode)
201 {
202 int ret = 0;
203 struct proc_dentry *dentry = RT_NULL;
204
205 ret = proc_find(parent, &name, 0);
206 if (ret >= 0)
207 {
208 dentry = *parent;
209 ret = proc_find(&dentry, &name, 1);
210 if (ret < 0)
211 {
212 dentry = rt_calloc(1, sizeof(struct proc_dentry));
213 if (dentry)
214 {
215 dentry->mode = mode;
216 dentry->ref_count = 1;
217 dentry->name = rt_strdup(name);
218 dfs_vfs_init_node(&dentry->node);
219 }
220 }
221 else
222 {
223 proc_release(dentry);
224 dentry = RT_NULL;
225 }
226 }
227
228 return dentry;
229 }
230
231 /**
232 * @brief The dentry reference count is incremented by one
233 *
234 * @param dentry
235 *
236 * @return dentry
237 */
proc_acquire(struct proc_dentry * dentry)238 struct proc_dentry *proc_acquire(struct proc_dentry *dentry)
239 {
240 if (dentry)
241 {
242 dentry->ref_count += 1;
243 }
244
245 return dentry;
246 }
247
248 /**
249 * @brief The dentry reference count is minus one, or release
250 *
251 * @param dentry
252 *
253 * @return none
254 */
proc_release(struct proc_dentry * dentry)255 void proc_release(struct proc_dentry *dentry)
256 {
257 if (dentry)
258 {
259 if (dentry->ref_count == 1)
260 {
261 if (dentry->name)
262 {
263 rt_free(dentry->name);
264 }
265
266 if (S_ISLNK(dentry->mode) && dentry->data)
267 {
268 rt_free(dentry->data);
269 }
270
271 rt_free(dentry);
272 }
273 else
274 {
275 dentry->ref_count -= 1;
276 }
277 }
278 }
279
proc_register(struct proc_dentry * parent,struct proc_dentry * child)280 static struct proc_dentry *proc_register(struct proc_dentry *parent, struct proc_dentry *child)
281 {
282 child->parent = parent;
283 dfs_vfs_append_node(&parent->node, &child->node);
284 child->ref_count += 1;
285 child->pid = parent->pid;
286
287 return child;
288 }
289
290 /**
291 * @brief Make a dir
292 *
293 * @param name fullpath based on _proc_root or parent
294 * @param mode permission configuration
295 * @param parent can be empty
296 * @param fops
297 * @param data
298 *
299 * @return dentry
300 */
proc_mkdir_data(const char * name,mode_t mode,struct proc_dentry * parent,const struct dfs_file_ops * fops,void * data)301 struct proc_dentry *proc_mkdir_data(const char *name, mode_t mode, struct proc_dentry *parent,
302 const struct dfs_file_ops *fops, void *data)
303 {
304 struct proc_dentry *dentry, *_parent = parent;
305
306 if (mode == 0)
307 mode = (S_IRUSR | S_IRGRP | S_IROTH) | (S_IXUSR | S_IXGRP | S_IXOTH);
308
309 dentry = proc_create(&_parent, name, S_IFDIR | mode);
310 if (dentry)
311 {
312 dentry->fops = fops;
313 dentry->data = data;
314
315 dentry = proc_register(_parent, dentry);
316 }
317 proc_release(_parent);
318
319 return dentry;
320 }
321
322 /**
323 * @brief Make a dir
324 *
325 * @param name fullpath based on _proc_root or parent
326 * @param mode permission configuration
327 * @param parent can be empty
328 *
329 * @return dentry
330 */
proc_mkdir_mode(const char * name,mode_t mode,struct proc_dentry * parent)331 struct proc_dentry *proc_mkdir_mode(const char *name, mode_t mode, struct proc_dentry *parent)
332 {
333 return proc_mkdir_data(name, mode, parent, NULL, NULL);
334 }
335
336 /**
337 * @brief Make a dir
338 *
339 * @param name fullpath based on _proc_root or parent
340 * @param parent can be empty
341 *
342 * @return dentry
343 */
proc_mkdir(const char * name,struct proc_dentry * parent)344 struct proc_dentry *proc_mkdir(const char *name, struct proc_dentry *parent)
345 {
346 return proc_mkdir_data(name, 0, parent, NULL, NULL);
347 }
348
proc_create_reg(const char * name,mode_t mode,struct proc_dentry ** parent)349 static struct proc_dentry *proc_create_reg(const char *name, mode_t mode, struct proc_dentry **parent)
350 {
351 struct proc_dentry *dentry = RT_NULL;
352
353 if ((mode & S_IFMT) == 0)
354 mode |= S_IFREG;
355 if ((mode & (S_ISUID | S_ISGID | S_ISVTX | S_IRWXU | S_IRWXG | S_IRWXO)) == 0)
356 mode |= S_IRUSR | S_IRGRP | S_IROTH;
357
358 if (!S_ISREG(mode))
359 {
360 *parent = RT_NULL;
361 return dentry;
362 }
363
364 return proc_create(parent, name, mode);
365 }
366
367 /**
368 * @brief Make a file
369 *
370 * @param name fullpath based on _proc_root or parent
371 * @param mode permission configuration
372 * @param parent can be empty
373 * @param fops
374 * @param data
375 *
376 * @return dentry
377 */
proc_create_data(const char * name,mode_t mode,struct proc_dentry * parent,const struct dfs_file_ops * fops,void * data)378 struct proc_dentry *proc_create_data(const char *name, mode_t mode, struct proc_dentry *parent,
379 const struct dfs_file_ops *fops, void *data)
380 {
381 struct proc_dentry *dentry, *_parent = parent;
382
383 dentry = proc_create_reg(name, mode, &_parent);
384 if (dentry)
385 {
386 dentry->fops = fops ? fops : &proc_file_ops;
387 dentry->data = data;
388
389 dentry = proc_register(_parent, dentry);
390 }
391 proc_release(_parent);
392
393 return dentry;
394 }
395
396 /**
397 * @brief Make a file
398 *
399 * @param name fullpath based on _proc_root or parent
400 * @param mode permission configuration
401 * @param parent can be empty
402 * @param show
403 * @param data
404 *
405 * @return dentry
406 */
proc_create_single_data(const char * name,mode_t mode,struct proc_dentry * parent,int (* show)(struct dfs_seq_file *,void *),void * data)407 struct proc_dentry *proc_create_single_data(const char *name, mode_t mode, struct proc_dentry *parent,
408 int (*show)(struct dfs_seq_file *, void *), void *data)
409 {
410 struct proc_dentry *dentry, *_parent = parent;
411
412 dentry = proc_create_reg(name, mode, &_parent);
413 if (dentry)
414 {
415 dentry->fops = &proc_file_ops;
416 dentry->single_show = show;
417 dentry->data = data;
418
419 dentry = proc_register(_parent, dentry);
420 }
421 proc_release(_parent);
422
423 return dentry;
424 }
425
426 /**
427 * @brief Make a symlink
428 *
429 * @param name fullpath based on _proc_root or parent
430 * @param parent can be empty
431 * @param dest link file fullpath
432 *
433 * @return dentry
434 */
proc_symlink(const char * name,struct proc_dentry * parent,const char * dest)435 struct proc_dentry *proc_symlink(const char *name, struct proc_dentry *parent, const char *dest)
436 {
437 struct proc_dentry *dentry, *_parent = parent;
438
439 dentry = proc_create(&_parent, name, (S_IFLNK | (S_IRUSR | S_IRGRP | S_IROTH)
440 | (S_IWUSR | S_IWGRP | S_IWOTH) | (S_IXUSR | S_IXGRP | S_IXOTH)));
441 if (dentry)
442 {
443 dentry->data = (void *)rt_strdup(dest);
444 if (dentry->data)
445 {
446 dentry = proc_register(_parent, dentry);
447 }
448 else
449 {
450 proc_release(dentry);
451 dentry = NULL;
452 }
453 }
454 proc_release(_parent);
455
456 return dentry;
457 }
458
remove_proc_subtree(struct proc_dentry * dentry)459 static void remove_proc_subtree(struct proc_dentry *dentry)
460 {
461 struct proc_dentry *iter = RT_NULL, *iter_tmp, *tmp = RT_NULL;
462
463 dfs_vfs_for_each_subnode(iter, iter_tmp, dentry, node)
464 {
465 if (iter == RT_NULL)
466 {
467 break;
468 }
469
470 if (tmp)
471 {
472 proc_release(tmp);
473 tmp = RT_NULL;
474 }
475
476 tmp = iter;
477
478 if (S_ISDIR(dentry->mode))
479 {
480 remove_proc_subtree(iter);
481 }
482 }
483
484 if (tmp)
485 {
486 proc_release(tmp);
487 tmp = RT_NULL;
488 }
489 }
490
491 /**
492 * @brief remove a dentry
493 *
494 * @param dentry
495 *
496 * @return none
497 */
proc_remove(struct proc_dentry * dentry)498 void proc_remove(struct proc_dentry *dentry)
499 {
500 if (dentry && dentry != &_proc_root)
501 {
502 if (S_ISDIR(dentry->mode))
503 {
504 remove_proc_subtree(dentry);
505 }
506
507 dfs_vfs_remove_node(&dentry->node);
508 proc_release(dentry);
509 }
510 }
511
512 /**
513 * @brief find dentry exist
514 *
515 * @param name fullpath based on _proc_root
516 *
517 * @return dentry
518 */
dfs_proc_find(const char * name)519 struct proc_dentry *dfs_proc_find(const char *name)
520 {
521 struct proc_dentry *dentry = RT_NULL;
522
523 proc_find(&dentry, &name, 1);
524
525 return dentry;
526 }
527
528 /**
529 * @brief remove a dentry on parent
530 *
531 * @param name fullpath based on parent
532 * @param parent
533 *
534 * @return none
535 */
proc_remove_dentry(const char * name,struct proc_dentry * parent)536 void proc_remove_dentry(const char *name, struct proc_dentry *parent)
537 {
538 struct proc_dentry *dentry = parent;
539
540 if (proc_find(&dentry, &name, 1) >= 0)
541 {
542 proc_remove(dentry);
543 proc_release(dentry);
544 }
545 }
546
547 #define _COLOR_RED "\033[31m"
548 #define _COLOR_GREEN "\033[32m"
549 #define _COLOR_BLUE "\033[34m"
550 #define _COLOR_CYAN "\033[36m"
551 #define _COLOR_WHITE "\033[37m"
552 #define _COLOR_NORMAL "\033[0m"
553
dump_proc_subtree(struct proc_dentry * dentry,int tab)554 static void dump_proc_subtree(struct proc_dentry *dentry, int tab)
555 {
556 struct proc_dentry *iter = RT_NULL, *tmp;
557
558 dfs_vfs_for_each_subnode(iter, tmp, dentry, node)
559 {
560 if (iter == RT_NULL)
561 {
562 break;
563 }
564
565 for(int i = 0; i < tab; i ++)
566 {
567 rt_kprintf("%-4s", i + 1 >= tab ? "|-" : " ");
568 }
569
570 if (S_ISDIR(iter->mode))
571 {
572 rt_kprintf(_COLOR_BLUE "%-20s" _COLOR_NORMAL " %d\n", iter->name, iter->ref_count);
573 dump_proc_subtree(iter, tab + 1);
574 }
575 else if (S_ISLNK(iter->mode))
576 {
577 rt_kprintf(_COLOR_CYAN "%-20s" _COLOR_NORMAL " %d\n", iter->name, iter->ref_count);
578 }
579 else
580 {
581 rt_kprintf("%-20s %d\n", iter->name, iter->ref_count);
582 }
583 }
584 }
585
proc_dump(struct proc_dentry * dentry)586 static void proc_dump(struct proc_dentry *dentry)
587 {
588 if (dentry)
589 {
590 if (S_ISDIR(dentry->mode))
591 {
592 rt_kprintf(_COLOR_BLUE "%-20s" _COLOR_NORMAL " %d\n", dentry->name, dentry->ref_count);
593 dump_proc_subtree(dentry, 1);
594 }
595 else if (S_ISLNK(dentry->mode))
596 {
597 rt_kprintf(_COLOR_CYAN "%-20s" _COLOR_NORMAL " %d\n", dentry->name, dentry->ref_count);
598 }
599 else
600 {
601 rt_kprintf("%-20s %d\n", dentry->name, dentry->ref_count);
602 }
603 }
604 }
605
msh_proc_dump(int argc,char ** argv)606 static int msh_proc_dump(int argc, char** argv)
607 {
608 const char *name = argc > 1 ? argv[1] : "/";
609 struct proc_dentry *dentry = RT_NULL;
610
611 int ret = proc_find(&dentry, &name, 1);
612 if (ret >= 0)
613 {
614 proc_dump(dentry);
615 }
616 proc_release(dentry);
617
618 return 0;
619 }
620 MSH_CMD_EXPORT_ALIAS(msh_proc_dump, proc_dump, proc dump);
621
msh_proc_remove(int argc,char ** argv)622 static int msh_proc_remove(int argc, char** argv)
623 {
624 if (argc > 1)
625 {
626 const char *name = argv[1];
627 struct proc_dentry *dentry = RT_NULL;
628
629 int ret = proc_find(&dentry, &name, 1);
630 if (ret >= 0)
631 {
632 if (dentry != &_proc_root)
633 {
634 proc_remove(dentry);
635 }
636 else
637 {
638 struct proc_dentry *iter = RT_NULL, *iter_tmp, *tmp = RT_NULL;
639
640 dfs_vfs_for_each_subnode(iter, iter_tmp, dentry, node)
641 {
642 if (iter == RT_NULL)
643 {
644 break;
645 }
646
647 if (tmp)
648 {
649 proc_remove(tmp);
650 }
651
652 tmp = iter;
653 }
654
655 if (tmp)
656 {
657 proc_remove(tmp);
658 }
659 }
660 }
661 proc_release(dentry);
662 }
663 else
664 {
665 rt_kprintf("proc_remove path\n");
666 }
667
668 return 0;
669 }
670 MSH_CMD_EXPORT_ALIAS(msh_proc_remove, proc_remove, proc remove);
671
msh_proc_symlink(int argc,char ** argv)672 static int msh_proc_symlink(int argc, char** argv)
673 {
674 if (argc > 2)
675 {
676 struct proc_dentry *entry = proc_symlink(argv[1], 0, argv[2]);
677 if (entry)
678 {
679 proc_release(entry);
680 }
681 }
682 else
683 {
684 rt_kprintf("proc_symlink path dest\n");
685 }
686
687 return 0;
688 }
689 MSH_CMD_EXPORT_ALIAS(msh_proc_symlink, proc_symlink, proc symlink);
690
msh_proc_echo(int argc,char ** argv)691 static int msh_proc_echo(int argc, char** argv)
692 {
693 if (argc > 1)
694 {
695 for(int i = 1; i <= argc - 1; i ++)
696 {
697 struct proc_dentry *entry = proc_create_data(argv[i], 0, 0, 0, 0);
698 if (entry)
699 {
700 proc_release(entry);
701 }
702 }
703 }
704 else
705 {
706 rt_kprintf("proc_echo path\n");
707 }
708
709 return 0;
710 }
711 MSH_CMD_EXPORT_ALIAS(msh_proc_echo, proc_echo, proc echo);
712
msh_proc_mkdir(int argc,char ** argv)713 static int msh_proc_mkdir(int argc, char** argv)
714 {
715 if (argc > 1)
716 {
717 for(int i = 1; i <= argc - 1; i ++)
718 {
719 struct proc_dentry *entry = proc_mkdir(argv[i], 0);
720 if (entry)
721 {
722 proc_release(entry);
723 }
724 }
725 }
726 else
727 {
728 rt_kprintf("proc_mkdir path\n");
729 }
730
731 return 0;
732 }
733 MSH_CMD_EXPORT_ALIAS(msh_proc_mkdir, proc_mkdir, proc mkdir);
734