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-28 Shell Add reference management for pgrp;
10 * Using lwp lock API and fix the dead lock problem
11 */
12
13 #include "lwp.h"
14 #include "lwp_internal.h"
15 #include "lwp_syscall.h"
16
17 #define DBG_TAG "lwp.pgrp"
18 #define DBG_LVL DBG_WARNING
19 #include <rtdbg.h>
20
lwp_pgrp_dec_ref(rt_processgroup_t pgrp)21 void lwp_pgrp_dec_ref(rt_processgroup_t pgrp)
22 {
23 if (rt_atomic_add(&pgrp->ref, -1) == 1)
24 {
25 rt_mutex_detach(&(pgrp->mutex));
26
27 /* clear self pgid */
28 pgrp->pgid = 0;
29 rt_free(pgrp);
30 }
31 }
32
lwp_pgrp_find_and_inc_ref(pid_t pgid)33 rt_processgroup_t lwp_pgrp_find_and_inc_ref(pid_t pgid)
34 {
35 rt_processgroup_t group;
36
37 group = lwp_pgrp_find(pgid);
38 if (group)
39 {
40 rt_atomic_add(&(group->ref), 1);
41 }
42
43 return group;
44 }
45
lwp_pgrp_find(pid_t pgid)46 rt_processgroup_t lwp_pgrp_find(pid_t pgid)
47 {
48 rt_base_t level;
49 rt_processgroup_t group = RT_NULL;
50 rt_list_t *node = RT_NULL;
51 struct rt_object_information *information = RT_NULL;
52
53 information = rt_object_get_information(RT_Object_Class_ProcessGroup);
54
55 /* parameter check */
56 if ((pgid < 0) || (information == RT_NULL))
57 {
58 return RT_NULL;
59 }
60
61 if (pgid == 0)
62 {
63 pgid = lwp_getpid();
64 }
65
66 /* enter critical */
67 level = rt_spin_lock_irqsave(&(information->spinlock));
68
69 /* try to find process group */
70 rt_list_for_each(node, &(information->object_list))
71 {
72 group = (rt_processgroup_t)rt_list_entry(node, struct rt_object, list);
73 if (group->pgid == pgid)
74 {
75 rt_spin_unlock_irqrestore(&(information->spinlock), level);
76
77 return group;
78 }
79 }
80
81 rt_spin_unlock_irqrestore(&(information->spinlock), level);
82
83 LOG_I("cannot find(pgid:%d)() by (pid:%d, pgid:%d)", pgid, lwp_getpid(), lwp_pgid_get_byprocess(lwp_self()));
84
85 return RT_NULL;
86 }
87
lwp_pgrp_create(rt_lwp_t leader)88 rt_processgroup_t lwp_pgrp_create(rt_lwp_t leader)
89 {
90 rt_processgroup_t group = RT_NULL;
91
92 /* parameter check */
93 if (leader == RT_NULL)
94 {
95 return RT_NULL;
96 }
97
98 group = rt_malloc(sizeof(struct rt_processgroup));
99 if (group != RT_NULL)
100 {
101 rt_object_init(&(group->object), RT_Object_Class_ProcessGroup, "pgrp");
102 rt_list_init(&(group->process));
103 rt_list_init(&(group->pgrp_list_node));
104 rt_mutex_init(&(group->mutex), "pgrp", RT_IPC_FLAG_PRIO);
105 group->leader = leader;
106 group->sid = 0;
107 group->session = RT_NULL;
108 group->is_orphaned = 0;
109 group->pgid = lwp_to_pid(leader);
110 rt_atomic_store(&group->ref, 1);
111 }
112
113 LOG_I("create(ptr:%p, pgid:%d)() by pid:%d", group, group->pgid, lwp_getpid());
114
115 return group;
116 }
117
118 #include <terminal/terminal.h>
119
lwp_pgrp_delete(rt_processgroup_t group)120 int lwp_pgrp_delete(rt_processgroup_t group)
121 {
122 int retry = 1;
123 rt_session_t session = RT_NULL;
124 int is_session_free = 0;
125 lwp_tty_t ctty;
126
127 /* parameter check */
128 if (group == RT_NULL)
129 {
130 return -EINVAL;
131 }
132
133 LOG_I("delete(ptr:%p, pgid:%d)() by pid:%d", group, group->pgid, lwp_getpid());
134
135 while (retry)
136 {
137 retry = 0;
138 session = lwp_session_find(lwp_sid_get_bypgrp(group));
139 if (session)
140 {
141 ctty = session->ctty;
142 if (ctty)
143 {
144 /**
145 * Note: it's safe to release pgrp even we do this multiple,
146 * the neccessary check is done before the tty actually detach
147 */
148 tty_lock(ctty);
149 tty_rel_pgrp(ctty, group); // tty_unlock
150 }
151
152 SESS_LOCK(session);
153 PGRP_LOCK_NESTED(group);
154 if (group->session == session && session->ctty == ctty)
155 {
156 rt_object_detach(&(group->object));
157 is_session_free = lwp_session_remove(session, group);
158 }
159 else
160 {
161 retry = 1;
162
163 }
164
165 PGRP_UNLOCK(group);
166 if (is_session_free != 1)
167 SESS_UNLOCK(session);
168 }
169 else
170 {
171 rt_object_detach(&(group->object));
172 }
173 }
174
175 lwp_pgrp_dec_ref(group);
176 return 0;
177 }
178
lwp_pgrp_insert(rt_processgroup_t group,rt_lwp_t process)179 int lwp_pgrp_insert(rt_processgroup_t group, rt_lwp_t process)
180 {
181 /* parameter check */
182 if (group == RT_NULL || process == RT_NULL)
183 {
184 return -EINVAL;
185 }
186
187 PGRP_LOCK_NESTED(group);
188 LWP_LOCK_NESTED(process);
189 RT_ASSERT(rt_mutex_get_hold(&process->lwp_lock) <= rt_mutex_get_hold(&group->mutex));
190
191 process->pgid = group->pgid;
192 process->pgrp = group;
193 process->sid = group->sid;
194 rt_list_insert_after(&(group->process), &(process->pgrp_node));
195
196 LWP_UNLOCK(process);
197 PGRP_UNLOCK(group);
198
199 return 0;
200 }
201
lwp_pgrp_remove(rt_processgroup_t group,rt_lwp_t process)202 int lwp_pgrp_remove(rt_processgroup_t group, rt_lwp_t process)
203 {
204 rt_bool_t is_empty = RT_FALSE;
205
206 /* parameter check */
207 if (group == RT_NULL || process == RT_NULL)
208 {
209 return -EINVAL;
210 }
211
212 PGRP_LOCK_NESTED(group);
213 LWP_LOCK_NESTED(process);
214 RT_ASSERT(rt_mutex_get_hold(&process->lwp_lock) <= rt_mutex_get_hold(&group->mutex));
215
216 rt_list_remove(&(process->pgrp_node));
217 /* clear children sid and pgid */
218 process->pgrp = RT_NULL;
219 process->pgid = 0;
220 process->sid = 0;
221
222 LWP_UNLOCK(process);
223
224 is_empty = rt_list_isempty(&(group->process));
225
226 PGRP_UNLOCK(group);
227
228 if (is_empty)
229 {
230 lwp_pgrp_delete(group);
231 return 1;
232 }
233
234 return 0;
235 }
236
lwp_pgrp_move(rt_processgroup_t group,rt_lwp_t process)237 int lwp_pgrp_move(rt_processgroup_t group, rt_lwp_t process)
238 {
239 int retry = 1;
240 rt_processgroup_t old_group;
241
242 /* parameter check */
243 if (group == RT_NULL || process == RT_NULL)
244 {
245 return -EINVAL;
246 }
247
248 if (lwp_pgid_get_bypgrp(group) == lwp_pgid_get_byprocess(process))
249 {
250 return 0;
251 }
252
253 PGRP_LOCK(group);
254 while (retry)
255 {
256 retry = 0;
257 old_group = lwp_pgrp_find_and_inc_ref(lwp_pgid_get_byprocess(process));
258
259 PGRP_LOCK(old_group);
260 LWP_LOCK(process);
261
262 if (process->pgrp == old_group)
263 {
264 lwp_pgrp_remove(old_group, process);
265 lwp_pgrp_insert(group, process);
266 }
267 else
268 {
269 retry = 1;
270 }
271 PGRP_UNLOCK(old_group);
272 LWP_UNLOCK(process);
273
274 lwp_pgrp_dec_ref(old_group);
275 }
276 PGRP_UNLOCK(group);
277
278 return 0;
279 }
280
lwp_pgrp_update_children_info(rt_processgroup_t group,pid_t sid,pid_t pgid)281 int lwp_pgrp_update_children_info(rt_processgroup_t group, pid_t sid, pid_t pgid)
282 {
283 rt_list_t *node = RT_NULL;
284 rt_lwp_t process = RT_NULL;
285
286 if (group == RT_NULL)
287 {
288 return -EINVAL;
289 }
290
291 PGRP_LOCK_NESTED(group);
292
293 /* try to find process group */
294 rt_list_for_each(node, &(group->process))
295 {
296 process = (rt_lwp_t)rt_list_entry(node, struct rt_lwp, pgrp_node);
297 LWP_LOCK(process);
298 if (sid != -1)
299 {
300 process->sid = sid;
301 }
302 if (pgid != -1)
303 {
304 process->pgid = pgid;
305 process->pgrp = group;
306 }
307 LWP_UNLOCK(process);
308 }
309
310 PGRP_UNLOCK(group);
311 return 0;
312 }
313
314 /**
315 * setpgid() sets the PGID of the process specified by pid to pgid.
316 * If pid is zero, then the process ID of the calling process is used.
317 * If pgid is zero, then the PGID of the process specified by pid is made the same as its process ID.
318 * If setpgid() is used to move a process from one process group to another (as is done by some shells when
319 * creating pipelines), both process groups must be part of the same session (see setsid(2) and credentials(7)).
320 * In this case, the pgid specifies an existing process group to be joined and the session ID of that group must
321 * match the session ID of the joining process.
322 */
sys_setpgid(pid_t pid,pid_t pgid)323 sysret_t sys_setpgid(pid_t pid, pid_t pgid)
324 {
325 rt_lwp_t process, self_process;
326 pid_t sid;
327 rt_processgroup_t group;
328 rt_session_t session;
329 sysret_t err = 0;
330
331 if (pgid == 0)
332 {
333 pgid = pid;
334 }
335 if (pgid < 0)
336 {
337 return -EINVAL;
338 }
339
340 self_process = lwp_self();
341
342 if (pid == 0)
343 {
344 pid = self_process->pid;
345 process = self_process;
346 }
347 else
348 {
349 lwp_pid_lock_take();
350 process = lwp_from_pid_locked(pid);
351 lwp_pid_lock_release();
352
353 if (process == RT_NULL)
354 {
355 return -ESRCH;
356 }
357 }
358
359 LWP_LOCK(process);
360
361 if (process->parent == self_process)
362 {
363 /**
364 * change the process group ID of one of the children of the calling process and the child was in
365 * a different session
366 */
367 if (lwp_sid_get_byprocess(process) != lwp_sid_get_byprocess(self_process))
368 {
369 err = -EPERM;
370 LWP_UNLOCK(process);
371 goto exit;
372 }
373 /**
374 * An attempt was made to change the process group ID of one of the children of the calling process
375 * and the child had already performed an execve(2)
376 */
377 if (process->did_exec)
378 {
379 err = -EACCES;
380 LWP_UNLOCK(process);
381 goto exit;
382 }
383 }
384 else
385 {
386 /**
387 * pid is not the calling process and not a child of the calling process.
388 */
389 if (process != self_process)
390 {
391 err = -ESRCH;
392 LWP_UNLOCK(process);
393 goto exit;
394 }
395 }
396
397 LWP_UNLOCK(process);
398
399 sid = lwp_sid_get_byprocess(self_process);
400 if (pgid != pid)
401 {
402 group = lwp_pgrp_find(pgid);
403 if (group == RT_NULL)
404 {
405 group = lwp_pgrp_create(process);
406 lwp_pgrp_move(group, process);
407 session = lwp_session_find(sid);
408 if (session == RT_NULL)
409 {
410 LOG_E("the session of sid: %d cannot be found", sid);
411 err = -EPERM;
412 goto exit;
413 }
414 else
415 {
416 lwp_session_insert(session, group);
417 }
418 }
419 else
420 {
421 /**
422 * An attempt was made to move a process into a process group in a different session
423 */
424 if (sid != lwp_sid_get_bypgrp(group))
425 {
426 err = -EPERM;
427 goto exit;
428 }
429 /**
430 * or to change the process group ID of a session leader
431 */
432 if (sid == lwp_to_pid(process))
433 {
434 err = -EPERM;
435 goto exit;
436 }
437 lwp_pgrp_move(group, process);
438 }
439 }
440 else
441 {
442 group = lwp_pgrp_find(pgid);
443 if (group == RT_NULL)
444 {
445 group = lwp_pgrp_create(process);
446 lwp_pgrp_move(group, process);
447 session = lwp_session_find(sid);
448 if (session == RT_NULL)
449 {
450 LOG_E("the session of sid: %d cannot be found", sid);
451 err = -EPERM;
452 goto exit;
453 }
454 else
455 {
456 lwp_session_insert(session, group);
457 }
458 }
459 else // this represents repeated calls
460 {
461 /**
462 * or to change the process group ID of a session leader
463 */
464 if (lwp_sid_get_bypgrp(group) == lwp_pgid_get_bypgrp(group))
465 {
466 err = -EPERM;
467 goto exit;
468 }
469 else
470 {
471 err = 0;
472 }
473 }
474 }
475
476 exit:
477 return err;
478 }
479
480 /**
481 * getpgid() returns the PGID of the process specified by pid.
482 * If pid is zero, the process ID of the calling process is used. (Retrieving the PGID of a process other
483 * than the caller is rarely necessary, and the POSIX.1 getpgrp() is preferred for that task.)
484 */
sys_getpgid(pid_t pid)485 sysret_t sys_getpgid(pid_t pid)
486 {
487 rt_lwp_t process;
488
489 lwp_pid_lock_take();
490 process = lwp_from_pid_locked(pid);
491 lwp_pid_lock_release();
492
493 if (process == RT_NULL)
494 {
495 return -ESRCH;
496 }
497
498 return lwp_pgid_get_byprocess(process);
499 }
500
501 #ifdef RT_USING_FINSH
502
503 #include "finsh.h"
504
list_processgroup(void)505 long list_processgroup(void)
506 {
507 int count = 0, index;
508 rt_processgroup_t *groups;
509 rt_processgroup_t group;
510 rt_thread_t thread;
511 char name[RT_NAME_MAX];
512
513 rt_kprintf("PGID SID leader process\n");
514 rt_kprintf("---- ---- ----------------\n");
515
516 count = rt_object_get_length(RT_Object_Class_ProcessGroup);
517 if (count > 0)
518 {
519 /* get pointers */
520 groups = (rt_processgroup_t *)rt_calloc(count, sizeof(rt_processgroup_t));
521 if (groups)
522 {
523 index = rt_object_get_pointers(RT_Object_Class_ProcessGroup, (rt_object_t *)groups, count);
524 if (index > 0)
525 {
526 for (index = 0; index < count; index++)
527 {
528 struct rt_processgroup pgrp;
529 group = groups[index];
530 PGRP_LOCK(group);
531 rt_memcpy(&pgrp, group, sizeof(struct rt_processgroup));
532 PGRP_UNLOCK(group);
533
534 if (pgrp.leader)
535 {
536 thread = rt_list_entry(pgrp.leader->t_grp.prev, struct rt_thread, sibling);
537 rt_strncpy(name, thread->parent.name, RT_NAME_MAX);
538 }
539 else
540 {
541 rt_strncpy(name, "nil", RT_NAME_MAX);
542 }
543
544 rt_kprintf("%4d %4d %-*.*s\n", pgrp.pgid, pgrp.sid, RT_NAME_MAX, RT_NAME_MAX, name);
545 }
546 }
547 rt_free(groups);
548 }
549 }
550
551 return 0;
552 }
553 MSH_CMD_EXPORT(list_processgroup, list process group);
554 #endif
555