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 #define __RT_IPC_SOURCE__
10 
11 #include "proc.h"
12 #include "procfs.h"
13 
14 #include <rthw.h>
15 #include <rtdbg.h>
16 
17 #include <fcntl.h>
18 #include <errno.h>
19 
20 #include "lwp_internal.h"
21 #include <dfs_dentry.h>
22 #include "lwp_internal.h"
23 
24 #if defined(RT_USING_SMART)
25 
26 #include "lwp.h"
27 #include "lwp_pid.h"
28 #include <lwp_user_mm.h>
29 
30 struct pid_dentry
31 {
32     const char *name;
33     mode_t mode;
34     const struct dfs_file_ops *fops;
35     const struct proc_ops *ops;
36     const struct dfs_seq_ops *seq_ops;
37     int (*single_show)(struct dfs_seq_file *seq, void *data);
38     void *data;
39 };
40 
stat_transform(int __stat)41 static char stat_transform(int __stat)
42 {
43     switch (__stat)
44     {
45     case RT_THREAD_RUNNING:
46         return 'R';
47     default:
48         return 'T';
49     }
50 }
51 
stat_single_show(struct dfs_seq_file * seq,void * data)52 static int stat_single_show(struct dfs_seq_file *seq, void *data)
53 {
54     struct proc_dentry *dentry = (struct proc_dentry *)seq->file->vnode->data;
55     rt_list_t *list;
56     int mask = 0;
57     rt_thread_t thread;
58     rt_uint64_t user_time_lwp = 0;
59     rt_uint64_t system_time_lwp = 0;
60     int lwp_oncpu = RT_CPUS_NR;
61     int lwp_oncpu_ok = 0;
62     struct rt_lwp *lwp = RT_NULL;
63     char** argv = RT_NULL;
64     char *filename = RT_NULL;
65     char *dot = RT_NULL;
66 
67     lwp_pid_lock_take();
68 
69     lwp = lwp_from_pid_locked(dentry->pid);
70     argv = lwp_get_command_line_args(lwp);
71 
72     if (lwp)
73     {
74         dfs_seq_printf(seq,"%d ",dentry->pid);
75         if (argv)
76         {
77             filename = strrchr(argv[0], '/');
78             dot = strchr(argv[0], '.');
79 
80             if (filename != NULL)
81             {
82                 filename++;
83             }
84             else
85             {
86                 filename = argv[0];
87             }
88 
89             if (dot != NULL)
90             {
91                 *dot = '\0';
92             }
93 
94             if (filename != NULL)
95             {
96                 dfs_seq_printf(seq,"(%s) ", filename);
97             }
98             else
99             {
100                 dfs_seq_printf(seq,"(%s) ", argv[0]);
101             }
102 
103             lwp_free_command_line_args(argv);
104         }
105         else
106         {
107             dfs_seq_printf(seq,"(%s) ", "");
108         }
109 
110         if (lwp->terminated)
111         {
112             dfs_seq_printf(seq,"%c ",'Z');
113         }
114         else
115         {
116             list = lwp->t_grp.next;
117             while (list != &lwp->t_grp)
118             {
119                 thread = rt_list_entry(list, struct rt_thread, sibling);
120                 user_time_lwp = user_time_lwp + thread->user_time;
121                 system_time_lwp = system_time_lwp + thread->system_time;
122 
123                 #if RT_CPUS_NR > 1
124                     #define ONCPU(thread) RT_SCHED_CTX(thread).oncpu
125                 #else
126                     #define ONCPU(thread) 0
127                 #endif
128                 if (lwp_oncpu_ok == 0)
129                 {
130                     lwp_oncpu = ONCPU(thread);
131                     lwp_oncpu_ok = 1;
132                 }
133                 if (stat_transform(RT_SCHED_CTX(thread).stat) == 'R')
134                 {
135                     lwp_oncpu = ONCPU(thread);
136                     mask = 1;
137                 }
138                 list = list->next;
139             }
140 
141             if (mask == 1)
142             {
143                 dfs_seq_printf(seq,"%c ",'R');
144             }
145             else
146             {
147                 dfs_seq_printf(seq,"%c ",'S');
148             }
149         }
150         lwp_pid_lock_release();
151 
152         if (lwp->parent != NULL)
153             dfs_seq_printf(seq,"%d ",lwp->parent->pid);
154         else
155             dfs_seq_printf(seq,"0 ");
156 
157         dfs_seq_printf(seq, "1 1 0 -1 4194560 48245 133976064 732 425574 ");
158         dfs_seq_printf(seq,"%llu ",user_time_lwp);//utime
159         dfs_seq_printf(seq,"%llu ",system_time_lwp);//stime
160         dfs_seq_printf(seq, "1204291 518742 20 0 1 0 50 ");
161         dfs_seq_printf(seq, "%d ",rt_aspace_count_vsz(lwp->aspace));//VSZ
162         dfs_seq_printf(seq, "1422 18446744073709551615 ");
163         dfs_seq_printf(seq, "1 1 0 0 0 0 671173123 4096 1260 0 0 0 17 ");
164         dfs_seq_printf(seq, "%d ", lwp_oncpu);//CPU
165         dfs_seq_printf(seq, "0 0 0 0 0 0 0 0 0 0 0 0 0");
166         dfs_seq_printf(seq,"\n");
167     }
168     else
169     {
170         lwp_pid_lock_release();
171     }
172 
173     return 0;
174 }
175 
cmdline_single_show(struct dfs_seq_file * seq,void * data)176 static int cmdline_single_show(struct dfs_seq_file *seq, void *data)
177 {
178     struct proc_dentry *dentry = (struct proc_dentry *)seq->file->vnode->data;
179     struct rt_lwp *lwp;
180     char** argv;
181 
182     lwp_pid_lock_take();
183     lwp = lwp_from_pid_locked(dentry->pid);
184     argv = lwp_get_command_line_args(lwp);
185     lwp_pid_lock_release();
186 
187     if (argv)
188     {
189         for (int i = 0; argv[i] != NULL; i++)
190         {
191             dfs_seq_printf(seq, "%s ", argv[i]);
192         }
193         dfs_seq_puts(seq, "\n");
194 
195         lwp_free_command_line_args(argv);
196     }
197     else
198     {
199         dfs_seq_puts(seq, "error\n");
200     }
201 
202     return 0;
203 }
204 
proc_pid_fd_lookup(struct proc_dentry * parent,const char * name)205 struct proc_dentry *proc_pid_fd_lookup(struct proc_dentry *parent, const char *name)
206 {
207     struct proc_dentry *dentry = RT_NULL;
208     char num[DIRENT_NAME_MAX];
209     struct rt_lwp *lwp;
210     struct dfs_fdtable *table;
211 
212     lwp_pid_lock_take();
213     lwp = lwp_from_pid_locked(parent->pid);
214     table = lwp ? &lwp->fdt : RT_NULL;
215     lwp_pid_lock_release();
216 
217     if (!table)
218     {
219         return RT_NULL;
220     }
221 
222     dfs_file_lock();
223     for (int i = 0; i < table->maxfd; i++)
224     {
225         struct dfs_file *file = table->fds[i];
226         if (file)
227         {
228             rt_snprintf(num, DIRENT_NAME_MAX, "%d", i);
229             if (rt_strcmp(num, name) == 0)
230             {
231                 dentry = rt_calloc(1, sizeof(struct proc_dentry));
232                 if (dentry)
233                 {
234                     dentry->mode = (S_IFLNK | (S_IRUSR | S_IRGRP | S_IROTH) | (S_IWUSR | S_IWGRP | S_IWOTH) | (S_IXUSR | S_IXGRP | S_IXOTH));
235                     dentry->ref_count = 1;
236                     dentry->name = rt_strdup(name);
237                     dentry->data = (void *)dfs_dentry_full_path(file->dentry);
238 
239                     if (dentry->data == RT_NULL)
240                     {
241                         //todo add vnode->data
242                         if (file->vnode->type == FT_SOCKET)
243                             dentry->data = (void *)rt_strdup("socket");
244                         else if (file->vnode->type == FT_USER)
245                             dentry->data = (void *)rt_strdup("user");
246                         else if (file->vnode->type == FT_DEVICE)
247                             dentry->data = (void *)rt_strdup("device");
248                         else
249                             dentry->data = (void *)rt_strdup("unknown");
250                     }
251 
252                     dentry->pid = parent->pid;
253                     break;
254                 }
255             }
256         }
257     }
258     dfs_file_unlock();
259 
260     return dentry;
261 }
262 
proc_pid_fd_getdents(struct dfs_file * file,struct dirent * dirp,uint32_t count)263 int proc_pid_fd_getdents(struct dfs_file *file, struct dirent *dirp, uint32_t count)
264 {
265     int ret = 0, index = 0;
266     struct proc_dentry *entry = (struct proc_dentry *)file->vnode->data;
267     struct rt_lwp *lwp;
268     struct dfs_fdtable *table;
269 
270     lwp_pid_lock_take();
271     lwp = lwp_from_pid_locked(entry->pid);
272     LWP_LOCK(lwp);
273     table = lwp ? &lwp->fdt : RT_NULL;
274 
275     if (!table->fds)
276     {
277         LWP_UNLOCK(lwp);
278         lwp_pid_lock_release();
279         return 0;
280     }
281 
282     count = (count / sizeof(struct dirent));
283     if (count == 0)
284     {
285         LWP_UNLOCK(lwp);
286         lwp_pid_lock_release();
287         return -EINVAL;
288     }
289 
290     dfs_file_lock();
291     for (int i = 0; i < table->maxfd; i++)
292     {
293         struct dfs_file *df = table->fds[i];
294         if (df)
295         {
296             if (index >= file->fpos)
297             {
298                 struct dirent *d = dirp + index - file->fpos;
299 
300                 d->d_type = DT_SYMLINK;
301                 d->d_reclen = (rt_uint16_t)sizeof(struct dirent);
302                 rt_snprintf(d->d_name, DIRENT_NAME_MAX, "%d", i);
303                 d->d_namlen = rt_strlen(d->d_name);
304 
305                 ret++;
306             }
307 
308             index++;
309             if (index - file->fpos >= count)
310             {
311                 break;
312             }
313         }
314     }
315     dfs_file_unlock();
316     LWP_UNLOCK(lwp);
317     lwp_pid_lock_release();
318 
319     if (ret > 0)
320     {
321         file->fpos = index;
322         ret = ret * sizeof(struct dirent);
323     }
324 
325     return ret;
326 }
327 
328 static const struct proc_ops proc_pid_fd_ops = {
329     .lookup = proc_pid_fd_lookup,
330 };
331 
332 static const struct dfs_file_ops proc_pid_fd_fops = {
333     .getdents = proc_pid_fd_getdents,
334 };
335 
proc_pid_exe_readlink(struct proc_dentry * dentry,char * buf,int len)336 int proc_pid_exe_readlink(struct proc_dentry *dentry, char *buf, int len)
337 {
338     struct rt_lwp *lwp;
339 
340     lwp = lwp_self();
341     len = rt_snprintf(buf, len, "%s", lwp ? lwp->exe_file : "null");
342 
343     return len;
344 }
345 
346 static const struct proc_ops proc_pid_exe_ops = {
347     .readlink = proc_pid_exe_readlink,
348 };
349 
proc_pid_cwd_readlink(struct proc_dentry * dentry,char * buf,int len)350 int proc_pid_cwd_readlink(struct proc_dentry *dentry, char *buf, int len)
351 {
352     struct rt_lwp *lwp;
353 
354     lwp = lwp_self();
355     len = rt_snprintf(buf, len, "%s", lwp ? lwp->working_directory : "null");
356 
357     return len;
358 }
359 
360 static const struct proc_ops proc_pid_cwd_ops = {
361     .readlink = proc_pid_cwd_readlink,
362 };
363 
364 static struct pid_dentry pid_dentry_base[] = {
365     {"cmdline", S_IFREG | S_IRUSR | S_IRGRP | S_IROTH, 0, 0, 0, cmdline_single_show, 0},
366     {"cwd", S_IFLNK | S_IRUSR | S_IXUSR, 0, &proc_pid_cwd_ops, 0, 0},
367     {"exe", S_IFLNK | S_IRUSR | S_IXUSR, 0, &proc_pid_exe_ops, 0, 0},
368     {"fd", S_IFDIR | S_IRUSR | S_IXUSR, &proc_pid_fd_fops, &proc_pid_fd_ops, 0, 0, 0},
369     {"mounts", S_IFLNK | S_IRUSR | S_IXUSR, 0, 0, 0, 0, "/proc/mounts"},
370     {"stat", S_IFREG | S_IRUSR | S_IRGRP | S_IROTH, 0, 0, 0, stat_single_show, 0},
371 };
372 
proc_pid(int pid)373 int proc_pid(int pid)
374 {
375     char pid_str[64] = {0};
376     struct proc_dentry *dentry;
377 
378     rt_snprintf(pid_str, 64, "%d", pid);
379     pid_str[63] = 0;
380 
381     dentry = proc_mkdir(pid_str, 0);
382     if (dentry)
383     {
384         struct proc_dentry *ent;
385 
386         dentry->pid = pid;
387         for (int j = 0; j < sizeof(pid_dentry_base) / sizeof(struct pid_dentry); j++)
388         {
389             if (S_ISDIR(pid_dentry_base[j].mode))
390             {
391                 ent = proc_mkdir_data(pid_dentry_base[j].name, pid_dentry_base[j].mode, dentry,
392                                       pid_dentry_base[j].fops, pid_dentry_base[j].data);
393             }
394             else if (S_ISLNK(pid_dentry_base[j].mode))
395             {
396                 if (pid_dentry_base[j].data == RT_NULL)
397                 {
398                     pid_dentry_base[j].data = "NULL";
399                 }
400 
401                 ent = proc_symlink(pid_dentry_base[j].name, dentry, pid_dentry_base[j].data);
402             }
403             else
404             {
405                 ent = proc_create_data(pid_dentry_base[j].name, pid_dentry_base[j].mode, dentry,
406                                        pid_dentry_base[j].fops, pid_dentry_base[j].data);
407             }
408 
409             if (ent)
410             {
411                 if (pid_dentry_base[j].ops)
412                 {
413                     ent->ops = pid_dentry_base[j].ops;
414                 }
415 
416                 if (pid_dentry_base[j].seq_ops)
417                 {
418                     ent->seq_ops = pid_dentry_base[j].seq_ops;
419                 }
420 
421                 if (pid_dentry_base[j].single_show)
422                 {
423                     ent->single_show = pid_dentry_base[j].single_show;
424                 }
425 
426                 proc_release(ent);
427             }
428         }
429         proc_release(dentry);
430     }
431 
432     return 0;
433 }
434 
msh_proc_pid(int argc,char ** argv)435 int msh_proc_pid(int argc, char **argv)
436 {
437     if (argc > 1)
438     {
439         for (int i = 1; i <= argc - 1; i++)
440         {
441             proc_pid(atoi(argv[i]));
442         }
443     }
444 
445     return 0;
446 }
447 MSH_CMD_EXPORT_ALIAS(msh_proc_pid, proc_pid, proc pid);
448 
449 #endif
450