543 lines
13 KiB
C
543 lines
13 KiB
C
|
/*
|
||
|
* Copyright (c) 2006-2023, RT-Thread Development Team
|
||
|
*
|
||
|
* SPDX-License-Identifier: Apache-2.0
|
||
|
*
|
||
|
* Change Logs:
|
||
|
* Date Author Notes
|
||
|
* 2023-11-17 xqyjlj the first version
|
||
|
* 2023-11-28 Shell Add reference management for pgrp;
|
||
|
* Using lwp lock API and fix the dead lock problem
|
||
|
*/
|
||
|
|
||
|
#include "lwp.h"
|
||
|
#include "lwp_internal.h"
|
||
|
#include "lwp_syscall.h"
|
||
|
|
||
|
#define DBG_TAG "lwp.pgrp"
|
||
|
#define DBG_LVL DBG_WARNING
|
||
|
#include <rtdbg.h>
|
||
|
|
||
|
void lwp_pgrp_dec_ref(rt_processgroup_t pgrp)
|
||
|
{
|
||
|
if (rt_atomic_add(&pgrp->ref, -1) == 1)
|
||
|
{
|
||
|
rt_mutex_detach(&(pgrp->mutex));
|
||
|
|
||
|
/* clear self pgid */
|
||
|
pgrp->pgid = 0;
|
||
|
rt_free(pgrp);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
rt_processgroup_t lwp_pgrp_find_and_inc_ref(pid_t pgid)
|
||
|
{
|
||
|
rt_processgroup_t group;
|
||
|
|
||
|
group = lwp_pgrp_find(pgid);
|
||
|
if (group)
|
||
|
{
|
||
|
rt_atomic_add(&(group->ref), 1);
|
||
|
}
|
||
|
|
||
|
return group;
|
||
|
}
|
||
|
|
||
|
rt_processgroup_t lwp_pgrp_find(pid_t pgid)
|
||
|
{
|
||
|
rt_base_t level;
|
||
|
rt_processgroup_t group = RT_NULL;
|
||
|
rt_list_t *node = RT_NULL;
|
||
|
struct rt_object_information *information = RT_NULL;
|
||
|
|
||
|
information = rt_object_get_information(RT_Object_Class_ProcessGroup);
|
||
|
|
||
|
/* parameter check */
|
||
|
if ((pgid < 0) || (information == RT_NULL))
|
||
|
{
|
||
|
return RT_NULL;
|
||
|
}
|
||
|
|
||
|
if (pgid == 0)
|
||
|
{
|
||
|
pgid = lwp_getpid();
|
||
|
}
|
||
|
|
||
|
/* enter critical */
|
||
|
level = rt_spin_lock_irqsave(&(information->spinlock));
|
||
|
|
||
|
/* try to find process group */
|
||
|
rt_list_for_each(node, &(information->object_list))
|
||
|
{
|
||
|
group = (rt_processgroup_t)rt_list_entry(node, struct rt_object, list);
|
||
|
if (group->pgid == pgid)
|
||
|
{
|
||
|
rt_spin_unlock_irqrestore(&(information->spinlock), level);
|
||
|
|
||
|
return group;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
rt_spin_unlock_irqrestore(&(information->spinlock), level);
|
||
|
|
||
|
LOG_I("cannot find(pgid:%d)() by (pid:%d, pgid:%d)", pgid, lwp_getpid(), lwp_pgid_get_byprocess(lwp_self()));
|
||
|
|
||
|
return RT_NULL;
|
||
|
}
|
||
|
|
||
|
rt_processgroup_t lwp_pgrp_create(rt_lwp_t leader)
|
||
|
{
|
||
|
rt_processgroup_t group = RT_NULL;
|
||
|
|
||
|
/* parameter check */
|
||
|
if (leader == RT_NULL)
|
||
|
{
|
||
|
return RT_NULL;
|
||
|
}
|
||
|
|
||
|
group = rt_malloc(sizeof(struct rt_processgroup));
|
||
|
if (group != RT_NULL)
|
||
|
{
|
||
|
rt_object_init(&(group->object), RT_Object_Class_ProcessGroup, "pgrp");
|
||
|
rt_list_init(&(group->process));
|
||
|
rt_list_init(&(group->pgrp_list_node));
|
||
|
rt_mutex_init(&(group->mutex), "pgrp", RT_IPC_FLAG_PRIO);
|
||
|
group->leader = leader;
|
||
|
group->sid = 0;
|
||
|
group->session = RT_NULL;
|
||
|
group->is_orphaned = 0;
|
||
|
group->pgid = lwp_to_pid(leader);
|
||
|
rt_atomic_store(&group->ref, 1);
|
||
|
}
|
||
|
|
||
|
LOG_I("create(ptr:%p, pgid:%d)() by pid:%d", group, group->pgid, lwp_getpid());
|
||
|
|
||
|
return group;
|
||
|
}
|
||
|
|
||
|
#include <terminal/terminal.h>
|
||
|
|
||
|
int lwp_pgrp_delete(rt_processgroup_t group)
|
||
|
{
|
||
|
int retry = 1;
|
||
|
rt_session_t session = RT_NULL;
|
||
|
int is_session_free = 0;
|
||
|
lwp_tty_t ctty;
|
||
|
|
||
|
/* parameter check */
|
||
|
if (group == RT_NULL)
|
||
|
{
|
||
|
return -EINVAL;
|
||
|
}
|
||
|
|
||
|
LOG_I("delete(ptr:%p, pgid:%d)() by pid:%d", group, group->pgid, lwp_getpid());
|
||
|
|
||
|
while (retry)
|
||
|
{
|
||
|
retry = 0;
|
||
|
session = lwp_session_find(lwp_sid_get_bypgrp(group));
|
||
|
if (session)
|
||
|
{
|
||
|
ctty = session->ctty;
|
||
|
if (ctty)
|
||
|
{
|
||
|
/**
|
||
|
* Note: it's safe to release pgrp even we do this multiple,
|
||
|
* the neccessary check is done before the tty actually detach
|
||
|
*/
|
||
|
tty_lock(ctty);
|
||
|
tty_rel_pgrp(ctty, group); // tty_unlock
|
||
|
}
|
||
|
|
||
|
SESS_LOCK(session);
|
||
|
PGRP_LOCK_NESTED(group);
|
||
|
if (group->session == session && session->ctty == ctty)
|
||
|
{
|
||
|
rt_object_detach(&(group->object));
|
||
|
is_session_free = lwp_session_remove(session, group);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
retry = 1;
|
||
|
|
||
|
}
|
||
|
|
||
|
PGRP_UNLOCK(group);
|
||
|
if (is_session_free != 1)
|
||
|
SESS_UNLOCK(session);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
rt_object_detach(&(group->object));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
lwp_pgrp_dec_ref(group);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
int lwp_pgrp_insert(rt_processgroup_t group, rt_lwp_t process)
|
||
|
{
|
||
|
/* parameter check */
|
||
|
if (group == RT_NULL || process == RT_NULL)
|
||
|
{
|
||
|
return -EINVAL;
|
||
|
}
|
||
|
|
||
|
PGRP_LOCK_NESTED(group);
|
||
|
LWP_LOCK_NESTED(process);
|
||
|
RT_ASSERT(rt_mutex_get_hold(&process->lwp_lock) <= rt_mutex_get_hold(&group->mutex));
|
||
|
|
||
|
process->pgid = group->pgid;
|
||
|
process->pgrp = group;
|
||
|
process->sid = group->sid;
|
||
|
rt_list_insert_after(&(group->process), &(process->pgrp_node));
|
||
|
|
||
|
LWP_UNLOCK(process);
|
||
|
PGRP_UNLOCK(group);
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
int lwp_pgrp_remove(rt_processgroup_t group, rt_lwp_t process)
|
||
|
{
|
||
|
rt_bool_t is_empty = RT_FALSE;
|
||
|
|
||
|
/* parameter check */
|
||
|
if (group == RT_NULL || process == RT_NULL)
|
||
|
{
|
||
|
return -EINVAL;
|
||
|
}
|
||
|
|
||
|
PGRP_LOCK_NESTED(group);
|
||
|
LWP_LOCK_NESTED(process);
|
||
|
RT_ASSERT(rt_mutex_get_hold(&process->lwp_lock) <= rt_mutex_get_hold(&group->mutex));
|
||
|
|
||
|
rt_list_remove(&(process->pgrp_node));
|
||
|
/* clear children sid and pgid */
|
||
|
process->pgrp = RT_NULL;
|
||
|
process->pgid = 0;
|
||
|
process->sid = 0;
|
||
|
|
||
|
LWP_UNLOCK(process);
|
||
|
|
||
|
is_empty = rt_list_isempty(&(group->process));
|
||
|
|
||
|
PGRP_UNLOCK(group);
|
||
|
|
||
|
if (is_empty)
|
||
|
{
|
||
|
lwp_pgrp_delete(group);
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
int lwp_pgrp_move(rt_processgroup_t group, rt_lwp_t process)
|
||
|
{
|
||
|
int retry = 1;
|
||
|
rt_processgroup_t old_group;
|
||
|
|
||
|
/* parameter check */
|
||
|
if (group == RT_NULL || process == RT_NULL)
|
||
|
{
|
||
|
return -EINVAL;
|
||
|
}
|
||
|
|
||
|
if (lwp_pgid_get_bypgrp(group) == lwp_pgid_get_byprocess(process))
|
||
|
{
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
PGRP_LOCK(group);
|
||
|
while (retry)
|
||
|
{
|
||
|
retry = 0;
|
||
|
old_group = lwp_pgrp_find_and_inc_ref(lwp_pgid_get_byprocess(process));
|
||
|
|
||
|
PGRP_LOCK(old_group);
|
||
|
LWP_LOCK(process);
|
||
|
|
||
|
if (process->pgrp == old_group)
|
||
|
{
|
||
|
lwp_pgrp_remove(old_group, process);
|
||
|
lwp_pgrp_insert(group, process);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
retry = 1;
|
||
|
}
|
||
|
PGRP_UNLOCK(old_group);
|
||
|
LWP_UNLOCK(process);
|
||
|
|
||
|
lwp_pgrp_dec_ref(old_group);
|
||
|
}
|
||
|
PGRP_UNLOCK(group);
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
int lwp_pgrp_update_children_info(rt_processgroup_t group, pid_t sid, pid_t pgid)
|
||
|
{
|
||
|
rt_list_t *node = RT_NULL;
|
||
|
rt_lwp_t process = RT_NULL;
|
||
|
|
||
|
if (group == RT_NULL)
|
||
|
{
|
||
|
return -EINVAL;
|
||
|
}
|
||
|
|
||
|
PGRP_LOCK_NESTED(group);
|
||
|
|
||
|
/* try to find process group */
|
||
|
rt_list_for_each(node, &(group->process))
|
||
|
{
|
||
|
process = (rt_lwp_t)rt_list_entry(node, struct rt_lwp, pgrp_node);
|
||
|
LWP_LOCK(process);
|
||
|
if (sid != -1)
|
||
|
{
|
||
|
process->sid = sid;
|
||
|
}
|
||
|
if (pgid != -1)
|
||
|
{
|
||
|
process->pgid = pgid;
|
||
|
process->pgrp = group;
|
||
|
}
|
||
|
LWP_UNLOCK(process);
|
||
|
}
|
||
|
|
||
|
PGRP_UNLOCK(group);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* setpgid() sets the PGID of the process specified by pid to pgid.
|
||
|
* If pid is zero, then the process ID of the calling process is used.
|
||
|
* If pgid is zero, then the PGID of the process specified by pid is made the same as its process ID.
|
||
|
* If setpgid() is used to move a process from one process group to another (as is done by some shells when
|
||
|
* creating pipelines), both process groups must be part of the same session (see setsid(2) and credentials(7)).
|
||
|
* In this case, the pgid specifies an existing process group to be joined and the session ID of that group must
|
||
|
* match the session ID of the joining process.
|
||
|
*/
|
||
|
sysret_t sys_setpgid(pid_t pid, pid_t pgid)
|
||
|
{
|
||
|
rt_lwp_t process, self_process;
|
||
|
pid_t sid;
|
||
|
rt_processgroup_t group;
|
||
|
rt_session_t session;
|
||
|
sysret_t err = 0;
|
||
|
|
||
|
if (pgid == 0)
|
||
|
{
|
||
|
pgid = pid;
|
||
|
}
|
||
|
if (pgid < 0)
|
||
|
{
|
||
|
return -EINVAL;
|
||
|
}
|
||
|
|
||
|
self_process = lwp_self();
|
||
|
|
||
|
if (pid == 0)
|
||
|
{
|
||
|
pid = self_process->pid;
|
||
|
process = self_process;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
lwp_pid_lock_take();
|
||
|
process = lwp_from_pid_locked(pid);
|
||
|
lwp_pid_lock_release();
|
||
|
|
||
|
if (process == RT_NULL)
|
||
|
{
|
||
|
return -ESRCH;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
LWP_LOCK(process);
|
||
|
|
||
|
if (process->parent == self_process)
|
||
|
{
|
||
|
/**
|
||
|
* change the process group ID of one of the children of the calling process and the child was in
|
||
|
* a different session
|
||
|
*/
|
||
|
if (lwp_sid_get_byprocess(process) != lwp_sid_get_byprocess(self_process))
|
||
|
{
|
||
|
err = -EPERM;
|
||
|
LWP_UNLOCK(process);
|
||
|
goto exit;
|
||
|
}
|
||
|
/**
|
||
|
* An attempt was made to change the process group ID of one of the children of the calling process
|
||
|
* and the child had already performed an execve(2)
|
||
|
*/
|
||
|
if (process->did_exec)
|
||
|
{
|
||
|
err = -EACCES;
|
||
|
LWP_UNLOCK(process);
|
||
|
goto exit;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
/**
|
||
|
* pid is not the calling process and not a child of the calling process.
|
||
|
*/
|
||
|
if (process != self_process)
|
||
|
{
|
||
|
err = -ESRCH;
|
||
|
LWP_UNLOCK(process);
|
||
|
goto exit;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
LWP_UNLOCK(process);
|
||
|
|
||
|
sid = lwp_sid_get_byprocess(self_process);
|
||
|
if (pgid != pid)
|
||
|
{
|
||
|
group = lwp_pgrp_find(pgid);
|
||
|
if (group == RT_NULL)
|
||
|
{
|
||
|
group = lwp_pgrp_create(process);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
/**
|
||
|
* An attempt was made to move a process into a process group in a different session
|
||
|
*/
|
||
|
if (sid != lwp_sid_get_bypgrp(group))
|
||
|
{
|
||
|
err = -EPERM;
|
||
|
goto exit;
|
||
|
}
|
||
|
/**
|
||
|
* or to change the process group ID of a session leader
|
||
|
*/
|
||
|
if (sid == lwp_to_pid(process))
|
||
|
{
|
||
|
err = -EPERM;
|
||
|
goto exit;
|
||
|
}
|
||
|
lwp_pgrp_move(group, process);
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
group = lwp_pgrp_find(pgid);
|
||
|
if (group == RT_NULL)
|
||
|
{
|
||
|
group = lwp_pgrp_create(process);
|
||
|
lwp_pgrp_move(group, process);
|
||
|
session = lwp_session_find(sid);
|
||
|
if (session == RT_NULL)
|
||
|
{
|
||
|
LOG_E("the session of sid: %d cannot be found", sid);
|
||
|
err = -EPERM;
|
||
|
goto exit;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
lwp_session_insert(session, group);
|
||
|
}
|
||
|
}
|
||
|
else // this represents repeated calls
|
||
|
{
|
||
|
/**
|
||
|
* or to change the process group ID of a session leader
|
||
|
*/
|
||
|
if (lwp_sid_get_bypgrp(group) == lwp_pgid_get_bypgrp(group))
|
||
|
{
|
||
|
err = -EPERM;
|
||
|
goto exit;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
err = 0;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
exit:
|
||
|
return err;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* getpgid() returns the PGID of the process specified by pid.
|
||
|
* If pid is zero, the process ID of the calling process is used. (Retrieving the PGID of a process other
|
||
|
* than the caller is rarely necessary, and the POSIX.1 getpgrp() is preferred for that task.)
|
||
|
*/
|
||
|
sysret_t sys_getpgid(pid_t pid)
|
||
|
{
|
||
|
rt_lwp_t process;
|
||
|
|
||
|
lwp_pid_lock_take();
|
||
|
process = lwp_from_pid_locked(pid);
|
||
|
lwp_pid_lock_release();
|
||
|
|
||
|
if (process == RT_NULL)
|
||
|
{
|
||
|
return -ESRCH;
|
||
|
}
|
||
|
|
||
|
return lwp_pgid_get_byprocess(process);
|
||
|
}
|
||
|
|
||
|
#ifdef RT_USING_FINSH
|
||
|
|
||
|
#include "finsh.h"
|
||
|
|
||
|
long list_processgroup(void)
|
||
|
{
|
||
|
int count = 0, index;
|
||
|
rt_processgroup_t *groups;
|
||
|
rt_processgroup_t group;
|
||
|
rt_thread_t thread;
|
||
|
char name[RT_NAME_MAX];
|
||
|
|
||
|
rt_kprintf("PGID SID leader process\n");
|
||
|
rt_kprintf("---- ---- ----------------\n");
|
||
|
|
||
|
count = rt_object_get_length(RT_Object_Class_ProcessGroup);
|
||
|
if (count > 0)
|
||
|
{
|
||
|
/* get pointers */
|
||
|
groups = (rt_processgroup_t *)rt_calloc(count, sizeof(rt_processgroup_t));
|
||
|
if (groups)
|
||
|
{
|
||
|
index = rt_object_get_pointers(RT_Object_Class_ProcessGroup, (rt_object_t *)groups, count);
|
||
|
if (index > 0)
|
||
|
{
|
||
|
for (index = 0; index < count; index++)
|
||
|
{
|
||
|
struct rt_processgroup pgrp;
|
||
|
group = groups[index];
|
||
|
PGRP_LOCK(group);
|
||
|
rt_memcpy(&pgrp, group, sizeof(struct rt_processgroup));
|
||
|
PGRP_UNLOCK(group);
|
||
|
|
||
|
if (pgrp.leader)
|
||
|
{
|
||
|
thread = rt_list_entry(pgrp.leader->t_grp.prev, struct rt_thread, sibling);
|
||
|
rt_strncpy(name, thread->parent.name, RT_NAME_MAX);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
rt_strncpy(name, "nil", RT_NAME_MAX);
|
||
|
}
|
||
|
|
||
|
rt_kprintf("%4d %4d %-*.*s\n", pgrp.pgid, pgrp.sid, RT_NAME_MAX, RT_NAME_MAX, name);
|
||
|
}
|
||
|
}
|
||
|
rt_free(groups);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
MSH_CMD_EXPORT(list_processgroup, list process group);
|
||
|
#endif
|