433 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			C
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			433 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			C
		
	
	
		
			Executable File
		
	
	
	
	
| /*
 | |
|  * 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 <rtdbg.h>
 | |
| 
 | |
| 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
 |