/* * 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-29 Shell Add direct reference of sess for group */ #include "lwp.h" #include "lwp_internal.h" #include "lwp_syscall.h" #include "terminal/terminal.h" #define DBG_TAG "lwp.session" #define DBG_LVL DBG_WARNING #include rt_session_t lwp_session_find(pid_t sid) { rt_base_t level; rt_session_t session = RT_NULL; rt_list_t *node = RT_NULL; struct rt_object_information *information = RT_NULL; information = rt_object_get_information(RT_Object_Class_Session); /* parameter check */ if ((sid < 0) || (information == RT_NULL)) { return RT_NULL; } if (sid == 0) { sid = lwp_getpid(); } /* enter critical */ level = rt_spin_lock_irqsave(&(information->spinlock)); /* try to find session */ rt_list_for_each(node, &(information->object_list)) { session = (rt_session_t)rt_list_entry(node, struct rt_object, list); if (session->sid == sid) { rt_spin_unlock_irqrestore(&(information->spinlock), level); return session; } } rt_spin_unlock_irqrestore(&(information->spinlock), level); return RT_NULL; } rt_session_t lwp_session_create(rt_lwp_t leader) { rt_session_t session = RT_NULL; /* parameter check */ if (leader == RT_NULL) { return RT_NULL; } session = rt_malloc(sizeof(struct rt_session)); if (session != RT_NULL) { rt_object_init(&(session->object), RT_Object_Class_Session, "session"); rt_list_init(&(session->processgroup)); rt_mutex_init(&(session->mutex), "session", RT_IPC_FLAG_PRIO); session->leader = leader; session->sid = leader->pid; lwp_pgrp_update_children_info(leader->pgrp, session->sid, leader->pgid); session->foreground_pgid = session->sid; session->ctty = RT_NULL; } return session; } int lwp_session_delete(rt_session_t session) { int retry = 1; lwp_tty_t ctty; /* parameter check */ if (session == RT_NULL) { return -EINVAL; } /* clear children sid */ lwp_session_update_children_info(session, 0); while (retry) { retry = 0; ctty = session->ctty; SESS_LOCK_NESTED(session); if (session->ctty == ctty) { if (ctty) { SESS_UNLOCK(session); /** * Note: it's safe to release the session lock now. Even if someone * race to acquire the tty, it's safe under protection of tty_lock() * and the check inside */ tty_lock(ctty); tty_rel_sess(ctty, session); session->ctty = RT_NULL; } else { SESS_UNLOCK(session); } } else { SESS_UNLOCK(session); retry = 1; } } rt_object_detach(&(session->object)); rt_mutex_detach(&(session->mutex)); rt_free(session); return 0; } int lwp_session_insert(rt_session_t session, rt_processgroup_t group) { /* parameter check */ if (session == RT_NULL || group == RT_NULL) { return -EINVAL; } SESS_LOCK_NESTED(session); PGRP_LOCK_NESTED(group); group->sid = session->sid; group->session = session; lwp_pgrp_update_children_info(group, session->sid, group->pgid); rt_list_insert_after(&(session->processgroup), &(group->pgrp_list_node)); PGRP_UNLOCK(group); SESS_UNLOCK(session); return 0; } int lwp_session_remove(rt_session_t session, rt_processgroup_t group) { rt_bool_t is_empty = RT_FALSE; /* parameter check */ if (session == RT_NULL || group == RT_NULL) { return -EINVAL; } SESS_LOCK_NESTED(session); PGRP_LOCK_NESTED(group); rt_list_remove(&(group->pgrp_list_node)); /* clear children sid */ lwp_pgrp_update_children_info(group, 0, group->pgid); group->sid = 0; group->session = RT_NULL; PGRP_UNLOCK(group); is_empty = rt_list_isempty(&(session->processgroup)); SESS_UNLOCK(session); if (is_empty) { lwp_session_delete(session); return 1; } return 0; } int lwp_session_move(rt_session_t session, rt_processgroup_t group) { rt_session_t prev_session; /* parameter check */ if (session == RT_NULL || group == RT_NULL) { return -EINVAL; } if (lwp_sid_get_bysession(session) == lwp_sid_get_bypgrp(group)) { return 0; } SESS_LOCK(session); prev_session = group->session; if (prev_session) { SESS_LOCK(prev_session); lwp_session_remove(prev_session, group); SESS_UNLOCK(prev_session); } lwp_session_insert(session, group); SESS_UNLOCK(session); return 0; } int lwp_session_update_children_info(rt_session_t session, pid_t sid) { rt_list_t *node = RT_NULL; rt_processgroup_t group = RT_NULL; if (session == RT_NULL) { return -EINVAL; } SESS_LOCK_NESTED(session); rt_list_for_each(node, &(session->processgroup)) { group = (rt_processgroup_t)rt_list_entry(node, struct rt_processgroup, pgrp_list_node); PGRP_LOCK_NESTED(group); if (sid != -1) { group->sid = sid; group->session = session; lwp_pgrp_update_children_info(group, sid, group->pgid); } PGRP_UNLOCK(group); } SESS_UNLOCK(session); return 0; } int lwp_session_set_foreground(rt_session_t session, pid_t pgid) { rt_processgroup_t group = RT_NULL; rt_list_t *node = RT_NULL; rt_bool_t is_contains = RT_FALSE; /* parameter check */ if (session == RT_NULL || pgid <= 0) { return -EINVAL; } SESS_LOCK(session); rt_list_for_each(node, &(session->processgroup)) { group = (rt_processgroup_t)rt_list_entry(node, struct rt_processgroup, pgrp_list_node); PGRP_LOCK(group); if (group->pgid == pgid) { is_contains = RT_TRUE; } PGRP_UNLOCK(group); } if (is_contains) { session->foreground_pgid = pgid; // TODO: maybe notify tty } SESS_UNLOCK(session); return is_contains ? 0 : -EINVAL; } /** * setsid() creates a new session if the calling process is not a process group leader. * The calling process is the leader of the new session (i.e., its session ID is made the same as its process ID). * The calling process also becomes the process group leader of a new process group in the session * (i.e., its process group ID is made the same as its process ID). */ sysret_t sys_setsid(void) { rt_lwp_t process; pid_t pid; rt_processgroup_t group; rt_session_t session; sysret_t err = 0; process = lwp_self(); pid = lwp_to_pid(process); /** * if the calling process is already a process group leader. */ if (lwp_pgrp_find(pid)) { err = -EPERM; goto exit; } group = lwp_pgrp_create(process); if (group) { lwp_pgrp_move(group, process); session = lwp_session_create(process); if (session) { lwp_session_move(session, group); } else { lwp_pgrp_delete(group); } err = lwp_sid_get_bysession(session); } else { err = -ENOMEM; } exit: return err; } /** * getsid() returns the session ID of the process with process ID pid. * If pid is 0, getsid() returns the session ID of the calling process. */ sysret_t sys_getsid(pid_t pid) { rt_lwp_t process, self_process; pid_t sid; lwp_pid_lock_take(); process = lwp_from_pid_locked(pid); lwp_pid_lock_release(); if (process == RT_NULL) { return -ESRCH; } self_process = lwp_self(); sid = lwp_sid_get_byprocess(process); if (sid != lwp_sid_get_byprocess(self_process)) { /** * A process with process ID pid exists, but it is not in the same session as the calling process, * and the implementation considers this an error. * * Note: Linux does not return EPERM. */ return -EPERM; } return sid; } #ifdef RT_USING_FINSH #include "finsh.h" long list_session(void) { int count = 0, index; rt_session_t *sessions; rt_session_t session; rt_thread_t thread; char name[RT_NAME_MAX]; rt_kprintf("SID leader process\n"); rt_kprintf("---- ----------------\n"); count = rt_object_get_length(RT_Object_Class_Session); if (count > 0) { /* get pointers */ sessions = (rt_session_t *)rt_calloc(count, sizeof(rt_session_t)); if (sessions) { index = rt_object_get_pointers(RT_Object_Class_Session, (rt_object_t *)sessions, count); if (index > 0) { for (index = 0; index < count; index++) { struct rt_session se; session = sessions[index]; SESS_LOCK(session); rt_memcpy(&se, session, sizeof(struct rt_session)); SESS_UNLOCK(session); if (se.leader && se.leader) { thread = rt_list_entry(se.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 %-*.*s\n", se.sid, RT_NAME_MAX, RT_NAME_MAX, name); } } rt_free(sessions); } } return 0; } MSH_CMD_EXPORT(list_session, list session); #endif