lwp_pgrp.c 13 KB

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