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