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  * 2023-11-17     xqyjlj       the first version
9  * 2023-11-29     Shell        Add direct reference of sess for group
10  */
11 
12 #include "lwp.h"
13 #include "lwp_internal.h"
14 #include "lwp_syscall.h"
15 #include "terminal/terminal.h"
16 
17 #define DBG_TAG "lwp.session"
18 #define DBG_LVL DBG_WARNING
19 #include <rtdbg.h>
20 
lwp_session_find(pid_t sid)21 rt_session_t lwp_session_find(pid_t sid)
22 {
23     rt_base_t level;
24     rt_session_t session = RT_NULL;
25     rt_list_t *node = RT_NULL;
26     struct rt_object_information *information = RT_NULL;
27 
28     information = rt_object_get_information(RT_Object_Class_Session);
29 
30     /* parameter check */
31     if ((sid < 0) || (information == RT_NULL))
32     {
33         return RT_NULL;
34     }
35 
36     if (sid == 0)
37     {
38         sid = lwp_getpid();
39     }
40 
41     /* enter critical */
42     level = rt_spin_lock_irqsave(&(information->spinlock));
43 
44     /* try to find session */
45     rt_list_for_each(node, &(information->object_list))
46     {
47         session = (rt_session_t)rt_list_entry(node, struct rt_object, list);
48         if (session->sid == sid)
49         {
50             rt_spin_unlock_irqrestore(&(information->spinlock), level);
51 
52             return session;
53         }
54     }
55 
56     rt_spin_unlock_irqrestore(&(information->spinlock), level);
57 
58     return RT_NULL;
59 }
60 
lwp_session_create(rt_lwp_t leader)61 rt_session_t lwp_session_create(rt_lwp_t leader)
62 {
63     rt_session_t session = RT_NULL;
64 
65     /* parameter check */
66     if (leader == RT_NULL)
67     {
68         return RT_NULL;
69     }
70 
71     session = rt_malloc(sizeof(struct rt_session));
72     if (session != RT_NULL)
73     {
74         rt_object_init(&(session->object), RT_Object_Class_Session, "session");
75         rt_list_init(&(session->processgroup));
76         rt_mutex_init(&(session->mutex), "session", RT_IPC_FLAG_PRIO);
77         session->leader = leader;
78         session->sid = leader->pid;
79         lwp_pgrp_update_children_info(leader->pgrp, session->sid, leader->pgid);
80         session->foreground_pgid = session->sid;
81         session->ctty = RT_NULL;
82     }
83     return session;
84 }
85 
lwp_session_delete(rt_session_t session)86 int lwp_session_delete(rt_session_t session)
87 {
88     int retry = 1;
89     lwp_tty_t ctty;
90 
91     /* parameter check */
92     if (session == RT_NULL)
93     {
94         return -EINVAL;
95     }
96 
97     /* clear children sid */
98     lwp_session_update_children_info(session, 0);
99 
100     while (retry)
101     {
102         retry = 0;
103         ctty = session->ctty;
104         SESS_LOCK_NESTED(session);
105 
106         if (session->ctty == ctty)
107         {
108             if (ctty)
109             {
110                 SESS_UNLOCK(session);
111 
112                 /**
113                  * Note: it's safe to release the session lock now. Even if someone
114                  * race to acquire the tty, it's safe under protection of tty_lock()
115                  * and the check inside
116                  */
117                 tty_lock(ctty);
118                 tty_rel_sess(ctty, session);
119                 session->ctty = RT_NULL;
120             }
121             else
122             {
123                 SESS_UNLOCK(session);
124             }
125         }
126         else
127         {
128             SESS_UNLOCK(session);
129             retry = 1;
130         }
131     }
132 
133     rt_object_detach(&(session->object));
134     rt_mutex_detach(&(session->mutex));
135     rt_free(session);
136 
137     return 0;
138 }
139 
lwp_session_insert(rt_session_t session,rt_processgroup_t group)140 int lwp_session_insert(rt_session_t session, rt_processgroup_t group)
141 {
142     /* parameter check */
143     if (session == RT_NULL || group == RT_NULL)
144     {
145         return -EINVAL;
146     }
147 
148     SESS_LOCK_NESTED(session);
149     PGRP_LOCK_NESTED(group);
150 
151     group->sid = session->sid;
152     group->session = session;
153     lwp_pgrp_update_children_info(group, session->sid, group->pgid);
154     rt_list_insert_after(&(session->processgroup), &(group->pgrp_list_node));
155 
156     PGRP_UNLOCK(group);
157     SESS_UNLOCK(session);
158 
159     return 0;
160 }
161 
lwp_session_remove(rt_session_t session,rt_processgroup_t group)162 int lwp_session_remove(rt_session_t session, rt_processgroup_t group)
163 {
164     rt_bool_t is_empty = RT_FALSE;
165 
166     /* parameter check */
167     if (session == RT_NULL || group == RT_NULL)
168     {
169         return -EINVAL;
170     }
171 
172     SESS_LOCK_NESTED(session);
173     PGRP_LOCK_NESTED(group);
174 
175     rt_list_remove(&(group->pgrp_list_node));
176     /* clear children sid */
177     lwp_pgrp_update_children_info(group, 0, group->pgid);
178     group->sid = 0;
179     group->session = RT_NULL;
180 
181     PGRP_UNLOCK(group);
182 
183     is_empty = rt_list_isempty(&(session->processgroup));
184 
185     SESS_UNLOCK(session);
186 
187     if (is_empty)
188     {
189         lwp_session_delete(session);
190         return 1;
191     }
192 
193     return 0;
194 }
195 
lwp_session_move(rt_session_t session,rt_processgroup_t group)196 int lwp_session_move(rt_session_t session, rt_processgroup_t group)
197 {
198     rt_session_t prev_session;
199 
200     /* parameter check */
201     if (session == RT_NULL || group == RT_NULL)
202     {
203         return -EINVAL;
204     }
205 
206     if (lwp_sid_get_bysession(session) == lwp_sid_get_bypgrp(group))
207     {
208         return 0;
209     }
210 
211     SESS_LOCK(session);
212 
213     prev_session = group->session;
214     if (prev_session)
215     {
216         SESS_LOCK(prev_session);
217         lwp_session_remove(prev_session, group);
218         SESS_UNLOCK(prev_session);
219     }
220 
221     lwp_session_insert(session, group);
222 
223     SESS_UNLOCK(session);
224 
225     return 0;
226 }
227 
lwp_session_update_children_info(rt_session_t session,pid_t sid)228 int lwp_session_update_children_info(rt_session_t session, pid_t sid)
229 {
230     rt_list_t *node = RT_NULL;
231     rt_processgroup_t group = RT_NULL;
232 
233     if (session == RT_NULL)
234     {
235         return -EINVAL;
236     }
237 
238     SESS_LOCK_NESTED(session);
239 
240     rt_list_for_each(node, &(session->processgroup))
241     {
242         group = (rt_processgroup_t)rt_list_entry(node, struct rt_processgroup, pgrp_list_node);
243         PGRP_LOCK_NESTED(group);
244         if (sid != -1)
245         {
246             group->sid = sid;
247             group->session = session;
248             lwp_pgrp_update_children_info(group, sid, group->pgid);
249         }
250         PGRP_UNLOCK(group);
251     }
252 
253     SESS_UNLOCK(session);
254     return 0;
255 }
256 
lwp_session_set_foreground(rt_session_t session,pid_t pgid)257 int lwp_session_set_foreground(rt_session_t session, pid_t pgid)
258 {
259     rt_processgroup_t group = RT_NULL;
260     rt_list_t *node = RT_NULL;
261     rt_bool_t is_contains = RT_FALSE;
262 
263     /* parameter check */
264     if (session == RT_NULL || pgid <= 0)
265     {
266         return -EINVAL;
267     }
268 
269     SESS_LOCK(session);
270 
271     rt_list_for_each(node, &(session->processgroup))
272     {
273         group = (rt_processgroup_t)rt_list_entry(node, struct rt_processgroup, pgrp_list_node);
274         PGRP_LOCK(group);
275         if (group->pgid == pgid)
276         {
277             is_contains = RT_TRUE;
278         }
279         PGRP_UNLOCK(group);
280     }
281 
282     if (is_contains)
283     {
284         session->foreground_pgid = pgid;
285         // TODO: maybe notify tty
286     }
287 
288     SESS_UNLOCK(session);
289 
290     return is_contains ? 0 : -EINVAL;
291 }
292 
293 /**
294  * setsid() creates a new session if the calling process is not a process group leader.
295  *  The calling process is the leader of the new session (i.e., its session ID is made the same as its process ID).
296  *  The calling process also becomes the process group leader of a new process group in the session
297  *      (i.e., its process group ID is made the same as its process ID).
298  */
sys_setsid(void)299 sysret_t sys_setsid(void)
300 {
301     rt_lwp_t process;
302     pid_t pid;
303     rt_processgroup_t group;
304     rt_session_t session;
305     sysret_t err = 0;
306 
307     process = lwp_self();
308     pid = lwp_to_pid(process);
309 
310     /**
311      * if the calling process is already a process group leader.
312      */
313     if (lwp_pgrp_find(pid))
314     {
315         err = -EPERM;
316         goto exit;
317     }
318 
319     group = lwp_pgrp_create(process);
320     if (group)
321     {
322         lwp_pgrp_move(group, process);
323         session = lwp_session_create(process);
324         if (session)
325         {
326             lwp_session_move(session, group);
327         }
328         else
329         {
330             lwp_pgrp_delete(group);
331         }
332         err = lwp_sid_get_bysession(session);
333     }
334     else
335     {
336         err = -ENOMEM;
337     }
338 
339 
340 exit:
341     return err;
342 }
343 
344 /**
345  * getsid() returns the session ID of the process with process ID pid.
346  *  If pid is 0, getsid() returns the session ID of the calling process.
347  */
sys_getsid(pid_t pid)348 sysret_t sys_getsid(pid_t pid)
349 {
350     rt_lwp_t process, self_process;
351     pid_t sid;
352 
353     lwp_pid_lock_take();
354     process = lwp_from_pid_locked(pid);
355     lwp_pid_lock_release();
356 
357     if (process == RT_NULL)
358     {
359         return -ESRCH;
360     }
361 
362     self_process = lwp_self();
363     sid = lwp_sid_get_byprocess(process);
364 
365     if (sid != lwp_sid_get_byprocess(self_process))
366     {
367         /**
368          * A process with process ID pid exists, but it is not in the same session as the calling process,
369          *  and the implementation considers this an error.
370          *
371          * Note: Linux does not return EPERM.
372          */
373         return -EPERM;
374     }
375 
376     return sid;
377 }
378 
379 #ifdef RT_USING_FINSH
380 
381 #include "finsh.h"
382 
list_session(void)383 long list_session(void)
384 {
385     int count = 0, index;
386     rt_session_t *sessions;
387     rt_session_t session;
388     rt_thread_t thread;
389     char name[RT_NAME_MAX];
390 
391     rt_kprintf("SID  leader process\n");
392     rt_kprintf("---- ----------------\n");
393 
394     count = rt_object_get_length(RT_Object_Class_Session);
395     if (count > 0)
396     {
397         /* get  pointers */
398         sessions = (rt_session_t *)rt_calloc(count, sizeof(rt_session_t));
399         if (sessions)
400         {
401             index = rt_object_get_pointers(RT_Object_Class_Session, (rt_object_t *)sessions, count);
402             if (index > 0)
403             {
404                 for (index = 0; index < count; index++)
405                 {
406                     struct rt_session se;
407                     session = sessions[index];
408                     SESS_LOCK(session);
409                     rt_memcpy(&se, session, sizeof(struct rt_session));
410                     SESS_UNLOCK(session);
411 
412                     if (se.leader && se.leader)
413                     {
414                         thread = rt_list_entry(se.leader->t_grp.prev, struct rt_thread, sibling);
415                         rt_strncpy(name, thread->parent.name, RT_NAME_MAX);
416                     }
417                     else
418                     {
419                         rt_strncpy(name, "nil", RT_NAME_MAX);
420                     }
421 
422                     rt_kprintf("%4d %-*.*s\n", se.sid, RT_NAME_MAX, RT_NAME_MAX, name);
423                 }
424             }
425             rt_free(sessions);
426         }
427     }
428 
429     return 0;
430 }
431 MSH_CMD_EXPORT(list_session, list session);
432 #endif
433