summaryrefslogtreecommitdiff
path: root/components/lwp
diff options
context:
space:
mode:
authorShell <[email protected]>2024-03-28 23:42:56 +0800
committerGitHub <[email protected]>2024-03-28 23:42:56 +0800
commit83e95bdff4c48c661b50e1eec56d5fd293b28fbb (patch)
tree9e0c2065950f0804e5009da71c8e0f590224cade /components/lwp
parent40e26f4909fa61ada775ba86b44d8300aaf5d250 (diff)
sync smart & dfs (#8672)
Signed-off-by: xqyjlj <[email protected]> Signed-off-by: Shell <[email protected]> Co-authored-by: xqyjlj <[email protected]>
Diffstat (limited to 'components/lwp')
-rw-r--r--components/lwp/Kconfig21
-rw-r--r--components/lwp/SConscript11
-rw-r--r--components/lwp/arch/aarch64/cortex-a/lwp_arch.c60
-rw-r--r--components/lwp/arch/aarch64/cortex-a/lwp_arch.h19
-rw-r--r--components/lwp/arch/aarch64/cortex-a/lwp_gcc.S70
-rw-r--r--components/lwp/arch/arm/cortex-a/lwp_arch.c6
-rw-r--r--components/lwp/arch/arm/cortex-a/lwp_arch.h2
-rw-r--r--components/lwp/arch/risc-v/rv64/lwp_arch.c7
-rw-r--r--components/lwp/libc_musl.h6
-rw-r--r--components/lwp/lwp.c268
-rw-r--r--components/lwp/lwp.h159
-rw-r--r--components/lwp/lwp_arch_comm.h4
-rw-r--r--components/lwp/lwp_elf.c1
-rw-r--r--components/lwp/lwp_futex.c29
-rw-r--r--components/lwp/lwp_internal.c69
-rw-r--r--components/lwp/lwp_internal.h111
-rw-r--r--components/lwp/lwp_jobctrl.c86
-rw-r--r--components/lwp/lwp_pgrp.c542
-rw-r--r--components/lwp/lwp_pid.c662
-rw-r--r--components/lwp/lwp_pid.h38
-rw-r--r--components/lwp/lwp_pmutex.c459
-rw-r--r--components/lwp/lwp_session.c432
-rw-r--r--components/lwp/lwp_setsid.c27
-rw-r--r--components/lwp/lwp_signal.c542
-rw-r--r--components/lwp/lwp_signal.h83
-rw-r--r--components/lwp/lwp_sys_socket.h21
-rw-r--r--components/lwp/lwp_syscall.c751
-rw-r--r--components/lwp/lwp_syscall.h7
-rw-r--r--components/lwp/lwp_tid.c21
-rw-r--r--components/lwp/lwp_user_mm.c35
-rw-r--r--components/lwp/lwp_user_mm.h4
-rw-r--r--components/lwp/terminal/Kconfig13
-rw-r--r--components/lwp/terminal/bsd_porting.h741
-rw-r--r--components/lwp/terminal/bsd_ttydisc.h81
-rw-r--r--components/lwp/terminal/bsd_ttyqueue.h180
-rw-r--r--components/lwp/terminal/freebsd/tty.c1447
-rw-r--r--components/lwp/terminal/freebsd/tty_compat.c711
-rw-r--r--components/lwp/terminal/freebsd/tty_inq.c507
-rw-r--r--components/lwp/terminal/freebsd/tty_outq.c370
-rw-r--r--components/lwp/terminal/freebsd/tty_pts.c837
-rw-r--r--components/lwp/terminal/freebsd/tty_ttydisc.c1479
-rw-r--r--components/lwp/terminal/terminal.h415
-rw-r--r--components/lwp/terminal/tty_config.h136
-rw-r--r--components/lwp/terminal/tty_cons.c128
-rw-r--r--components/lwp/terminal/tty_ctty.c106
-rw-r--r--components/lwp/terminal/tty_device.c456
-rw-r--r--components/lwp/terminal/tty_internal.h67
-rw-r--r--components/lwp/terminal/tty_ptmx.c350
48 files changed, 11208 insertions, 1369 deletions
diff --git a/components/lwp/Kconfig b/components/lwp/Kconfig
index 719e8ffca4..9b79e51207 100644
--- a/components/lwp/Kconfig
+++ b/components/lwp/Kconfig
@@ -8,7 +8,7 @@ menuconfig RT_USING_LWP
if RT_USING_LWP
config LWP_DEBUG
bool "Enable debugging features of LwP"
- default n
+ default y
config RT_LWP_MAX_NR
int "The max number of light-weight process"
@@ -51,24 +51,6 @@ if RT_USING_LWP
default y
endif
- config LWP_UNIX98_PTY
- bool "The unix98 PTY support"
- default n
-
- if LWP_UNIX98_PTY
- config LWP_PTY_INPUT_BFSZ
- int "The unix98 PTY input buffer size"
- default 1024
-
- config LWP_PTY_PTS_SIZE
- int "The unix98 PTY device max num"
- default 3
-
- config LWP_PTY_USING_DEBUG
- bool "The unix98 PTY debug output"
- default n
- endif
-
menuconfig RT_USING_LDSO
bool "LDSO: dynamic load shared objects"
depends on RT_USING_DFS_V2
@@ -85,5 +67,6 @@ if RT_USING_LWP
default n
endif
+source "$RTT_DIR/components/lwp/terminal/Kconfig"
endif
diff --git a/components/lwp/SConscript b/components/lwp/SConscript
index 3c43dcd86e..5f4a618d48 100644
--- a/components/lwp/SConscript
+++ b/components/lwp/SConscript
@@ -22,11 +22,6 @@ if arch == 'risc-v':
if cpu in rv64:
cpu = 'rv64'
-if GetDepend('LWP_UNIX98_PTY'):
- # print("LWP_UNIX98_PTY")
- src += Glob('unix98pty/*.c')
- CPPPATH += ['unix98pty/']
-
if platform in platform_file.keys(): # support platforms
if arch in support_arch.keys() and cpu in support_arch[arch]:
asm_path = 'arch/' + arch + '/' + cpu + '/*_' + platform_file[platform]
@@ -40,6 +35,12 @@ if platform in platform_file.keys(): # support platforms
CPPPATH = [cwd]
CPPPATH += [cwd + '/arch/' + arch + '/' + cpu]
+# Terminal I/O Subsystem
+termios_path = ['./terminal/', './terminal/freebsd/']
+for item in termios_path:
+ src += Glob(item + '*.c')
+CPPPATH += ['./terminal/']
+
group = DefineGroup('lwP', src, depend = ['RT_USING_SMART'], CPPPATH = CPPPATH)
Return('group')
diff --git a/components/lwp/arch/aarch64/cortex-a/lwp_arch.c b/components/lwp/arch/aarch64/cortex-a/lwp_arch.c
index b9f9ec58dd..3df6b2f4f7 100644
--- a/components/lwp/arch/aarch64/cortex-a/lwp_arch.c
+++ b/components/lwp/arch/aarch64/cortex-a/lwp_arch.c
@@ -8,6 +8,7 @@
* 2021-05-18 Jesven first version
* 2023-07-16 Shell Move part of the codes to C from asm in signal handling
* 2023-10-16 Shell Support a new backtrace framework
+ * 2023-08-03 Shell Support of syscall restart (SA_RESTART)
*/
#include <armv8.h>
@@ -123,6 +124,7 @@ int arch_set_thread_context(void (*exit)(void), void *new_thread_stack,
#define ALGIN_BYTES (16)
+/* the layout is part of ABI, dont change it */
struct signal_ucontext
{
rt_int64_t sigreturn;
@@ -130,11 +132,62 @@ struct signal_ucontext
siginfo_t si;
- rt_align(16)
+ rt_align(ALGIN_BYTES)
struct rt_hw_exp_stack frame;
};
-void *arch_signal_ucontext_restore(rt_base_t user_sp)
+RT_STATIC_ASSERT(abi_offset_compatible, offsetof(struct signal_ucontext, si) == UCTX_ABI_OFFSET_TO_SI);
+
+void *arch_signal_ucontext_get_frame(struct signal_ucontext *uctx)
+{
+ return &uctx->frame;
+}
+
+/* internal used only */
+void arch_syscall_prepare_signal(rt_base_t rc, struct rt_hw_exp_stack *exp_frame)
+{
+ long x0 = exp_frame->x0;
+ exp_frame->x0 = rc;
+ exp_frame->x7 = x0;
+ return ;
+}
+
+void arch_syscall_restart(void *sp, void *ksp);
+
+void arch_syscall_set_errno(void *eframe, int expected, int code)
+{
+ struct rt_hw_exp_stack *exp_frame = eframe;
+ if (exp_frame->x0 == -expected)
+ exp_frame->x0 = -code;
+ return ;
+}
+
+void arch_signal_check_erestart(void *eframe, void *ksp)
+{
+ struct rt_hw_exp_stack *exp_frame = eframe;
+ long rc = exp_frame->x0;
+ long sys_id = exp_frame->x8;
+ (void)sys_id;
+
+ if (rc == -ERESTART)
+ {
+ LOG_D("%s(rc=%ld,sys_id=%ld,pid=%d)", __func__, rc, sys_id, lwp_self()->pid);
+ LOG_D("%s: restart rc = %ld", lwp_get_syscall_name(sys_id), rc);
+ exp_frame->x0 = exp_frame->x7;
+ arch_syscall_restart(eframe, ksp);
+ }
+
+ return ;
+}
+
+static void arch_signal_post_action(struct signal_ucontext *new_sp, rt_base_t kernel_sp)
+{
+ arch_signal_check_erestart(&new_sp->frame, (void *)kernel_sp);
+
+ return ;
+}
+
+void *arch_signal_ucontext_restore(rt_base_t user_sp, rt_base_t kernel_sp)
{
struct signal_ucontext *new_sp;
new_sp = (void *)user_sp;
@@ -142,6 +195,7 @@ void *arch_signal_ucontext_restore(rt_base_t user_sp)
if (lwp_user_accessable(new_sp, sizeof(*new_sp)))
{
lwp_thread_signal_mask(rt_thread_self(), LWP_SIG_MASK_CMD_SET_MASK, &new_sp->save_sigmask, RT_NULL);
+ arch_signal_post_action(new_sp, kernel_sp);
}
else
{
@@ -157,7 +211,7 @@ void *arch_signal_ucontext_save(rt_base_t user_sp, siginfo_t *psiginfo,
lwp_sigset_t *save_sig_mask)
{
struct signal_ucontext *new_sp;
- new_sp = (void *)(user_sp - sizeof(struct signal_ucontext));
+ new_sp = (void *)((user_sp - sizeof(struct signal_ucontext)) & ~0xf);
if (lwp_user_accessable(new_sp, sizeof(*new_sp)))
{
diff --git a/components/lwp/arch/aarch64/cortex-a/lwp_arch.h b/components/lwp/arch/aarch64/cortex-a/lwp_arch.h
index c7d9c8d74e..191f8e1bdc 100644
--- a/components/lwp/arch/aarch64/cortex-a/lwp_arch.h
+++ b/components/lwp/arch/aarch64/cortex-a/lwp_arch.h
@@ -11,8 +11,7 @@
#ifndef LWP_ARCH_H__
#define LWP_ARCH_H__
-#include <lwp.h>
-#include <lwp_arch_comm.h>
+#include <rtconfig.h>
#ifdef ARCH_MM_MMU
@@ -26,6 +25,13 @@
#define USER_VADDR_START 0x00200000UL
#define USER_LOAD_VADDR USER_VADDR_START
+#define UCTX_ABI_OFFSET_TO_SI 16
+
+#ifndef __ASSEMBLY__
+
+#include <lwp.h>
+#include <lwp_arch_comm.h>
+
#ifdef __cplusplus
extern "C" {
#endif
@@ -34,7 +40,7 @@ unsigned long rt_hw_ffz(unsigned long x);
rt_inline void icache_invalid_all(void)
{
- asm volatile ("ic ialluis\n\tisb sy":::"memory");
+ __asm__ volatile ("ic ialluis\n\tisb sy":::"memory");
}
/**
@@ -57,11 +63,14 @@ void *arch_signal_ucontext_save(rt_base_t user_sp, siginfo_t *psiginfo,
* @param user_sp sp of user
* @return void*
*/
-void *arch_signal_ucontext_restore(rt_base_t user_sp);
+void *arch_signal_ucontext_restore(rt_base_t user_sp, rt_base_t kernel_sp);
+void arch_syscall_restart(void *sp, void *ksp);
+
#ifdef __cplusplus
}
#endif
+#endif /* __ASSEMBLY__ */
-#endif
+#endif /* ARCH_MM_MMU */
#endif /*LWP_ARCH_H__*/
diff --git a/components/lwp/arch/aarch64/cortex-a/lwp_gcc.S b/components/lwp/arch/aarch64/cortex-a/lwp_gcc.S
index e33127b62b..f5a93698d9 100644
--- a/components/lwp/arch/aarch64/cortex-a/lwp_gcc.S
+++ b/components/lwp/arch/aarch64/cortex-a/lwp_gcc.S
@@ -7,6 +7,7 @@
* Date Author Notes
* 2021-05-18 Jesven first version
* 2023-07-16 Shell Move part of the codes to C from asm in signal handling
+ * 2023-08-03 Shell Support of syscall restart (SA_RESTART)
*/
#ifndef __ASSEMBLY__
@@ -17,6 +18,7 @@
#include "asm-generic.h"
#include "asm-fpu.h"
#include "armv8.h"
+#include "lwp_arch.h"
/*********************
* SPSR BIT *
@@ -155,6 +157,14 @@ START_POINT_END(SVC_Handler)
.global arch_syscall_exit
arch_syscall_exit:
+
+ /**
+ * @brief back up former x0 which is required to restart syscall, then setup
+ * syscall return value in stack frame
+ */
+ mov x1, sp
+ bl arch_syscall_prepare_signal
+
msr daifset, #3
ldp x2, x3, [sp], #0x10 /* SPSR and ELR. */
@@ -177,7 +187,10 @@ arch_syscall_exit:
ldp x12, x13, [sp], #0x10
ldp x10, x11, [sp], #0x10
ldp x8, x9, [sp], #0x10
- add sp, sp, #0x40
+ ldp x6, x7, [sp], #0x10
+ ldp x4, x5, [sp], #0x10
+ ldp x2, x3, [sp], #0x10
+ ldp x0, x1, [sp], #0x10
RESTORE_FPU sp
/* the sp is reset to the outer most level, irq and fiq are disabled */
@@ -383,10 +396,53 @@ lwp_check_debug_quit:
ldp x29, x30, [sp], #0x10
ret
+.global arch_syscall_restart
+arch_syscall_restart:
+ msr daifset, 3
+
+ mov sp, x1
+ /* drop exception frame in user stack */
+ msr sp_el0, x0
+
+ /* restore previous exception frame */
+ msr spsel, #0
+
+ ldp x2, x3, [sp], #0x10
+ msr elr_el1, x2
+ msr spsr_el1, x3
+
+ ldp x29, x30, [sp], #0x10
+
+ ldp x28, x29, [sp], #0x10
+ msr fpcr, x28
+ msr fpsr, x29
+
+ ldp x28, x29, [sp], #0x10
+ ldp x26, x27, [sp], #0x10
+ ldp x24, x25, [sp], #0x10
+ ldp x22, x23, [sp], #0x10
+ ldp x20, x21, [sp], #0x10
+ ldp x18, x19, [sp], #0x10
+ ldp x16, x17, [sp], #0x10
+ ldp x14, x15, [sp], #0x10
+ ldp x12, x13, [sp], #0x10
+ ldp x10, x11, [sp], #0x10
+ ldp x8, x9, [sp], #0x10
+ ldp x6, x7, [sp], #0x10
+ ldp x4, x5, [sp], #0x10
+ ldp x2, x3, [sp], #0x10
+ ldp x0, x1, [sp], #0x10
+ RESTORE_FPU sp
+
+ msr spsel, #1
+
+ b vector_exception
+
arch_signal_quit:
/* drop current exception frame */
add sp, sp, #CONTEXT_SIZE
+ mov x1, sp
mrs x0, sp_el0
bl arch_signal_ucontext_restore
add x0, x0, #-CONTEXT_SIZE
@@ -456,6 +512,11 @@ arch_thread_signal_enter:
/* arch_signal_ucontext_save(user_sp, psiginfo, exp_frame, save_sig_mask); */
bl arch_signal_ucontext_save
+ mov x22, x0
+ /* get and saved pointer to uframe */
+ bl arch_signal_ucontext_get_frame
+ mov x2, x0
+ mov x0, x22
dc cvau, x0
dsb sy
@@ -483,12 +544,15 @@ arch_thread_signal_enter:
/** set the return address to the sigreturn */
mov x30, x0
+ cbnz x21, 1f
+ mov x21, x30
+1:
/** set the entry address of signal handler */
msr elr_el1, x21
/* siginfo is above the return address */
- add x2, x30, 16
- add x1, x2, #CONTEXT_SIZE
+ add x1, x30, UCTX_ABI_OFFSET_TO_SI
+ /* uframe is saved in x2 */
mov x0, x19
/**
diff --git a/components/lwp/arch/arm/cortex-a/lwp_arch.c b/components/lwp/arch/arm/cortex-a/lwp_arch.c
index 0d8f965429..0582ea70b7 100644
--- a/components/lwp/arch/arm/cortex-a/lwp_arch.c
+++ b/components/lwp/arch/arm/cortex-a/lwp_arch.c
@@ -192,6 +192,12 @@ void *arch_signal_ucontext_save(rt_base_t lr, siginfo_t *psiginfo,
return new_sp;
}
+void arch_syscall_set_errno(void *eframe, int expected, int code)
+{
+ /* NO support */
+ return ;
+}
+
#ifdef LWP_ENABLE_ASID
#define MAX_ASID_BITS 8
#define MAX_ASID (1 << MAX_ASID_BITS)
diff --git a/components/lwp/arch/arm/cortex-a/lwp_arch.h b/components/lwp/arch/arm/cortex-a/lwp_arch.h
index 7262f446c8..6f5518e4c4 100644
--- a/components/lwp/arch/arm/cortex-a/lwp_arch.h
+++ b/components/lwp/arch/arm/cortex-a/lwp_arch.h
@@ -35,7 +35,7 @@ rt_inline unsigned long rt_hw_ffz(unsigned long x)
rt_inline void icache_invalid_all(void)
{
- asm volatile ("mcr p15, 0, r0, c7, c5, 0\ndsb\nisb":::"memory");//iciallu
+ __asm__ volatile ("mcr p15, 0, r0, c7, c5, 0\ndsb\nisb":::"memory");//iciallu
}
unsigned int arch_get_asid(struct rt_lwp *lwp);
diff --git a/components/lwp/arch/risc-v/rv64/lwp_arch.c b/components/lwp/arch/risc-v/rv64/lwp_arch.c
index 4b3dfc2e19..62383f91fa 100644
--- a/components/lwp/arch/risc-v/rv64/lwp_arch.c
+++ b/components/lwp/arch/risc-v/rv64/lwp_arch.c
@@ -242,6 +242,7 @@ int arch_set_thread_context(void (*exit)(void), void *new_thread_stack,
* | |
* +------------------------+ --> thread sp
*/
+ return 0;
}
#define ALGIN_BYTES (16)
@@ -317,6 +318,12 @@ void *arch_signal_ucontext_save(int signo, siginfo_t *psiginfo,
return new_sp;
}
+void arch_syscall_set_errno(void *eframe, int expected, int code)
+{
+ /* NO support */
+ return ;
+}
+
/**
* void lwp_exec_user(void *args, void *kernel_stack, void *user_entry)
*/
diff --git a/components/lwp/libc_musl.h b/components/lwp/libc_musl.h
index 3129a0a6b7..fbc2ad5cb4 100644
--- a/components/lwp/libc_musl.h
+++ b/components/lwp/libc_musl.h
@@ -28,9 +28,9 @@
#define FUTEX_CLOCK_REALTIME 256
-#define FUTEX_WAITERS 0x80000000
-#define FUTEX_OWNER_DIED 0x40000000
-#define FUTEX_TID_MASK 0x3fffffff
+#define FUTEX_WAITERS 0x80000000
+#define FUTEX_OWNER_DIED 0x40000000
+#define FUTEX_TID_MASK 0x3fffffff
struct robust_list
{
diff --git a/components/lwp/lwp.c b/components/lwp/lwp.c
index 7e85aaff9e..27d9f2f234 100644
--- a/components/lwp/lwp.c
+++ b/components/lwp/lwp.c
@@ -12,6 +12,8 @@
* 2023-02-20 wangxiaoyao inv icache before new app startup
* 2023-02-20 wangxiaoyao fix bug on foreground app switch
* 2023-10-16 Shell Support a new backtrace framework
+ * 2023-11-17 xqyjlj add process group and session support
+ * 2023-11-30 Shell add lwp_startup()
*/
#define DBG_TAG "lwp"
@@ -39,7 +41,7 @@
#include "lwp_arch_comm.h"
#include "lwp_signal.h"
#include "lwp_dbg.h"
-#include "console.h"
+#include <terminal/terminal.h>
#ifdef ARCH_MM_MMU
#include <lwp_user_mm.h>
@@ -59,16 +61,31 @@ static const char elf_magic[] = {0x7f, 'E', 'L', 'F'};
#ifdef DFS_USING_WORKDIR
extern char working_directory[];
#endif
-static struct termios stdin_termios, old_stdin_termios;
-int load_ldso(struct rt_lwp *lwp, char *exec_name, char *const argv[], char *const envp[]);
-
-struct termios *get_old_termios(void)
+/**
+ * @brief The default console is only a backup device with lowest priority.
+ * It's always recommended to scratch the console from the boot arguments.
+ * And dont forget to register the device with a higher priority.
+ */
+static rt_err_t lwp_default_console_setup(void)
{
- return &old_stdin_termios;
+ rt_device_t bakdev = rt_device_find("ttyS0");
+ rt_err_t rc;
+
+ if (bakdev)
+ {
+ lwp_console_register_backend(bakdev, LWP_CONSOLE_LOWEST_PRIOR);
+ rc = RT_EOK;
+ }
+ else
+ {
+ rc = -RT_EINVAL;
+ }
+
+ return rc;
}
-int lwp_component_init(void)
+static int lwp_component_init(void)
{
int rc;
if ((rc = lwp_tid_init()) != RT_EOK)
@@ -83,10 +100,99 @@ int lwp_component_init(void)
{
LOG_E("%s: rt_channel_component_init failed", __func__);
}
+ else if ((rc = lwp_futex_init()) != RT_EOK)
+ {
+ LOG_E("%s: lwp_futex_init() failed", __func__);
+ }
+ else if ((rc = lwp_default_console_setup()) != RT_EOK)
+ {
+ LOG_E("%s: lwp_default_console_setup() failed", __func__);
+ }
return rc;
}
INIT_COMPONENT_EXPORT(lwp_component_init);
+rt_weak int lwp_startup_debug_request(void)
+{
+ return 0;
+}
+
+#define LATENCY_TIMES (3)
+#define LATENCY_IN_MSEC (128)
+#define LWP_CONSOLE_PATH "CONSOLE=/dev/console"
+const char *init_search_path[] = {
+ "/sbin/init",
+ "/bin/init",
+};
+
+/**
+ * Startup process 0 and do the essential works
+ * This is the "Hello World" point of RT-Smart
+ */
+static int lwp_startup(void)
+{
+ int error;
+
+ const char *init_path;
+ char *argv[] = {0, "&"};
+ char *envp[] = {LWP_CONSOLE_PATH, 0};
+
+#ifdef LWP_DEBUG
+ int command;
+ int countdown = LATENCY_TIMES;
+ while (countdown)
+ {
+ command = lwp_startup_debug_request();
+ if (command)
+ {
+ return 0;
+ }
+ rt_kprintf("Press any key to stop init process startup ... %d\n", countdown);
+ countdown -= 1;
+ rt_thread_mdelay(LATENCY_IN_MSEC);
+ }
+ rt_kprintf("Starting init ...\n");
+#endif
+
+ for (size_t i = 0; i < sizeof(init_search_path)/sizeof(init_search_path[0]); i++)
+ {
+ struct stat s;
+ init_path = init_search_path[i];
+ error = stat(init_path, &s);
+ if (error == 0)
+ {
+ argv[0] = (void *)init_path;
+ error = lwp_execve((void *)init_path, 0, sizeof(argv)/sizeof(argv[0]), argv, envp);
+ if (error < 0)
+ {
+ LOG_E("%s: failed to startup process 0 (init)\n"
+ "Switching to legacy mode...", __func__);
+ }
+ else if (error != 1)
+ {
+ LOG_E("%s: pid 1 is already allocated", __func__);
+ error = -EBUSY;
+ }
+ else
+ {
+ rt_lwp_t p = lwp_from_pid_locked(1);
+ p->sig_protected = 0;
+
+ error = 0;
+ }
+ break;
+ }
+ }
+
+ if (error)
+ {
+ LOG_E("%s: init program not found\n"
+ "Switching to legacy mode...", __func__);
+ }
+ return error;
+}
+INIT_APP_EXPORT(lwp_startup);
+
void lwp_setcwd(char *buf)
{
struct rt_lwp *lwp = RT_NULL;
@@ -100,11 +206,11 @@ void lwp_setcwd(char *buf)
lwp = (struct rt_lwp *)rt_thread_self()->lwp;
if (lwp)
{
- rt_strncpy(lwp->working_directory, buf, DFS_PATH_MAX);
+ rt_strncpy(lwp->working_directory, buf, DFS_PATH_MAX - 1);
}
else
{
- rt_strncpy(working_directory, buf, DFS_PATH_MAX);
+ rt_strncpy(working_directory, buf, DFS_PATH_MAX - 1);
}
return ;
@@ -114,8 +220,13 @@ char *lwp_getcwd(void)
{
char *dir_buf = RT_NULL;
struct rt_lwp *lwp = RT_NULL;
+ rt_thread_t thread = rt_thread_self();
+
+ if (thread)
+ {
+ lwp = (struct rt_lwp *)thread->lwp;
+ }
- lwp = (struct rt_lwp *)rt_thread_self()->lwp;
if (lwp)
{
if(lwp->working_directory[0] != '/')
@@ -1077,25 +1188,35 @@ void lwp_cleanup(struct rt_thread *tid)
return;
}
-static void lwp_copy_stdio_fdt(struct rt_lwp *lwp)
+static void lwp_execve_setup_stdio(struct rt_lwp *lwp)
{
- struct dfs_file *d;
struct dfs_fdtable *lwp_fdt;
+ struct dfs_file *cons_file;
+ int cons_fd;
lwp_fdt = &lwp->fdt;
+
+ /* open console */
+ cons_fd = open("/dev/console", O_RDWR);
+ if (cons_fd < 0)
+ {
+ LOG_E("%s: Cannot open console tty", __func__);
+ return ;
+ }
+ LOG_D("%s: open console as fd %d", __func__, cons_fd);
+
/* init 4 fds */
lwp_fdt->fds = rt_calloc(4, sizeof(void *));
if (lwp_fdt->fds)
{
+ cons_file = fd_get(cons_fd);
lwp_fdt->maxfd = 4;
- d = fd_get(0);
- fdt_fd_associate_file(lwp_fdt, 0, d);
- d = fd_get(1);
- fdt_fd_associate_file(lwp_fdt, 1, d);
- d = fd_get(2);
- fdt_fd_associate_file(lwp_fdt, 2, d);
+ fdt_fd_associate_file(lwp_fdt, 0, cons_file);
+ fdt_fd_associate_file(lwp_fdt, 1, cons_file);
+ fdt_fd_associate_file(lwp_fdt, 2, cons_file);
}
+ close(cons_fd);
return;
}
@@ -1197,11 +1318,8 @@ pid_t lwp_execve(char *filename, int debug, int argc, char **argv, char **envp)
int result;
struct rt_lwp *lwp;
char *thread_name;
- char *argv_last = argv[argc - 1];
- int bg = 0;
struct process_aux *aux;
int tid = 0;
- int ret;
if (filename == RT_NULL)
{
@@ -1213,7 +1331,7 @@ pid_t lwp_execve(char *filename, int debug, int argc, char **argv, char **envp)
return -EACCES;
}
- lwp = lwp_create(LWP_CREATE_FLAG_ALLOC_PID);
+ lwp = lwp_create(LWP_CREATE_FLAG_ALLOC_PID | LWP_CREATE_FLAG_NOTRACE_EXEC);
if (lwp == RT_NULL)
{
@@ -1236,12 +1354,6 @@ pid_t lwp_execve(char *filename, int debug, int argc, char **argv, char **envp)
}
#endif
- if (argv_last[0] == '&' && argv_last[1] == '\0')
- {
- argc--;
- bg = 1;
- }
-
if ((aux = lwp_argscopy(lwp, argc, argv, envp)) == RT_NULL)
{
lwp_tid_put(tid);
@@ -1263,7 +1375,7 @@ pid_t lwp_execve(char *filename, int debug, int argc, char **argv, char **envp)
rt_thread_t thread = RT_NULL;
rt_uint32_t priority = 25, tick = 200;
- lwp_copy_stdio_fdt(lwp);
+ lwp_execve_setup_stdio(lwp);
/* obtain the base name */
thread_name = strrchr(filename, '/');
@@ -1284,88 +1396,46 @@ pid_t lwp_execve(char *filename, int debug, int argc, char **argv, char **envp)
if (thread != RT_NULL)
{
struct rt_lwp *self_lwp;
+ rt_session_t session;
+ rt_processgroup_t group;
thread->tid = tid;
lwp_tid_set_thread(tid, thread);
LOG_D("lwp kernel => (0x%08x, 0x%08x)\n", (rt_size_t)thread->stack_addr,
(rt_size_t)thread->stack_addr + thread->stack_size);
self_lwp = lwp_self();
+
+ /* when create init, self_lwp == null */
+ if (self_lwp == RT_NULL && lwp_to_pid(lwp) != 1)
+ {
+ self_lwp = lwp_from_pid_and_lock(1);
+ }
+
if (self_lwp)
{
- //lwp->tgroup_leader = &thread; //add thread group leader for lwp
- lwp->__pgrp = tid;
- lwp->session = self_lwp->session;
/* lwp add to children link */
lwp_children_register(self_lwp, lwp);
}
- else
- {
- //lwp->tgroup_leader = &thread; //add thread group leader for lwp
- lwp->__pgrp = tid;
- }
- if (!bg)
- {
- if (lwp->session == -1)
- {
- struct tty_struct *tty = RT_NULL;
- struct rt_lwp *old_lwp;
- tty = (struct tty_struct *)console_tty_get();
- old_lwp = tty->foreground;
- if (old_lwp)
- {
- rt_mutex_take(&tty->lock, RT_WAITING_FOREVER);
- ret = tty_push(&tty->head, old_lwp);
- rt_mutex_release(&tty->lock);
- if (ret < 0)
- {
- lwp_tid_put(tid);
- lwp_ref_dec(lwp);
- LOG_E("malloc fail!\n");
- return -ENOMEM;
- }
- }
+ session = RT_NULL;
+ group = RT_NULL;
- lwp->tty = tty;
- lwp->tty->pgrp = lwp->__pgrp;
- lwp->tty->session = lwp->session;
- lwp->tty->foreground = lwp;
- tcgetattr(1, &stdin_termios);
- old_stdin_termios = stdin_termios;
- stdin_termios.c_lflag |= ICANON | ECHO | ECHOCTL;
- tcsetattr(1, 0, &stdin_termios);
+ group = lwp_pgrp_create(lwp);
+ if (group)
+ {
+ lwp_pgrp_insert(group, lwp);
+ if (self_lwp == RT_NULL)
+ {
+ session = lwp_session_create(lwp);
+ lwp_session_insert(session, group);
}
else
{
- if (self_lwp != RT_NULL)
- {
- rt_mutex_take(&self_lwp->tty->lock, RT_WAITING_FOREVER);
- ret = tty_push(&self_lwp->tty->head, self_lwp);
- rt_mutex_release(&self_lwp->tty->lock);
- if (ret < 0)
- {
- lwp_tid_put(tid);
- lwp_ref_dec(lwp);
- LOG_E("malloc fail!\n");
- return -ENOMEM;
- }
-
- lwp->tty = self_lwp->tty;
- lwp->tty->pgrp = lwp->__pgrp;
- lwp->tty->session = lwp->session;
- lwp->tty->foreground = lwp;
- }
- else
- {
- lwp->tty = RT_NULL;
- }
-
+ session = lwp_session_find(lwp_sid_get_byprocess(self_lwp));
+ lwp_session_insert(session, group);
}
}
- else
- {
- lwp->background = RT_TRUE;
- }
+
thread->lwp = lwp;
#ifndef ARCH_MM_MMU
struct lwp_app_head *app_head = (struct lwp_app_head*)lwp->text_entry;
@@ -1381,6 +1451,8 @@ pid_t lwp_execve(char *filename, int debug, int argc, char **argv, char **envp)
#endif /* not defined ARCH_MM_MMU */
rt_list_insert_after(&lwp->t_grp, &thread->sibling);
+ lwp->did_exec = RT_TRUE;
+
if (debug && rt_dbg_ops)
{
lwp->debug = debug;
@@ -1482,30 +1554,30 @@ rt_err_t lwp_backtrace_frame(rt_thread_t uthread, struct rt_hw_backtrace_frame *
char **argv;
rt_lwp_t lwp;
- if (uthread->lwp)
+ if (uthread && uthread->lwp && rt_scheduler_is_available())
{
lwp = uthread->lwp;
argv = lwp_get_command_line_args(lwp);
if (argv)
{
- LOG_RAW("please use: addr2line -e %s -a -f", argv[0]);
+ rt_kprintf("please use: addr2line -e %s -a -f", argv[0]);
lwp_free_command_line_args(argv);
}
else
{
- LOG_RAW("please use: addr2line -e %s -a -f", lwp->cmd);
+ rt_kprintf("please use: addr2line -e %s -a -f", lwp->cmd);
}
while (nesting < RT_BACKTRACE_LEVEL_MAX_NR)
{
- LOG_RAW(" 0x%lx", frame->pc);
+ rt_kprintf(" 0x%lx", frame->pc);
if (rt_hw_backtrace_frame_unwind(uthread, frame))
{
break;
}
nesting++;
}
- LOG_RAW("\n");
+ rt_kprintf("\n");
rc = RT_EOK;
}
return rc;
diff --git a/components/lwp/lwp.h b/components/lwp/lwp.h
index 1ac98493a1..c504ef60ad 100644
--- a/components/lwp/lwp.h
+++ b/components/lwp/lwp.h
@@ -9,6 +9,9 @@
* 2019-10-12 Jesven Add MMU and userspace support
* 2020-10-08 Bernard Architecture and code cleanup
* 2021-08-26 linzhenxing add lwp_setcwd\lwp_getcwd
+ * 2023-11-17 xqyjlj add process group and session support
+ * 2023-12-02 Shell Add macro to create lwp status and
+ * fix dead lock problem on pgrp
*/
/*
@@ -48,10 +51,6 @@
#include <locale.h>
#endif /* RT_USING_MUSLLIBC */
-#ifdef RT_USING_TTY
-struct tty_struct;
-#endif /* RT_USING_TTY */
-
#ifdef __cplusplus
extern "C" {
#endif
@@ -76,12 +75,48 @@ struct rt_lwp_notify
rt_slist_t list_node;
};
+struct lwp_tty;
+
#ifdef RT_USING_MUSLLIBC
-#define LWP_CREATE_STAT(exit_code) (((exit_code) & 0xff) << 8)
+#define LWP_COREDUMP_FLAG 0x80
+#define LWP_CREATE_STAT_EXIT(exit_code) (((exit_code)&0xff) << 8)
+#define LWP_CREATE_STAT_SIGNALED(signo, coredump) (((signo) & 0x7f) | (coredump ? LWP_COREDUMP_FLAG : 0))
+#define LWP_CREATE_STAT_STOPPED(signo) (LWP_CREATE_STAT_EXIT(signo) | 0x7f)
+#define LWP_CREATE_STAT_CONTINUED (0xffff)
#else
#error "No compatible lwp set status provided for this libc"
#endif
+typedef struct rt_lwp *rt_lwp_t;
+typedef struct rt_session *rt_session_t;
+typedef struct rt_processgroup *rt_processgroup_t;
+
+struct rt_session {
+ struct rt_object object;
+ rt_lwp_t leader;
+ rt_list_t processgroup;
+ pid_t sid;
+ pid_t foreground_pgid;
+ struct rt_mutex mutex;
+ struct lwp_tty *ctty;
+};
+
+struct rt_processgroup {
+ struct rt_object object;
+ rt_lwp_t leader;
+ rt_list_t process;
+ rt_list_t pgrp_list_node;
+ pid_t pgid;
+ pid_t sid;
+ struct rt_session *session;
+ struct rt_mutex mutex;
+
+ rt_atomic_t ref;
+
+ /* flags on process group */
+ unsigned int is_orphaned:1;
+};
+
struct rt_lwp
{
#ifdef ARCH_MM_MMU
@@ -100,14 +135,21 @@ struct rt_lwp
uint8_t lwp_type;
uint8_t reserv[3];
- struct rt_lwp *parent;
- struct rt_lwp *first_child;
- struct rt_lwp *sibling;
+ /* flags */
+ unsigned int terminated:1;
+ unsigned int background:1;
+ unsigned int term_ctrlterm:1; /* have control terminal? */
+ unsigned int did_exec:1; /* Whether exec has been performed */
+ unsigned int jobctl_stopped:1; /* job control: current proc is stopped */
+ unsigned int wait_reap_stp:1; /* job control: has wait event for parent */
+ unsigned int sig_protected:1; /* signal: protected proc cannot be killed or stopped */
- rt_list_t wait_list;
- rt_bool_t terminated;
- rt_bool_t background;
- int lwp_ret;
+ struct rt_lwp *parent; /* parent process */
+ struct rt_lwp *first_child; /* first child process */
+ struct rt_lwp *sibling; /* sibling(child) process */
+
+ struct rt_wqueue waitpid_waiters;
+ lwp_status_t lwp_status;
void *text_entry;
uint32_t text_size;
@@ -118,15 +160,16 @@ struct rt_lwp
void *args;
uint32_t args_length;
pid_t pid;
- pid_t __pgrp; /*Accessed via process_group()*/
- pid_t tty_old_pgrp;
- pid_t session;
- rt_list_t t_grp;
- rt_list_t timer; /* POSIX timer object binding to a process */
+ pid_t sid; /* session ID */
+ pid_t pgid; /* process group ID */
+ struct rt_processgroup *pgrp;
+ rt_list_t pgrp_node; /* process group node */
+ rt_list_t t_grp; /* thread group */
+ rt_list_t timer; /* POSIX timer object binding to a process */
- int leader; /* boolean value for session group_leader*/
struct dfs_fdtable fdt;
char cmd[RT_NAME_MAX];
+ char *exe_file; /* process file path */
/* POSIX signal */
struct lwp_signal signal;
@@ -135,7 +178,7 @@ struct rt_lwp
struct rt_mutex object_mutex;
struct rt_user_context user_ctx;
- struct rt_wqueue wait_queue; /*for console */
+ struct rt_wqueue wait_queue; /* for console */
struct tty_struct *tty; /* NULL if no tty */
struct lwp_avl_struct *address_search_head; /* for addressed object fast search */
@@ -152,8 +195,9 @@ struct rt_lwp
uint64_t generation;
unsigned int asid;
#endif
+ struct rusage rt_rusage;
};
-typedef struct rt_lwp *rt_lwp_t;
+
struct rt_lwp *lwp_self(void);
rt_err_t lwp_children_register(struct rt_lwp *parent, struct rt_lwp *child);
@@ -182,7 +226,7 @@ void lwp_tid_put(int tid);
* @return rt_thread_t
*/
rt_thread_t lwp_tid_get_thread_and_inc_ref(int tid);
-
+rt_thread_t lwp_tid_get_thread_raw(int tid);
/**
* @brief Decrease a reference count
*
@@ -216,6 +260,78 @@ rt_err_t lwp_futex_init(void);
rt_err_t lwp_futex(struct rt_lwp *lwp, int *uaddr, int op, int val,
const struct timespec *timeout, int *uaddr2, int val3);
+/* processgroup api */
+rt_inline pid_t lwp_pgid_get_bypgrp(rt_processgroup_t group)
+{
+ return group ? group->pgid : 0;
+}
+
+rt_inline pid_t lwp_pgid_get_byprocess(rt_lwp_t process)
+{
+ return process ? process->pgid : 0;
+}
+rt_processgroup_t lwp_pgrp_find(pid_t pgid);
+void lwp_pgrp_dec_ref(rt_processgroup_t pgrp);
+rt_processgroup_t lwp_pgrp_find_and_inc_ref(pid_t pgid);
+rt_processgroup_t lwp_pgrp_create(rt_lwp_t leader);
+int lwp_pgrp_delete(rt_processgroup_t group);
+
+/**
+ * Note: all the pgrp with process operation must be called in the context where
+ * process lock is taken. This is protect us from a possible dead lock condition
+ *
+ * The order is mandatory in the case:
+ * PGRP_LOCK(pgrp);
+ * LWP_LOCK(p);
+ * ... bussiness logic
+ * LWP_UNLOCK(p);
+ * PGRP_UNLOCK(pgrp);
+ */
+int lwp_pgrp_insert(rt_processgroup_t group, rt_lwp_t process);
+int lwp_pgrp_remove(rt_processgroup_t group, rt_lwp_t process);
+int lwp_pgrp_move(rt_processgroup_t group, rt_lwp_t process);
+
+int lwp_pgrp_update_children_info(rt_processgroup_t group, pid_t sid, pid_t pgid);
+
+/* session api */
+rt_inline pid_t lwp_sid_get_bysession(rt_session_t session)
+{
+ return session ? session->sid : 0;
+}
+
+rt_inline pid_t lwp_sid_get_bypgrp(rt_processgroup_t group)
+{
+ return group ? group->sid : 0;
+}
+
+rt_inline pid_t lwp_sid_get_byprocess(rt_lwp_t process)
+{
+ return process ? process->sid : 0;
+}
+
+rt_session_t lwp_session_find(pid_t sid);
+rt_session_t lwp_session_create(struct rt_lwp *leader);
+int lwp_session_delete(rt_session_t session);
+
+/**
+ * Note: all the session operation must be called in the context where
+ * process lock is taken. This is protect us from a possible dead lock condition
+ *
+ * The order is mandatory in the case:
+ * PGRP_LOCK(pgrp);
+ * LWP_LOCK(p);
+ * ... bussiness logic
+ * LWP_UNLOCK(p);
+ * PGRP_UNLOCK(pgrp);
+ */
+int lwp_session_insert(rt_session_t session, rt_processgroup_t group);
+int lwp_session_remove(rt_session_t session, rt_processgroup_t group);
+int lwp_session_move(rt_session_t session, rt_processgroup_t group);
+int lwp_session_update_children_info(rt_session_t session, pid_t sid);
+int lwp_session_set_foreground(rt_session_t session, pid_t pgid);
+
+/* complete the job control related bussiness on process exit */
+void lwp_jobctrl_on_exit(struct rt_lwp *lwp);
#ifdef __cplusplus
}
@@ -301,6 +417,7 @@ int dbg_step_type(void);
void dbg_attach_req(void *pc);
int dbg_check_suspend(void);
void rt_hw_set_process_id(int pid);
+void lwp_futex_exit_robust_list(rt_thread_t thread);
/* backtrace service */
rt_err_t lwp_backtrace_frame(rt_thread_t uthread, struct rt_hw_backtrace_frame *frame);
diff --git a/components/lwp/lwp_arch_comm.h b/components/lwp/lwp_arch_comm.h
index 885cd63ca9..3eec407862 100644
--- a/components/lwp/lwp_arch_comm.h
+++ b/components/lwp/lwp_arch_comm.h
@@ -61,6 +61,10 @@ rt_noreturn void arch_thread_signal_enter(int signo, siginfo_t *psiginfo,
void *exp_frame, void *entry_uaddr,
lwp_sigset_t *save_sig_mask);
+void arch_signal_check_erestart(void *eframe, void *ksp);
+
+void arch_syscall_set_errno(void *eframe, int expected, int code);
+
int arch_backtrace_uthread(rt_thread_t thread);
#endif /* __LWP_ARCH_COMM__ */
diff --git a/components/lwp/lwp_elf.c b/components/lwp/lwp_elf.c
index 0610769b26..f654adc5b7 100644
--- a/components/lwp/lwp_elf.c
+++ b/components/lwp/lwp_elf.c
@@ -797,6 +797,7 @@ int lwp_load(const char *filename, struct rt_lwp *lwp, uint8_t *load_addr, size_
/* copy file name to process name */
rt_strncpy(lwp->cmd, filename, RT_NAME_MAX);
+ lwp->exe_file = dfs_normalize_path(NULL, filename); // malloc
ret = elf_file_load(&load_info);
if (ret != RT_EOK)
diff --git a/components/lwp/lwp_futex.c b/components/lwp/lwp_futex.c
index f32b4a18f4..a49a86680a 100644
--- a/components/lwp/lwp_futex.c
+++ b/components/lwp/lwp_futex.c
@@ -475,6 +475,11 @@ static long _futex_wake(rt_futex_t futex, struct rt_lwp *lwp, int number,
{
number--;
woken_cnt++;
+ is_empty = RT_FALSE;
+ }
+ else
+ {
+ is_empty = RT_TRUE;
}
_futex_unlock(lwp, op_flags);
}
@@ -512,23 +517,16 @@ static long _futex_requeue(rt_futex_t futex1, rt_futex_t futex2,
*/
while (nr_wake && !is_empty)
{
- rt_sched_lock_level_t slvl;
- rt_sched_lock(&slvl);
- is_empty = rt_list_isempty(&(futex1->waiting_thread));
- if (!is_empty)
+ if (rt_susp_list_dequeue(&futex1->waiting_thread, RT_EOK))
{
- thread = RT_THREAD_LIST_NODE_ENTRY(futex1->waiting_thread.next);
- /* remove from waiting list */
- rt_list_remove(&RT_THREAD_LIST_NODE(thread));
-
- thread->error = RT_EOK;
- /* resume the suspended thread */
- rt_thread_resume(thread);
-
nr_wake--;
woken_cnt++;
+ is_empty = RT_FALSE;
+ }
+ else
+ {
+ is_empty = RT_TRUE;
}
- rt_sched_unlock(slvl);
}
rtn = woken_cnt;
@@ -542,7 +540,10 @@ static long _futex_requeue(rt_futex_t futex1, rt_futex_t futex2,
{
rt_sched_lock_level_t slvl;
rt_sched_lock(&slvl);
+
+ /* moving from one susp list to another */
is_empty = rt_list_isempty(&(futex1->waiting_thread));
+
if (!is_empty)
{
thread = RT_THREAD_LIST_NODE_ENTRY(futex1->waiting_thread.next);
@@ -914,7 +915,7 @@ void lwp_futex_exit_robust_list(rt_thread_t thread)
rc = _fetch_robust_entry(&next_entry, &entry->next, &next_pi);
if (entry != pending)
{
- if (_handle_futex_death((void *)entry + futex_offset, thread, pi,
+ if (_handle_futex_death((int *)((size_t)entry + futex_offset), thread, pi,
RT_FALSE))
return;
}
diff --git a/components/lwp/lwp_internal.c b/components/lwp/lwp_internal.c
index bb31650790..e460a1ac3f 100644
--- a/components/lwp/lwp_internal.c
+++ b/components/lwp/lwp_internal.c
@@ -6,6 +6,7 @@
* Change Logs:
* Date Author Notes
* 2023-07-25 Shell first version
+ * 2023-11-25 Shell Add pgrp, session lock API
*/
#define DBG_TAG "lwp.internal"
@@ -15,7 +16,7 @@
#include <stdlib.h>
#include "lwp_internal.h"
-static rt_err_t _mutex_take_safe(rt_mutex_t mtx, rt_int32_t timeout, rt_bool_t interruptable)
+static rt_err_t _mutex_take_safe(rt_mutex_t mtx, rt_int32_t timeout, int flags)
{
LWP_DEF_RETURN_CODE(rc);
int retry;
@@ -45,15 +46,15 @@ static rt_err_t _mutex_take_safe(rt_mutex_t mtx, rt_int32_t timeout, rt_bool_t i
do {
retry = 0;
- if (interruptable)
+ if (flags & LWP_MTX_FLAGS_INTR)
rc = rt_mutex_take_interruptible(mtx, effect_timeout);
else
- rc = rt_mutex_take(mtx, effect_timeout);
+ rc = rt_mutex_take_killable(mtx, effect_timeout);
#ifdef LWP_DEBUG
if (rc == RT_EOK)
{
- if (rt_mutex_get_hold(mtx) > 1)
+ if (!(flags & LWP_MTX_FALGS_NESTED) && rt_mutex_get_hold(mtx) > 1)
{
LOG_W("Already hold the lock");
rt_backtrace();
@@ -88,6 +89,7 @@ static rt_err_t _mutex_take_safe(rt_mutex_t mtx, rt_int32_t timeout, rt_bool_t i
}
else
{
+ rc = -RT_EINVAL;
LOG_W("%s: mtx should not be NULL", __func__);
RT_ASSERT(0);
}
@@ -95,10 +97,10 @@ static rt_err_t _mutex_take_safe(rt_mutex_t mtx, rt_int32_t timeout, rt_bool_t i
LWP_RETURN(rc);
}
-rt_err_t lwp_mutex_take_safe(rt_mutex_t mtx, rt_int32_t timeout, rt_bool_t interruptable)
+rt_err_t lwp_mutex_take_safe(rt_mutex_t mtx, rt_int32_t timeout, int flags)
{
LWP_DEF_RETURN_CODE(rc);
- rc = _mutex_take_safe(mtx, timeout, interruptable);
+ rc = _mutex_take_safe(mtx, timeout, flags);
LWP_RETURN(rc);
}
@@ -116,18 +118,17 @@ rt_err_t lwp_mutex_release_safe(rt_mutex_t mtx)
LWP_RETURN(rc);
}
-rt_err_t lwp_critical_enter(struct rt_lwp *lwp)
+rt_err_t lwp_critical_enter(struct rt_lwp *lwp, int flags)
{
rt_err_t rc;
- rc = lwp_mutex_take_safe(&lwp->lwp_lock, RT_WAITING_FOREVER, 0);
+ do {
+ rc = lwp_mutex_take_safe(&lwp->lwp_lock, RT_WAITING_FOREVER, flags);
+ } while (rc != RT_EOK && !(flags & LWP_MTX_FLAGS_INTR) && rc == -RT_EINTR);
/* if current process is force killed */
- if (rc != RT_EOK)
+ if (rc != RT_EOK && rc != -RT_EINTR)
{
- if (rc == -RT_EINTR && lwp_self() != RT_NULL)
- sys_exit(EXIT_SUCCESS);
- else
- LOG_I("%s: unexpected return code = %ld", __func__, rc);
+ LOG_I("%s: unexpected return code = %ld", __func__, rc);
}
return rc;
@@ -137,3 +138,45 @@ rt_err_t lwp_critical_exit(struct rt_lwp *lwp)
{
return lwp_mutex_release_safe(&lwp->lwp_lock);
}
+
+rt_err_t lwp_pgrp_critical_enter(struct rt_processgroup *pgrp, int flags)
+{
+ rt_err_t rc;
+ do {
+ rc = lwp_mutex_take_safe(&pgrp->mutex, RT_WAITING_FOREVER, flags);
+ } while (rc != RT_EOK && !(flags & LWP_MTX_FLAGS_INTR) && rc == -RT_EINTR);
+
+ /* if current process is force killed */
+ if (rc != RT_EOK && rc != -RT_EINTR)
+ {
+ LOG_I("%s: unexpected return code = %ld", __func__, rc);
+ }
+
+ return rc;
+}
+
+rt_err_t lwp_pgrp_critical_exit(struct rt_processgroup *pgrp)
+{
+ return lwp_mutex_release_safe(&pgrp->mutex);
+}
+
+rt_err_t lwp_sess_critical_enter(struct rt_session *sess, int flags)
+{
+ rt_err_t rc;
+ do {
+ rc = lwp_mutex_take_safe(&sess->mutex, RT_WAITING_FOREVER, flags);
+ } while (rc != RT_EOK && !(flags & LWP_MTX_FLAGS_INTR) && rc == -RT_EINTR);
+
+ /* if current process is force killed */
+ if (rc != RT_EOK && rc != -RT_EINTR)
+ {
+ LOG_I("%s: unexpected return code = %ld", __func__, rc);
+ }
+
+ return rc;
+}
+
+rt_err_t lwp_sess_critical_exit(struct rt_session *sess)
+{
+ return lwp_mutex_release_safe(&sess->mutex);
+}
diff --git a/components/lwp/lwp_internal.h b/components/lwp/lwp_internal.h
index 1a605b007a..c942be0136 100644
--- a/components/lwp/lwp_internal.h
+++ b/components/lwp/lwp_internal.h
@@ -6,21 +6,32 @@
* Change Logs:
* Date Author Notes
* 2023-07-25 Shell first version
+ * 2023-11-25 Shell Add pgrp, session lock API
*/
#ifndef __LWP_INTERNAL_H__
#define __LWP_INTERNAL_H__
#include "lwp.h"
+#include "lwp_arch.h"
#include "lwp_user_mm.h"
+#include "lwp_mm.h"
#include <rtthread.h>
#include "libc_musl.h"
struct rt_lwp;
-rt_err_t lwp_mutex_take_safe(rt_mutex_t mtx, rt_int32_t timeout, rt_bool_t interruptable);
+
+#define LWP_MTX_FLAGS_INTR 0x1 /* interruptible waiting */
+#define LWP_MTX_FALGS_NESTED 0x2 /* allow nested */
+rt_err_t lwp_mutex_take_safe(rt_mutex_t mtx, rt_int32_t timeout, int flags);
rt_err_t lwp_mutex_release_safe(rt_mutex_t mtx);
+rt_inline rt_bool_t lwp_in_user_space(const char *addr)
+{
+ return (addr >= (char *)USER_VADDR_START && addr < (char *)USER_VADDR_TOP);
+}
+
#ifdef RT_USING_SMP
#define LOCAL_IRQ_MASK() rt_hw_local_irq_disable()
#define LOCAL_IRQ_UNMASK(level) rt_hw_local_irq_enable(level)
@@ -30,16 +41,34 @@ rt_err_t lwp_mutex_release_safe(rt_mutex_t mtx);
#endif
#ifndef LWP_USING_CPUS_LOCK
-rt_err_t lwp_critical_enter(struct rt_lwp *lwp);
+rt_err_t lwp_sess_critical_enter(struct rt_session *sess, int flags);
+rt_err_t lwp_sess_critical_exit(struct rt_session *sess);
+rt_err_t lwp_pgrp_critical_enter(struct rt_processgroup *pgrp, int flags);
+rt_err_t lwp_pgrp_critical_exit(struct rt_processgroup *pgrp);
+rt_err_t lwp_critical_enter(struct rt_lwp *lwp, int flags);
rt_err_t lwp_critical_exit(struct rt_lwp *lwp);
-#define LWP_LOCK(lwp) \
- do { \
- RT_DEBUG_SCHEDULER_AVAILABLE(1); \
- if (lwp_critical_enter(lwp) != RT_EOK) \
- { \
- RT_ASSERT(0); \
- } \
+#define LWP_ASSERT_LOCKED(proc) RT_ASSERT(rt_mutex_get_owner(&(proc)->lwp_lock) == rt_thread_self())
+#define PGRP_ASSERT_LOCKED(pgrp) RT_ASSERT(rt_mutex_get_owner(&(pgrp)->mutex) == rt_thread_self())
+
+#define LWP_LOCK(lwp) \
+ do \
+ { \
+ RT_DEBUG_SCHEDULER_AVAILABLE(1); \
+ if (lwp_critical_enter(lwp, 0) != RT_EOK) \
+ { \
+ RT_ASSERT(0); \
+ } \
+ } while (0)
+
+#define LWP_LOCK_NESTED(lwp) \
+ do \
+ { \
+ RT_DEBUG_SCHEDULER_AVAILABLE(1); \
+ if (lwp_critical_enter(lwp, LWP_MTX_FALGS_NESTED) != RT_EOK) \
+ { \
+ RT_ASSERT(0); \
+ } \
} while (0)
#define LWP_UNLOCK(lwp) \
@@ -50,10 +79,72 @@ rt_err_t lwp_critical_exit(struct rt_lwp *lwp);
} \
} while (0)
+#define PGRP_LOCK(pgrp) \
+ do \
+ { \
+ RT_DEBUG_SCHEDULER_AVAILABLE(1); \
+ if (lwp_pgrp_critical_enter(pgrp, 0) != RT_EOK) \
+ { \
+ RT_ASSERT(0); \
+ } \
+ } while (0)
+
+#define PGRP_LOCK_NESTED(pgrp) \
+ do \
+ { \
+ RT_DEBUG_SCHEDULER_AVAILABLE(1); \
+ if (lwp_pgrp_critical_enter(pgrp, LWP_MTX_FALGS_NESTED) != RT_EOK) \
+ { \
+ RT_ASSERT(0); \
+ } \
+ } while (0)
+
+#define PGRP_UNLOCK(pgrp) \
+ do \
+ { \
+ if (lwp_pgrp_critical_exit(pgrp) != RT_EOK) \
+ { \
+ RT_ASSERT(0); \
+ } \
+ } while (0)
+
+#define SESS_LOCK(sess) \
+ do \
+ { \
+ RT_DEBUG_SCHEDULER_AVAILABLE(1); \
+ if (lwp_sess_critical_enter(sess, 0) != RT_EOK) \
+ { \
+ RT_ASSERT(0); \
+ } \
+ } while (0)
+
+#define SESS_LOCK_NESTED(sess) \
+ do \
+ { \
+ RT_DEBUG_SCHEDULER_AVAILABLE(1); \
+ if (lwp_sess_critical_enter(sess, LWP_MTX_FALGS_NESTED) != RT_EOK) \
+ { \
+ RT_ASSERT(0); \
+ } \
+ } while (0)
+
+#define SESS_UNLOCK(sess) \
+ do \
+ { \
+ if (lwp_sess_critical_exit(sess) != RT_EOK) \
+ { \
+ RT_ASSERT(0); \
+ } \
+ } while (0)
+
#else
#define LWP_LOCK(lwp) rt_base_t level = rt_hw_interrupt_disable()
#define LWP_UNLOCK(lwp) rt_hw_interrupt_enable(level)
+#define PGRP_LOCK(pgrp) rt_base_t level = rt_hw_interrupt_disable()
+#define PGRP_UNLOCK(pgrp) rt_hw_interrupt_enable(level)
+#define SESS_LOCK(sess) rt_base_t level = rt_hw_interrupt_disable()
+#define SESS_UNLOCK(sess) rt_hw_interrupt_enable(level)
#endif /* LWP_USING_CPUS_LOCK */
@@ -95,4 +186,6 @@ rt_err_t lwp_critical_exit(struct rt_lwp *lwp);
#define LWP_RETURN(name) {RT_ASSERT(name != _LWP_UNINITIALIZED_RC);return name;}
#endif /* LWP_DEBUG */
+int load_ldso(struct rt_lwp *lwp, char *exec_name, char *const argv[], char *const envp[]);
+
#endif /* __LWP_INTERNAL_H__ */
diff --git a/components/lwp/lwp_jobctrl.c b/components/lwp/lwp_jobctrl.c
new file mode 100644
index 0000000000..5f4848f500
--- /dev/null
+++ b/components/lwp/lwp_jobctrl.c
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 2006-2023, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2023-11-13 Shell init ver.
+ */
+
+#define DBG_TAG "lwp.tty"
+#define DBG_LVL DBG_INFO
+#include <rtdbg.h>
+
+#include <terminal/terminal.h>
+#include "lwp_internal.h"
+
+static void jobctrl_set_pgrp_orphaned(struct rt_processgroup *pgrp)
+{
+ rt_lwp_t proc, nx_proc;
+ PGRP_LOCK(pgrp);
+
+ pgrp->is_orphaned = 1;
+ rt_list_for_each_entry(proc, &pgrp->process, pgrp_node)
+ {
+ LWP_LOCK(proc);
+ if (proc->jobctl_stopped)
+ {
+ LWP_UNLOCK(proc);
+ rt_list_for_each_entry_safe(proc, nx_proc, &pgrp->process, pgrp_node)
+ {
+ LWP_LOCK(proc);
+ lwp_signal_kill(proc, SIGHUP, SI_KERNEL, 0);
+ lwp_signal_kill(proc, SIGCONT, SI_KERNEL, 0);
+ LWP_UNLOCK(proc);
+ }
+ }
+ LWP_UNLOCK(proc);
+ }
+
+ PGRP_UNLOCK(pgrp);
+}
+
+void lwp_jobctrl_on_exit(struct rt_lwp *lwp)
+{
+ rt_processgroup_t pgrp;
+ rt_session_t session;
+ lwp_tty_t tp;
+
+ pgrp = lwp->pgrp;
+ RT_ASSERT(pgrp);
+ session = pgrp->session;
+ RT_ASSERT(session);
+
+ /**
+ * as a session leader, we have to mark tty as freed. So others can race to
+ * take it before we actually close and released that tty
+ */
+ SESS_LOCK(session);
+ if (session->sid == lwp->pid)
+ {
+ tp = session->ctty;
+ session->leader = 0;
+
+ /* signal to foreground group that modem is disconnected */
+ if (tp)
+ {
+ tty_lock(tp);
+ if (tp->t_session == session)
+ lwp_tty_signal_pgrp(tp, SIGHUP);
+ tty_unlock(tp);
+ }
+
+ /* revoke tty vnode ? */
+
+ rt_list_for_each_entry(pgrp, &session->processgroup, pgrp_list_node)
+ {
+ jobctrl_set_pgrp_orphaned(pgrp);
+ }
+
+ }
+ SESS_UNLOCK(session);
+
+ /* release tty */
+ /* allow tty stolen? */
+}
diff --git a/components/lwp/lwp_pgrp.c b/components/lwp/lwp_pgrp.c
new file mode 100644
index 0000000000..700565b165
--- /dev/null
+++ b/components/lwp/lwp_pgrp.c
@@ -0,0 +1,542 @@
+/*
+ * 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
diff --git a/components/lwp/lwp_pid.c b/components/lwp/lwp_pid.c
index 7479df0357..5b9f1f4098 100644
--- a/components/lwp/lwp_pid.c
+++ b/components/lwp/lwp_pid.c
@@ -14,32 +14,47 @@
* error
* 2023-10-27 shell Format codes of sys_exit(). Fix the data racing where lock is missed
* Add reference on pid/tid, so the resource is not freed while using.
+ * Add support for waitpid(options=WNOHANG)
+ * 2023-11-16 xqyjlj Fix the case where pid is 0
+ * 2023-11-17 xqyjlj add process group and session support
+ * 2023-11-24 shell Support of waitpid(options=WNOTRACED|WCONTINUED);
+ * Reimplement the waitpid with a wait queue method, and fixup problem
+ * with waitpid(pid=-1)/waitpid(pid=-pgid)/waitpid(pid=0) that only one
+ * process can be traced while waiter suspend
* 2024-01-25 shell porting to new sched API
*/
/* includes scheduler related API */
#define __RT_IPC_SOURCE__
-#include <rthw.h>
-#include <rtthread.h>
+/* for waitpid, we are compatible to GNU extension */
+#define _GNU_SOURCE
#define DBG_TAG "lwp.pid"
#define DBG_LVL DBG_INFO
#include <rtdbg.h>
+#include "lwp_internal.h"
+
+#include <rthw.h>
+#include <rtthread.h>
#include <dfs_file.h>
#include <unistd.h>
#include <stdio.h> /* rename() */
+#include <stdlib.h>
#include <sys/stat.h>
#include <sys/statfs.h> /* statfs() */
-
-#include "lwp_internal.h"
-#include "tty.h"
+#include <stdatomic.h>
#ifdef ARCH_MM_MMU
#include "lwp_user_mm.h"
#endif
+#ifdef RT_USING_DFS_PROCFS
+#include "proc.h"
+#include "procfs.h"
+#endif
+
#define PID_MAX 10000
#define PID_CT_ASSERT(name, x) \
@@ -147,8 +162,23 @@ static void lwp_pid_put_locked(pid_t pid)
}
}
+#ifdef RT_USING_DFS_PROCFS
+ rt_inline void _free_proc_dentry(rt_lwp_t lwp)
+ {
+ char pid_str[64] = {0};
+
+ rt_snprintf(pid_str, 64, "%d", lwp->pid);
+ pid_str[63] = 0;
+ proc_remove_dentry(pid_str, 0);
+ }
+#else
+ #define _free_proc_dentry(lwp)
+#endif
+
void lwp_pid_put(struct rt_lwp *lwp)
{
+ _free_proc_dentry(lwp);
+
lwp_pid_lock_take();
lwp_pid_put_locked(lwp->pid);
lwp_pid_lock_release();
@@ -168,6 +198,13 @@ static void lwp_pid_set_lwp_locked(pid_t pid, struct rt_lwp *lwp)
{
p->data = lwp;
lwp_ref_inc(lwp);
+
+#ifdef RT_USING_DFS_PROCFS
+ if (pid)
+ {
+ proc_pid(pid);
+ }
+#endif
}
}
@@ -352,19 +389,23 @@ rt_lwp_t lwp_create(rt_base_t flags)
if (new_lwp)
{
/* minimal setup of lwp object */
- new_lwp->session = -1;
new_lwp->ref = 1;
#ifdef RT_USING_SMP
new_lwp->bind_cpu = RT_CPUS_NR;
#endif
- rt_list_init(&new_lwp->wait_list);
+ new_lwp->exe_file = RT_NULL;
rt_list_init(&new_lwp->t_grp);
+ rt_list_init(&new_lwp->pgrp_node);
rt_list_init(&new_lwp->timer);
lwp_user_object_lock_init(new_lwp);
rt_wqueue_init(&new_lwp->wait_queue);
+ rt_wqueue_init(&new_lwp->waitpid_waiters);
lwp_signal_init(&new_lwp->signal);
rt_mutex_init(&new_lwp->lwp_lock, "lwp_lock", RT_IPC_FLAG_PRIO);
+ if (flags & LWP_CREATE_FLAG_NOTRACE_EXEC)
+ new_lwp->did_exec = RT_TRUE;
+
/* lwp with pid */
if (flags & LWP_CREATE_FLAG_ALLOC_PID)
{
@@ -375,7 +416,7 @@ rt_lwp_t lwp_create(rt_base_t flags)
lwp_user_object_lock_destroy(new_lwp);
rt_free(new_lwp);
new_lwp = RT_NULL;
- LOG_E("pid slot fulled!\n");
+ LOG_E("%s: pid slot fulled", __func__);
}
else
{
@@ -384,6 +425,20 @@ rt_lwp_t lwp_create(rt_base_t flags)
}
lwp_pid_lock_release();
}
+ rt_memset(&new_lwp->rt_rusage,0, sizeof(new_lwp->rt_rusage));
+
+ if (flags & LWP_CREATE_FLAG_INIT_USPACE)
+ {
+ rt_err_t error = lwp_user_space_init(new_lwp, 0);
+ if (error)
+ {
+ lwp_pid_put(new_lwp);
+ lwp_user_object_lock_destroy(new_lwp);
+ rt_free(new_lwp);
+ new_lwp = RT_NULL;
+ LOG_E("%s: failed to initialize user space", __func__);
+ }
+ }
}
LOG_D("%s(pid=%d) => %p", __func__, new_lwp ? new_lwp->pid : -1, new_lwp);
@@ -393,6 +448,8 @@ rt_lwp_t lwp_create(rt_base_t flags)
/** when reference is 0, a lwp can be released */
void lwp_free(struct rt_lwp* lwp)
{
+ rt_processgroup_t group = RT_NULL;
+
if (lwp == RT_NULL)
{
return;
@@ -406,6 +463,10 @@ void lwp_free(struct rt_lwp* lwp)
* all the reference is clear)
*/
LOG_D("lwp free: %p", lwp);
+ rt_free(lwp->exe_file);
+ group = lwp_pgrp_find(lwp_pgid_get_byprocess(lwp));
+ if (group)
+ lwp_pgrp_remove(group, lwp);
LWP_LOCK(lwp);
@@ -472,6 +533,15 @@ void lwp_free(struct rt_lwp* lwp)
rt_inline rt_noreturn
void _thread_exit(rt_lwp_t lwp, rt_thread_t thread)
{
+ LWP_LOCK(lwp);
+ lwp->rt_rusage.ru_stime.tv_sec += thread->system_time / RT_TICK_PER_SECOND;
+ lwp->rt_rusage.ru_stime.tv_usec += thread->system_time % RT_TICK_PER_SECOND * (1000000 / RT_TICK_PER_SECOND);
+ lwp->rt_rusage.ru_utime.tv_sec += thread->user_time / RT_TICK_PER_SECOND;
+ lwp->rt_rusage.ru_utime.tv_usec += thread->user_time % RT_TICK_PER_SECOND * (1000000 / RT_TICK_PER_SECOND);
+ rt_list_remove(&thread->sibling);
+ LWP_UNLOCK(lwp);
+ lwp_futex_exit_robust_list(thread);
+
/**
* Note: the tid tree always hold a reference to thread, hence the tid must
* be release before cleanup of thread
@@ -479,10 +549,6 @@ void _thread_exit(rt_lwp_t lwp, rt_thread_t thread)
lwp_tid_put(thread->tid);
thread->tid = 0;
- LWP_LOCK(lwp);
- rt_list_remove(&thread->sibling);
- LWP_UNLOCK(lwp);
-
rt_thread_delete(thread);
rt_schedule();
while (1) ;
@@ -497,11 +563,11 @@ rt_inline void _clear_child_tid(rt_thread_t thread)
thread->clear_child_tid = RT_NULL;
lwp_put_to_user(clear_child_tid, &t, sizeof t);
- sys_futex(clear_child_tid, FUTEX_WAKE | FUTEX_PRIVATE, 1, RT_NULL, RT_NULL, 0);
+ sys_futex(clear_child_tid, FUTEX_WAKE, 1, RT_NULL, RT_NULL, 0);
}
}
-void lwp_exit(rt_lwp_t lwp, rt_base_t status)
+void lwp_exit(rt_lwp_t lwp, lwp_status_t status)
{
rt_thread_t thread;
@@ -523,7 +589,7 @@ void lwp_exit(rt_lwp_t lwp, rt_base_t status)
* Brief: only one thread should calls exit_group(),
* but we can not ensured that during run-time
*/
- lwp->lwp_ret = LWP_CREATE_STAT(status);
+ lwp->lwp_status = status;
LWP_UNLOCK(lwp);
lwp_terminate(lwp);
@@ -550,7 +616,7 @@ void lwp_exit(rt_lwp_t lwp, rt_base_t status)
_thread_exit(lwp, thread);
}
-void lwp_thread_exit(rt_thread_t thread, rt_base_t status)
+void lwp_thread_exit(rt_thread_t thread, int status)
{
rt_thread_t header_thr;
struct rt_lwp *lwp;
@@ -568,7 +634,11 @@ void lwp_thread_exit(rt_thread_t thread, rt_base_t status)
header_thr = rt_list_entry(lwp->t_grp.prev, struct rt_thread, sibling);
if (header_thr == thread && thread->sibling.prev == &lwp->t_grp)
{
- lwp->lwp_ret = LWP_CREATE_STAT(status);
+ /**
+ * if thread exit, treated as process exit normally.
+ * This is reasonable since trap event is exited through lwp_exit()
+ */
+ lwp->lwp_status = LWP_CREATE_STAT_EXIT(status);
LWP_UNLOCK(lwp);
lwp_terminate(lwp);
@@ -582,38 +652,6 @@ void lwp_thread_exit(rt_thread_t thread, rt_base_t status)
_thread_exit(lwp, thread);
}
-static void _pop_tty(rt_lwp_t lwp)
-{
- if (!lwp->background)
- {
- struct termios *old_stdin_termios = get_old_termios();
- struct rt_lwp *old_lwp = NULL;
-
- if (lwp->session == -1)
- {
- tcsetattr(1, 0, old_stdin_termios);
- }
- if (lwp->tty != RT_NULL)
- {
- rt_mutex_take(&lwp->tty->lock, RT_WAITING_FOREVER);
- if (lwp->tty->foreground == lwp)
- {
- old_lwp = tty_pop(&lwp->tty->head, RT_NULL);
- lwp->tty->foreground = old_lwp;
- }
- else
- {
- tty_pop(&lwp->tty->head, lwp);
- }
- rt_mutex_release(&lwp->tty->lock);
-
- LWP_LOCK(lwp);
- lwp->tty = RT_NULL;
- LWP_UNLOCK(lwp);
- }
- }
-}
-
/** @note the reference is not for synchronization, but for the release of resource. the synchronization is done through lwp & pid lock */
int lwp_ref_inc(struct rt_lwp *lwp)
{
@@ -657,7 +695,7 @@ int lwp_ref_dec(struct rt_lwp *lwp)
return ref;
}
-struct rt_lwp* lwp_from_pid_locked(pid_t pid)
+struct rt_lwp* lwp_from_pid_raw_locked(pid_t pid)
{
struct lwp_avl_struct *p;
struct rt_lwp *lwp = RT_NULL;
@@ -671,6 +709,13 @@ struct rt_lwp* lwp_from_pid_locked(pid_t pid)
return lwp;
}
+struct rt_lwp* lwp_from_pid_locked(pid_t pid)
+{
+ struct rt_lwp* lwp;
+ lwp = pid ? lwp_from_pid_raw_locked(pid) : lwp_self();
+ return lwp;
+}
+
pid_t lwp_to_pid(struct rt_lwp* lwp)
{
if (!lwp)
@@ -733,162 +778,377 @@ pid_t lwp_name2pid(const char *name)
int lwp_getpid(void)
{
- return ((struct rt_lwp *)rt_thread_self()->lwp)->pid;
+ rt_lwp_t lwp = lwp_self();
+ return lwp ? lwp->pid : 1;
+ // return ((struct rt_lwp *)rt_thread_self()->lwp)->pid;
}
-/**
- * @brief Wait for a child lwp to terminate. Do the essential recycling. Setup
- * status code for user
- */
-static sysret_t _lwp_wait_and_recycle(struct rt_lwp *child, rt_thread_t cur_thr,
- struct rt_lwp *self_lwp, int *status,
- int options)
+rt_inline void _update_ru(struct rt_lwp *child, struct rt_lwp *self_lwp, struct rusage *uru)
+{
+ struct rusage rt_rusage;
+ if (uru != RT_NULL)
+ {
+ rt_rusage.ru_stime.tv_sec = child->rt_rusage.ru_stime.tv_sec;
+ rt_rusage.ru_stime.tv_usec = child->rt_rusage.ru_stime.tv_usec;
+ rt_rusage.ru_utime.tv_sec = child->rt_rusage.ru_utime.tv_sec;
+ rt_rusage.ru_utime.tv_usec = child->rt_rusage.ru_utime.tv_usec;
+ lwp_data_put(self_lwp, uru, &rt_rusage, sizeof(*uru));
+ }
+}
+
+/* do statistical summary and reap the child if neccessary */
+static rt_err_t _stats_and_reap_child(rt_lwp_t child, rt_thread_t cur_thr,
+ struct rt_lwp *self_lwp, int *ustatus,
+ int options, struct rusage *uru)
{
- sysret_t error;
- int lwp_stat;
- int terminated;
+ int lwp_stat = child->lwp_status;
- if (!child)
+ /* report statistical data to process */
+ _update_ru(child, self_lwp, uru);
+
+ if (child->terminated && !(options & WNOWAIT))
+ {
+ /** Reap the child process if it's exited */
+ LOG_D("func %s: child detached", __func__);
+ lwp_pid_put(child);
+ lwp_children_unregister(self_lwp, child);
+ }
+
+ if (ustatus)
+ lwp_data_put(self_lwp, ustatus, &lwp_stat, sizeof(*ustatus));
+
+ return RT_EOK;
+}
+
+#define HAS_CHILD_BUT_NO_EVT (-1024)
+
+/* check if the process is already terminate */
+static sysret_t _query_event_from_lwp(rt_lwp_t child, rt_thread_t cur_thr, rt_lwp_t self_lwp,
+ int options, int *status)
+{
+ sysret_t rc;
+
+ LWP_LOCK(child);
+ if (child->terminated)
+ {
+ rc = child->pid;
+ }
+ else if ((options & WSTOPPED) && child->jobctl_stopped && !child->wait_reap_stp)
{
- error = -RT_ERROR;
+ child->wait_reap_stp = 1;
+ rc = child->pid;
}
else
{
+ rc = HAS_CHILD_BUT_NO_EVT;
+ }
+ LWP_UNLOCK(child);
+
+ LOG_D("%s(child_pid=%d ('%s'), stopped=%d) => %d", __func__, child->pid, child->cmd, child->jobctl_stopped, rc);
+ return rc;
+}
+
+/* verify if the process is child, and reap it */
+static pid_t _verify_child_and_reap(rt_thread_t cur_thr, rt_lwp_t self_lwp,
+ pid_t wait_pid, int options, int *ustatus,
+ struct rusage *uru)
+{
+ sysret_t rc;
+ struct rt_lwp *child;
+
+ /* check if pid is reference to a valid child */
+ lwp_pid_lock_take();
+ child = lwp_from_pid_locked(wait_pid);
+ if (!child)
+ rc = -EINVAL;
+ else if (child->parent != self_lwp)
+ rc = -ESRCH;
+ else
+ rc = wait_pid;
+
+ lwp_pid_lock_release();
+
+ if (rc > 0)
+ {
+ rc = _query_event_from_lwp(child, cur_thr, self_lwp, options, ustatus);
+ if (rc > 0)
+ {
+ _stats_and_reap_child(child, cur_thr, self_lwp, ustatus, options, uru);
+ }
+ }
+ return rc;
+}
+
+/* try to reap any child */
+static pid_t _reap_any_child_pid(rt_thread_t cur_thr, rt_lwp_t self_lwp, pid_t pair_pgid,
+ int options, int *ustatus, struct rusage *uru)
+{
+ sysret_t rc = -ECHILD;
+ struct rt_lwp *child;
+
+ LWP_LOCK(self_lwp);
+ child = self_lwp->first_child;
+
+ /* find a exited child if any */
+ while (child)
+ {
+ if (pair_pgid && child->pgid != pair_pgid)
+ continue;
+
+ rc = _query_event_from_lwp(child, cur_thr, self_lwp, options, ustatus);
+ if (rc > 0)
+ break;
+
+ child = child->sibling;
+ }
+ LWP_UNLOCK(self_lwp);
+
+ if (rc > 0)
+ {
+ _stats_and_reap_child(child, cur_thr, self_lwp, ustatus, options, uru);
+ }
+ return rc;
+}
+
+rt_err_t lwp_waitpid_kick(rt_lwp_t parent, rt_lwp_t self_lwp)
+{
+ /* waker provide the message mainly through its lwp_status */
+ rt_wqueue_wakeup(&parent->waitpid_waiters, self_lwp);
+ return RT_EOK;
+}
+
+struct waitpid_handle {
+ struct rt_wqueue_node wq_node;
+ int options;
+ rt_lwp_t waker_lwp;
+};
+
+/* the IPC message is setup and notify the parent */
+static int _waitq_filter(struct rt_wqueue_node *wait_node, void *key)
+{
+ int can_accept_evt = 0;
+ rt_thread_t waiter = wait_node->polling_thread;
+ pid_t destiny = (pid_t)wait_node->key;
+ rt_lwp_t waker_lwp = key;
+ struct waitpid_handle *handle;
+ rt_ubase_t options;
+
+ handle = rt_container_of(wait_node, struct waitpid_handle, wq_node);
+
+ RT_ASSERT(waiter != RT_NULL);
+ options = handle->options;
+
+ /* filter out if waker is not the one */
+ if (destiny > 0)
+ {
/**
- * Note: Critical Section
- * - child lwp (RW. This will modify its parent if valid)
+ * in waitpid immediately return routine, we already do the check
+ * that pid is one of the child process of waiting thread
*/
- LWP_LOCK(child);
- if (child->terminated)
+ can_accept_evt = waker_lwp->pid == destiny;
+ }
+ else if (destiny == -1)
+ {
+ can_accept_evt = waker_lwp->parent == waiter->lwp;
+ }
+ else
+ {
+ /* destiny == 0 || destiny == -pgid */
+ pid_t waiter_pgid;
+ if (destiny == 0)
{
- error = child->pid;
+ waiter_pgid = lwp_pgid_get_byprocess(waiter->lwp);
}
- else if (rt_list_isempty(&child->wait_list))
+ else
{
- /**
- * Note: only one thread can wait on wait_list.
- * dont reschedule before mutex unlock
- */
- rt_enter_critical();
+ waiter_pgid = -destiny;
+ }
+ can_accept_evt = waiter_pgid == lwp_pgid_get_byprocess(waker_lwp);
+ }
- error = rt_thread_suspend_with_flag(cur_thr, RT_INTERRUPTIBLE);
- if (error == 0)
- {
- rt_list_insert_before(&child->wait_list, &RT_THREAD_LIST_NODE(cur_thr));
- LWP_UNLOCK(child);
+ /* filter out if event is not desired */
+ if (can_accept_evt)
+ {
+ if ((options & WEXITED) && waker_lwp->terminated)
+ can_accept_evt = 1;
+ else if ((options & WSTOPPED) && WIFSTOPPED(waker_lwp->lwp_status))
+ can_accept_evt = 1;
+ else if ((options & WCONTINUED) && WIFCONTINUED(waker_lwp->lwp_status))
+ can_accept_evt = 1;
+ else
+ can_accept_evt = 0;
+ }
- rt_set_errno(RT_EINTR);
- rt_exit_critical();
- rt_schedule();
+ /* setup message for waiter if accepted */
+ if (can_accept_evt)
+ handle->waker_lwp = waker_lwp;
- /**
- * Since parent is holding a reference to children this lock will
- * not be freed before parent dereference to it.
- */
- LWP_LOCK(child);
+ /* 0 if event is accepted, otherwise discard */
+ return !can_accept_evt;
+}
- error = rt_get_errno();
- if (error == RT_EINTR)
- {
- error = -EINTR;
- }
- else if (error != RT_EOK)
- {
- LOG_W("%s: unexpected error code %ld", __func__, error);
- }
- else
- {
- error = child->pid;
- }
- }
- else
- rt_exit_critical();
- }
- else
- error = -RT_EINTR;
+/* the waiter cleanup IPC message and wait for desired event here */
+static rt_err_t _wait_for_event(rt_thread_t cur_thr, rt_lwp_t self_lwp,
+ struct waitpid_handle *handle, pid_t destiny)
+{
+ rt_err_t ret;
- lwp_stat = child->lwp_ret;
- terminated = child->terminated;
- LWP_UNLOCK(child);
+ /* current context checking */
+ RT_DEBUG_SCHEDULER_AVAILABLE(RT_TRUE);
- if (error > 0)
- {
- if (terminated)
- {
- LOG_D("func %s: child detached", __func__);
- /** Reap the child process if it's exited */
- lwp_pid_put(child);
- lwp_children_unregister(self_lwp, child);
- }
+ handle->wq_node.polling_thread = cur_thr;
+ handle->wq_node.key = destiny;
+ handle->wq_node.wakeup = _waitq_filter;
+ handle->wq_node.wqueue = &self_lwp->waitpid_waiters;
+ rt_list_init(&handle->wq_node.list);
- if (status)
- lwp_data_put(self_lwp, status, &lwp_stat, sizeof(*status));
- }
+ cur_thr->error = RT_EOK;
+
+ LOG_D("%s(self_lwp=%d) wait for event", __func__, self_lwp->pid);
+
+ rt_enter_critical();
+ ret = rt_thread_suspend_with_flag(cur_thr, RT_INTERRUPTIBLE);
+ if (ret == RT_EOK)
+ {
+ rt_wqueue_add(handle->wq_node.wqueue, &handle->wq_node);
+ rt_exit_critical();
+
+ rt_schedule();
+
+ ret = cur_thr->error;
+
+ /**
+ * cur_thr error is a positive value, but some legacy implementation
+ * use a negative one. So we check to avoid errors
+ */
+ ret = ret > 0 ? -ret : ret;
+
+ /**
+ * we dont rely on this actually, but we cleanup it since wakeup API
+ * set this up durint operation, and this will cause some messy condition
+ */
+ handle->wq_node.wqueue->flag = RT_WQ_FLAG_CLEAN;
+ rt_wqueue_remove(&handle->wq_node);
+ }
+ else
+ {
+ /* failed to suspend, return immediately with failure */
+ rt_exit_critical();
}
- return error;
+ return ret;
}
-pid_t waitpid(pid_t pid, int *status, int options) __attribute__((alias("lwp_waitpid")));
+/* wait for IPC event and do the cleanup if neccessary */
+static sysret_t _wait_and_reap(rt_thread_t cur_thr, rt_lwp_t self_lwp, const pid_t pid,
+ int options, int *ustatus, struct rusage *uru)
+{
+ sysret_t rc;
+ struct waitpid_handle handle;
+ rt_lwp_t waker;
+
+ /* wait for SIGCHLD or other async events */
+ handle.options = options;
+ handle.waker_lwp = 0;
+ rc = _wait_for_event(cur_thr, self_lwp, &handle, pid);
+
+ waker = handle.waker_lwp;
+ if (waker != RT_NULL)
+ {
+ rc = waker->pid;
+
+ /* check out if any process exited */
+ LOG_D("%s: woken up by lwp=%d", __func__, waker->pid);
+ _stats_and_reap_child(waker, cur_thr, self_lwp, ustatus, options, uru);
+ }
+ /**
+ * else if (rc != RT_EOK)
+ * unable to do a suspend, or wakeup unexpectedly
+ * -> then returned a failure
+ */
+
+ return rc;
+}
-pid_t lwp_waitpid(const pid_t pid, int *status, int options)
+pid_t lwp_waitpid(const pid_t pid, int *status, int options, struct rusage *ru)
{
pid_t rc = -1;
- struct rt_thread *thread;
- struct rt_lwp *child;
+ struct rt_thread *cur_thr;
struct rt_lwp *self_lwp;
- thread = rt_thread_self();
+ cur_thr = rt_thread_self();
self_lwp = lwp_self();
- if (!self_lwp)
+ if (!cur_thr || !self_lwp)
{
- rc = -RT_EINVAL;
+ rc = -EINVAL;
}
else
{
+ /* check if able to reap desired child immediately */
if (pid > 0)
{
- lwp_pid_lock_take();
- child = lwp_from_pid_locked(pid);
- if (child->parent != self_lwp)
- rc = -RT_ERROR;
- else
- rc = RT_EOK;
- lwp_pid_lock_release();
-
- if (rc == RT_EOK)
- rc = _lwp_wait_and_recycle(child, thread, self_lwp, status, options);
+ /* if pid is child then try to reap it */
+ rc = _verify_child_and_reap(cur_thr, self_lwp, pid, options, status, ru);
}
else if (pid == -1)
{
- LWP_LOCK(self_lwp);
- child = self_lwp->first_child;
- LWP_UNLOCK(self_lwp);
- RT_ASSERT(!child || child->parent == self_lwp);
-
- rc = _lwp_wait_and_recycle(child, thread, self_lwp, status, options);
+ /* any terminated child */
+ rc = _reap_any_child_pid(cur_thr, self_lwp, 0, options, status, ru);
}
else
{
- /* not supported yet */
- rc = -RT_EINVAL;
+ /**
+ * (pid < -1 || pid == 0)
+ * any terminated child with matched pgid
+ */
+
+ pid_t pair_pgid;
+ if (pid == 0)
+ {
+ pair_pgid = lwp_pgid_get_byprocess(self_lwp);
+ }
+ else
+ {
+ pair_pgid = -pid;
+ }
+ rc = _reap_any_child_pid(cur_thr, self_lwp, pair_pgid, options, status, ru);
}
- }
- if (rc > 0)
- {
- LOG_D("%s: recycle child id %ld (status=0x%x)", __func__, (long)rc, status ? *status : 0);
- }
- else
- {
- RT_ASSERT(rc != 0);
- LOG_D("%s: wait failed with code %ld", __func__, (long)rc);
+ if (rc == HAS_CHILD_BUT_NO_EVT)
+ {
+ if (!(options & WNOHANG))
+ {
+ /* otherwise, arrange a suspend and wait for async event */
+ options |= WEXITED;
+ rc = _wait_and_reap(cur_thr, self_lwp, pid, options, status, ru);
+ }
+ else
+ {
+ /**
+ * POSIX.1: If waitpid() was invoked with WNOHANG set in options,
+ * it has at least one child process specified by pid for which
+ * status is not available, and status is not available for any
+ * process specified by pid, 0 is returned
+ */
+ rc = 0;
+ }
+ }
+ else
+ {
+ RT_ASSERT(rc != 0);
+ }
}
+ LOG_D("waitpid() => %d, *status=0x%x", rc, status ? *status:0);
return rc;
}
+pid_t waitpid(pid_t pid, int *status, int options)
+{
+ return lwp_waitpid(pid, status, options, RT_NULL);
+}
+
#ifdef RT_USING_FINSH
/* copy from components/finsh/cmd.c */
static void object_split(int len)
@@ -910,7 +1170,7 @@ static void print_thread_info(struct rt_thread* thread, int maxlen)
else
rt_kprintf("%-*.*s N/A %3d ", maxlen, RT_NAME_MAX, thread->parent.name, RT_SCHED_PRIV(thread).current_priority);
#else
- rt_kprintf("%-*.*s %3d ", maxlen, RT_NAME_MAX, thread->parent.name, thread->current_priority);
+ rt_kprintf("%-*.*s %3d ", maxlen, RT_NAME_MAX, thread->parent.name, RT_SCHED_PRIV(thread).current_priority);
#endif /*RT_USING_SMP*/
stat = (RT_SCHED_CTX(thread).stat & RT_THREAD_STAT_MASK);
@@ -1044,7 +1304,7 @@ static void cmd_kill(int argc, char** argv)
}
}
lwp_pid_lock_take();
- lwp_signal_kill(lwp_from_pid_locked(pid), sig, SI_USER, 0);
+ lwp_signal_kill(lwp_from_pid_raw_locked(pid), sig, SI_USER, 0);
lwp_pid_lock_release();
}
MSH_CMD_EXPORT_ALIAS(cmd_kill, kill, send a signal to a process);
@@ -1061,7 +1321,7 @@ static void cmd_killall(int argc, char** argv)
while((pid = lwp_name2pid(argv[1])) > 0)
{
lwp_pid_lock_take();
- lwp_signal_kill(lwp_from_pid_locked(pid), SIGKILL, SI_USER, 0);
+ lwp_signal_kill(lwp_from_pid_raw_locked(pid), SIGKILL, SI_USER, 0);
lwp_pid_lock_release();
rt_thread_mdelay(100);
}
@@ -1073,15 +1333,15 @@ MSH_CMD_EXPORT_ALIAS(cmd_killall, killall, kill processes by name);
int lwp_check_exit_request(void)
{
rt_thread_t thread = rt_thread_self();
- rt_base_t expected = LWP_EXIT_REQUEST_TRIGGERED;
+ rt_size_t expected = LWP_EXIT_REQUEST_TRIGGERED;
if (!thread->lwp)
{
return 0;
}
- return rt_atomic_compare_exchange_strong(&thread->exit_request, &expected,
- LWP_EXIT_REQUEST_IN_PROCESS);
+ return atomic_compare_exchange_strong(&thread->exit_request, &expected,
+ LWP_EXIT_REQUEST_IN_PROCESS);
}
static void _wait_sibling_exit(rt_lwp_t lwp, rt_thread_t curr_thread);
@@ -1119,7 +1379,7 @@ static void _wait_sibling_exit(rt_lwp_t lwp, rt_thread_t curr_thread)
rt_sched_lock_level_t slvl;
rt_list_t *list;
rt_thread_t thread;
- rt_base_t expected = LWP_EXIT_REQUEST_NONE;
+ rt_size_t expected = LWP_EXIT_REQUEST_NONE;
/* broadcast exit request for sibling threads */
LWP_LOCK(lwp);
@@ -1127,8 +1387,8 @@ static void _wait_sibling_exit(rt_lwp_t lwp, rt_thread_t curr_thread)
{
thread = rt_list_entry(list, struct rt_thread, sibling);
- rt_atomic_compare_exchange_strong(&thread->exit_request, &expected,
- LWP_EXIT_REQUEST_TRIGGERED);
+ atomic_compare_exchange_strong(&thread->exit_request, &expected,
+ LWP_EXIT_REQUEST_TRIGGERED);
rt_sched_lock(&slvl);
/* dont release, otherwise thread may have been freed */
@@ -1213,8 +1473,43 @@ static void _wait_sibling_exit(rt_lwp_t lwp, rt_thread_t curr_thread)
}
}
+static void _notify_parent(rt_lwp_t lwp)
+{
+ int si_code;
+ int signo_or_exitcode;
+ lwp_siginfo_ext_t ext;
+ lwp_status_t lwp_status = lwp->lwp_status;
+ rt_lwp_t parent = lwp->parent;
+
+ if (WIFSIGNALED(lwp_status))
+ {
+ si_code = (lwp_status & LWP_COREDUMP_FLAG) ? CLD_DUMPED : CLD_KILLED;
+ signo_or_exitcode = WTERMSIG(lwp_status);
+ }
+ else
+ {
+ si_code = CLD_EXITED;
+ signo_or_exitcode = WEXITSTATUS(lwp->lwp_status);
+ }
+
+ lwp_waitpid_kick(parent, lwp);
+
+ ext = rt_malloc(sizeof(struct lwp_siginfo));
+
+ if (ext)
+ {
+ rt_thread_t cur_thr = rt_thread_self();
+ ext->sigchld.status = signo_or_exitcode;
+ ext->sigchld.stime = cur_thr->system_time;
+ ext->sigchld.utime = cur_thr->user_time;
+ }
+ lwp_signal_kill(parent, SIGCHLD, si_code, ext);
+}
+
static void _resr_cleanup(struct rt_lwp *lwp)
{
+ lwp_jobctrl_on_exit(lwp);
+
LWP_LOCK(lwp);
lwp_signal_detach(&lwp->signal);
@@ -1254,8 +1549,6 @@ static void _resr_cleanup(struct rt_lwp *lwp)
}
LWP_UNLOCK(lwp);
- _pop_tty(lwp);
-
/**
* @brief Wakeup parent if it's waiting for this lwp, otherwise a signal
* will be sent to parent
@@ -1264,29 +1557,26 @@ static void _resr_cleanup(struct rt_lwp *lwp)
* - the parent lwp (RW.)
*/
LWP_LOCK(lwp);
- if (lwp->parent)
+ if (lwp->parent &&
+ !lwp_sigismember(&lwp->parent->signal.sig_action_nocldwait, SIGCHLD))
{
- struct rt_thread *thread;
-
+ /* if successfully race to setup lwp->terminated before parent detach */
LWP_UNLOCK(lwp);
- if (!rt_list_isempty(&lwp->wait_list))
- {
- thread = RT_THREAD_LIST_NODE_ENTRY(lwp->wait_list.next);
- thread->error = RT_EOK;
- thread->msg_ret = (void*)(rt_size_t)lwp->lwp_ret;
- rt_thread_resume(thread);
- }
- else
- {
- /* children cannot detach itself and must wait for parent to take care of it */
- lwp_signal_kill(lwp->parent, SIGCHLD, CLD_EXITED, 0);
- }
+
+ /**
+ * Note: children cannot detach itself and must wait for parent to take
+ * care of it
+ */
+ _notify_parent(lwp);
}
else
{
LWP_UNLOCK(lwp);
- /* INFO: orphan hasn't parents to do the reap of pid */
+ /**
+ * if process is orphan, it doesn't have parent to do the recycling.
+ * Otherwise, its parent had setup a flag to mask out recycling event
+ */
lwp_pid_put(lwp);
}
@@ -1315,10 +1605,8 @@ static int _lwp_setaffinity(pid_t pid, int cpu)
int ret = -1;
lwp_pid_lock_take();
- if(pid == 0)
- lwp = lwp_self();
- else
- lwp = lwp_from_pid_locked(pid);
+ lwp = lwp_from_pid_locked(pid);
+
if (lwp)
{
#ifdef RT_USING_SMP
diff --git a/components/lwp/lwp_pid.h b/components/lwp/lwp_pid.h
index c9e8b945b7..5e170f652d 100644
--- a/components/lwp/lwp_pid.h
+++ b/components/lwp/lwp_pid.h
@@ -11,14 +11,14 @@
#ifndef LWP_PID_H__
#define LWP_PID_H__
-#include "lwp.h"
-
#ifdef __cplusplus
extern "C" {
#endif
-#define LWP_CREATE_FLAG_NONE 0x0000
-#define LWP_CREATE_FLAG_ALLOC_PID 0x0001 /* allocate pid on lwp object create */
+#define LWP_CREATE_FLAG_NONE 0x0000
+#define LWP_CREATE_FLAG_ALLOC_PID 0x0001 /* allocate pid on lwp object create */
+#define LWP_CREATE_FLAG_INIT_USPACE 0x0002 /* do user space initialization */
+#define LWP_CREATE_FLAG_NOTRACE_EXEC 0x0004 /* not trace if execve() after fork() */
struct rt_lwp;
@@ -46,6 +46,7 @@ void lwp_free(struct rt_lwp* lwp);
int lwp_ref_inc(struct rt_lwp *lwp);
int lwp_ref_dec(struct rt_lwp *lwp);
+struct rt_lwp* lwp_from_pid_raw_locked(pid_t pid);
struct rt_lwp* lwp_from_pid_locked(pid_t pid);
pid_t lwp_to_pid(struct rt_lwp* lwp);
@@ -54,7 +55,29 @@ char* lwp_pid2name(int32_t pid);
int lwp_getpid(void);
-pid_t lwp_waitpid(const pid_t pid, int *status, int options);
+struct rusage
+{
+ struct timeval ru_utime;
+ struct timeval ru_stime;
+
+ long ru_maxrss;
+ long ru_ixrss;
+ long ru_idrss;
+ long ru_isrss;
+ long ru_minflt;
+ long ru_majflt;
+ long ru_nswap;
+ long ru_inblock;
+ long ru_oublock;
+ long ru_msgsnd;
+ long ru_msgrcv;
+ long ru_nsignals;
+ long ru_nvcsw;
+ long ru_nivcsw;
+ long reserved[16];
+};
+pid_t lwp_waitpid(const pid_t pid, int *status, int options, struct rusage *ru);
+rt_err_t lwp_waitpid_kick(struct rt_lwp *parent, struct rt_lwp *self_lwp);
pid_t waitpid(pid_t pid, int *status, int options);
long list_process(void);
@@ -85,8 +108,9 @@ rt_inline void lwp_from_pid_release_lock(struct rt_lwp *lwp)
lwp_ref_dec(lwp);
}
-void lwp_thread_exit(rt_thread_t thread, rt_base_t status);
-void lwp_exit(struct rt_lwp *lwp, rt_base_t status);
+typedef rt_base_t lwp_status_t;
+void lwp_thread_exit(rt_thread_t thread, int status);
+void lwp_exit(struct rt_lwp *lwp, lwp_status_t status);
#ifdef __cplusplus
}
diff --git a/components/lwp/lwp_pmutex.c b/components/lwp/lwp_pmutex.c
deleted file mode 100644
index 08c8b3b082..0000000000
--- a/components/lwp/lwp_pmutex.c
+++ /dev/null
@@ -1,459 +0,0 @@
-/*
- * Copyright (c) 2006-2021, RT-Thread Development Team
- *
- * SPDX-License-Identifier: Apache-2.0
- *
- * Change Logs:
- * Date Author Notes
- * 2021/01/02 bernard the first version
- * 2022/12/18 bernard fix the _m_lock to tid in user land.
- */
-
-#include "lwp_internal.h"
-#include <rtthread.h>
-#ifdef ARCH_MM_MMU
-#include <lwp_user_mm.h>
-#endif
-#include <sys/time.h>
-#include <syscall_generic.h>
-
-#define PMUTEX_NORMAL 0 /* Unable to recursion */
-#define PMUTEX_RECURSIVE 1 /* Can be recursion */
-#define PMUTEX_ERRORCHECK 2 /* This type of mutex provides error checking */
-
-struct rt_pmutex
-{
- union
- {
- rt_mutex_t kmutex;
- rt_sem_t ksem; /* use sem to emulate the mutex without recursive */
- } lock;
-
- struct lwp_avl_struct node;
- struct rt_object *custom_obj;
- rt_uint8_t type; /* pmutex type */
-};
-
-/*
- * userspace mutex definitions in musl
- */
-struct rt_umutex
-{
- union
- {
- int __i[6];
- volatile int __vi[6];
- volatile void *volatile __p[6];
- } __u;
-};
-#define _m_type __u.__i[0]
-#define _m_lock __u.__vi[1]
-#define _m_waiters __u.__vi[2]
-#define _m_prev __u.__p[3]
-#define _m_next __u.__p[4]
-#define _m_count __u.__i[5]
-
-static struct rt_mutex _pmutex_lock;
-
-static int pmutex_system_init(void)
-{
- rt_mutex_init(&_pmutex_lock, "pmtxLock", RT_IPC_FLAG_FIFO);
- return 0;
-}
-INIT_PREV_EXPORT(pmutex_system_init);
-
-static rt_err_t pmutex_destory(void *data)
-{
- rt_err_t ret = -1;
- struct rt_pmutex *pmutex = (struct rt_pmutex *)data;
-
- if (pmutex)
- {
- lwp_mutex_take_safe(&_pmutex_lock, RT_WAITING_FOREVER, 0);
- /* remove pmutex from pmutext avl */
- lwp_avl_remove(&pmutex->node, (struct lwp_avl_struct **)pmutex->node.data);
- lwp_mutex_release_safe(&_pmutex_lock);
-
- if (pmutex->type == PMUTEX_NORMAL)
- {
- rt_sem_delete(pmutex->lock.ksem);
- }
- else
- {
- rt_mutex_delete(pmutex->lock.kmutex);
- }
-
- /* release object */
- rt_free(pmutex);
- ret = 0;
- }
- return ret;
-}
-
-static struct rt_pmutex* pmutex_create(void *umutex, struct rt_lwp *lwp)
-{
- struct rt_pmutex *pmutex = RT_NULL;
- struct rt_object *obj = RT_NULL;
- rt_ubase_t type;
-
- if (!lwp)
- {
- return RT_NULL;
- }
-
- long *p = (long *)umutex;
- /* umutex[0] bit[0-1] saved mutex type */
- type = *p & 3;
- if (type != PMUTEX_NORMAL && type != PMUTEX_RECURSIVE && type != PMUTEX_ERRORCHECK)
- {
- return RT_NULL;
- }
-
- pmutex = (struct rt_pmutex *)rt_malloc(sizeof(struct rt_pmutex));
- if (!pmutex)
- {
- return RT_NULL;
- }
-
- if (type == PMUTEX_NORMAL)
- {
- pmutex->lock.ksem = rt_sem_create("pmutex", 1, RT_IPC_FLAG_PRIO);
- if (!pmutex->lock.ksem)
- {
- rt_free(pmutex);
- return RT_NULL;
- }
- }
- else
- {
- pmutex->lock.kmutex = rt_mutex_create("pmutex", RT_IPC_FLAG_PRIO);
- if (!pmutex->lock.kmutex)
- {
- rt_free(pmutex);
- return RT_NULL;
- }
- }
-
- obj = rt_custom_object_create("pmutex", (void *)pmutex, pmutex_destory);
- if (!obj)
- {
- if (pmutex->type == PMUTEX_NORMAL)
- {
- rt_sem_delete(pmutex->lock.ksem);
- }
- else
- {
- rt_mutex_delete(pmutex->lock.kmutex);
- }
- rt_free(pmutex);
- return RT_NULL;
- }
- pmutex->node.avl_key = (avl_key_t)umutex;
- pmutex->node.data = &lwp->address_search_head;
- pmutex->custom_obj = obj;
- pmutex->type = type;
-
- /* insert into pmutex head */
- lwp_avl_insert(&pmutex->node, &lwp->address_search_head);
- return pmutex;
-}
-
-static struct rt_pmutex* pmutex_get(void *umutex, struct rt_lwp *lwp)
-{
- struct rt_pmutex *pmutex = RT_NULL;
- struct lwp_avl_struct *node = RT_NULL;
-
- node = lwp_avl_find((avl_key_t)umutex, lwp->address_search_head);
- if (!node)
- {
- return RT_NULL;
- }
- pmutex = rt_container_of(node, struct rt_pmutex, node);
- return pmutex;
-}
-
-static int _pthread_mutex_init(void *umutex)
-{
- struct rt_lwp *lwp = RT_NULL;
- struct rt_pmutex *pmutex = RT_NULL;
- rt_err_t lock_ret = 0;
-
- /* umutex union is 6 x (void *) */
- if (!lwp_user_accessable(umutex, sizeof(void *) * 6))
- {
- rt_set_errno(EINVAL);
- return -EINVAL;
- }
-
- lock_ret = rt_mutex_take_interruptible(&_pmutex_lock, RT_WAITING_FOREVER);
- if (lock_ret != RT_EOK)
- {
- rt_set_errno(EAGAIN);
- return -EAGAIN;
- }
-
- lwp = lwp_self();
- pmutex = pmutex_get(umutex, lwp);
- if (pmutex == RT_NULL)
- {
- /* create a pmutex according to this umutex */
- pmutex = pmutex_create(umutex, lwp);
- if (pmutex == RT_NULL)
- {
- rt_mutex_release(&_pmutex_lock);
- rt_set_errno(ENOMEM);
- return -ENOMEM;
- }
- if (lwp_user_object_add(lwp, pmutex->custom_obj) != 0)
- {
- rt_custom_object_destroy(pmutex->custom_obj);
- rt_set_errno(ENOMEM);
- return -ENOMEM;
- }
- }
- else
- {
- lwp_mutex_take_safe(&_pmutex_lock, RT_WAITING_FOREVER, 1);
- if (pmutex->type == PMUTEX_NORMAL)
- {
- pmutex->lock.ksem->value = 1;
- }
- else
- {
- pmutex->lock.kmutex->owner = RT_NULL;
- pmutex->lock.kmutex->priority = 0xFF;
- pmutex->lock.kmutex->hold = 0;
- pmutex->lock.kmutex->ceiling_priority = 0xFF;
- }
- lwp_mutex_release_safe(&_pmutex_lock);
- }
-
- rt_mutex_release(&_pmutex_lock);
-
- return 0;
-}
-
-static int _pthread_mutex_lock_timeout(void *umutex, struct timespec *timeout)
-{
- struct rt_lwp *lwp = RT_NULL;
- struct rt_pmutex *pmutex = RT_NULL;
- struct rt_umutex *umutex_p = (struct rt_umutex*)umutex;
- rt_err_t lock_ret = 0;
- rt_int32_t time = RT_WAITING_FOREVER;
-
- if (!lwp_user_accessable((void *)umutex, sizeof(struct rt_umutex)))
- {
- rt_set_errno(EINVAL);
- return -EINVAL;
- }
-
- if (timeout)
- {
- if (!lwp_user_accessable((void *)timeout, sizeof(struct timespec)))
- {
- rt_set_errno(EINVAL);
- return -EINVAL;
- }
- time = rt_timespec_to_tick(timeout);
- }
-
- lock_ret = rt_mutex_take_interruptible(&_pmutex_lock, RT_WAITING_FOREVER);
- if (lock_ret != RT_EOK)
- {
- rt_set_errno(EINTR);
- return -EINTR;
- }
-
- lwp = lwp_self();
- pmutex = pmutex_get(umutex, lwp);
- if (pmutex == RT_NULL)
- {
- rt_mutex_release(&_pmutex_lock);
- rt_set_errno(EINVAL);
- return -ENOMEM; /* umutex not recored in kernel */
- }
-
- rt_mutex_release(&_pmutex_lock);
-
- switch (pmutex->type)
- {
- case PMUTEX_NORMAL:
- lock_ret = rt_sem_take_interruptible(pmutex->lock.ksem, time);
- break;
- case PMUTEX_RECURSIVE:
- lock_ret = rt_mutex_take_interruptible(pmutex->lock.kmutex, time);
- if (lock_ret == RT_EOK)
- {
- umutex_p->_m_lock = rt_thread_self()->tid;
- }
- break;
- case PMUTEX_ERRORCHECK:
- lock_ret = lwp_mutex_take_safe(&_pmutex_lock, RT_WAITING_FOREVER, 1);
- if (lock_ret != RT_EOK)
- {
- return -EINTR;
- }
- if (pmutex->lock.kmutex->owner == rt_thread_self())
- {
- lwp_mutex_release_safe(&_pmutex_lock);
- return -EDEADLK;
- }
- lwp_mutex_release_safe(&_pmutex_lock);
- lock_ret = rt_mutex_take_interruptible(pmutex->lock.kmutex, time);
- if (lock_ret == RT_EOK)
- {
- umutex_p->_m_lock = rt_thread_self()->tid;
- }
- break;
- default: /* unknown type */
- return -EINVAL;
- }
-
- if (lock_ret != RT_EOK)
- {
- if (lock_ret == -RT_ETIMEOUT)
- {
- if (time == 0) /* timeout is 0, means try lock failed */
- {
- rt_set_errno(EBUSY);
- return -EBUSY;
- }
- else
- {
- rt_set_errno(ETIMEDOUT);
- return -ETIMEDOUT;
- }
- }
- else if (lock_ret == -RT_EINTR)
- {
- rt_set_errno(EINTR);
- return -EINTR;
- }
- else
- {
- rt_set_errno(EAGAIN);
- return -EAGAIN;
- }
- }
- return 0;
-}
-
-static int _pthread_mutex_unlock(void *umutex)
-{
- rt_err_t lock_ret = 0;
- struct rt_lwp *lwp = RT_NULL;
- struct rt_pmutex *pmutex = RT_NULL;
- struct rt_umutex *umutex_p = (struct rt_umutex*)umutex;
-
- lock_ret = rt_mutex_take_interruptible(&_pmutex_lock, RT_WAITING_FOREVER);
- if (lock_ret != RT_EOK)
- {
- rt_set_errno(EAGAIN);
- return -EAGAIN;
- }
-
- lwp = lwp_self();
- pmutex = pmutex_get(umutex, lwp);
- if (pmutex == RT_NULL)
- {
- rt_mutex_release(&_pmutex_lock);
- rt_set_errno(EPERM);
- return -EPERM;//unlock static mutex of unlock state
- }
-
- rt_mutex_release(&_pmutex_lock);
-
- switch (pmutex->type)
- {
- case PMUTEX_NORMAL:
- if(pmutex->lock.ksem->value >=1)
- {
- rt_set_errno(EPERM);
- return -EPERM;//unlock dynamic mutex of unlock state
- }
- else
- {
- lock_ret = rt_sem_release(pmutex->lock.ksem);
- }
- break;
- case PMUTEX_RECURSIVE:
- case PMUTEX_ERRORCHECK:
- lock_ret = rt_mutex_release(pmutex->lock.kmutex);
- if ((lock_ret == RT_EOK) && pmutex->lock.kmutex->owner == NULL)
- {
- umutex_p->_m_lock = 0;
- }
- break;
- default: /* unknown type */
- return -EINVAL;
- }
-
- if (lock_ret != RT_EOK)
- {
- rt_set_errno(EPERM);
- return -EPERM;
- }
- return 0;
-}
-
-static int _pthread_mutex_destroy(void *umutex)
-{
- struct rt_lwp *lwp = RT_NULL;
- struct rt_pmutex *pmutex = RT_NULL;
- rt_err_t lock_ret = 0;
-
- lock_ret = rt_mutex_take_interruptible(&_pmutex_lock, RT_WAITING_FOREVER);
- if (lock_ret != RT_EOK)
- {
- rt_set_errno(EAGAIN);
- return -EAGAIN;
- }
-
- lwp = lwp_self();
- pmutex = pmutex_get(umutex, lwp);
- if (pmutex == RT_NULL)
- {
- rt_mutex_release(&_pmutex_lock);
- rt_set_errno(EINVAL);
- return -EINVAL;
- }
-
- lwp_user_object_delete(lwp, pmutex->custom_obj);
- rt_mutex_release(&_pmutex_lock);
-
- return 0;
-}
-
-sysret_t sys_pmutex(void *umutex, int op, void *arg)
-{
- int ret = -EINVAL;
-
- switch (op)
- {
- case PMUTEX_INIT:
- ret = _pthread_mutex_init(umutex);
- break;
- case PMUTEX_LOCK:
- ret = _pthread_mutex_lock_timeout(umutex, (struct timespec*)arg);
- if (ret == -ENOMEM)
- {
- /* lock not init, try init it and lock again. */
- ret = _pthread_mutex_init(umutex);
- if (ret == 0)
- {
- ret = _pthread_mutex_lock_timeout(umutex, (struct timespec*)arg);
- }
- }
- break;
- case PMUTEX_UNLOCK:
- ret = _pthread_mutex_unlock(umutex);
- break;
- case PMUTEX_DESTROY:
- ret = _pthread_mutex_destroy(umutex);
- break;
- default:
- rt_set_errno(EINVAL);
- break;
- }
- return ret;
-}
diff --git a/components/lwp/lwp_session.c b/components/lwp/lwp_session.c
new file mode 100644
index 0000000000..6d51948267
--- /dev/null
+++ b/components/lwp/lwp_session.c
@@ -0,0 +1,432 @@
+/*
+ * 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
diff --git a/components/lwp/lwp_setsid.c b/components/lwp/lwp_setsid.c
deleted file mode 100644
index d72363bde5..0000000000
--- a/components/lwp/lwp_setsid.c
+++ /dev/null
@@ -1,27 +0,0 @@
-#include <rthw.h>
-#include <rtthread.h>
-
-#include "lwp.h"
-//#include "lwp_tid.h"
-#include "lwp_pid.h"
-
-int setsid(void)
-{
- int err = -EPERM;
- struct rt_thread *current_thread = rt_thread_self();
- struct rt_lwp *current_lwp = (struct rt_lwp *)rt_thread_self()->lwp;
-
- if (current_lwp->session == current_thread->tid)
- {
- return err;
- }
-
- current_lwp->session = current_thread->tid;
- current_lwp->__pgrp = current_thread->tid;
- current_lwp->leader = 1;
- current_lwp->tty = RT_NULL;
- current_lwp->tty_old_pgrp = 0;
-
- err = current_lwp->session;
- return err;
-}
diff --git a/components/lwp/lwp_signal.c b/components/lwp/lwp_signal.c
index 9e947ce7de..1f20ad5096 100644
--- a/components/lwp/lwp_signal.c
+++ b/components/lwp/lwp_signal.c
@@ -10,6 +10,9 @@
* 2023-07-04 Shell Support siginfo, sigqueue
* remove lwp_signal_backup/restore() to reduce architecture codes
* update the generation, pending and delivery routines
+ * 2023-11-22 Shell Support for job control signal. Fixup of signal catch while
+ * some of the signals is blocked, but no more further dequeue is applied.
+ * Add itimer support
*/
#define __RT_IPC_SOURCE__
#define DBG_TAG "lwp.signal"
@@ -24,7 +27,7 @@
#include "sys/signal.h"
#include "syscall_generic.h"
-static lwp_siginfo_t siginfo_create(int signo, int code, int value)
+static lwp_siginfo_t siginfo_create(rt_thread_t current, int signo, int code, lwp_siginfo_ext_t ext)
{
lwp_siginfo_t siginfo;
struct rt_lwp *self_lwp;
@@ -35,10 +38,10 @@ static lwp_siginfo_t siginfo_create(int signo, int code, int value)
{
siginfo->ksiginfo.signo = signo;
siginfo->ksiginfo.code = code;
- siginfo->ksiginfo.value = value;
+ siginfo->ext = ext;
- self_lwp = lwp_self();
- self_thr = rt_thread_self();
+ self_thr = current;
+ self_lwp = current->lwp;
if (self_lwp)
{
siginfo->ksiginfo.from_pid = self_lwp->pid;
@@ -56,6 +59,12 @@ static lwp_siginfo_t siginfo_create(int signo, int code, int value)
rt_inline void siginfo_delete(lwp_siginfo_t siginfo)
{
+ if (siginfo->ext)
+ {
+ rt_free(siginfo->ext);
+ siginfo->ext = RT_NULL;
+ }
+
rt_free(siginfo);
}
@@ -296,10 +305,30 @@ static lwp_siginfo_t sigqueue_dequeue(lwp_sigqueue_t sigqueue, int signo)
return found;
}
+/**
+ * Discard all the signal matching `signo` in sigqueue
+ */
static void sigqueue_discard(lwp_sigqueue_t sigqueue, int signo)
{
lwp_siginfo_t queuing_si;
- while (!sigqueue_isempty(sigqueue))
+ while (sigqueue_ismember(sigqueue, signo))
+ {
+ queuing_si = sigqueue_dequeue(sigqueue, signo);
+ siginfo_delete(queuing_si);
+ }
+}
+
+/**
+ * Discard all the queuing signals in sigset
+ */
+static void sigqueue_discard_sigset(lwp_sigqueue_t sigqueue, lwp_sigset_t *sigset)
+{
+ lwp_siginfo_t queuing_si;
+ lwp_sigset_t mask;
+ int signo;
+
+ _signotsets(&mask, sigset);
+ while ((signo = sigqueue_peek(sigqueue, &mask)) != 0)
{
queuing_si = sigqueue_dequeue(sigqueue, signo);
siginfo_delete(queuing_si);
@@ -312,11 +341,21 @@ RT_STATIC_ASSERT(lp_width_same, sizeof(void *) == sizeof(long));
/** translate lwp siginfo to user siginfo_t */
rt_inline void siginfo_k2u(lwp_siginfo_t ksigi, siginfo_t *usigi)
{
+ int signo = ksigi->ksiginfo.signo;
usigi->si_code = ksigi->ksiginfo.code;
- usigi->si_signo = ksigi->ksiginfo.signo;
- usigi->si_value.sival_ptr = (void *)ksigi->ksiginfo.value;
+ usigi->si_signo = signo;
usigi->si_pid = ksigi->ksiginfo.from_pid;
+ if (ksigi->ext)
+ {
+ if (signo == SIGCHLD)
+ {
+ usigi->si_status = ksigi->ext->sigchld.status;
+ usigi->si_utime = ksigi->ext->sigchld.stime;
+ usigi->si_stime = ksigi->ext->sigchld.utime;
+ }
+ }
+
/* deprecated field */
usigi->si_errno = 0;
}
@@ -417,7 +456,10 @@ rt_err_t lwp_signal_init(struct lwp_signal *sig)
memset(&sig->sig_action_onstack, 0, sizeof(sig->sig_action_onstack));
memset(&sig->sig_action_restart, 0, sizeof(sig->sig_action_restart));
memset(&sig->sig_action_siginfo, 0, sizeof(sig->sig_action_siginfo));
+ memset(&sig->sig_action_nocldstop, 0, sizeof(sig->sig_action_nocldstop));
+ memset(&sig->sig_action_nocldwait, 0, sizeof(sig->sig_action_nocldwait));
lwp_sigqueue_init(&sig->sig_queue);
+
return rc;
}
@@ -433,17 +475,19 @@ rt_err_t lwp_signal_detach(struct lwp_signal *signal)
int lwp_thread_signal_suspend_check(rt_thread_t thread, int suspend_flag)
{
- struct rt_lwp *lwp = (struct rt_lwp*)thread->lwp;
+ struct rt_lwp *lwp = (struct rt_lwp *)thread->lwp;
+ lwp_sigset_t sigmask = thread->signal.sigset_mask;
int ret = 0;
+ _sigaddset(&sigmask, SIGCONT);
switch (suspend_flag)
{
case RT_INTERRUPTIBLE:
- if (!sigqueue_isempty(_SIGQ(thread)))
+ if (sigqueue_peek(_SIGQ(thread), &sigmask))
{
break;
}
- if (thread->lwp && !sigqueue_isempty(_SIGQ(lwp)))
+ if (thread->lwp && sigqueue_peek(_SIGQ(lwp), &sigmask))
{
break;
}
@@ -470,84 +514,202 @@ int lwp_thread_signal_suspend_check(rt_thread_t thread, int suspend_flag)
return ret;
}
-void lwp_thread_signal_catch(void *exp_frame)
+rt_inline rt_bool_t _is_jobctl_signal(rt_lwp_t lwp, int signo)
{
- int signo = 0;
- struct rt_thread *thread;
- struct rt_lwp *lwp;
- lwp_siginfo_t siginfo = 0;
- lwp_sigqueue_t pending;
- lwp_sigset_t *sig_mask;
- lwp_sigset_t save_sig_mask;
- lwp_sigset_t new_sig_mask;
- lwp_sighandler_t handler = 0;
- siginfo_t usiginfo;
- siginfo_t *p_usi = RT_NULL;
+ lwp_sigset_t jobctl_sigset = lwp_sigset_init(LWP_SIG_JOBCTL_SET);
- thread = rt_thread_self();
- lwp = (struct rt_lwp*)thread->lwp;
+ return lwp_sigismember(&jobctl_sigset, signo);
+}
- RT_ASSERT(!!lwp);
- LWP_LOCK(lwp);
+rt_inline rt_bool_t _is_stop_signal(rt_lwp_t lwp, int signo)
+{
+ lwp_sigset_t stop_sigset = lwp_sigset_init(LWP_SIG_STOP_SET);
+
+ return lwp_sigismember(&stop_sigset, signo);
+}
+
+rt_inline rt_bool_t _need_notify_status_changed(rt_lwp_t lwp, int signo)
+{
+ RT_ASSERT(lwp_sigismember(&lwp_sigset_init(LWP_SIG_JOBCTL_SET), signo));
+ return !lwp_sigismember(&lwp->signal.sig_action_nocldstop, SIGCHLD);
+}
+
+/**
+ * wakeup the waitpid_waiters if any, and try to generate SIGCHLD if they are
+ * not disable explicitly by user.
+ *
+ * TODO: This event is always per-process and doesn't make whole lot of
+ * sense for ptracers, who shouldn't consume the state via wait(2) either,
+ * but, for backward compatibility, notify the ptracer of the group leader
+ * too unless it's gonna be a duplicate.
+ */
+static void _notify_parent_and_leader(rt_lwp_t child_lwp, rt_thread_t child_thr, int trig_signo, rt_bool_t is_stop)
+{
+ int si_code;
+ lwp_siginfo_ext_t ext;
+ rt_lwp_t parent_lwp = child_lwp->parent;
+
+ if (!parent_lwp)
+ return ;
- /* check if signal exist */
- if (!sigqueue_isempty(_SIGQ(thread)))
+ /* prepare the event data for parent to query */
+ if (is_stop)
{
- pending = _SIGQ(thread);
- sig_mask = &thread->signal.sigset_mask;
+ si_code = CLD_STOPPED;
+ child_lwp->lwp_status = LWP_CREATE_STAT_STOPPED(trig_signo);
}
- else if (!sigqueue_isempty(_SIGQ(lwp)))
+ else
{
- pending = _SIGQ(lwp);
- sig_mask = &thread->signal.sigset_mask;
+ si_code = CLD_CONTINUED;
+ child_lwp->lwp_status = LWP_CREATE_STAT_CONTINUED;
}
- else
+
+ /* wakeup waiter on waitpid(2) */
+ lwp_waitpid_kick(parent_lwp, child_lwp);
+
+ if (_need_notify_status_changed(parent_lwp, trig_signo))
{
- pending = RT_NULL;
+ ext = rt_malloc(sizeof(struct lwp_siginfo_ext));
+ if (ext)
+ {
+ ext->sigchld.status = trig_signo;
+
+ /* TODO: signal usage is not supported */
+ ext->sigchld.stime = child_thr->system_time;
+ ext->sigchld.utime = child_thr->user_time;
+ }
+
+ /* generate SIGCHLD for parent */
+ lwp_signal_kill(parent_lwp, SIGCHLD, si_code, ext);
}
+}
+
+static int _do_signal_wakeup(rt_thread_t thread, int sig);
+static rt_err_t _stop_thread_locked(rt_lwp_t self_lwp, rt_thread_t cur_thr, int signo,
+ lwp_siginfo_t si, lwp_sigqueue_t sq)
+{
+ rt_err_t error;
+ int jobctl_stopped = self_lwp->jobctl_stopped;
+ rt_thread_t iter;
- if (pending)
+ /* race to setup jobctl stopped flags */
+ if (!jobctl_stopped)
{
- /* peek the pending signal */
- signo = sigqueue_peek(pending, sig_mask);
- if (signo)
+ self_lwp->jobctl_stopped = RT_TRUE;
+ self_lwp->wait_reap_stp = RT_FALSE;
+ rt_list_for_each_entry(iter, &self_lwp->t_grp, sibling)
{
- siginfo = sigqueue_dequeue(pending, signo);
- RT_ASSERT(siginfo != RT_NULL);
- handler = _get_sighandler_locked(lwp, signo);
+ if (iter != cur_thr)
+ _do_signal_wakeup(iter, signo);
+ }
+ }
- /* IGN signal will never be queued */
- RT_ASSERT(handler != LWP_SIG_ACT_IGN);
+ /**
+ * raise the event again so siblings is able to catch it again.
+ * `si` will be discarded while SIGCONT is generatd
+ */
+ sigqueue_enqueue(sq, si);
- /* copy the blocked signal mask from the registered signal action */
- memcpy(&new_sig_mask, &lwp->signal.sig_action_mask[signo - 1], sizeof(new_sig_mask));
+ /* release the lwp lock so we can happily suspend */
+ LWP_UNLOCK(self_lwp);
- if (!_sigismember(&lwp->signal.sig_action_nodefer, signo))
- _sigaddset(&new_sig_mask, signo);
+ rt_set_errno(RT_EOK);
- _thread_signal_mask(thread, LWP_SIG_MASK_CMD_BLOCK, &new_sig_mask, &save_sig_mask);
+ /* After suspension, only the SIGKILL and SIGCONT will wake this thread up */
+ error = rt_thread_suspend_with_flag(cur_thr, RT_KILLABLE);
+ if (error == RT_EOK)
+ {
+ rt_schedule();
+ error = rt_get_errno();
+ error = error > 0 ? -error : error;
+ }
- /* siginfo is need for signal action */
- if (_sigismember(&lwp->signal.sig_action_siginfo, signo))
- {
- siginfo_k2u(siginfo, &usiginfo);
- p_usi = &usiginfo;
- }
- else
- p_usi = RT_NULL;
- }
+ if (!jobctl_stopped &&
+ (sigqueue_ismember(_SIGQ(self_lwp), SIGCONT) ||
+ sigqueue_ismember(_SIGQ(cur_thr), SIGCONT)))
+ {
+ /**
+ * if we are resumed by a SIGCONT and we are the winner of racing
+ * notify parent of the incoming event
+ */
+ _notify_parent_and_leader(self_lwp, cur_thr, SIGCONT, RT_FALSE);
}
+
+ /* reacquire the lock since we release it before */
+ LWP_LOCK(self_lwp);
+
+ return error;
+}
+
+static void _catch_signal_locked(rt_lwp_t lwp, rt_thread_t thread, int signo,
+ lwp_siginfo_t siginfo, lwp_sighandler_t handler,
+ void *exp_frame)
+{
+ lwp_sigset_t new_sig_mask;
+ lwp_sigset_t save_sig_mask;
+ siginfo_t usiginfo;
+ siginfo_t *p_usi;
+
+ /* siginfo is need for signal action */
+ if (_sigismember(&lwp->signal.sig_action_siginfo, signo))
+ {
+ siginfo_k2u(siginfo, &usiginfo);
+ p_usi = &usiginfo;
+ }
+ else
+ {
+ p_usi = RT_NULL;
+ }
+
+ /**
+ * lock is acquired by caller. Release it so that we can happily go to the
+ * signal handler in user space
+ */
LWP_UNLOCK(lwp);
- if (pending && signo)
+ siginfo_delete(siginfo);
+
+ /* signal default handler */
+ if (handler == LWP_SIG_ACT_DFL)
{
- siginfo_delete(siginfo);
+ lwp_sigset_t ign_sigset;
- /* signal default handler */
- if (handler == LWP_SIG_ACT_DFL)
+ ign_sigset = lwp_sigset_init(LWP_SIG_IGNORE_SET);
+ if (signo == SIGCONT)
+ {
+ arch_syscall_set_errno(exp_frame, EINTR, ERESTART);
+ arch_thread_signal_enter(signo, p_usi, exp_frame, 0, &thread->signal.sigset_mask);
+ }
+ else if (!lwp_sigismember(&ign_sigset, signo) && !lwp->sig_protected)
{
+ /* for those defautl handler is to terminate process */
LOG_D("%s: default handler; and exit", __func__);
- sys_exit_group(0);
+
+ /* TODO: coredump if neccessary */
+ lwp_exit(lwp, LWP_CREATE_STAT_SIGNALED(signo, 0));
+ }
+ /**
+ * otherwise is to ignore the signal,
+ * -> then reacquire the lock and return
+ */
+ }
+ else if (handler == LWP_SIG_ACT_IGN)
+ {
+ /* do nothing */
+ }
+ else
+ {
+ /* copy the blocked signal mask from the registered signal action */
+ memcpy(&new_sig_mask, &lwp->signal.sig_action_mask[signo - 1], sizeof(new_sig_mask));
+
+ if (!_sigismember(&lwp->signal.sig_action_nodefer, signo))
+ _sigaddset(&new_sig_mask, signo);
+
+ _thread_signal_mask(thread, LWP_SIG_MASK_CMD_BLOCK, &new_sig_mask, &save_sig_mask);
+
+ if (_sigismember(&lwp->signal.sig_action_restart, signo))
+ {
+ arch_syscall_set_errno(exp_frame, EINTR, ERESTART);
}
/**
@@ -557,18 +719,92 @@ void lwp_thread_signal_catch(void *exp_frame)
*/
LOG_D("%s: enter signal handler(signo=%d) at %p", __func__, signo, handler);
arch_thread_signal_enter(signo, p_usi, exp_frame, handler, &save_sig_mask);
+
/* the arch_thread_signal_enter() never return */
RT_ASSERT(0);
}
+
+ /* reacquire the lock because we release it before */
+ LWP_LOCK(lwp);
}
+
+void lwp_thread_signal_catch(void *exp_frame)
+{
+ struct rt_thread *thread;
+ struct rt_lwp *lwp;
+ lwp_sigqueue_t pending;
+ lwp_sigset_t *sig_mask;
+ int retry_signal_catch;
+ int signo;
+
+ thread = rt_thread_self();
+ lwp = (struct rt_lwp *)thread->lwp;
+
+ RT_ASSERT(!!lwp);
+ LWP_LOCK(lwp);
+
+ do {
+ /* if stopped process resume, we will retry to catch the signal */
+ retry_signal_catch = 0;
+ signo = 0;
+
+ /* try to peek a signal which is pending and not blocked by this thread */
+ if (!sigqueue_isempty(_SIGQ(thread)))
+ {
+ pending = _SIGQ(thread);
+ sig_mask = &thread->signal.sigset_mask;
+ signo = sigqueue_peek(pending, sig_mask);
+ }
+ if (!signo && !sigqueue_isempty(_SIGQ(lwp)))
+ {
+ pending = _SIGQ(lwp);
+ sig_mask = &thread->signal.sigset_mask;
+ signo = sigqueue_peek(pending, sig_mask);
+ }
+
+ if (signo)
+ {
+ lwp_siginfo_t siginfo;
+ lwp_sighandler_t handler;
+
+ LOG_D("%s(signo=%d)", __func__, signo);
+
+ siginfo = sigqueue_dequeue(pending, signo);
+ RT_ASSERT(siginfo != RT_NULL);
+ handler = _get_sighandler_locked(lwp, signo);
+
+ if (_is_stop_signal(lwp, signo) && handler == LWP_SIG_ACT_DFL)
+ {
+ /* notify the status update for parent process */
+ _notify_parent_and_leader(lwp, thread, signo, RT_TRUE);
+
+ LOG_D("%s: pid=%d stopped", __func__, lwp->pid);
+ _stop_thread_locked(lwp, thread, signo, siginfo, pending);
+ LOG_D("%s: pid=%d continued", __func__, lwp->pid);
+
+ /* wakeup and retry to catch signals send to us */
+ retry_signal_catch = 1;
+ }
+ else
+ {
+ /* do a normal, non-jobctl signal handling */
+ _catch_signal_locked(lwp, thread, signo, siginfo, handler, exp_frame);
+ }
+ }
+ } while (retry_signal_catch);
+
+ LWP_UNLOCK(lwp);
+}
+
static int _do_signal_wakeup(rt_thread_t thread, int sig)
{
int need_schedule;
rt_sched_lock_level_t slvl;
if (!_sigismember(&thread->signal.sigset_mask, sig))
{
+ int stat;
rt_sched_lock(&slvl);
- int stat = rt_sched_thread_get_stat(thread);
+ stat = rt_sched_thread_get_stat(thread);
if ((stat & RT_THREAD_SUSPEND_MASK) == RT_THREAD_SUSPEND_MASK)
{
if ((stat & RT_SIGNAL_COMMON_WAKEUP_MASK) != RT_SIGNAL_COMMON_WAKEUP_MASK)
@@ -599,6 +835,8 @@ static int _do_signal_wakeup(rt_thread_t thread, int sig)
rt_sched_unlock(slvl);
need_schedule = 0;
}
+
+ RT_SCHED_DEBUG_IS_UNLOCKED;
}
else
need_schedule = 0;
@@ -689,7 +927,63 @@ rt_inline rt_bool_t _sighandler_cannot_caught(struct rt_lwp *lwp, int signo)
return signo == SIGKILL || signo == SIGSTOP;
}
-rt_err_t lwp_signal_kill(struct rt_lwp *lwp, long signo, long code, long value)
+/* before signal is killed to target process/thread */
+static void _before_sending_jobctl_signal(int signo, rt_lwp_t target_lwp, lwp_siginfo_t si)
+{
+ rt_thread_t thr_iter;
+ rt_sched_lock_level_t slvl;
+ lwp_sigset_t jobctl_sigset = lwp_sigset_init(LWP_SIG_JOBCTL_SET);
+
+ LWP_ASSERT_LOCKED(target_lwp);
+
+ /**
+ * dequeue all the pending jobctl signals (including
+ * the one we are adding, since we don't want to pend it)
+ */
+ sigqueue_discard_sigset(_SIGQ(target_lwp), &jobctl_sigset);
+
+ if (signo == SIGCONT)
+ {
+ target_lwp->jobctl_stopped = RT_FALSE;
+ rt_list_for_each_entry(thr_iter, &target_lwp->t_grp, sibling)
+ {
+ rt_base_t stat;
+ sigqueue_discard_sigset(_SIGQ(thr_iter), &jobctl_sigset);
+
+ /**
+ * Note: all stopped thread will be resumed
+ */
+ rt_sched_lock(&slvl);
+ stat = rt_sched_thread_get_stat(thr_iter);
+ if ((stat & RT_THREAD_SUSPEND_MASK) == RT_THREAD_SUSPEND_MASK &&
+ (stat & RT_SIGNAL_KILL_WAKEUP_MASK) == 0)
+ {
+ thr_iter->error = RT_EINTR;
+
+ /**
+ * don't matter if we failed to resume the thread, since we
+ * only care about the event passing, but not ordering here
+ */
+ rt_sched_unlock(slvl);
+ rt_thread_wakeup(thr_iter);
+ }
+ else
+ {
+ rt_sched_unlock(slvl);
+ }
+
+ }
+ }
+ else
+ {
+ rt_list_for_each_entry(thr_iter, &target_lwp->t_grp, sibling)
+ {
+ sigqueue_discard_sigset(_SIGQ(thr_iter), &jobctl_sigset);
+ }
+ }
+}
+
+rt_err_t lwp_signal_kill(struct rt_lwp *lwp, long signo, long code, lwp_siginfo_ext_t value)
{
rt_err_t ret = -1;
@@ -700,10 +994,15 @@ rt_err_t lwp_signal_kill(struct rt_lwp *lwp, long signo, long code, long value)
/** must be able to be suspended */
RT_DEBUG_SCHEDULER_AVAILABLE(RT_TRUE);
- if (!lwp || signo <= 0 || signo > _LWP_NSIG)
+ if (!lwp || signo < 0 || signo > _LWP_NSIG)
{
ret = -RT_EINVAL;
}
+ else if (signo == 0)
+ {
+ /* process exist and current process have privileges */
+ ret = 0;
+ }
else
{
LOG_D("%s(lwp=%p \"%s\",signo=%ld,code=%ld,value=%ld)",
@@ -715,19 +1014,25 @@ rt_err_t lwp_signal_kill(struct rt_lwp *lwp, long signo, long code, long value)
terminated = lwp->terminated;
/* short-circuit code for inactive task, ignored signals */
- if (terminated || _sighandler_is_ignored(lwp, signo))
+ if (terminated)
{
+ /* no one rely on this, then free the resource */
+ if (value)
+ rt_free(value);
ret = 0;
}
else
{
- siginfo = siginfo_create(signo, code, value);
+ siginfo = siginfo_create(rt_thread_self(), signo, code, value);
if (siginfo)
{
+ if (_is_jobctl_signal(lwp, signo))
+ _before_sending_jobctl_signal(signo, lwp, siginfo);
+
need_schedule = _siginfo_deliver_to_lwp(lwp, siginfo);
- ret = 0;
lwp_signal_notify(&lwp->signalfd_notify_head, siginfo);
+ ret = 0;
}
else
{
@@ -755,6 +1060,10 @@ static void _signal_action_flag_k2u(int signo, struct lwp_signal *signal, struct
flags |= SA_RESTART;
if (_sigismember(&signal->sig_action_siginfo, signo))
flags |= SA_SIGINFO;
+ if (_sigismember(&signal->sig_action_nocldstop, signo))
+ flags |= SA_NOCLDSTOP;
+ if (_sigismember(&signal->sig_action_nocldwait, signo))
+ flags |= SA_NOCLDWAIT;
act->sa_flags = flags;
}
@@ -770,6 +1079,25 @@ static void _signal_action_flag_u2k(int signo, struct lwp_signal *signal, const
_sigaddset(&signal->sig_action_restart, signo);
if (flags & SA_SIGINFO)
_sigaddset(&signal->sig_action_siginfo, signo);
+ if (signo == SIGCHLD)
+ {
+ /* These flags are meaningful only when establishing a handler for SIGCHLD */
+ if (flags & SA_NOCLDSTOP)
+ _sigaddset(&signal->sig_action_nocldstop, signo);
+ if (flags & SA_NOCLDWAIT)
+ _sigaddset(&signal->sig_action_nocldwait, signo);
+ }
+
+ #define _HANDLE_FLAGS (SA_RESTORER | SA_NODEFER | SA_ONSTACK | SA_RESTART | SA_SIGINFO | SA_NOCLDSTOP | SA_NOCLDWAIT)
+ if (flags & ~_HANDLE_FLAGS)
+ LOG_W("Unhandled flags: 0x%lx", flags & ~_HANDLE_FLAGS);
+}
+
+rt_bool_t lwp_sigisign(struct rt_lwp *lwp, int _sig)
+{
+ unsigned long sig = _sig - 1;
+
+ return lwp->signal.sig_action[sig] == LWP_SIG_ACT_IGN;
}
rt_err_t lwp_signal_action(struct rt_lwp *lwp, int signo,
@@ -793,6 +1121,7 @@ rt_err_t lwp_signal_action(struct rt_lwp *lwp, int signo,
oact->sa_restorer = RT_NULL;
_signal_action_flag_k2u(signo, &lwp->signal, oact);
}
+
if (act)
{
/**
@@ -800,15 +1129,29 @@ rt_err_t lwp_signal_action(struct rt_lwp *lwp, int signo,
* argument succeed, even in the case of signals that cannot be caught or ignored
*/
if (_sighandler_cannot_caught(lwp, signo))
- ret = -RT_EINVAL;
+ ret = -EINVAL;
else
{
prev_handler = _get_sighandler_locked(lwp, signo);
lwp->signal.sig_action_mask[signo - 1] = act->sa_mask;
if (act->__sa_handler._sa_handler == SIG_IGN)
- lwp->signal.sig_action[signo - 1] = LWP_SIG_ACT_IGN;
+ {
+ lwp_sigset_t no_ign_set = lwp_sigset_init(LWP_SIG_NO_IGN_SET);
+ if (!lwp_sigismember(&no_ign_set, signo))
+ {
+ /* except those unignorable signals, discard them for proc */
+ lwp->signal.sig_action[signo - 1] = LWP_SIG_ACT_IGN;
+ }
+ else
+ {
+ /* POSIX.1: SIG_IGN and SIG_DFL are equivalent for SIGCONT */
+ lwp->signal.sig_action[signo - 1] = LWP_SIG_ACT_DFL;
+ }
+ }
else
+ {
lwp->signal.sig_action[signo - 1] = act->__sa_handler._sa_handler;
+ }
_signal_action_flag_u2k(signo, &lwp->signal, act);
@@ -837,12 +1180,12 @@ rt_err_t lwp_signal_action(struct rt_lwp *lwp, int signo,
LWP_UNLOCK(lwp);
}
else
- ret = -RT_EINVAL;
+ ret = -EINVAL;
return ret;
}
-rt_err_t lwp_thread_signal_kill(rt_thread_t thread, long signo, long code, long value)
+rt_err_t lwp_thread_signal_kill(rt_thread_t thread, long signo, long code, lwp_siginfo_ext_t value)
{
rt_err_t ret = -1;
@@ -855,10 +1198,15 @@ rt_err_t lwp_thread_signal_kill(rt_thread_t thread, long signo, long code, long
LOG_D("%s(signo=%d)", __func__, signo);
- if (!thread || signo <= 0 || signo >= _LWP_NSIG)
+ if (!thread || signo < 0 || signo >= _LWP_NSIG)
{
ret = -RT_EINVAL;
}
+ else if (signo == 0)
+ {
+ /* thread exist and current thread have privileges */
+ ret = 0;
+ }
else
{
lwp = thread->lwp;
@@ -874,7 +1222,7 @@ rt_err_t lwp_thread_signal_kill(rt_thread_t thread, long signo, long code, long
ret = 0;
else
{
- siginfo = siginfo_create(signo, code, value);
+ siginfo = siginfo_create(rt_thread_self(), signo, code, value);
if (siginfo)
{
@@ -919,7 +1267,7 @@ rt_err_t lwp_thread_signal_mask(rt_thread_t thread, lwp_sig_mask_cmd_t how,
if (thread)
{
- lwp = (struct rt_lwp*)thread->lwp;
+ lwp = (struct rt_lwp *)thread->lwp;
LWP_LOCK(lwp);
if (!lwp)
@@ -948,13 +1296,14 @@ static int _dequeue_signal(rt_thread_t thread, lwp_sigset_t *mask, siginfo_t *us
lwp_sigset_t *pending;
lwp_sigqueue_t sigqueue;
+ lwp = thread->lwp;
+ RT_ASSERT(lwp);
+
sigqueue = _SIGQ(thread);
pending = &sigqueue->sigset_pending;
signo = _next_signal(pending, mask);
if (!signo)
{
- lwp = thread->lwp;
- RT_ASSERT(lwp);
sigqueue = _SIGQ(lwp);
pending = &sigqueue->sigset_pending;
signo = _next_signal(pending, mask);
@@ -978,6 +1327,7 @@ rt_err_t lwp_thread_signal_timedwait(rt_thread_t thread, lwp_sigset_t *sigset,
rt_err_t ret;
lwp_sigset_t saved_sigset;
lwp_sigset_t blocked_sigset;
+ lwp_sigset_t dontwait_sigset;
int sig;
struct rt_lwp *lwp = thread->lwp;
@@ -990,10 +1340,10 @@ rt_err_t lwp_thread_signal_timedwait(rt_thread_t thread, lwp_sigset_t *sigset,
/* Create a mask of signals user dont want or cannot catch */
_sigdelset(sigset, SIGKILL);
_sigdelset(sigset, SIGSTOP);
- _signotsets(sigset, sigset);
+ _signotsets(&dontwait_sigset, sigset);
LWP_LOCK(lwp);
- sig = _dequeue_signal(thread, sigset, usi);
+ sig = _dequeue_signal(thread, &dontwait_sigset, usi);
LWP_UNLOCK(lwp);
if (sig)
return sig;
@@ -1007,13 +1357,12 @@ rt_err_t lwp_thread_signal_timedwait(rt_thread_t thread, lwp_sigset_t *sigset,
* Note: If the pending signal arrives before thread suspend, the suspend
* operation will return a failure
*/
- _sigandsets(&blocked_sigset, &thread->signal.sigset_mask, sigset);
+ _sigandsets(&blocked_sigset, &thread->signal.sigset_mask, &dontwait_sigset);
_thread_signal_mask(thread, LWP_SIG_MASK_CMD_SET_MASK, &blocked_sigset, &saved_sigset);
if (timeout)
{
- rt_uint32_t time;
- time = rt_timespec_to_tick(timeout);
-
+ rt_tick_t time;
+ time = (timeout->tv_sec * RT_TICK_PER_SECOND) + ((timeout->tv_nsec * RT_TICK_PER_SECOND) / NANOSECOND_PER_SECOND);
/**
* Brief: POSIX
* If the timespec structure pointed to by timeout is zero-valued and
@@ -1023,11 +1372,13 @@ rt_err_t lwp_thread_signal_timedwait(rt_thread_t thread, lwp_sigset_t *sigset,
if (time == 0)
return -EAGAIN;
+ rt_enter_critical();
ret = rt_thread_suspend_with_flag(thread, RT_INTERRUPTIBLE);
rt_timer_control(&(thread->thread_timer),
RT_TIMER_CTRL_SET_TIME,
&time);
rt_timer_start(&(thread->thread_timer));
+ rt_exit_critical();
}
else
@@ -1040,7 +1391,7 @@ rt_err_t lwp_thread_signal_timedwait(rt_thread_t thread, lwp_sigset_t *sigset,
{
rt_schedule();
/* If thread->error reliable? */
- if (thread->error == -RT_EINTR)
+ if (thread->error == RT_EINTR)
ret = -EINTR;
else
ret = -EAGAIN;
@@ -1049,7 +1400,7 @@ rt_err_t lwp_thread_signal_timedwait(rt_thread_t thread, lwp_sigset_t *sigset,
_thread_signal_mask(thread, LWP_SIG_MASK_CMD_SET_MASK, &saved_sigset, RT_NULL);
LWP_LOCK(lwp);
- sig = _dequeue_signal(thread, sigset, usi);
+ sig = _dequeue_signal(thread, &dontwait_sigset, usi);
LWP_UNLOCK(lwp);
return sig ? sig : ret;
@@ -1072,3 +1423,22 @@ void lwp_thread_signal_pending(rt_thread_t thread, lwp_sigset_t *pending)
_sigandsets(pending, pending, &thread->signal.sigset_mask);
}
}
+
+rt_err_t lwp_pgrp_signal_kill(rt_processgroup_t pgrp, long signo, long code,
+ lwp_siginfo_ext_t value)
+{
+ struct rt_lwp *lwp;
+ rt_err_t rc = 0;
+
+ PGRP_ASSERT_LOCKED(pgrp);
+
+ if (pgrp)
+ {
+ rt_list_for_each_entry(lwp, &pgrp->process, pgrp_node)
+ {
+ lwp_signal_kill(lwp, signo, code, value);
+ }
+ }
+
+ return rc;
+}
diff --git a/components/lwp/lwp_signal.h b/components/lwp/lwp_signal.h
index 3b5fd91321..f98b43dd3f 100644
--- a/components/lwp/lwp_signal.h
+++ b/components/lwp/lwp_signal.h
@@ -7,6 +7,7 @@
* Date Author Notes
* 2020-02-23 Jesven first version.
* 2023-07-06 Shell update the generation, pending and delivery API
+ * 2023-11-22 Shell support for job control signal
*/
#ifndef __LWP_SIGNAL_H__
@@ -17,23 +18,33 @@
#include <rtthread.h>
#include <sys/signal.h>
-struct timespec;
-struct itimerspec;
-
#ifdef __cplusplus
extern "C" {
#endif
#define _USIGNAL_SIGMASK(signo) (1u << ((signo)-1))
-#define LWP_SIG_IGNORE_SET (_USIGNAL_SIGMASK(SIGCHLD) | _USIGNAL_SIGMASK(SIGURG))
-#define LWP_SIG_ACT_DFL ((lwp_sighandler_t)0)
-#define LWP_SIG_ACT_IGN ((lwp_sighandler_t)1)
-#define LWP_SIG_USER_SA_FLAGS \
- (SA_NOCLDSTOP | SA_NOCLDWAIT | SA_SIGINFO | SA_ONSTACK | SA_RESTART | \
+#define LWP_SIG_NO_IGN_SET \
+ (_USIGNAL_SIGMASK(SIGCONT) | _USIGNAL_SIGMASK(SIGSTOP) | \
+ _USIGNAL_SIGMASK(SIGKILL))
+#define LWP_SIG_IGNORE_SET \
+ (_USIGNAL_SIGMASK(SIGCHLD) | _USIGNAL_SIGMASK(SIGURG) | \
+ _USIGNAL_SIGMASK(SIGWINCH) /* from 4.3 BSD, not POSIX.1 */)
+#define LWP_SIG_JOBCTL_SET \
+ (_USIGNAL_SIGMASK(SIGCONT) | _USIGNAL_SIGMASK(SIGSTOP) | \
+ _USIGNAL_SIGMASK(SIGTSTP) | _USIGNAL_SIGMASK(SIGTTIN) | \
+ _USIGNAL_SIGMASK(SIGTTOU))
+#define LWP_SIG_STOP_SET \
+ (_USIGNAL_SIGMASK(SIGSTOP) | _USIGNAL_SIGMASK(SIGTSTP) | \
+ _USIGNAL_SIGMASK(SIGTTIN) | _USIGNAL_SIGMASK(SIGTTOU))
+#define LWP_SIG_ACT_DFL ((lwp_sighandler_t)0)
+#define LWP_SIG_ACT_IGN ((lwp_sighandler_t)1)
+#define LWP_SIG_USER_SA_FLAGS \
+ (SA_NOCLDSTOP | SA_NOCLDWAIT | SA_SIGINFO | SA_ONSTACK | SA_RESTART | \
SA_NODEFER | SA_RESETHAND | SA_EXPOSE_TAGBITS)
#define LWP_SIG_INVALID_TIMER ((timer_t)-1)
-typedef enum {
+typedef enum
+{
LWP_SIG_MASK_CMD_BLOCK,
LWP_SIG_MASK_CMD_UNBLOCK,
LWP_SIG_MASK_CMD_SET_MASK,
@@ -43,7 +54,8 @@ typedef enum {
/**
* LwP implementation of POSIX signal
*/
-struct lwp_signal {
+struct lwp_signal
+{
timer_t real_timer;
struct lwp_sigqueue sig_queue;
rt_thread_t sig_dispatch_thr[_LWP_NSIG];
@@ -55,9 +67,12 @@ struct lwp_signal {
lwp_sigset_t sig_action_onstack;
lwp_sigset_t sig_action_restart;
lwp_sigset_t sig_action_siginfo;
+ lwp_sigset_t sig_action_nocldstop;
+ lwp_sigset_t sig_action_nocldwait;
};
struct rt_lwp;
+struct rt_processgroup;
#ifndef ARCH_MM_MMU
void lwp_sighandler_set(int sig, lwp_sighandler_t func);
@@ -93,12 +108,14 @@ rt_inline void lwp_thread_signal_detach(struct lwp_thread_signal *tsig)
* @param signo the signal number
* @param code as in siginfo
* @param value as in siginfo
- * @return rt_err_t RT_EINVAL if the parameter is invalid, RT_EOK as successful
+ * @return rt_err_t RT_EINVAL if the parameter is invalid, RT_EOK as
+ * successful
*
* @note the *signal_kill have the same definition of a successful return as
* kill() in IEEE Std 1003.1-2017
*/
-rt_err_t lwp_signal_kill(struct rt_lwp *lwp, long signo, long code, long value);
+rt_err_t lwp_signal_kill(struct rt_lwp *lwp, long signo, long code,
+ lwp_siginfo_ext_t value);
/**
* @brief set or examine the signal action of signo
@@ -119,9 +136,11 @@ rt_err_t lwp_signal_action(struct rt_lwp *lwp, int signo,
* @param signo the signal number
* @param code as in siginfo
* @param value as in siginfo
- * @return rt_err_t RT_EINVAL if the parameter is invalid, RT_EOK as successful
+ * @return rt_err_t RT_EINVAL if the parameter is invalid, RT_EOK as
+ * successful
*/
-rt_err_t lwp_thread_signal_kill(rt_thread_t thread, long signo, long code, long value);
+rt_err_t lwp_thread_signal_kill(rt_thread_t thread, long signo, long code,
+ lwp_siginfo_ext_t value);
/**
* @brief set signal mask of target thread
@@ -136,7 +155,8 @@ rt_err_t lwp_thread_signal_mask(rt_thread_t thread, lwp_sig_mask_cmd_t how,
const lwp_sigset_t *sigset, lwp_sigset_t *oset);
/**
- * @brief Catch signal if exists and no return, otherwise return with no side effect
+ * @brief Catch signal if exists and no return, otherwise return with no
+ * side effect
*
* @param exp_frame the exception frame on kernel stack
*/
@@ -172,10 +192,43 @@ rt_err_t lwp_thread_signal_timedwait(rt_thread_t thread, lwp_sigset_t *sigset,
*/
void lwp_thread_signal_pending(rt_thread_t thread, lwp_sigset_t *sigset);
+/**
+ * @brief send a signal to the process group
+ *
+ * @param pgrp target process group
+ * @param signo the signal number
+ * @param code as in siginfo
+ * @param value as in siginfo
+ * @return rt_err_t RT_EINVAL if the parameter is invalid, RT_EOK as
+ * successful
+ */
+rt_err_t lwp_pgrp_signal_kill(struct rt_processgroup *pgrp, long signo,
+ long code, lwp_siginfo_ext_t value);
+
+rt_inline int lwp_sigismember(lwp_sigset_t *set, int _sig)
+{
+ unsigned long sig = _sig - 1;
+
+ if (_LWP_NSIG_WORDS == 1)
+ {
+ return 1 & (set->sig[0] >> sig);
+ }
+ else
+ {
+ return 1 & (set->sig[sig / _LWP_NSIG_BPW] >> (sig % _LWP_NSIG_BPW));
+ }
+}
+
+struct itimerspec;
+
+rt_bool_t lwp_sigisign(struct rt_lwp *lwp, int _sig);
+
rt_err_t lwp_signal_setitimer(struct rt_lwp *lwp, int which,
const struct itimerspec *restrict new,
struct itimerspec *restrict old);
+rt_bool_t lwp_signal_restart_syscall(struct rt_lwp *lwp, int error_code);
+
#ifdef __cplusplus
}
#endif
diff --git a/components/lwp/lwp_sys_socket.h b/components/lwp/lwp_sys_socket.h
index 527d5ebbe8..28a0887203 100644
--- a/components/lwp/lwp_sys_socket.h
+++ b/components/lwp/lwp_sys_socket.h
@@ -113,7 +113,7 @@ struct musl_ifreq
{
union
{
-#define IFNAMSIZ 16
+#define IFNAMSIZ 16
char ifrn_name[IFNAMSIZ];
} ifr_ifrn;
union
@@ -133,4 +133,23 @@ struct musl_ifreq
} ifr_ifru;
};
+struct musl_rtentry
+{
+ unsigned long int rt_pad1;
+ struct musl_sockaddr rt_dst;
+ struct musl_sockaddr rt_gateway;
+ struct musl_sockaddr rt_genmask;
+ unsigned short int rt_flags;
+ short int rt_pad2;
+ unsigned long int rt_pad3;
+ unsigned char rt_tos;
+ unsigned char rt_class;
+ short int rt_pad4[sizeof(long)/2-1];
+ short int rt_metric;
+ char *rt_dev;
+ unsigned long int rt_mtu;
+ unsigned long int rt_window;
+ unsigned short int rt_irtt;
+};
+
#endif /* __LWP_SYS_SOCKET_H__ */
diff --git a/components/lwp/lwp_syscall.c b/components/lwp/lwp_syscall.c
index 718636e102..5d9cf05b63 100644
--- a/components/lwp/lwp_syscall.c
+++ b/components/lwp/lwp_syscall.c
@@ -13,6 +13,9 @@
* 2023-03-13 WangXiaoyao Format & fix syscall return value
* 2023-07-06 Shell adapt the signal API, and clone, fork to new implementation of lwp signal
* 2023-07-27 Shell Move tid_put() from lwp_free() to sys_exit()
+ * 2023-11-16 xqyjlj fix some syscalls (about sched_*, get/setpriority)
+ * 2023-11-17 xqyjlj add process group and session support
+ * 2023-11-30 Shell Fix sys_setitimer() and exit(status)
*/
#define __RT_IPC_SOURCE__
#define _GNU_SOURCE
@@ -22,7 +25,6 @@
#include <rthw.h>
#include <board.h>
-#include <console.h>
#include <string.h>
#include <stdint.h>
@@ -57,6 +59,7 @@
#include <sys/stat.h>
#include <sys/statfs.h> /* statfs() */
#include <sys/timerfd.h>
+#include <sys/ioctl.h>
#ifdef RT_USING_MUSLLIBC
#include <sys/signalfd.h>
#endif
@@ -88,7 +91,6 @@
#define SYSCALL_USPACE(f) SYSCALL_SIGN(sys_notimpl)
#endif /* defined(RT_USING_DFS) && defined(ARCH_MM_MMU) */
-#include <tty.h>
#include "lwp_ipc_internal.h"
#include <sched.h>
@@ -329,10 +331,14 @@ static void _crt_thread_entry(void *parameter)
sysret_t sys_exit_group(int value)
{
sysret_t rc = 0;
+ lwp_status_t lwp_status;
struct rt_lwp *lwp = lwp_self();
if (lwp)
- lwp_exit(lwp, value);
+ {
+ lwp_status = LWP_CREATE_STAT_EXIT(value);
+ lwp_exit(lwp, lwp_status);
+ }
else
{
LOG_E("Can't find matching process of current thread");
@@ -350,7 +356,10 @@ sysret_t sys_exit(int status)
tid = rt_thread_self();
if (tid && tid->lwp)
+ {
lwp_thread_exit(tid, status);
+ }
+ else
{
LOG_E("Can't find matching process of current thread");
rc = -EINVAL;
@@ -616,102 +625,9 @@ sysret_t sys_fstat(int file, struct stat *buf)
#endif
}
-/* DFS and lwip definitions */
-#define IMPL_POLLIN (0x01)
-
-#define IMPL_POLLOUT (0x02)
-
-#define IMPL_POLLERR (0x04)
-#define IMPL_POLLHUP (0x08)
-#define IMPL_POLLNVAL (0x10)
-
-/* musl definitions */
-#define INTF_POLLIN 0x001
-#define INTF_POLLPRI 0x002
-#define INTF_POLLOUT 0x004
-#define INTF_POLLERR 0x008
-#define INTF_POLLHUP 0x010
-#define INTF_POLLNVAL 0x020
-#define INTF_POLLRDNORM 0x040
-#define INTF_POLLRDBAND 0x080
-#define INTF_POLLWRNORM 0x100
-#define INTF_POLLWRBAND 0x200
-#define INTF_POLLMSG 0x400
-#define INTF_POLLRDHUP 0x2000
-
-#define INTF_POLLIN_MASK (INTF_POLLIN | INTF_POLLRDNORM | INTF_POLLRDBAND | INTF_POLLPRI)
-#define INTF_POLLOUT_MASK (INTF_POLLOUT | INTF_POLLWRNORM | INTF_POLLWRBAND)
-
-static void musl2dfs_events(short *events)
-{
- short origin_e = *events;
- short result_e = 0;
-
- if (origin_e & INTF_POLLIN_MASK)
- {
- result_e |= IMPL_POLLIN;
- }
-
- if (origin_e & INTF_POLLOUT_MASK)
- {
- result_e |= IMPL_POLLOUT;
- }
-
- if (origin_e & INTF_POLLERR)
- {
- result_e |= IMPL_POLLERR;
- }
-
- if (origin_e & INTF_POLLHUP)
- {
- result_e |= IMPL_POLLHUP;
- }
-
- if (origin_e & INTF_POLLNVAL)
- {
- result_e |= IMPL_POLLNVAL;
- }
-
- *events = result_e;
-}
-
-static void dfs2musl_events(short *events)
-{
- short origin_e = *events;
- short result_e = 0;
-
- if (origin_e & IMPL_POLLIN)
- {
- result_e |= INTF_POLLIN_MASK;
- }
-
- if (origin_e & IMPL_POLLOUT)
- {
- result_e |= INTF_POLLOUT_MASK;
- }
-
- if (origin_e & IMPL_POLLERR)
- {
- result_e |= INTF_POLLERR;
- }
-
- if (origin_e & IMPL_POLLHUP)
- {
- result_e |= INTF_POLLHUP;
- }
-
- if (origin_e & IMPL_POLLNVAL)
- {
- result_e |= INTF_POLLNVAL;
- }
-
- *events = result_e;
-}
-
sysret_t sys_poll(struct pollfd *fds, nfds_t nfds, int timeout)
{
int ret = -1;
- int i = 0;
#ifdef ARCH_MM_MMU
struct pollfd *kfds = RT_NULL;
@@ -728,43 +644,20 @@ sysret_t sys_poll(struct pollfd *fds, nfds_t nfds, int timeout)
lwp_get_from_user(kfds, fds, nfds * sizeof *kfds);
- for (i = 0; i < nfds; i++)
- {
- musl2dfs_events(&kfds[i].events);
- }
ret = poll(kfds, nfds, timeout);
if (ret > 0)
{
- for (i = 0; i < nfds; i++)
- {
- dfs2musl_events(&kfds->revents);
- }
lwp_put_to_user(fds, kfds, nfds * sizeof *kfds);
}
kmem_put(kfds);
return ret;
#else
-#ifdef RT_USING_MUSLLIBC
- for (i = 0; i < nfds; i++)
- {
- musl2dfs_events(&fds->events);
- }
-#endif /* RT_USING_MUSLLIBC */
if (!lwp_user_accessable((void *)fds, nfds * sizeof *fds))
{
return -EFAULT;
}
ret = poll(fds, nfds, timeout);
-#ifdef RT_USING_MUSLLIBC
- if (ret > 0)
- {
- for (i = 0; i < nfds; i++)
- {
- dfs2musl_events(&fds->revents);
- }
- }
-#endif /* RT_USING_MUSLLIBC */
return ret;
#endif /* ARCH_MM_MMU */
}
@@ -1014,9 +907,9 @@ sysret_t sys_exec(char *filename, int argc, char **argv, char **envp)
sysret_t sys_kill(int pid, int signo)
{
- rt_err_t kret;
+ rt_err_t kret = 0;
sysret_t sysret;
- struct rt_lwp *lwp;
+ struct rt_lwp *lwp = RT_NULL;
/* handling the semantics of sys_kill */
if (pid > 0)
@@ -1027,31 +920,60 @@ sysret_t sys_kill(int pid, int signo)
* - pid tree (READ. since the lwp is fetch from the pid tree, it must stay there)
*/
lwp_pid_lock_take();
- lwp = lwp_from_pid_locked(pid);
+ lwp = lwp_from_pid_raw_locked(pid);
if (lwp)
{
lwp_ref_inc(lwp);
lwp_pid_lock_release();
-
- kret = lwp_signal_kill(lwp, signo, SI_USER, 0);
- lwp_ref_dec(lwp);
- kret = 0;
}
else
{
lwp_pid_lock_release();
kret = -RT_ENOENT;
}
+
+ if (lwp)
+ {
+ kret = lwp_signal_kill(lwp, signo, SI_USER, 0);
+ lwp_ref_dec(lwp);
+ }
}
- else if (pid == 0)
+ else if (pid < -1 || pid == 0)
{
- /**
- * sig shall be sent to all processes (excluding an unspecified set
- * of system processes) whose process group ID is equal to the process
- * group ID of the sender, and for which the process has permission to
- * send a signal.
- */
- kret = -RT_ENOSYS;
+ pid_t pgid = 0;
+ rt_processgroup_t group;
+
+ if (pid == 0)
+ {
+ /**
+ * sig shall be sent to all processes (excluding an unspecified set
+ * of system processes) whose process group ID is equal to the process
+ * group ID of the sender, and for which the process has permission to
+ * send a signal.
+ */
+ pgid = lwp_pgid_get_byprocess(lwp_self());
+ }
+ else
+ {
+ /**
+ * sig shall be sent to all processes (excluding an unspecified set
+ * of system processes) whose process group ID is equal to the absolute
+ * value of pid, and for which the process has permission to send a signal.
+ */
+ pgid = -pid;
+ }
+
+ group = lwp_pgrp_find(pgid);
+ if (group != RT_NULL)
+ {
+ PGRP_LOCK(group);
+ kret = lwp_pgrp_signal_kill(group, signo, SI_USER, 0);
+ PGRP_UNLOCK(group);
+ }
+ else
+ {
+ kret = -ECHILD;
+ }
}
else if (pid == -1)
{
@@ -1062,15 +984,6 @@ sysret_t sys_kill(int pid, int signo)
*/
kret = -RT_ENOSYS;
}
- else /* pid < -1 */
- {
- /**
- * sig shall be sent to all processes (excluding an unspecified set
- * of system processes) whose process group ID is equal to the absolute
- * value of pid, and for which the process has permission to send a signal.
- */
- kret = -RT_ENOSYS;
- }
switch (kret)
{
@@ -1102,6 +1015,22 @@ sysret_t sys_getpid(void)
return lwp_getpid();
}
+sysret_t sys_getppid(void)
+{
+ rt_lwp_t process;
+
+ process = lwp_self();
+ if (process->parent == RT_NULL)
+ {
+ LOG_E("%s: process %d has no parent process", __func__, lwp_to_pid(process));
+ return 0;
+ }
+ else
+ {
+ return lwp_to_pid(process->parent);
+ }
+}
+
/* syscall: "getpriority" ret: "int" args: "int" "id_t" */
sysret_t sys_getpriority(int which, id_t who)
{
@@ -1112,10 +1041,7 @@ sysret_t sys_getpriority(int which, id_t who)
struct rt_lwp *lwp = RT_NULL;
lwp_pid_lock_take();
- if(who == 0)
- lwp = lwp_self();
- else
- lwp = lwp_from_pid_locked(who);
+ lwp = lwp_from_pid_locked(who);
if (lwp)
{
@@ -1137,10 +1063,7 @@ sysret_t sys_setpriority(int which, id_t who, int prio)
struct rt_lwp *lwp = RT_NULL;
lwp_pid_lock_take();
- if(who == 0)
- lwp = lwp_self();
- else
- lwp = lwp_from_pid_locked(who);
+ lwp = lwp_from_pid_locked(who);
if (lwp && prio >= 0 && prio < RT_THREAD_PRIORITY_MAX)
{
@@ -1317,7 +1240,7 @@ sysret_t sys_munmap(void *addr, size_t length)
void *sys_mremap(void *old_address, size_t old_size,
size_t new_size, int flags, void *new_address)
{
- return (void *)-1;
+ return lwp_mremap(lwp_self(), old_address, old_size, new_size, flags, new_address);
}
sysret_t sys_madvise(void *addr, size_t len, int behav)
@@ -1475,6 +1398,34 @@ sysret_t sys_mb_recv(rt_mailbox_t mb, rt_ubase_t *value, rt_int32_t timeout)
return ret;
}
+rt_weak int syslog_ctrl(int type, char *buf, int len)
+{
+ return -EINVAL;
+}
+
+sysret_t sys_syslog(int type, char *buf, int len)
+{
+ char *tmp;
+ int ret = -1;
+
+ if (!lwp_user_accessable((void *)buf, len))
+ {
+ return -EFAULT;
+ }
+
+ tmp = (char *)rt_malloc(len);
+ if (!tmp)
+ {
+ return -ENOMEM;
+ }
+
+ ret = syslog_ctrl(type, tmp, len);
+ lwp_put_to_user(buf, tmp, len);
+ rt_free(tmp);
+
+ return ret;
+}
+
rt_mq_t sys_mq_create(const char *name,
rt_size_t msg_size,
rt_size_t max_msgs,
@@ -2012,13 +1963,21 @@ static void lwp_struct_copy(struct rt_lwp *dst, struct rt_lwp *src)
dst->data_entry = src->data_entry;
dst->data_size = src->data_size;
dst->args = src->args;
- dst->leader = 0;
- dst->session = src->session;
dst->background = src->background;
- dst->tty_old_pgrp = 0;
- dst->__pgrp = src->__pgrp;
dst->tty = src->tty;
+
+ /* terminal API */
+ dst->term_ctrlterm = src->term_ctrlterm;
+
rt_memcpy(dst->cmd, src->cmd, RT_NAME_MAX);
+ if (src->exe_file)
+ {
+ if (dst->exe_file)
+ {
+ rt_free(dst->exe_file);
+ }
+ dst->exe_file = strndup(src->exe_file, DFS_PATH_MAX);
+ }
rt_memcpy(&dst->signal.sig_action, &src->signal.sig_action, sizeof(dst->signal.sig_action));
rt_memcpy(&dst->signal.sig_action_mask, &src->signal.sig_action_mask, sizeof(dst->signal.sig_action_mask));
@@ -2026,6 +1985,8 @@ static void lwp_struct_copy(struct rt_lwp *dst, struct rt_lwp *src)
rt_memcpy(&dst->signal.sig_action_onstack, &src->signal.sig_action_onstack, sizeof(dst->signal.sig_action_onstack));
rt_memcpy(&dst->signal.sig_action_restart, &dst->signal.sig_action_restart, sizeof(dst->signal.sig_action_restart));
rt_memcpy(&dst->signal.sig_action_siginfo, &dst->signal.sig_action_siginfo, sizeof(dst->signal.sig_action_siginfo));
+ rt_memcpy(&dst->signal.sig_action_nocldstop, &dst->signal.sig_action_nocldstop, sizeof(dst->signal.sig_action_nocldstop));
+ rt_memcpy(&dst->signal.sig_action_nocldwait, &dst->signal.sig_action_nocldwait, sizeof(dst->signal.sig_action_nocldwait));
rt_strcpy(dst->working_directory, src->working_directory);
}
@@ -2071,6 +2032,7 @@ sysret_t _sys_fork(void)
rt_thread_t thread = RT_NULL;
rt_thread_t self_thread = RT_NULL;
void *user_stack = RT_NULL;
+ rt_processgroup_t group;
/* new lwp */
lwp = lwp_create(LWP_CREATE_FLAG_ALLOC_PID);
@@ -2145,6 +2107,17 @@ sysret_t _sys_fork(void)
lwp_children_register(self_lwp, lwp);
+ /* set pgid and sid */
+ group = lwp_pgrp_find(lwp_pgid_get_byprocess(self_lwp));
+ if (group)
+ {
+ lwp_pgrp_insert(group, lwp);
+ }
+ else
+ {
+ LOG_W("the process group of pid: %d cannot be found", lwp_pgid_get_byprocess(self_lwp));
+ }
+
/* copy kernel stack context from self thread */
lwp_memcpy(thread->stack_addr, self_thread->stack_addr, self_thread->stack_size);
lwp_tid_set_thread(tid, thread);
@@ -2157,28 +2130,6 @@ sysret_t _sys_fork(void)
(void *)((char *)thread->stack_addr + thread->stack_size),
user_stack, &thread->sp);
- /* new thread never reach there */
- LWP_LOCK(lwp);
- if (lwp->tty != RT_NULL)
- {
- int ret;
- struct rt_lwp *old_lwp;
-
- old_lwp = lwp->tty->foreground;
- rt_mutex_take(&lwp->tty->lock, RT_WAITING_FOREVER);
- ret = tty_push(&lwp->tty->head, old_lwp);
- rt_mutex_release(&lwp->tty->lock);
- if (ret < 0)
- {
- LOG_E("malloc fail!\n");
- SET_ERRNO(ENOMEM);
- goto fail;
- }
-
- lwp->tty->foreground = lwp;
- }
- LWP_UNLOCK(lwp);
-
rt_thread_startup(thread);
return lwp_to_pid(lwp);
fail:
@@ -2246,7 +2197,7 @@ static char *_insert_args(int new_argc, char *new_argv[], struct lwp_args_info *
nsize = new_argc * sizeof(char *);
for (i = 0; i < new_argc; i++)
{
- nsize += lwp_user_strlen(new_argv[i]) + 1;
+ nsize += lwp_strlen(lwp_self(), new_argv[i]) + 1;
}
if (nsize + args->size > ARCH_PAGE_SIZE)
{
@@ -2260,7 +2211,7 @@ static char *_insert_args(int new_argc, char *new_argv[], struct lwp_args_info *
for (i = 0; i < new_argc; i++)
{
nargv[i] = p;
- len = lwp_user_strlen(new_argv[i]) + 1;
+ len = lwp_strlen(lwp_self(), new_argv[i]) + 1;
lwp_memcpy(p, new_argv[i], len);
p += len;
}
@@ -2269,7 +2220,7 @@ static char *_insert_args(int new_argc, char *new_argv[], struct lwp_args_info *
for (i = 0; i < args->argc; i++)
{
nargv[i] = p;
- len = lwp_user_strlen(args->argv[i]) + 1;
+ len = lwp_strlen(lwp_self(), args->argv[i]) + 1;
lwp_memcpy(p, args->argv[i], len);
p += len;
}
@@ -2278,7 +2229,7 @@ static char *_insert_args(int new_argc, char *new_argv[], struct lwp_args_info *
for (i = 0; i < args->envc; i++)
{
nenvp[i] = p;
- len = lwp_user_strlen(args->envp[i]) + 1;
+ len = lwp_strlen(lwp_self(), args->envp[i]) + 1;
lwp_memcpy(p, args->envp[i], len);
p += len;
}
@@ -2427,7 +2378,7 @@ int load_ldso(struct rt_lwp *lwp, char *exec_name, char *const argv[], char *con
{
break;
}
- len = lwp_user_strlen_ext(lwp, (const char *)argv[argc]);
+ len = lwp_strlen(lwp, (const char *)argv[argc]);
size += sizeof(char *) + len + 1;
argc++;
}
@@ -2440,7 +2391,7 @@ int load_ldso(struct rt_lwp *lwp, char *exec_name, char *const argv[], char *con
{
break;
}
- len = lwp_user_strlen_ext(lwp, (const char *)envp[envc]);
+ len = lwp_strlen(lwp, (const char *)envp[envc]);
size += sizeof(char *) + len + 1;
envc++;
}
@@ -2461,7 +2412,7 @@ int load_ldso(struct rt_lwp *lwp, char *exec_name, char *const argv[], char *con
for (i = 0; i < argc; i++)
{
kargv[i] = p;
- len = lwp_user_strlen_ext(lwp, argv[i]) + 1;
+ len = lwp_strlen(lwp, argv[i]) + 1;
lwp_memcpy(p, argv[i], len);
p += len;
}
@@ -2473,7 +2424,7 @@ int load_ldso(struct rt_lwp *lwp, char *exec_name, char *const argv[], char *con
for (i = 0; i < envc; i++)
{
kenvp[i] = p;
- len = lwp_user_strlen_ext(lwp, envp[i]) + 1;
+ len = lwp_strlen(lwp, envp[i]) + 1;
lwp_memcpy(p, envp[i], len);
p += len;
}
@@ -2523,7 +2474,7 @@ int load_ldso(struct rt_lwp *lwp, char *exec_name, char *const argv[], char *con
ret = lwp_load("/lib/ld.so", lwp, RT_NULL, 0, aux);
- rt_strncpy(lwp->cmd, exec_name, RT_NAME_MAX);
+ rt_strncpy(lwp->cmd, exec_name, RT_NAME_MAX - 1);
quit:
if (page)
{
@@ -2751,6 +2702,8 @@ sysret_t sys_execve(const char *path, char *const argv[], char *const envp[])
strncpy(thread->parent.name, run_name + last_backslash, RT_NAME_MAX);
strncpy(lwp->cmd, new_lwp->cmd, RT_NAME_MAX);
+ rt_free(lwp->exe_file);
+ lwp->exe_file = strndup(new_lwp->exe_file, DFS_PATH_MAX);
rt_pages_free(page, 0);
@@ -4412,7 +4365,7 @@ sysret_t sys_waitpid(int32_t pid, int *status, int options)
}
else
{
- ret = lwp_waitpid(pid, status, options);
+ ret = lwp_waitpid(pid, status, options, RT_NULL);
}
#else
if (!lwp_user_accessable((void *)status, sizeof(int)))
@@ -4715,7 +4668,7 @@ sysret_t sys_gethostbyname2_r(const char *name, int af, struct hostent *ret,
rt_strncpy(ptr, k_name, buflen - (ptr - buf));
ret->h_name = ptr;
- ptr += lwp_user_strlen(k_name);
+ ptr += strlen(k_name);
ret->h_addr_list = (char**)ptr;
ptr += cnt * sizeof(char *);
@@ -4733,6 +4686,10 @@ sysret_t sys_gethostbyname2_r(const char *name, int af, struct hostent *ret,
#endif
ret_val = 0;
}
+ else
+ {
+ SET_ERRNO(EINVAL);
+ }
__exit:
if (ret_val < 0)
@@ -4794,6 +4751,7 @@ sysret_t sys_chdir(const char *path)
#ifdef ARCH_MM_MMU
int err = 0;
int len = 0;
+ int errcode;
char *kpath = RT_NULL;
len = lwp_user_strlen(path);
@@ -4815,16 +4773,46 @@ sysret_t sys_chdir(const char *path)
}
err = chdir(kpath);
+ errcode = err != 0 ? GET_ERRNO() : 0;
kmem_put(kpath);
- return (err < 0 ? GET_ERRNO() : err);
+ return errcode;
#else
int ret = chdir(path);
return (ret < 0 ? GET_ERRNO() : ret);
#endif
}
+sysret_t sys_fchdir(int fd)
+{
+ int errcode = -ENOSYS;
+#ifdef ARCH_MM_MMU
+#ifdef RT_USING_DFS_V2
+ int err = -1;
+ struct dfs_file *d;
+ char *kpath;
+
+ d = fd_get(fd);
+ if (!d || !d->vnode)
+ {
+ return -EBADF;
+ }
+ kpath = dfs_dentry_full_path(d->dentry);
+ if (!kpath)
+ {
+ return -EACCES;
+ }
+
+ err = chdir(kpath);
+ errcode = err != 0 ? GET_ERRNO() : 0;
+
+ kmem_put(kpath);
+#endif
+#endif
+ return errcode;
+}
+
sysret_t sys_mkdir(const char *path, mode_t mode)
{
#ifdef ARCH_MM_MMU
@@ -5055,6 +5043,11 @@ sysret_t sys_pipe(int fd[2])
return (ret < 0 ? GET_ERRNO() : ret);
}
+sysret_t sys_wait4(pid_t pid, int *status, int options, struct rusage *ru)
+{
+ return lwp_waitpid(pid, status, options, ru);
+}
+
sysret_t sys_clock_settime(clockid_t clk, const struct timespec *ts)
{
int ret = 0;
@@ -5271,7 +5264,7 @@ sysret_t sys_getrlimit(unsigned int resource, unsigned long rlim[2])
}
lwp_put_to_user((void *)rlim, krlim, sizeof(unsigned long [2]));
- kmem_put(krlim);
+
return (ret < 0 ? GET_ERRNO() : ret);
}
@@ -5280,13 +5273,6 @@ sysret_t sys_setrlimit(unsigned int resource, struct rlimit *rlim)
return -ENOSYS;
}
-sysret_t sys_setsid(void)
-{
- int ret = 0;
- ret = setsid();
- return (ret < 0 ? GET_ERRNO() : ret);
-}
-
sysret_t sys_getrandom(void *buf, size_t buflen, unsigned int flags)
{
int ret = -1;
@@ -5353,12 +5339,16 @@ sysret_t sys_getrandom(void *buf, size_t buflen, unsigned int flags)
return ret;
}
+/**
+ * readlink() places the contents of the symbolic link pathname in the buffer buf, which has size bufsiz.
+ * readlink() does not append a null byte to buf.
+ * It will (silently) truncate the contents(to a length of bufsiz characters),
+ * in case the buffer is too small to hold all of the contents.
+ */
ssize_t sys_readlink(char* path, char *buf, size_t bufsz)
{
size_t len, copy_len;
int err, rtn;
- int fd = -1;
- struct dfs_file *d;
char *copy_path;
len = lwp_user_strlen(path);
@@ -5381,91 +5371,27 @@ ssize_t sys_readlink(char* path, char *buf, size_t bufsz)
copy_len = lwp_get_from_user(copy_path, path, len);
copy_path[copy_len] = '\0';
- /* musl __procfdname */
- err = sscanf(copy_path, "/proc/self/fd/%d", &fd);
-
- if (err != 1)
+ char *link_fn = (char *)rt_malloc(DFS_PATH_MAX);
+ if (link_fn)
{
- rtn = 0;
- if (access(copy_path, 0))
+ err = dfs_file_readlink(copy_path, link_fn, DFS_PATH_MAX);
+ if (err > 0)
{
- rtn = -ENOENT;
- LOG_E("readlink: path not is /proc/self/fd/* and path not exits, call by musl __procfdname()?");
+ rtn = lwp_put_to_user(buf, link_fn, bufsz > err ? err : bufsz - 1);
}
else
{
-#ifdef RT_USING_DFS_V2
- char *link_fn = (char *)rt_malloc(DFS_PATH_MAX);
- if (link_fn)
- {
- err = dfs_file_readlink(copy_path, link_fn, DFS_PATH_MAX);
- if (err > 0)
- {
- rtn = lwp_put_to_user(buf, link_fn, bufsz > err ? err : bufsz - 1);
- }
- else
- {
- rtn = -EIO;
- }
- rt_free(link_fn);
- }
- else
- {
- rtn = -ENOMEM;
- }
-#else
- rtn = lwp_put_to_user(buf, copy_path, copy_len);
-#endif
+ rtn = -EIO;
}
- rt_free(copy_path);
- return rtn;
+ rt_free(link_fn);
}
else
{
- rt_free(copy_path);
- }
-
- d = fd_get(fd);
- if (!d)
- {
- return -EBADF;
- }
-
- if (!d->vnode)
- {
- return -EBADF;
+ rtn = -ENOMEM;
}
-#ifdef RT_USING_DFS_V2
- {
- char *fullpath = dfs_dentry_full_path(d->dentry);
- if (fullpath)
- {
- copy_len = strlen(fullpath);
- if (copy_len > bufsz)
- {
- copy_len = bufsz;
- }
-
- bufsz = lwp_put_to_user(buf, fullpath, copy_len);
- rt_free(fullpath);
- }
- else
- {
- bufsz = 0;
- }
- }
-#else
- copy_len = strlen(d->vnode->fullpath);
- if (copy_len > bufsz)
- {
- copy_len = bufsz;
- }
-
- bufsz = lwp_put_to_user(buf, d->vnode->fullpath, copy_len);
-#endif
-
- return bufsz;
+ rt_free(copy_path);
+ return rtn;
}
sysret_t sys_sched_setaffinity(pid_t pid, size_t size, void *set)
@@ -5511,7 +5437,6 @@ sysret_t sys_sched_getaffinity(const pid_t pid, size_t size, void *set)
LWP_DEF_RETURN_CODE(rc);
void *mask;
struct rt_lwp *lwp;
- rt_bool_t need_release = RT_FALSE;
if (size <= 0 || size > sizeof(cpu_set_t))
{
@@ -5529,16 +5454,8 @@ sysret_t sys_sched_getaffinity(const pid_t pid, size_t size, void *set)
CPU_ZERO_S(size, mask);
- if (pid == 0)
- {
- lwp = lwp_self();
- }
- else
- {
- need_release = RT_TRUE;
- lwp_pid_lock_take();
- lwp = lwp_from_pid_locked(pid);
- }
+ lwp_pid_lock_take();
+ lwp = lwp_from_pid_locked(pid);
if (!lwp)
{
@@ -5569,8 +5486,7 @@ sysret_t sys_sched_getaffinity(const pid_t pid, size_t size, void *set)
rc = size;
}
- if (need_release)
- lwp_pid_lock_release();
+ lwp_pid_lock_release();
kmem_put(mask);
@@ -5626,12 +5542,10 @@ sysret_t sys_sysinfo(void *info)
#endif
}
-sysret_t sys_sched_setparam(pid_t pid, void *param)
+sysret_t sys_sched_setparam(pid_t tid, void *param)
{
struct sched_param *sched_param = RT_NULL;
- struct rt_lwp *lwp = NULL;
- rt_thread_t main_thread;
- rt_bool_t need_release = RT_FALSE;
+ rt_thread_t thread;
int ret = -1;
if (!lwp_user_accessable(param, sizeof(struct sched_param)))
@@ -5651,26 +5565,15 @@ sysret_t sys_sched_setparam(pid_t pid, void *param)
return -EINVAL;
}
- if (pid > 0)
- {
- need_release = RT_TRUE;
- lwp_pid_lock_take();
- lwp = lwp_from_pid_locked(pid);
- }
- else if (pid == 0)
- {
- lwp = lwp_self();
- }
+ thread = lwp_tid_get_thread_and_inc_ref(tid);
- if (lwp)
+ if (thread)
{
- main_thread = rt_list_entry(lwp->t_grp.prev, struct rt_thread, sibling);
- if (need_release)
- lwp_pid_lock_release();
-
- ret = rt_thread_control(main_thread, RT_THREAD_CTRL_CHANGE_PRIORITY, (void *)&sched_param->sched_priority);
+ ret = rt_thread_control(thread, RT_THREAD_CTRL_CHANGE_PRIORITY, (void *)&sched_param->sched_priority);
}
+ lwp_tid_dec_ref(thread);
+
kmem_put(sched_param);
return ret;
@@ -5767,37 +5670,24 @@ sysret_t sys_sched_setscheduler(int tid, int policy, void *param)
return ret;
}
-sysret_t sys_sched_getscheduler(int tid, int *policy, void *param)
+sysret_t sys_sched_getscheduler(int tid)
{
- struct sched_param *sched_param = RT_NULL;
rt_thread_t thread = RT_NULL;
+ int rtn;
- if (!lwp_user_accessable(param, sizeof(struct sched_param)))
- {
- return -EFAULT;
- }
+ thread = lwp_tid_get_thread_and_inc_ref(tid);
+ lwp_tid_dec_ref(thread);
- sched_param = kmem_get(sizeof(struct sched_param));
- if (sched_param == RT_NULL)
+ if (thread)
{
- return -ENOMEM;
+ rtn = SCHED_RR;
}
-
- if (lwp_get_from_user(sched_param, param, sizeof(struct sched_param)) != sizeof(struct sched_param))
+ else
{
- kmem_put(sched_param);
- return -EINVAL;
+ rtn = -ESRCH;
}
- thread = lwp_tid_get_thread_and_inc_ref(tid);
- sched_param->sched_priority = RT_SCHED_PRIV(thread).current_priority;
- lwp_tid_dec_ref(thread);
-
- lwp_put_to_user((void *)param, sched_param, sizeof(struct sched_param));
- kmem_put(sched_param);
-
- *policy = 0;
- return 0;
+ return rtn;
}
sysret_t sys_fsync(int fd)
@@ -6409,24 +6299,31 @@ sysret_t sys_epoll_ctl(int fd, int op, int fd2, struct epoll_event *ev)
int ret = 0;
struct epoll_event *kev = RT_NULL;
- if (!lwp_user_accessable((void *)ev, sizeof(struct epoll_event)))
+ if (ev)
+ {
+ if (!lwp_user_accessable((void *)ev, sizeof(struct epoll_event)))
return -EFAULT;
- kev = kmem_get(sizeof(struct epoll_event));
- if (kev == RT_NULL)
- {
- return -ENOMEM;
- }
+ kev = kmem_get(sizeof(struct epoll_event));
+ if (kev == RT_NULL)
+ {
+ return -ENOMEM;
+ }
- if (lwp_get_from_user(kev, ev, sizeof(struct epoll_event)) != sizeof(struct epoll_event))
- {
- kmem_put(kev);
- return -EINVAL;
- }
+ if (lwp_get_from_user(kev, ev, sizeof(struct epoll_event)) != sizeof(struct epoll_event))
+ {
+ kmem_put(kev);
+ return -EINVAL;
+ }
- ret = epoll_ctl(fd, op, fd2, kev);
+ ret = epoll_ctl(fd, op, fd2, kev);
- kmem_put(kev);
+ kmem_put(kev);
+ }
+ else
+ {
+ ret = epoll_ctl(fd, op, fd2, RT_NULL);
+ }
return (ret < 0 ? GET_ERRNO() : ret);
}
@@ -6537,45 +6434,93 @@ sysret_t sys_utimensat(int __fd, const char *__path, const struct timespec __tim
#endif
}
-sysret_t sys_chmod(const char *fileName, mode_t mode)
+sysret_t sys_chmod(const char *pathname, mode_t mode)
{
- char *copy_fileName;
- size_t len_fileName, copy_len_fileName;
-#ifdef RT_USING_DFS_V2
- struct dfs_attr attr;
+ char *copy_file;
+ size_t len_file, copy_len_file;
+ struct dfs_attr attr = {0};
+ int ret = 0;
+
+ len_file = lwp_user_strlen(pathname);
+ if (len_file <= 0)
+ {
+ return -EFAULT;
+ }
+
+ copy_file = (char*)rt_malloc(len_file + 1);
+ if (!copy_file)
+ {
+ return -ENOMEM;
+ }
+
+ copy_len_file = lwp_get_from_user(copy_file, (void *)pathname, len_file);
+
attr.st_mode = mode;
-#endif
+ attr.ia_valid |= ATTR_MODE_SET;
+
+ copy_file[copy_len_file] = '\0';
+ ret = dfs_file_setattr(copy_file, &attr);
+ rt_free(copy_file);
+
+ return (ret < 0 ? GET_ERRNO() : ret);
+}
+
+sysret_t sys_chown(const char *pathname, uid_t owner, gid_t group)
+{
+ char *copy_file;
+ size_t len_file, copy_len_file;
+ struct dfs_attr attr = {0};
int ret = 0;
- len_fileName = lwp_user_strlen(fileName);
- if (len_fileName <= 0)
+ len_file = lwp_user_strlen(pathname);
+ if (len_file <= 0)
{
return -EFAULT;
}
- copy_fileName = (char*)rt_malloc(len_fileName + 1);
- if (!copy_fileName)
+ copy_file = (char*)rt_malloc(len_file + 1);
+ if (!copy_file)
{
return -ENOMEM;
}
- copy_len_fileName = lwp_get_from_user(copy_fileName, (void *)fileName, len_fileName);
- copy_fileName[copy_len_fileName] = '\0';
-#ifdef RT_USING_DFS_V2
- ret = dfs_file_setattr(copy_fileName, &attr);
-#else
- SET_ERRNO(EFAULT);
-#endif
- rt_free(copy_fileName);
+ copy_len_file = lwp_get_from_user(copy_file, (void *)pathname, len_file);
+
+ if(owner >= 0)
+ {
+ attr.st_uid = owner;
+ attr.ia_valid |= ATTR_UID_SET;
+ }
+
+ if(group >= 0)
+ {
+ attr.st_gid = group;
+ attr.ia_valid |= ATTR_GID_SET;
+ }
+
+ copy_file[copy_len_file] = '\0';
+ ret = dfs_file_setattr(copy_file, &attr);
+ rt_free(copy_file);
return (ret < 0 ? GET_ERRNO() : ret);
}
-sysret_t sys_reboot(int magic)
+#include <sys/reboot.h>
+sysret_t sys_reboot(int magic, int magic2, int type)
{
- rt_hw_cpu_reset();
+ sysret_t rc;
+ switch (type)
+ {
+ /* TODO add software poweroff protocols */
+ case RB_AUTOBOOT:
+ case RB_POWER_OFF:
+ rt_hw_cpu_reset();
+ break;
+ default:
+ rc = -ENOSYS;
+ }
- return 0;
+ return rc;
}
ssize_t sys_pread64(int fd, void *buf, int size, size_t offset)
@@ -6824,6 +6769,59 @@ sysret_t sys_setitimer(int which, const struct itimerspec *restrict new, struct
return rc;
}
+sysret_t sys_set_robust_list(struct robust_list_head *head, size_t len)
+{
+ if (len != sizeof(*head))
+ return -EINVAL;
+
+ rt_thread_self()->robust_list = head;
+ return 0;
+}
+
+sysret_t sys_get_robust_list(int tid, struct robust_list_head **head_ptr, size_t *len_ptr)
+{
+ rt_thread_t thread;
+ size_t len;
+ struct robust_list_head *head;
+
+ if (!lwp_user_accessable((void *)head_ptr, sizeof(struct robust_list_head *)))
+ {
+ return -EFAULT;
+ }
+ if (!lwp_user_accessable((void *)len_ptr, sizeof(size_t)))
+ {
+ return -EFAULT;
+ }
+
+ if (tid == 0)
+ {
+ thread = rt_thread_self();
+ head = thread->robust_list;
+ }
+ else
+ {
+ thread = lwp_tid_get_thread_and_inc_ref(tid);
+ if (thread)
+ {
+ head = thread->robust_list;
+ lwp_tid_dec_ref(thread);
+ }
+ else
+ {
+ return -ESRCH;
+ }
+ }
+
+ len = sizeof(*(head));
+
+ if (!lwp_put_to_user(len_ptr, &len, sizeof(size_t)))
+ return -EFAULT;
+ if (!lwp_put_to_user(head_ptr, &head, sizeof(struct robust_list_head *)))
+ return -EFAULT;
+
+ return 0;
+}
+
const static struct rt_syscall_def func_table[] =
{
SYSCALL_SIGN(sys_exit), /* 01 */
@@ -6994,22 +6992,22 @@ const static struct rt_syscall_def func_table[] =
SYSCALL_SIGN(sys_clock_settime),
SYSCALL_SIGN(sys_clock_gettime),
SYSCALL_SIGN(sys_clock_getres),
- SYSCALL_USPACE(SYSCALL_SIGN(sys_clone)), /* 130 */
+ SYSCALL_USPACE(SYSCALL_SIGN(sys_clone)), /* 130 */
SYSCALL_USPACE(SYSCALL_SIGN(sys_futex)),
- SYSCALL_USPACE(SYSCALL_SIGN(sys_pmutex)),
+ SYSCALL_SIGN(sys_notimpl), /* discarded: sys_pmutex */
SYSCALL_SIGN(sys_dup),
SYSCALL_SIGN(sys_dup2),
- SYSCALL_SIGN(sys_rename), /* 135 */
+ SYSCALL_SIGN(sys_rename), /* 135 */
SYSCALL_USPACE(SYSCALL_SIGN(sys_fork)),
SYSCALL_USPACE(SYSCALL_SIGN(sys_execve)),
SYSCALL_USPACE(SYSCALL_SIGN(sys_vfork)),
SYSCALL_SIGN(sys_gettid),
- SYSCALL_SIGN(sys_prlimit64), /* 140 */
+ SYSCALL_SIGN(sys_prlimit64), /* 140 */
SYSCALL_SIGN(sys_getrlimit),
SYSCALL_SIGN(sys_setrlimit),
SYSCALL_SIGN(sys_setsid),
SYSCALL_SIGN(sys_getrandom),
- SYSCALL_SIGN(sys_readlink), // SYSCALL_SIGN(sys_readlink) /* 145 */
+ SYSCALL_SIGN(sys_readlink), /* 145 */
SYSCALL_USPACE(SYSCALL_SIGN(sys_mremap)),
SYSCALL_USPACE(SYSCALL_SIGN(sys_madvise)),
SYSCALL_SIGN(sys_sched_setparam),
@@ -7044,7 +7042,7 @@ const static struct rt_syscall_def func_table[] =
SYSCALL_SIGN(sys_umount2),
SYSCALL_SIGN(sys_link),
SYSCALL_SIGN(sys_symlink),
- SYSCALL_SIGN(sys_sched_getaffinity), /* 180 */
+ SYSCALL_SIGN(sys_sched_getaffinity), /* 180 */
SYSCALL_SIGN(sys_sysinfo),
SYSCALL_SIGN(sys_chmod),
SYSCALL_SIGN(sys_reboot),
@@ -7069,12 +7067,21 @@ const static struct rt_syscall_def func_table[] =
SYSCALL_SIGN(sys_setitimer),
SYSCALL_SIGN(sys_utimensat),
#ifdef RT_USING_POSIX_SOCKET
- SYSCALL_SIGN(sys_notimpl),
- SYSCALL_SIGN(sys_socketpair), /* 205 */
+ SYSCALL_SIGN(sys_syslog),
+ SYSCALL_SIGN(sys_socketpair), /* 205 */
#else
SYSCALL_SIGN(sys_notimpl),
- SYSCALL_SIGN(sys_notimpl),
+ SYSCALL_SIGN(sys_notimpl), /* 205 */
#endif
+ SYSCALL_SIGN(sys_wait4),
+ SYSCALL_SIGN(sys_set_robust_list),
+ SYSCALL_SIGN(sys_get_robust_list),
+ SYSCALL_SIGN(sys_setpgid),
+ SYSCALL_SIGN(sys_getpgid), /* 210 */
+ SYSCALL_SIGN(sys_getsid),
+ SYSCALL_SIGN(sys_getppid),
+ SYSCALL_SIGN(sys_fchdir),
+ SYSCALL_SIGN(sys_chown),
};
const void *lwp_get_sys_api(rt_uint32_t number)
diff --git a/components/lwp/lwp_syscall.h b/components/lwp/lwp_syscall.h
index b7e7b09f55..1527a764f1 100644
--- a/components/lwp/lwp_syscall.h
+++ b/components/lwp/lwp_syscall.h
@@ -108,10 +108,15 @@ sysret_t sys_log(const char* log, int size);
#ifdef ARCH_MM_MMU
sysret_t sys_futex(int *uaddr, int op, int val, const struct timespec *timeout, int *uaddr2, int val3);
-sysret_t sys_pmutex(void *umutex, int op, void *arg);
sysret_t sys_cacheflush(void *addr, int len, int cache);
#endif /* ARCH_MM_MMU */
+sysret_t sys_setsid(void);
+sysret_t sys_getsid(pid_t pid);
+sysret_t sys_setpgid(pid_t pid, pid_t pgid);
+sysret_t sys_getpgid(pid_t pid);
+
+
#ifdef __cplusplus
}
#endif
diff --git a/components/lwp/lwp_tid.c b/components/lwp/lwp_tid.c
index fdac54a36f..3e330027de 100644
--- a/components/lwp/lwp_tid.c
+++ b/components/lwp/lwp_tid.c
@@ -6,6 +6,7 @@
* Change Logs:
* Date Author Notes
* 2021-01-15 shaojinchun first version
+ * 2023-11-16 xqyjlj Fix the case where tid is 0
*/
#define DBG_TAG "lwp.tid"
@@ -126,20 +127,28 @@ void lwp_tid_put(int tid)
lwp_mutex_release_safe(&tid_lock);
}
-rt_thread_t lwp_tid_get_thread_and_inc_ref(int tid)
+rt_thread_t lwp_tid_get_thread_raw(int tid)
{
struct lwp_avl_struct *p;
rt_thread_t thread = RT_NULL;
- lwp_mutex_take_safe(&tid_lock, RT_WAITING_FOREVER, 0);
p = lwp_avl_find(tid, lwp_tid_root);
if (p)
{
thread = (rt_thread_t)p->data;
- if (thread != RT_NULL)
- {
- thread->tid_ref_count += 1;
- }
+ }
+ return thread;
+}
+
+rt_thread_t lwp_tid_get_thread_and_inc_ref(int tid)
+{
+ rt_thread_t thread = RT_NULL;
+
+ lwp_mutex_take_safe(&tid_lock, RT_WAITING_FOREVER, 0);
+ thread = tid ? lwp_tid_get_thread_raw(tid) : rt_thread_self();
+ if (thread != RT_NULL)
+ {
+ thread->tid_ref_count += 1;
}
lwp_mutex_release_safe(&tid_lock);
return thread;
diff --git a/components/lwp/lwp_user_mm.c b/components/lwp/lwp_user_mm.c
index 3c23e59e05..084b5bcf28 100644
--- a/components/lwp/lwp_user_mm.c
+++ b/components/lwp/lwp_user_mm.c
@@ -23,10 +23,7 @@
#ifdef ARCH_MM_MMU
-#include <lwp.h>
-#include <lwp_arch.h>
-#include <lwp_mm.h>
-#include <lwp_user_mm.h>
+#include "lwp_internal.h"
#include <mm_aspace.h>
#include <mm_fault.h>
@@ -161,10 +158,10 @@ void lwp_aspace_switch(struct rt_thread *thread)
void lwp_unmap_user_space(struct rt_lwp *lwp)
{
- arch_user_space_free(lwp);
+ if (lwp->aspace)
+ arch_user_space_free(lwp);
}
-
static void *_lwp_map_user(struct rt_lwp *lwp, void *map_va, size_t map_size,
int text)
{
@@ -566,6 +563,14 @@ int lwp_munmap(struct rt_lwp *lwp, void *addr, size_t length)
return lwp_errno_to_posix(ret);
}
+void *lwp_mremap(struct rt_lwp *lwp, void *old_address, size_t old_size,
+ size_t new_size, int flags, void *new_address)
+{
+ RT_ASSERT(lwp);
+
+ return rt_aspace_mremap_range(lwp->aspace, old_address, old_size, new_size, flags, new_address);
+}
+
size_t lwp_get_from_user(void *dst, void *src, size_t size)
{
struct rt_lwp *lwp = RT_NULL;
@@ -621,11 +626,6 @@ size_t lwp_put_to_user(void *dst, void *src, size_t size)
return lwp_data_put(lwp, dst, src, size);
}
-rt_inline rt_bool_t _in_user_space(const char *addr)
-{
- return (addr >= (char *)USER_VADDR_START && addr < (char *)USER_VADDR_TOP);
-}
-
rt_inline rt_bool_t _can_unaligned_access(const char *addr)
{
return rt_kmem_v2p((char *)addr) - PV_OFFSET == addr;
@@ -636,9 +636,9 @@ void *lwp_memcpy(void * __restrict dst, const void * __restrict src, size_t size
void *rc = dst;
long len;
- if (_in_user_space(dst))
+ if (lwp_in_user_space(dst))
{
- if (!_in_user_space(src))
+ if (!lwp_in_user_space(src))
{
len = lwp_put_to_user(dst, (void *)src, size);
if (!len)
@@ -654,7 +654,7 @@ void *lwp_memcpy(void * __restrict dst, const void * __restrict src, size_t size
}
else
{
- if (_in_user_space(src))
+ if (lwp_in_user_space(src))
{
len = lwp_get_from_user(dst, (void *)src, size);
if (!len)
@@ -979,6 +979,13 @@ size_t lwp_user_strlen(const char *s)
return lwp_user_strlen_ext(lwp, s);
}
+size_t lwp_strlen(struct rt_lwp *lwp, const char *s)
+{
+ if (lwp_in_user_space(s))
+ return lwp_user_strlen_ext(lwp, s);
+ else
+ return strlen(s);
+}
char** lwp_get_command_line_args(struct rt_lwp *lwp)
{
diff --git a/components/lwp/lwp_user_mm.h b/components/lwp/lwp_user_mm.h
index cfc5b57152..232038c914 100644
--- a/components/lwp/lwp_user_mm.h
+++ b/components/lwp/lwp_user_mm.h
@@ -56,6 +56,9 @@ void* lwp_mmap2(struct rt_lwp *lwp, void *addr, size_t length, int prot, int fla
*/
int lwp_munmap(struct rt_lwp *lwp, void *addr, size_t length);
+void *lwp_mremap(struct rt_lwp *lwp, void *old_address, size_t old_size,
+ size_t new_size, int flags, void *new_address);
+
/**
* @brief Test if address from user is accessible address by user
*
@@ -163,6 +166,7 @@ rt_base_t lwp_brk(void *addr);
size_t lwp_user_strlen(const char *s);
size_t lwp_user_strlen_ext(struct rt_lwp *lwp, const char *s);
+size_t lwp_strlen(struct rt_lwp *lwp, const char *s);
int lwp_fork_aspace(struct rt_lwp *dest_lwp, struct rt_lwp *src_lwp);
diff --git a/components/lwp/terminal/Kconfig b/components/lwp/terminal/Kconfig
new file mode 100644
index 0000000000..7fd77e7505
--- /dev/null
+++ b/components/lwp/terminal/Kconfig
@@ -0,0 +1,13 @@
+menuconfig LWP_USING_TERMINAL
+ bool "Terminal I/O Subsystem"
+ depends on RT_USING_SMART
+ default y
+
+if LWP_USING_TERMINAL
+ config LWP_PTY_MAX_PARIS_LIMIT
+ int "Max number of pty devices registered at the same time"
+ default 64
+ help
+ This upper limit is set to protect kernel memory from draining
+ out by the application if it keeps allocating pty devices.
+endif
diff --git a/components/lwp/terminal/bsd_porting.h b/components/lwp/terminal/bsd_porting.h
new file mode 100644
index 0000000000..fba8f47fb3
--- /dev/null
+++ b/components/lwp/terminal/bsd_porting.h
@@ -0,0 +1,741 @@
+/*
+ * Copyright (c) 2006-2023, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2023-11-13 Shell Add compatible layer for FreeBSD
+ */
+
+#ifndef __LWP_TTY_BSD_PORTING_H__
+#define __LWP_TTY_BSD_PORTING_H__
+
+#include <rtthread.h>
+#include <lwp_internal.h>
+#define _KERNEL
+
+#ifndef __unused
+#define __unused __attribute__((__unused__))
+#endif
+
+/* functionability of bsd tty layer */
+#if 0
+#define USING_BSD_HOOK
+#endif
+
+/* Only for devfs d_close() flags. */
+#define FLASTCLOSE O_DIRECTORY
+#define FREVOKE 0x00200000
+
+/*
+ * Output flags - software output processing
+ */
+#if !((OPOST | OLCUC | ONLCR) & 0x8)
+#define ONOEOT 0x0008 /* discard EOT's (^D) on output) */
+#endif
+
+/*
+ * Kernel encoding of open mode; separate read and write bits that are
+ * independently testable: 1 greater than the above.
+ *
+ * XXX
+ * FREAD and FWRITE are excluded from the #ifdef _KERNEL so that TIOCFLUSH,
+ * which was documented to use FREAD/FWRITE, continues to work.
+ */
+#define FREAD 0x0001
+#define FWRITE 0x0002
+
+/*
+ * Flags to memory allocation functions.
+ */
+#define M_NOWAIT 0x0001 /* do not block */
+#define M_WAITOK 0x0002 /* ok to block */
+#define M_NORECLAIM 0x0080 /* do not reclaim after failure */
+#define M_ZERO 0x0100 /* bzero the allocation */
+#define M_NOVM 0x0200 /* don't ask VM for pages */
+#define M_USE_RESERVE 0x0400 /* can alloc out of reserve memory */
+#define M_NODUMP 0x0800 /* don't dump pages in this allocation */
+#define M_FIRSTFIT 0x1000 /* only for vmem, fast fit */
+#define M_BESTFIT 0x2000 /* only for vmem, low fragmentation */
+#define M_EXEC 0x4000 /* allocate executable space */
+#define M_NEXTFIT 0x8000 /* only for vmem, follow cursor */
+
+#define M_VERSION 2020110501
+
+/*
+ * The INVARIANTS-enabled mtx_assert() functionality.
+ *
+ * The constants need to be defined for INVARIANT_SUPPORT infrastructure
+ * support as _mtx_assert() itself uses them and the latter implies that
+ * _mtx_assert() must build.
+ */
+#define MA_OWNED (1)
+#define MA_NOTOWNED (2)
+#define MA_RECURSED (4)
+#define MA_NOTRECURSED (8)
+
+/*
+ * Indentification of modem control signals. These definitions match
+ * the TIOCMGET definitions in <sys/ttycom.h> shifted a bit down, and
+ * that identity is enforced with CTASSERT at the bottom of kern/tty.c
+ * Both the modem bits and delta bits must fit in 16 bit.
+ */
+#define SER_DTR 0x0001 /* data terminal ready */
+#define SER_RTS 0x0002 /* request to send */
+#define SER_STX 0x0004 /* secondary transmit */
+#define SER_SRX 0x0008 /* secondary receive */
+#define SER_CTS 0x0010 /* clear to send */
+#define SER_DCD 0x0020 /* data carrier detect */
+#define SER_RI 0x0040 /* ring indicate */
+#define SER_DSR 0x0080 /* data set ready */
+
+#define SER_MASK_STATE 0x00ff
+
+/*
+ * Flags for ioflag. (high 16 bits used to ask for read-ahead and
+ * help with write clustering)
+ * NB: IO_NDELAY and IO_DIRECT are linked to fcntl.h
+ */
+#if 0
+#define IO_UNIT 0x0001 /* do I/O as atomic unit */
+#define IO_APPEND 0x0002 /* append write to end */
+#endif /* not porting */
+
+#define IO_NDELAY 0x0004 /* FNDELAY flag set in file table */
+#if 0
+#define IO_NODELOCKED 0x0008 /* underlying node already locked */
+#define IO_ASYNC 0x0010 /* bawrite rather then bdwrite */
+#define IO_VMIO 0x0020 /* data already in VMIO space */
+#define IO_INVAL 0x0040 /* invalidate after I/O */
+#define IO_SYNC 0x0080 /* do I/O synchronously */
+#define IO_DIRECT 0x0100 /* attempt to bypass buffer cache */
+#define IO_NOREUSE 0x0200 /* VMIO data won't be reused */
+#define IO_EXT 0x0400 /* operate on external attributes */
+#define IO_NORMAL 0x0800 /* operate on regular data */
+#define IO_NOMACCHECK 0x1000 /* MAC checks unnecessary */
+#define IO_BUFLOCKED 0x2000 /* ffs flag; indir buf is locked */
+#define IO_RANGELOCKED 0x4000 /* range locked */
+#define IO_DATASYNC 0x8000 /* do only data I/O synchronously */
+
+#define IO_SEQMAX 0x7F /* seq heuristic max value */
+#define IO_SEQSHIFT 16 /* seq heuristic in upper 16 bits */
+#endif /* not porting */
+
+/** Used to distinguish between normal, callout, lock and init devices.
+ * Note: this is not used in smart system.
+ */
+#define TTYUNIT_INIT 0x1
+#define TTYUNIT_LOCK 0x2
+#define TTYUNIT_CALLOUT 0x4
+
+/*
+ * TTY privileges.
+ */
+#define PRIV_TTY_CONSOLE 250 /* Set console to tty. */
+#define PRIV_TTY_DRAINWAIT 251 /* Set tty drain wait time. */
+#define PRIV_TTY_DTRWAIT 252 /* Set DTR wait on tty. */
+#define PRIV_TTY_EXCLUSIVE 253 /* Override tty exclusive flag. */
+#define _PRIV_TTY_PRISON 254 /* Removed. */
+#define PRIV_TTY_STI 255 /* Simulate input on another tty. */
+#define PRIV_TTY_SETA 256 /* Set tty termios structure. */
+
+#define MPASS(ex) RT_ASSERT(ex)
+
+#if !defined(MIN)
+#define MIN(a, b) ((a) < (b) ? (a) : (b))
+#endif
+
+#if !defined(MAX)
+#define MAX(a, b) ((a) > (b) ? (a) : (b))
+#endif
+
+#define curthread rt_thread_self()
+
+#ifdef USING_BSD_HOOK
+#define ttyhook_hashook(tp, hook) \
+ ((tp)->t_hook != NULL && (tp)->t_hook->th_##hook != NULL)
+#else
+#define ttyhook_hashook(tp, hook) (RT_FALSE)
+#endif
+
+/* condvar API */
+#include <rtdevice.h>
+
+#define cv_init(cvp, name) rt_condvar_init(cvp, name)
+#define cv_destroy(cvp) rt_condvar_detach(cvp)
+#define cv_wait(cvp, mp) \
+ rt_condvar_timedwait(cvp, mp, RT_KILLABLE, RT_WAITING_FOREVER)
+#define cv_wait_sig(cvp, mp) \
+ rt_condvar_timedwait(cvp, mp, RT_INTERRUPTIBLE, RT_WAITING_FOREVER)
+#define cv_signal(cvp) rt_condvar_signal(cvp)
+#define cv_broadcast(cvp) rt_condvar_broadcast(cvp)
+#define cv_timedwait(cvp, mp, t) rt_condvar_timedwait(cvp, mp, RT_KILLABLE, t)
+#define cv_timedwait_sig(cvp, mp, t) \
+ rt_condvar_timedwait(cvp, mp, RT_INTERRUPTIBLE, t)
+
+struct lwp_tty;
+struct uio;
+
+/* TODO: just a place holder since devfs is not capable of doing this currently
+ */
+struct file
+{
+};
+
+typedef rt_base_t sbintime_t;
+typedef rt_ubase_t vm_offset_t;
+typedef rt_base_t vm_ooffset_t;
+typedef rt_ubase_t vm_paddr_t;
+typedef rt_ubase_t vm_pindex_t;
+typedef rt_ubase_t vm_size_t;
+typedef char *rt_caddr_t;
+
+/*
+ * The exact set of memory attributes is machine dependent. However,
+ * every machine is required to define VM_MEMATTR_DEFAULT and
+ * VM_MEMATTR_UNCACHEABLE.
+ */
+typedef char vm_memattr_t; /* memory attribute codes */
+
+typedef int d_open_t(struct lwp_tty *tp, int oflags, int devtype,
+ struct rt_thread *td);
+typedef int d_fdopen_t(struct lwp_tty *tp, int oflags, struct rt_thread *td,
+ struct file *fp);
+typedef int d_close_t(struct lwp_tty *tp, int fflag, int devtype,
+ struct rt_thread *td);
+
+#ifdef USING_BSD_DEVICE_STRATEGY
+typedef void d_strategy_t(struct bio *bp);
+#endif
+
+typedef int d_ioctl_t(struct lwp_tty *tp, rt_ubase_t cmd, rt_caddr_t data,
+ int fflag, struct rt_thread *td);
+
+typedef int d_read_t(struct lwp_tty *tp, struct uio *uio, int ioflag);
+typedef int d_write_t(struct lwp_tty *tp, struct uio *uio, int ioflag);
+typedef int d_poll_t(struct lwp_tty *tp, rt_pollreq_t *req,
+ struct rt_thread *td);
+
+#ifdef USING_BSD_KNOTE
+typedef int d_kqfilter_t(struct lwp_tty *tp, struct knote *kn);
+#endif /* USING_BSD_KNOTE */
+
+typedef int d_mmap_t(struct lwp_tty *tp, vm_ooffset_t offset, vm_paddr_t *paddr,
+ int nprot, vm_memattr_t *memattr);
+
+#ifdef USING_BSD_MMAP_SINGLE
+typedef int d_mmap_single_t(struct cdev *cdev, vm_ooffset_t *offset,
+ vm_size_t size, struct vm_object **object,
+ int nprot);
+#endif /* USING_BSD_MMAP_SINGLE */
+
+typedef void d_purge_t(struct lwp_tty *tp);
+
+/*
+ * Character device switch table
+ */
+struct cdevsw
+{
+#ifdef USING_BSD_RAW_CDEVSW
+ int d_version;
+ u_int d_flags;
+ const char *d_name;
+#endif /* USING_BSD_RAW_CDEVSW */
+
+ d_open_t *d_open;
+ d_fdopen_t *d_fdopen;
+ d_close_t *d_close;
+ d_read_t *d_read;
+ d_write_t *d_write;
+ d_ioctl_t *d_ioctl;
+ d_poll_t *d_poll;
+ d_mmap_t *d_mmap;
+#ifdef USING_BSD_DEVICE_STRATEGY
+ d_strategy_t *d_strategy;
+#endif /* USING_BSD_DEVICE_STRATEGY */
+
+#ifdef USING_BSD_RAW_CDEVSW
+ void *d_spare0;
+ d_kqfilter_t *d_kqfilter;
+ d_purge_t *d_purge;
+ d_mmap_single_t *d_mmap_single;
+
+ int32_t d_spare1[3];
+ void *d_spare2[3];
+
+ /* These fields should not be messed with by drivers */
+ LIST_HEAD(, cdev) d_devs;
+ int d_spare3;
+ union
+ {
+ struct cdevsw *gianttrick;
+ SLIST_ENTRY(cdevsw) postfree_list;
+ } __d_giant;
+#endif
+};
+
+struct iovec
+{
+ void *iov_base; /* Base address. */
+ size_t iov_len; /* Length. */
+};
+
+enum uio_rw
+{
+ UIO_READ,
+ UIO_WRITE
+};
+
+struct uio
+{
+ struct iovec *uio_iov; /* scatter/gather list */
+ int uio_iovcnt; /* length of scatter/gather list */
+ off_t uio_offset; /* offset in target object */
+ ssize_t uio_resid; /* remaining bytes to process */
+#ifdef USING_BSD_UIO
+ enum uio_seg uio_segflg; /* address space */
+#endif
+ enum uio_rw uio_rw; /* operation */
+#ifdef USING_BSD_UIO
+ struct rt_thread *uio_td; /* owner */
+
+#endif /* USING_BSD_UIO */
+};
+
+#include <lwp_user_mm.h>
+rt_inline int uiomove(void *operand, int n, struct uio *uio)
+{
+ switch (uio->uio_rw)
+ {
+ case UIO_READ:
+ memcpy(uio->uio_iov->iov_base, operand, n);
+ break;
+ case UIO_WRITE:
+ memcpy(operand, uio->uio_iov->iov_base, n);
+ break;
+ default:
+ return -1;
+ }
+
+ uio->uio_iov->iov_base += n;
+ uio->uio_iov->iov_len--;
+ uio->uio_offset += n;
+ uio->uio_resid -= n;
+ return 0;
+}
+
+/* privileges checking: 0 if okay */
+rt_inline int priv_check(struct rt_thread *td, int priv)
+{
+ return 0;
+}
+
+/* Disable console redirection to a tty. */
+rt_inline int constty_clear(struct lwp_tty *tp)
+{
+ // rt_kprintf("\nTODO: %s unimplemented!\n", __func__);
+ return 0;
+}
+
+rt_inline int constty_set(struct lwp_tty *tp)
+{
+ // rt_kprintf("\nTODO: %s unimplemented!\n", __func__);
+ return 0;
+}
+
+/**
+ * UMA (Universal Memory Allocator)
+ */
+#define UMA_ALIGN_PTR (sizeof(void *) - 1) /* Alignment fit for ptr */
+
+typedef int (*uma_ctor)(void *mem, int size, void *arg, int flags);
+typedef void (*uma_dtor)(void *mem, int size, void *arg);
+typedef int (*uma_init)(void *mem, int size, int flags);
+typedef void (*uma_fini)(void *mem, int size);
+
+struct uma_zone
+{
+ char *name;
+ int align;
+ int size;
+};
+
+/* Opaque type used as a handle to the zone */
+typedef struct uma_zone *uma_zone_t;
+
+rt_inline uma_zone_t uma_zcreate(char *name, int size, uma_ctor ctor,
+ uma_dtor dtor, uma_init zinit, uma_fini zfini,
+ int align, uint16_t flags)
+{
+ uma_zone_t zone = rt_malloc(sizeof(struct uma_zone));
+ if (zone)
+ {
+ RT_ASSERT(ctor == RT_NULL);
+ RT_ASSERT(dtor == RT_NULL);
+ RT_ASSERT(zinit == RT_NULL);
+ RT_ASSERT(zfini == RT_NULL);
+
+ zone->size = size;
+ zone->name = name;
+ zone->align = align;
+ }
+ return zone;
+}
+
+rt_inline void *uma_zalloc(uma_zone_t zone, int flags)
+{
+ void *buf = rt_malloc_align(zone->size, zone->align + 1);
+ if (buf)
+ rt_memset(buf, 0, sizeof(zone->size));
+ return buf;
+}
+
+rt_inline void uma_zfree(uma_zone_t zone, void *item)
+{
+ rt_free_align(item);
+}
+
+/**
+ * bsd type of speed to linux type.
+ * Note: with switch blocks, compiler can generate the optimized version for us
+ */
+#include <termios.h>
+rt_inline long bsd_speed_to_integer(speed_t speed)
+{
+ long speed_value;
+ switch (speed)
+ {
+ case B0:
+ speed_value = 0;
+ break;
+ case B50:
+ speed_value = 50;
+ break;
+ case B75:
+ speed_value = 75;
+ break;
+ case B110:
+ speed_value = 110;
+ break;
+ case B134:
+ speed_value = 134;
+ break;
+ case B150:
+ speed_value = 150;
+ break;
+ case B200:
+ speed_value = 200;
+ break;
+ case B300:
+ speed_value = 300;
+ break;
+ case B600:
+ speed_value = 600;
+ break;
+ case B1200:
+ speed_value = 1200;
+ break;
+ case B1800:
+ speed_value = 1800;
+ break;
+ case B2400:
+ speed_value = 2400;
+ break;
+ case B4800:
+ speed_value = 4800;
+ break;
+ case B9600:
+ speed_value = 9600;
+ break;
+ case B19200:
+ speed_value = 19200;
+ break;
+ case B38400:
+ speed_value = 38400;
+ break;
+ case B57600:
+ speed_value = 57600;
+ break;
+ case B115200:
+ speed_value = 115200;
+ break;
+ case B230400:
+ speed_value = 230400;
+ break;
+ case B460800:
+ speed_value = 460800;
+ break;
+ case B500000:
+ speed_value = 500000;
+ break;
+ case B576000:
+ speed_value = 576000;
+ break;
+ case B921600:
+ speed_value = 921600;
+ break;
+ case B1000000:
+ speed_value = 1000000;
+ break;
+ case B1152000:
+ speed_value = 1152000;
+ break;
+ case B1500000:
+ speed_value = 1500000;
+ break;
+ case B2000000:
+ speed_value = 2000000;
+ break;
+ case B2500000:
+ speed_value = 2500000;
+ break;
+ case B3000000:
+ speed_value = 3000000;
+ break;
+ case B3500000:
+ speed_value = 3500000;
+ break;
+ case B4000000:
+ speed_value = 4000000;
+ break;
+ default:
+ speed_value = -1; // invalid speed
+ break;
+ }
+ return speed_value;
+}
+
+/* time.h */
+
+/* Operations on timevals. */
+
+#define timevalclear(tvp) ((tvp)->tv_sec = (tvp)->tv_usec = 0)
+#define timevalisset(tvp) ((tvp)->tv_sec || (tvp)->tv_usec)
+#define timevalcmp(tvp, uvp, cmp) \
+ (((tvp)->tv_sec == (uvp)->tv_sec) ? ((tvp)->tv_usec cmp(uvp)->tv_usec) \
+ : ((tvp)->tv_sec cmp(uvp)->tv_sec))
+
+rt_inline void getmicrotime(struct timeval *now)
+{
+ gettimeofday(now, RT_NULL);
+}
+
+rt_inline void timevalfix(struct timeval *tv)
+{
+ if (tv->tv_usec < 0)
+ {
+ tv->tv_sec--;
+ tv->tv_usec += 1000000;
+ }
+ if (tv->tv_usec >= 1000000)
+ {
+ tv->tv_sec++;
+ tv->tv_usec -= 1000000;
+ }
+}
+
+rt_inline void timevaladd(struct timeval *op1, const struct timeval *op2)
+{
+ op1->tv_sec += op2->tv_sec;
+ op1->tv_usec += op2->tv_usec;
+ timevalfix(op1);
+}
+
+rt_inline void timevalsub(struct timeval *op1, const struct timeval *op2)
+{
+ op1->tv_sec -= op2->tv_sec;
+ op1->tv_usec -= op2->tv_usec;
+ timevalfix(op1);
+}
+
+rt_inline rt_tick_t tvtohz(struct timeval *tv)
+{
+ rt_tick_t rc;
+ rc = tv->tv_sec * RT_TICK_PER_SECOND;
+ rc += tv->tv_usec * RT_TICK_PER_SECOND / MICROSECOND_PER_SECOND;
+ return rc;
+}
+
+/* ioctl */
+#define _BSD_TIOCTL(val) ((val) << 16)
+enum bsd_ioctl_cmd
+{
+ BSD_TIOCDRAIN = 1,
+ BSD_TIOCFLUSH,
+ BSD_TIOCSTART,
+ BSD_TIOCSTOP,
+ BSD_TIOCSTAT,
+ BSD_TIOCGDRAINWAIT,
+ BSD_TIOCSDRAINWAIT,
+ BSD_TIOCSDTR,
+ BSD_TIOCCDTR,
+};
+
+#ifndef TIOCGETA /* get termios struct */
+#define TIOCGETA TCGETS
+#endif
+#ifndef TIOCSETA /* set termios struct */
+#define TIOCSETA TCSETS
+#endif
+#ifndef TIOCSETAW /* drain output, set */
+#define TIOCSETAW TCSETSW
+#endif
+#ifndef TIOCSETAF /* drn out, fls in, set */
+#define TIOCSETAF TCSETSF
+#endif
+#ifndef TIOCDRAIN /* wait till output drained */
+#define TIOCDRAIN _BSD_TIOCTL(BSD_TIOCDRAIN)
+#endif
+#ifndef TIOCFLUSH /* flush buffers */
+#define TIOCFLUSH _BSD_TIOCTL(BSD_TIOCFLUSH)
+#endif
+#ifndef TIOCSTART /* start output, like ^Q */
+#define TIOCSTART _BSD_TIOCTL(BSD_TIOCSTART)
+#endif
+#ifndef TIOCSTOP /* stop output, like ^S */
+#define TIOCSTOP _BSD_TIOCTL(BSD_TIOCSTOP)
+#endif
+#ifndef TIOCSTAT /* simulate ^T status message */
+#define TIOCSTAT _BSD_TIOCTL(BSD_TIOCSTAT)
+#endif
+#ifndef TIOCGDRAINWAIT /* get ttywait timeout */
+#define TIOCGDRAINWAIT _BSD_TIOCTL(BSD_TIOCGDRAINWAIT)
+#endif
+#ifndef TIOCSDRAINWAIT /* set ttywait timeout */
+#define TIOCSDRAINWAIT _BSD_TIOCTL(BSD_TIOCSDRAINWAIT)
+#endif
+#ifndef TIOCSDTR /* set data terminal ready */
+#define TIOCSDTR _BSD_TIOCTL(BSD_TIOCSDTR)
+#endif
+#ifndef TIOCCDTR /* clear data terminal ready */
+#define TIOCCDTR _BSD_TIOCTL(BSD_TIOCCDTR)
+#endif
+
+#define ENOIOCTL ENOSYS
+#define NO_PID -1
+
+/* line discipline */
+#define TTYDISC 0 /* termios tty line discipline */
+#define SLIPDISC 4 /* serial IP discipline */
+#define PPPDISC 5 /* PPP discipline */
+#define NETGRAPHDISC 6 /* Netgraph tty node discipline */
+#define H4DISC 7 /* Netgraph Bluetooth H4 discipline */
+
+/*
+ * Control flags - hardware control of terminal
+ */
+#if __BSD_VISIBLE
+#define CIGNORE 0x00000001 /* ignore control flags */
+#define CCTS_OFLOW 0x00010000 /* CTS flow control of output */
+#define CRTSCTS (CCTS_OFLOW | CRTS_IFLOW)
+#define CRTS_IFLOW 0x00020000 /* RTS flow control of input */
+#define CDTR_IFLOW 0x00040000 /* DTR flow control of input */
+#define CDSR_OFLOW 0x00080000 /* DSR flow control of output */
+#define CCAR_OFLOW 0x00100000 /* DCD flow control of output */
+#define CNO_RTSDTR 0x00200000 /* Do not assert RTS or DTR automatically */
+#else
+#define CIGNORE 0 /* ignore control flags */
+#define CCTS_OFLOW 0 /* CTS flow control of output */
+#define CRTS_IFLOW 0 /* RTS flow control of input */
+#define CDTR_IFLOW 0 /* DTR flow control of input */
+#define CDSR_OFLOW 0 /* DSR flow control of output */
+#define CCAR_OFLOW 0 /* DCD flow control of output */
+#define CNO_RTSDTR 0 /* Do not assert RTS or DTR automatically */
+#endif
+
+#ifndef CRTSCTS
+#define CRTSCTS (CCTS_OFLOW | CRTS_IFLOW)
+#endif
+
+#ifndef howmany
+#define howmany(x, y) (((x) + ((y)-1)) / (y))
+#endif
+
+struct ucred
+{
+};
+#define NOCRED ((struct ucred *)0) /* no credential available */
+#define FSCRED ((struct ucred *)-1) /* filesystem credential */
+
+/* convert from open() flags to/from fflags; convert O_RD/WR to FREAD/FWRITE */
+#include <fcntl.h>
+#define FFLAGS(oflags) ((oflags)&O_EXEC ? (oflags) : (oflags) + 1)
+#define OFLAGS(fflags) \
+ (((fflags) & (O_EXEC | O_PATH)) != 0 ? (fflags) : (fflags)-1)
+
+typedef int fo_rdwr_t(struct lwp_tty *tp, struct uio *uio,
+ struct ucred *active_cred, int flags,
+ struct rt_thread *td);
+typedef int fo_truncate_t(struct lwp_tty *tp, off_t length,
+ struct ucred *active_cred, struct rt_thread *td);
+typedef int fo_ioctl_t(struct lwp_tty *tp, rt_ubase_t com, void *data,
+ struct ucred *active_cred, int fflags, struct rt_thread *td);
+typedef int fo_poll_t(struct lwp_tty *tp, struct rt_pollreq *rq, struct ucred *active_cred,
+ struct rt_thread *td);
+typedef int fo_stat_t(struct lwp_tty *tp, struct stat *sb,
+ struct ucred *active_cred);
+typedef int fo_close_t(struct lwp_tty *tp, struct rt_thread *td);
+
+#ifdef USING_BSD_FO_EXT
+typedef int fo_chmod_t(struct file *fp, mode_t mode, struct ucred *active_cred,
+ struct rt_thread *td);
+typedef int fo_chown_t(struct file *fp, uid_t uid, gid_t gid,
+ struct ucred *active_cred, struct rt_thread *td);
+typedef int fo_sendfile_t(struct file *fp, int sockfd, struct uio *hdr_uio,
+ struct uio *trl_uio, off_t offset, size_t nbytes,
+ off_t *sent, int flags, struct rt_thread *td);
+typedef int fo_seek_t(struct file *fp, off_t offset, int whence,
+ struct rt_thread *td);
+
+typedef int fo_kqfilter_t(struct file *fp, struct knote *kn);
+typedef int fo_fill_kinfo_t(struct file *fp, struct kinfo_file *kif,
+ struct filedesc *fdp);
+typedef int fo_mmap_t(struct file *fp, vm_map_t map, vm_offset_t *addr,
+ vm_size_t size, vm_prot_t prot, vm_prot_t cap_maxprot,
+ int flags, vm_ooffset_t foff, struct rt_thread *td);
+typedef int fo_aio_queue_t(struct file *fp, struct kaiocb *job);
+
+typedef int fo_add_seals_t(struct file *fp, int flags);
+typedef int fo_get_seals_t(struct file *fp, int *flags);
+typedef int fo_fallocate_t(struct file *fp, off_t offset, off_t len,
+ struct rt_thread *td);
+typedef int fo_fspacectl_t(struct file *fp, int cmd, off_t *offset,
+ off_t *length, int flags, struct ucred *active_cred,
+ struct rt_thread *td);
+typedef int fo_spare_t(struct file *fp);
+#endif /* USING_BSD_FO_EXT */
+
+typedef int fo_flags_t;
+
+struct bsd_fileops
+{
+ fo_rdwr_t *fo_read;
+ fo_rdwr_t *fo_write;
+ fo_truncate_t *fo_truncate;
+ fo_ioctl_t *fo_ioctl;
+ fo_poll_t *fo_poll;
+ fo_stat_t *fo_stat;
+ fo_close_t *fo_close;
+#ifdef USING_BSD_FO_EXT
+ fo_chmod_t *fo_chmod;
+ fo_chown_t *fo_chown;
+ fo_sendfile_t *fo_sendfile;
+ fo_seek_t *fo_seek;
+ fo_kqfilter_t *fo_kqfilter;
+ fo_fill_kinfo_t *fo_fill_kinfo;
+ fo_mmap_t *fo_mmap;
+ fo_aio_queue_t *fo_aio_queue;
+ fo_add_seals_t *fo_add_seals;
+ fo_get_seals_t *fo_get_seals;
+ fo_fallocate_t *fo_fallocate;
+ fo_fspacectl_t *fo_fspacectl;
+ fo_spare_t *fo_spares[8]; /* Spare slots */
+#endif
+ fo_flags_t fo_flags; /* DFLAG_* below */
+};
+
+#define DFLAG_PASSABLE 0x01 /* may be passed via unix sockets. */
+#define DFLAG_SEEKABLE 0x02 /* seekable / nonsequential */
+
+#endif /* __LWP_TTY_BSD_PORTING_H__ */
diff --git a/components/lwp/terminal/bsd_ttydisc.h b/components/lwp/terminal/bsd_ttydisc.h
new file mode 100644
index 0000000000..3d25f61e12
--- /dev/null
+++ b/components/lwp/terminal/bsd_ttydisc.h
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2006-2023, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2023-11-13 Shell init ver.
+ */
+
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2008 Ed Schouten <[email protected]>
+ * All rights reserved.
+ *
+ * Portions of this software were developed under sponsorship from Snow
+ * B.V., the Netherlands.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _SYS_TTYDISC_H_
+#define _SYS_TTYDISC_H_
+
+#ifndef __LWP_TERMINAL_H__
+#error "can only be included through <terminal.h>"
+#endif /* !__LWP_TERMINAL_H__ */
+
+#include <rtdef.h>
+
+struct rt_wqueue;
+struct rt_thread;
+struct lwp_tty;
+struct uio;
+
+/* Top half routines. */
+void ttydisc_open(struct lwp_tty *tp);
+void ttydisc_close(struct lwp_tty *tp);
+int ttydisc_read(struct lwp_tty *tp, struct uio *uio, int ioflag);
+int ttydisc_write(struct lwp_tty *tp, struct uio *uio, int ioflag);
+void ttydisc_optimize(struct lwp_tty *tp);
+
+/* Bottom half routines. */
+void ttydisc_modem(struct lwp_tty *tp, int open);
+#define ttydisc_can_bypass(tp) ((tp)->t_flags & TF_BYPASS)
+int ttydisc_rint(struct lwp_tty *tp, char c, int flags);
+size_t ttydisc_rint_simple(struct lwp_tty *tp, const void *buf, size_t len);
+size_t ttydisc_rint_bypass(struct lwp_tty *tp, const void *buf, size_t len);
+void ttydisc_rint_done(struct lwp_tty *tp);
+size_t ttydisc_rint_poll(struct lwp_tty *tp);
+size_t ttydisc_getc(struct lwp_tty *tp, void *buf, size_t len);
+int ttydisc_getc_uio(struct lwp_tty *tp, struct uio *uio);
+size_t ttydisc_getc_poll(struct lwp_tty *tp);
+
+/* Error codes for ttydisc_rint(). */
+#define TRE_FRAMING 0x01
+#define TRE_PARITY 0x02
+#define TRE_OVERRUN 0x04
+#define TRE_BREAK 0x08
+
+#endif /* !_SYS_TTYDISC_H_ */
diff --git a/components/lwp/terminal/bsd_ttyqueue.h b/components/lwp/terminal/bsd_ttyqueue.h
new file mode 100644
index 0000000000..96b7ae3d8b
--- /dev/null
+++ b/components/lwp/terminal/bsd_ttyqueue.h
@@ -0,0 +1,180 @@
+/*
+ * Copyright (c) 2006-2023, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2023-11-13 Shell init ver.
+ */
+
+#include "bsd_porting.h"
+
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2008 Ed Schouten <[email protected]>
+ * All rights reserved.
+ *
+ * Portions of this software were developed under sponsorship from Snow
+ * B.V., the Netherlands.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _SYS_TTYQUEUE_H_
+#define _SYS_TTYQUEUE_H_
+
+#ifndef __LWP_TERMINAL_H__
+#error "can only be included through <teminal.h>"
+#endif /* !__LWP_TERMINAL_H__ */
+
+struct lwp_tty;
+struct ttyinq_block;
+struct ttyoutq_block;
+struct uio;
+
+/* Data input queue. */
+struct ttyinq
+{
+ struct ttyinq_block *ti_firstblock;
+ struct ttyinq_block *ti_startblock;
+ struct ttyinq_block *ti_reprintblock;
+ struct ttyinq_block *ti_lastblock;
+ unsigned int ti_begin;
+ unsigned int ti_linestart;
+ unsigned int ti_reprint;
+ unsigned int ti_end;
+ unsigned int ti_nblocks;
+ unsigned int ti_quota;
+};
+#define TTYINQ_DATASIZE 128
+
+/* Data output queue. */
+struct ttyoutq
+{
+ struct ttyoutq_block *to_firstblock;
+ struct ttyoutq_block *to_lastblock;
+ unsigned int to_begin;
+ unsigned int to_end;
+ unsigned int to_nblocks;
+ unsigned int to_quota;
+};
+#define TTYOUTQ_DATASIZE (256 - sizeof(struct ttyoutq_block *))
+
+/* Input queue handling routines. */
+int ttyinq_setsize(struct ttyinq *ti, struct lwp_tty *tp, size_t len);
+void ttyinq_free(struct ttyinq *ti);
+int ttyinq_read_uio(struct ttyinq *ti, struct lwp_tty *tp, struct uio *uio,
+ size_t readlen, size_t flushlen);
+size_t ttyinq_write(struct ttyinq *ti, const void *buf, size_t len, int quote);
+int ttyinq_write_nofrag(struct ttyinq *ti, const void *buf, size_t len,
+ int quote);
+void ttyinq_canonicalize(struct ttyinq *ti);
+size_t ttyinq_findchar(struct ttyinq *ti, const char *breakc, size_t maxlen,
+ char *lastc);
+void ttyinq_flush(struct ttyinq *ti);
+int ttyinq_peekchar(struct ttyinq *ti, char *c, int *quote);
+void ttyinq_unputchar(struct ttyinq *ti);
+void ttyinq_reprintpos_set(struct ttyinq *ti);
+void ttyinq_reprintpos_reset(struct ttyinq *ti);
+
+rt_inline size_t ttyinq_getsize(struct ttyinq *ti)
+{
+ return (ti->ti_nblocks * TTYINQ_DATASIZE);
+}
+
+rt_inline size_t ttyinq_getallocatedsize(struct ttyinq *ti)
+{
+ return (ti->ti_quota * TTYINQ_DATASIZE);
+}
+
+rt_inline size_t ttyinq_bytesleft(struct ttyinq *ti)
+{
+ size_t len;
+
+ /* Make sure the usage never exceeds the length. */
+ len = ti->ti_nblocks * TTYINQ_DATASIZE;
+ MPASS(len >= ti->ti_end);
+
+ return (len - ti->ti_end);
+}
+
+rt_inline size_t ttyinq_bytescanonicalized(struct ttyinq *ti)
+{
+ MPASS(ti->ti_begin <= ti->ti_linestart);
+
+ return (ti->ti_linestart - ti->ti_begin);
+}
+
+rt_inline size_t ttyinq_bytesline(struct ttyinq *ti)
+{
+ MPASS(ti->ti_linestart <= ti->ti_end);
+
+ return (ti->ti_end - ti->ti_linestart);
+}
+
+/* Input buffer iteration. */
+typedef void ttyinq_line_iterator_t(void *data, char c, int flags);
+void ttyinq_line_iterate_from_linestart(struct ttyinq *ti,
+ ttyinq_line_iterator_t *iterator,
+ void *data);
+void ttyinq_line_iterate_from_reprintpos(struct ttyinq *ti,
+ ttyinq_line_iterator_t *iterator,
+ void *data);
+
+/* Output queue handling routines. */
+void ttyoutq_flush(struct ttyoutq *to);
+int ttyoutq_setsize(struct ttyoutq *to, struct lwp_tty *tp, size_t len);
+void ttyoutq_free(struct ttyoutq *to);
+size_t ttyoutq_read(struct ttyoutq *to, void *buf, size_t len);
+int ttyoutq_read_uio(struct ttyoutq *to, struct lwp_tty *tp, struct uio *uio);
+size_t ttyoutq_write(struct ttyoutq *to, const void *buf, size_t len);
+int ttyoutq_write_nofrag(struct ttyoutq *to, const void *buf, size_t len);
+
+rt_inline size_t ttyoutq_getsize(struct ttyoutq *to)
+{
+ return (to->to_nblocks * TTYOUTQ_DATASIZE);
+}
+
+rt_inline size_t ttyoutq_getallocatedsize(struct ttyoutq *to)
+{
+ return (to->to_quota * TTYOUTQ_DATASIZE);
+}
+
+rt_inline size_t ttyoutq_bytesleft(struct ttyoutq *to)
+{
+ size_t len;
+
+ /* Make sure the usage never exceeds the length. */
+ len = to->to_nblocks * TTYOUTQ_DATASIZE;
+ MPASS(len >= to->to_end);
+
+ return (len - to->to_end);
+}
+
+rt_inline size_t ttyoutq_bytesused(struct ttyoutq *to)
+{
+ return (to->to_end - to->to_begin);
+}
+
+#endif /* !_SYS_TTYQUEUE_H_ */
diff --git a/components/lwp/terminal/freebsd/tty.c b/components/lwp/terminal/freebsd/tty.c
new file mode 100644
index 0000000000..cdd9006279
--- /dev/null
+++ b/components/lwp/terminal/freebsd/tty.c
@@ -0,0 +1,1447 @@
+/*
+ * Copyright (c) 2006-2023, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2023-11-13 Shell init ver.
+ */
+#include "../bsd_porting.h"
+#include "../tty_config.h"
+#include "../terminal.h"
+#include "../tty_internal.h"
+
+#include <rtdef.h>
+#include <sys/ioctl.h>
+
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2008 Ed Schouten <[email protected]>
+ * All rights reserved.
+ *
+ * Portions of this software were developed under sponsorship from Snow
+ * B.V., the Netherlands.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+static void tty_rel_free(struct lwp_tty *tp);
+
+/* Character device of /dev/console. */
+static struct rt_device *dev_console;
+#ifdef USING_BSD_CONSOLE_NAME
+static const char *dev_console_filename;
+#endif
+
+/*
+ * Flags that are supported and stored by this implementation.
+ */
+#ifndef ALTWERASE
+#define ALTWERASE 0
+#endif
+#ifndef NOKERNINFO
+#define NOKERNINFO 0
+#endif
+#define TTYSUP_IFLAG \
+ (IGNBRK | BRKINT | IGNPAR | PARMRK | INPCK | ISTRIP | INLCR | IGNCR | \
+ ICRNL | IXON | IXOFF | IXANY | IMAXBEL | IUTF8)
+#define TTYSUP_OFLAG (OPOST | ONLCR | TAB3 | ONOEOT | OCRNL | ONOCR | ONLRET)
+#define TTYSUP_LFLAG \
+ (ECHOKE | ECHOE | ECHOK | ECHO | ECHONL | ECHOPRT | ECHOCTL | ISIG | \
+ ICANON | ALTWERASE | IEXTEN | TOSTOP | FLUSHO | NOKERNINFO | NOFLSH)
+#define TTYSUP_CFLAG \
+ (CIGNORE | CSIZE | CSTOPB | CREAD | PARENB | PARODD | HUPCL | CLOCAL | \
+ CCTS_OFLOW | CRTS_IFLOW | CDTR_IFLOW | CDSR_OFLOW | CCAR_OFLOW | \
+ CNO_RTSDTR)
+
+/*
+ * Set TTY buffer sizes.
+ */
+
+#define TTYBUF_MAX 65536
+
+#ifdef PRINTF_BUFR_SIZE
+#define TTY_PRBUF_SIZE PRINTF_BUFR_SIZE
+#else
+#define TTY_PRBUF_SIZE 256
+#endif
+
+/* Note: access to struct cdev:si_drv0.
+ Since pull-in, pull-out is not provided, this field
+ is constant value 0 in smart system */
+#define dev2unit(d) (0)
+#define TTY_CALLOUT(tp, d) (dev2unit(d) & TTYUNIT_CALLOUT)
+
+/*
+ * Allocate buffer space if necessary, and set low watermarks, based on speed.
+ * Note that the ttyxxxq_setsize() functions may drop and then reacquire the tty
+ * lock during memory allocation. They will return ENXIO if the tty disappears
+ * while unlocked.
+ */
+static int tty_watermarks(struct lwp_tty *tp)
+{
+ size_t bs = 0;
+ int error;
+
+ /* Provide an input buffer for 2 seconds of data. */
+ if (tp->t_termios.c_cflag & CREAD)
+ bs =
+ MIN(bsd_speed_to_integer(tp->t_termios.__c_ispeed) / 5, TTYBUF_MAX);
+ error = ttyinq_setsize(&tp->t_inq, tp, bs);
+ if (error != 0)
+ return error;
+
+ /* Set low watermark at 10% (when 90% is available). */
+ tp->t_inlow = (ttyinq_getallocatedsize(&tp->t_inq) * 9) / 10;
+
+ /* Provide an output buffer for 2 seconds of data. */
+ bs = MIN(bsd_speed_to_integer(tp->t_termios.__c_ospeed) / 5, TTYBUF_MAX);
+ error = ttyoutq_setsize(&tp->t_outq, tp, bs);
+ if (error != 0)
+ return error;
+
+ /* Set low watermark at 10% (when 90% is available). */
+ tp->t_outlow = (ttyoutq_getallocatedsize(&tp->t_outq) * 9) / 10;
+
+ return 0;
+}
+
+/**
+ * Drain outq
+ */
+static int tty_drain(struct lwp_tty *tp, int leaving)
+{
+ rt_tick_t timeout_tick;
+ size_t bytes;
+ int error;
+
+#ifdef USING_BSD_HOOK
+ if (ttyhook_hashook(tp, getc_inject))
+ /* buffer is inaccessible */
+ return 0;
+#endif /* USING_BSD_HOOK */
+
+ /*
+ * For close(), use the recent historic timeout of "1 second without
+ * making progress". For tcdrain(), use t_drainwait as the timeout,
+ * with zero meaning "no timeout" which gives POSIX behavior.
+ */
+ if (leaving)
+ timeout_tick = rt_tick_get() + RT_TICK_PER_SECOND;
+ else if (tp->t_drainwait != 0)
+ timeout_tick = rt_tick_get() + RT_TICK_PER_SECOND * tp->t_drainwait;
+ else
+ timeout_tick = 0;
+
+ /*
+ * Poll the output buffer and the hardware for completion, at 10 Hz.
+ * Polling is required for devices which are not able to signal an
+ * interrupt when the transmitter becomes idle (most USB serial devs).
+ * The unusual structure of this loop ensures we check for busy one more
+ * time after tty_timedwait() returns EWOULDBLOCK, so that success has
+ * higher priority than timeout if the IO completed in the last 100mS.
+ */
+ error = 0;
+ bytes = ttyoutq_bytesused(&tp->t_outq);
+ for (;;)
+ {
+ if (ttyoutq_bytesused(&tp->t_outq) == 0 && !ttydevsw_busy(tp))
+ return 0;
+ if (error != 0)
+ return error;
+ ttydevsw_outwakeup(tp);
+ error = tty_timedwait(tp, &tp->t_outwait, RT_TICK_PER_SECOND / 10);
+ if (error != 0 && error != EWOULDBLOCK)
+ return error;
+ else if (timeout_tick == 0 || rt_tick_get() < timeout_tick)
+ error = 0;
+ else if (leaving && ttyoutq_bytesused(&tp->t_outq) < bytes)
+ {
+ /* In close, making progress, grant an extra second. */
+ error = 0;
+ timeout_tick += RT_TICK_PER_SECOND;
+ bytes = ttyoutq_bytesused(&tp->t_outq);
+ }
+ }
+}
+
+/*
+ * Though ttydev_enter() and ttydev_leave() seem to be related, they
+ * don't have to be used together. ttydev_enter() is used by the cdev
+ * operations to prevent an actual operation from being processed when
+ * the TTY has been abandoned. ttydev_leave() is used by ttydev_open()
+ * and ttydev_close() to determine whether per-TTY data should be
+ * deallocated.
+ */
+
+rt_inline int ttydev_enter(struct lwp_tty *tp)
+{
+ rt_err_t error = tty_lock(tp);
+ if (error)
+ RT_ASSERT(0);
+
+ if (tty_gone(tp) || !tty_opened(tp))
+ {
+ /* Device is already gone. */
+ tty_unlock(tp);
+ return -ENXIO;
+ }
+
+ return 0;
+}
+
+static void ttydev_leave(struct lwp_tty *tp)
+{
+ tty_assert_locked(tp);
+
+ if (tty_opened(tp) || tp->t_flags & TF_OPENCLOSE)
+ {
+ /* Device is still opened somewhere. */
+ tty_unlock(tp);
+ return;
+ }
+
+ tp->t_flags |= TF_OPENCLOSE;
+
+ /* Remove console TTY. */
+ constty_clear(tp);
+
+ /* Drain any output. */
+ if (!tty_gone(tp))
+ tty_drain(tp, 1);
+
+ ttydisc_close(tp);
+
+ /* Free i/o queues now since they might be large. */
+ ttyinq_free(&tp->t_inq);
+ tp->t_inlow = 0;
+ ttyoutq_free(&tp->t_outq);
+ tp->t_outlow = 0;
+
+#ifdef USING_BSD_KNOTE
+ knlist_clear(&tp->t_inpoll.si_note, 1);
+ knlist_clear(&tp->t_outpoll.si_note, 1);
+#endif
+
+ if (!tty_gone(tp))
+ ttydevsw_close(tp);
+
+ tp->t_flags &= ~TF_OPENCLOSE;
+ cv_broadcast(&tp->t_dcdwait);
+ tty_rel_free(tp);
+}
+
+/*
+ * Operations that are exposed through the character device in /dev.
+ */
+static int ttydev_open(struct lwp_tty *tp, int oflags, int devtype,
+ struct rt_thread *td)
+{
+ rt_device_t dev = &tp->parent;
+ int error;
+
+ error = 0;
+ tty_lock(tp);
+ if (tty_gone(tp))
+ {
+ /* Device is already gone. */
+ tty_unlock(tp);
+ return -ENXIO;
+ }
+
+ /*
+ * Block when other processes are currently opening or closing
+ * the TTY.
+ */
+ while (tp->t_flags & TF_OPENCLOSE)
+ {
+ error = tty_wait(tp, &tp->t_dcdwait);
+ if (error != 0)
+ {
+ tty_unlock(tp);
+ return error;
+ }
+ }
+ tp->t_flags |= TF_OPENCLOSE;
+
+ /*
+ * Make sure the "tty" and "cua" device cannot be opened at the
+ * same time. The console is a "tty" device.
+ */
+ if (TTY_CALLOUT(tp, dev))
+ {
+ if (tp->t_flags & (TF_OPENED_CONS | TF_OPENED_IN))
+ {
+ error = EBUSY;
+ goto done;
+ }
+ }
+ else
+ {
+ if (tp->t_flags & TF_OPENED_OUT)
+ {
+ error = EBUSY;
+ goto done;
+ }
+ }
+
+ if (tp->t_flags & TF_EXCLUDE && priv_check(td, PRIV_TTY_EXCLUSIVE))
+ {
+ error = EBUSY;
+ goto done;
+ }
+
+ if (!tty_opened(tp))
+ {
+ /* Set proper termios flags. */
+ if (TTY_CALLOUT(tp, dev))
+#ifdef USING_BSD_INIT_LOCK_DEVICE
+ tp->t_termios = tp->t_termios_init_out;
+#else
+ ;
+#endif /* USING_BSD_INIT_LOCK_DEVICE */
+ else
+ tp->t_termios = tp->t_termios_init_in;
+
+ ttydevsw_param(tp, &tp->t_termios);
+ /* Prevent modem control on callout devices and /dev/console. */
+ if (TTY_CALLOUT(tp, dev) || dev == dev_console)
+ tp->t_termios.c_cflag |= CLOCAL;
+
+ if ((tp->t_termios.c_cflag & CNO_RTSDTR) == 0)
+ ttydevsw_modem(tp, SER_DTR | SER_RTS, 0);
+
+ error = ttydevsw_open(tp);
+ if (error != 0)
+ goto done;
+
+ ttydisc_open(tp);
+ error = tty_watermarks(tp);
+ if (error != 0)
+ goto done;
+ }
+
+ /* Wait for Carrier Detect. */
+ if ((oflags & O_NONBLOCK) == 0 && (tp->t_termios.c_cflag & CLOCAL) == 0)
+ {
+ while ((ttydevsw_modem(tp, 0, 0) & SER_DCD) == 0)
+ {
+ error = tty_wait(tp, &tp->t_dcdwait);
+ if (error != 0)
+ goto done;
+ }
+ }
+
+ if (dev == dev_console)
+ tp->t_flags |= TF_OPENED_CONS;
+ else if (TTY_CALLOUT(tp, dev))
+ tp->t_flags |= TF_OPENED_OUT;
+ else
+ tp->t_flags |= TF_OPENED_IN;
+ MPASS((tp->t_flags & (TF_OPENED_CONS | TF_OPENED_IN)) == 0 ||
+ (tp->t_flags & TF_OPENED_OUT) == 0);
+
+done:
+ tp->t_flags &= ~TF_OPENCLOSE;
+ cv_broadcast(&tp->t_dcdwait);
+ ttydev_leave(tp);
+
+ return error;
+}
+
+static int ttydev_close(struct lwp_tty *tp, int fflag, int devtype __unused,
+ struct rt_thread *td __unused)
+{
+ rt_device_t dev = &tp->parent;
+
+ tty_lock(tp);
+
+ /*
+ * Don't actually close the device if it is being used as the
+ * console.
+ */
+ MPASS((tp->t_flags & (TF_OPENED_CONS | TF_OPENED_IN)) == 0 ||
+ (tp->t_flags & TF_OPENED_OUT) == 0);
+ if (dev == dev_console)
+ tp->t_flags &= ~TF_OPENED_CONS;
+ else
+ tp->t_flags &= ~(TF_OPENED_IN | TF_OPENED_OUT);
+
+ if (tp->t_flags & TF_OPENED)
+ {
+ tty_unlock(tp);
+ return 0;
+ }
+
+ /* If revoking, flush output now to avoid draining it later. */
+ if (fflag & FREVOKE)
+ tty_flush(tp, FWRITE);
+
+ tp->t_flags &= ~TF_EXCLUDE;
+
+ /* Properly wake up threads that are stuck - revoke(). */
+ tp->t_revokecnt++;
+ tty_wakeup(tp, FREAD | FWRITE);
+ cv_broadcast(&tp->t_bgwait);
+ cv_broadcast(&tp->t_dcdwait);
+
+ ttydev_leave(tp);
+
+ return 0;
+}
+
+int tty_wait_background(struct lwp_tty *tp, struct rt_thread *td, int sig)
+{
+ struct rt_lwp *p;
+ struct rt_processgroup *pg;
+ int error;
+
+ MPASS(sig == SIGTTIN || sig == SIGTTOU);
+ tty_assert_locked(tp);
+
+ p = td->lwp;
+ for (;;)
+ {
+ pg = p->pgrp;
+ PGRP_LOCK(pg);
+ LWP_LOCK(p);
+
+ /*
+ * pg may no longer be our process group.
+ * Re-check after locking.
+ */
+ if (p->pgrp != pg)
+ {
+ LWP_UNLOCK(p);
+ PGRP_UNLOCK(pg);
+ continue;
+ }
+
+ /*
+ * The process should only sleep, when:
+ * - This terminal is the controlling terminal
+ * - Its process group is not the foreground process
+ * group
+ * - The parent process isn't waiting for the child to
+ * exit
+ * - the signal to send to the process isn't masked
+ */
+ if (!tty_is_ctty(tp, p) || pg == tp->t_pgrp)
+ {
+ /* Allow the action to happen. */
+ LWP_UNLOCK(p);
+ PGRP_UNLOCK(pg);
+ return 0;
+ }
+
+ /* Note: process itself don't have a sigmask in smart */
+ if (lwp_sigisign(p, sig) ||
+ lwp_sigismember(&td->signal.sigset_mask, sig))
+ {
+ /* Only allow them in write()/ioctl(). */
+ LWP_UNLOCK(p);
+ PGRP_UNLOCK(pg);
+ return (sig == SIGTTOU ? 0 : -EIO);
+ }
+
+#ifdef USING_VFORK_FLAG
+ if ((p->p_flag & P_PPWAIT) != 0 || pg->is_orphaned)
+#else
+ if (pg->is_orphaned)
+#endif
+ {
+ /* Don't allow the action to happen. */
+ LWP_UNLOCK(p);
+ PGRP_UNLOCK(pg);
+ return -EIO;
+ }
+ LWP_UNLOCK(p);
+
+ /*
+ * Send the signal and sleep until we're the new
+ * foreground process group.
+ */
+ if (sig != 0)
+ {
+ lwp_pgrp_signal_kill(pg, sig, SI_KERNEL, 0);
+ }
+
+ PGRP_UNLOCK(pg);
+
+ error = tty_wait(tp, &tp->t_bgwait);
+ if (error)
+ return error;
+ }
+}
+
+static int ttydev_read(struct lwp_tty *tp, struct uio *uio, int ioflag)
+{
+ int error;
+
+ error = ttydev_enter(tp);
+ if (error)
+ goto done;
+ error = ttydisc_read(tp, uio, ioflag);
+ tty_unlock(tp);
+
+ /*
+ * The read() call should not throw an error when the device is
+ * being destroyed. Silently convert it to an EOF.
+ */
+done:
+ if (error == -ENXIO)
+ error = 0;
+ return error;
+}
+
+static int ttydev_write(struct lwp_tty *tp, struct uio *uio, int ioflag)
+{
+#ifdef USING_BSD_DEFER_STOP
+ int defer;
+#endif
+ int error;
+
+ error = ttydev_enter(tp);
+ if (error)
+ return error;
+
+ if (tp->t_termios.c_lflag & TOSTOP)
+ {
+ error = tty_wait_background(tp, curthread, SIGTTOU);
+ if (error)
+ goto done;
+ }
+
+ if (ioflag & IO_NDELAY && tp->t_flags & TF_BUSY_OUT)
+ {
+ /* Allow non-blocking writes to bypass serialization. */
+ error = ttydisc_write(tp, uio, ioflag);
+ }
+ else
+ {
+ /* Serialize write() calls. */
+ while (tp->t_flags & TF_BUSY_OUT)
+ {
+ error = tty_wait(tp, &tp->t_outserwait);
+ if (error)
+ goto done;
+ }
+
+ tp->t_flags |= TF_BUSY_OUT;
+#ifdef USING_BSD_DEFER_STOP
+ defer = sigdeferstop(SIGDEFERSTOP_ERESTART);
+#endif
+ error = ttydisc_write(tp, uio, ioflag);
+#ifdef USING_BSD_DEFER_STOP
+ sigallowstop(defer);
+#endif
+ tp->t_flags &= ~TF_BUSY_OUT;
+ cv_signal(&tp->t_outserwait);
+ }
+
+done:
+ tty_unlock(tp);
+ return error;
+}
+
+static int ttydev_ioctl(struct lwp_tty *tp, rt_ubase_t cmd, rt_caddr_t data, int fflag,
+ struct rt_thread *td)
+{
+ int error;
+
+ error = ttydev_enter(tp);
+ if (error)
+ return (error);
+
+ switch (cmd)
+ {
+ case TIOCCBRK:
+ case TIOCCONS:
+ case TIOCDRAIN:
+ case TIOCEXCL:
+ case TIOCFLUSH:
+ case TIOCNXCL:
+ case TIOCSBRK:
+ case TIOCSCTTY:
+ case TIOCSETA:
+ case TIOCSETAF:
+ case TIOCSETAW:
+ case TIOCSPGRP:
+ case TIOCSTART:
+ case TIOCSTAT:
+ case TIOCSTI:
+ case TIOCSTOP:
+ case TIOCSWINSZ:
+#if USING_BSD_TIOCSDRAINWAIT
+ case TIOCSDRAINWAIT:
+ case TIOCSETD:
+#endif /* USING_BSD_TIOCSDRAINWAIT */
+#ifdef COMPAT_43TTY
+ case TIOCLBIC:
+ case TIOCLBIS:
+ case TIOCLSET:
+ case TIOCSETC:
+ case OTIOCSETD:
+ case TIOCSETN:
+ case TIOCSETP:
+ case TIOCSLTC:
+#endif /* COMPAT_43TTY */
+ /*
+ * If the ioctl() causes the TTY to be modified, let it
+ * wait in the background.
+ */
+ error = tty_wait_background(tp, curthread, SIGTTOU);
+ if (error)
+ goto done;
+ }
+
+#ifdef USING_BSD_INIT_LOCK_DEVICE
+ if (cmd == TIOCSETA || cmd == TIOCSETAW || cmd == TIOCSETAF)
+ {
+ struct termios *old = &tp->t_termios;
+ struct termios *new = (struct termios *)data;
+ struct termios *lock = TTY_CALLOUT(tp, dev) ? &tp->t_termios_lock_out
+ : &tp->t_termios_lock_in;
+ int cc;
+
+ /*
+ * Lock state devices. Just overwrite the values of the
+ * commands that are currently in use.
+ */
+ new->c_iflag =
+ (old->c_iflag & lock->c_iflag) | (new->c_iflag & ~lock->c_iflag);
+ new->c_oflag =
+ (old->c_oflag & lock->c_oflag) | (new->c_oflag & ~lock->c_oflag);
+ new->c_cflag =
+ (old->c_cflag & lock->c_cflag) | (new->c_cflag & ~lock->c_cflag);
+ new->c_lflag =
+ (old->c_lflag & lock->c_lflag) | (new->c_lflag & ~lock->c_lflag);
+ for (cc = 0; cc < NCCS; ++cc)
+ if (lock->c_cc[cc])
+ new->c_cc[cc] = old->c_cc[cc];
+ if (lock->__c_ispeed)
+ new->__c_ispeed = old->__c_ispeed;
+ if (lock->__c_ospeed)
+ new->__c_ospeed = old->__c_ospeed;
+ }
+#endif /* USING_BSD_INIT_LOCK_DEVICE */
+
+ error = tty_ioctl(tp, cmd, data, fflag, td);
+done:
+ tty_unlock(tp);
+
+ return (error);
+}
+
+static int ttydev_poll(struct lwp_tty *tp, rt_pollreq_t *req, struct rt_thread *td)
+{
+ int events = req->_key;
+ int error, revents = 0;
+
+ error = ttydev_enter(tp);
+ if (error)
+ return ((events & (POLLIN | POLLRDNORM)) | POLLHUP);
+
+ if (events & (POLLIN | POLLRDNORM))
+ {
+ /* See if we can read something. */
+ if (ttydisc_read_poll(tp) > 0)
+ revents |= events & (POLLIN | POLLRDNORM);
+ }
+
+ if (tp->t_flags & TF_ZOMBIE)
+ {
+ /* Hangup flag on zombie state. */
+ revents |= POLLHUP;
+ }
+ else if (events & (POLLOUT | POLLWRNORM))
+ {
+ /* See if we can write something. */
+ if (ttydisc_write_poll(tp) > 0)
+ revents |= events & (POLLOUT | POLLWRNORM);
+ }
+
+ if (revents == 0)
+ {
+ if (events & (POLLIN | POLLRDNORM))
+ rt_poll_add(&tp->t_inpoll, req);
+ if (events & (POLLOUT | POLLWRNORM))
+ rt_poll_add(&tp->t_outpoll, req);
+ }
+
+ tty_unlock(tp);
+
+ return revents;
+}
+
+static struct cdevsw ttydev_cdevsw = {
+ .d_open = ttydev_open,
+ .d_close = ttydev_close,
+ .d_read = ttydev_read,
+ .d_write = ttydev_write,
+ .d_ioctl = ttydev_ioctl,
+#if 0
+ .d_kqfilter = ttydev_kqfilter,
+#endif
+ .d_poll = ttydev_poll,
+#if 0
+ .d_mmap = ttydev_mmap,
+#endif
+
+#ifdef USING_BSD_RAW_CDEVSW
+ .d_version = D_VERSION.d_name = "ttydev",
+ .d_flags = D_TTY,
+#endif /* USING_BSD_RAW_CDEVSW */
+};
+
+extern struct cdevsw bsd_ttydev_methods __attribute__((alias("ttydev_cdevsw")));
+
+/*
+ * Standard device routine implementations, mostly meant for
+ * pseudo-terminal device drivers. When a driver creates a new terminal
+ * device class, missing routines are patched.
+ */
+#define panic(msg) RT_ASSERT(0 && msg)
+
+static int ttydevsw_defopen(struct lwp_tty *tp __unused)
+{
+ return 0;
+}
+
+static void ttydevsw_defclose(struct lwp_tty *tp __unused)
+{
+}
+
+static void ttydevsw_defoutwakeup(struct lwp_tty *tp __unused)
+{
+ panic("Terminal device has output, while not implemented");
+}
+
+static void ttydevsw_definwakeup(struct lwp_tty *tp __unused)
+{
+}
+
+static int ttydevsw_defioctl(struct lwp_tty *tp __unused, rt_ubase_t cmd __unused,
+ rt_caddr_t data __unused,
+ struct rt_thread *td __unused)
+{
+ return -ENOSYS;
+}
+
+static int ttydevsw_defcioctl(struct lwp_tty *tp __unused, int unit __unused,
+ rt_ubase_t cmd __unused, rt_caddr_t data __unused,
+ struct rt_thread *td __unused)
+{
+ return -ENOSYS;
+}
+
+static int ttydevsw_defparam(struct lwp_tty *tp __unused, struct termios *t)
+{
+ /*
+ * Allow the baud rate to be adjusted for pseudo-devices, but at
+ * least restrict it to 115200 to prevent excessive buffer
+ * usage. Also disallow 0, to prevent foot shooting.
+ */
+ if (t->__c_ispeed < B50)
+ t->__c_ispeed = B50;
+ else if (t->__c_ispeed > B115200)
+ t->__c_ispeed = B115200;
+ if (t->__c_ospeed < B50)
+ t->__c_ospeed = B50;
+ else if (t->__c_ospeed > B115200)
+ t->__c_ospeed = B115200;
+ t->c_cflag |= CREAD;
+
+ return 0;
+}
+
+static int ttydevsw_defmodem(struct lwp_tty *tp __unused, int sigon __unused,
+ int sigoff __unused)
+{
+ /* Simulate a carrier to make the TTY layer happy. */
+ return (SER_DCD);
+}
+
+static int ttydevsw_defmmap(struct lwp_tty *tp __unused,
+ vm_ooffset_t offset __unused,
+ vm_paddr_t *paddr __unused, int nprot __unused,
+ vm_memattr_t *memattr __unused)
+{
+ return (-1);
+}
+
+static void ttydevsw_defpktnotify(struct lwp_tty *tp __unused,
+ char event __unused)
+{
+}
+
+static void ttydevsw_deffree(void *softc __unused)
+{
+ panic("Terminal device freed without a free-handler");
+}
+
+static rt_bool_t ttydevsw_defbusy(struct lwp_tty *tp __unused)
+{
+ return (RT_FALSE);
+}
+
+void bsd_devsw_init(struct lwp_ttydevsw *tsw)
+{
+ /* Make sure the driver defines all routines. */
+#define PATCH_FUNC(x) \
+ do \
+ { \
+ if (tsw->tsw_##x == NULL) \
+ tsw->tsw_##x = ttydevsw_def##x; \
+ } while (0)
+
+ PATCH_FUNC(open);
+ PATCH_FUNC(close);
+ PATCH_FUNC(outwakeup);
+ PATCH_FUNC(inwakeup);
+ PATCH_FUNC(ioctl);
+ PATCH_FUNC(cioctl);
+ PATCH_FUNC(param);
+ PATCH_FUNC(modem);
+ PATCH_FUNC(mmap);
+ PATCH_FUNC(pktnotify);
+ PATCH_FUNC(free);
+ PATCH_FUNC(busy);
+#undef PATCH_FUNC
+}
+
+/* release tty, and free the cdev resource */
+static void tty_rel_free(struct lwp_tty *tp)
+{
+#ifdef USING_BSD_CHAR_DEVICE
+ struct cdev *dev;
+#endif
+
+ tty_assert_locked(tp);
+
+#define TF_ACTIVITY (TF_GONE | TF_OPENED | TF_HOOK | TF_OPENCLOSE)
+ if (tp->t_sessioncnt != 0 || (tp->t_flags & TF_ACTIVITY) != TF_GONE)
+ {
+ /* TTY is still in use. */
+ tty_unlock(tp);
+ return;
+ }
+
+#ifdef USING_BSD_AIO
+ /* Stop asynchronous I/O. */
+ funsetown(&tp->t_sigio);
+#endif /* USING_BSD_AIO */
+
+#ifdef USING_BSD_CHAR_DEVICE
+ /* TTY can be deallocated. */
+ dev = tp->t_dev;
+ tp->t_dev = NULL;
+#endif /* USING_BSD_CHAR_DEVICE */
+
+ tty_unlock(tp);
+
+#ifdef USING_BSD_CHAR_DEVICE
+ if (dev != NULL)
+ {
+ sx_xlock(&tty_list_sx);
+ TAILQ_REMOVE(&tty_list, tp, t_list);
+ tty_list_count--;
+ sx_xunlock(&tty_list_sx);
+ destroy_dev_sched_cb(dev, tty_dealloc, tp);
+ }
+#else
+ lwp_tty_delete(tp);
+#endif
+}
+
+void tty_rel_pgrp(struct lwp_tty *tp, struct rt_processgroup *pg)
+{
+ MPASS(tp->t_sessioncnt > 0);
+ tty_assert_locked(tp);
+
+ if (tp->t_pgrp == pg)
+ tp->t_pgrp = NULL;
+
+ tty_unlock(tp);
+}
+
+void tty_rel_sess(struct lwp_tty *tp, struct rt_session *sess)
+{
+ MPASS(tp->t_sessioncnt > 0);
+
+ /* Current session has left. */
+ if (tp->t_session == sess)
+ {
+ tp->t_session = NULL;
+ MPASS(tp->t_pgrp == NULL);
+ }
+ tp->t_sessioncnt--;
+ tty_rel_free(tp);
+}
+
+/* deallocate the tty */
+void tty_rel_gone(struct lwp_tty *tp)
+{
+ tty_assert_locked(tp);
+ MPASS(!tty_gone(tp));
+
+ /* Simulate carrier removal. */
+ ttydisc_modem(tp, 0);
+
+ /* Wake up all blocked threads. */
+ tty_wakeup(tp, FREAD | FWRITE);
+ cv_broadcast(&tp->t_bgwait);
+ cv_broadcast(&tp->t_dcdwait);
+
+ tp->t_flags |= TF_GONE;
+ tty_rel_free(tp);
+}
+
+static int tty_drop_ctty(struct lwp_tty *tp, struct rt_lwp *p)
+{
+ struct rt_session *session;
+#ifdef USING_BSD_VNODE
+ struct vnode *vp;
+#endif
+
+ /*
+ * This looks terrible, but it's generally safe as long as the tty
+ * hasn't gone away while we had the lock dropped. All of our sanity
+ * checking that this operation is OK happens after we've picked it back
+ * up, so other state changes are generally not fatal and the potential
+ * for this particular operation to happen out-of-order in a
+ * multithreaded scenario is likely a non-issue.
+ */
+ tty_unlock(tp);
+ LWP_LOCK(p);
+ tty_lock(tp);
+ if (tty_gone(tp))
+ {
+ LWP_UNLOCK(p);
+ return -ENODEV;
+ }
+
+ /*
+ * If the session doesn't have a controlling TTY, or if we weren't
+ * invoked on the controlling TTY, we'll return ENOIOCTL as we've
+ * historically done.
+ */
+ session = p->pgrp->session;
+ if (session->ctty == NULL || session->ctty != tp)
+ {
+ LWP_UNLOCK(p);
+ return -ENOTTY;
+ }
+
+ if (!is_sess_leader(p))
+ {
+ LWP_UNLOCK(p);
+ return -EPERM;
+ }
+
+ SESS_LOCK(session);
+#ifdef USING_BSD_VNODE
+ vp = session->s_ttyvp;
+#endif
+ session->ctty = NULL;
+#ifdef USING_BSD_VNODE
+ session->s_ttyvp = NULL;
+ session->s_ttydp = NULL;
+#endif
+ SESS_UNLOCK(session);
+
+ tp->t_sessioncnt--;
+ p->term_ctrlterm = RT_FALSE;
+ LWP_UNLOCK(p);
+
+#ifdef USING_BSD_VNODE
+ /*
+ * If we did have a vnode, release our reference. Ordinarily we manage
+ * these at the devfs layer, but we can't necessarily know that we were
+ * invoked on the vnode referenced in the session (i.e. the vnode we
+ * hold a reference to). We explicitly don't check VBAD/VIRF_DOOMED here
+ * to avoid a vnode leak -- in circumstances elsewhere where we'd hit a
+ * VIRF_DOOMED vnode, release has been deferred until the controlling TTY
+ * is either changed or released.
+ */
+ if (vp != NULL)
+ devfs_ctty_unref(vp);
+#endif
+
+ return 0;
+}
+
+void tty_wakeup(struct lwp_tty *tp, int flags)
+{
+#ifdef USING_BSD_AIO
+ if (tp->t_flags & TF_ASYNC && tp->t_sigio != NULL)
+ pgsigio(&tp->t_sigio, SIGIO, (tp->t_session != NULL));
+#endif
+
+ if (flags & FWRITE)
+ {
+ cv_broadcast(&tp->t_outwait);
+#ifdef USING_BSD_POLL
+ selwakeup(&tp->t_outpoll);
+ KNOTE_LOCKED(&tp->t_outpoll.si_note, 0);
+#else
+ rt_wqueue_wakeup_all(&tp->t_outpoll, (void *)POLLOUT);
+#endif
+ }
+ if (flags & FREAD)
+ {
+ cv_broadcast(&tp->t_inwait);
+#ifdef USING_BSD_POLL
+ selwakeup(&tp->t_inpoll);
+ KNOTE_LOCKED(&tp->t_inpoll.si_note, 0);
+#else
+ rt_wqueue_wakeup_all(&tp->t_inpoll, (void *)POLLIN);
+#endif
+ }
+}
+
+int tty_wait(struct lwp_tty *tp, struct rt_condvar *cv)
+{
+ int error;
+ int revokecnt = tp->t_revokecnt;
+
+ tty_lock_assert(tp, MA_OWNED | MA_NOTRECURSED);
+ MPASS(!tty_gone(tp));
+
+ error = cv_wait_sig(cv, tp->t_mtx);
+
+ /* Bail out when the device slipped away. */
+ if (tty_gone(tp))
+ return -ENXIO;
+
+ /* Restart the system call when we may have been revoked. */
+ if (tp->t_revokecnt != revokecnt)
+ return -ERESTART;
+
+ return error;
+}
+
+int tty_timedwait(struct lwp_tty *tp, struct rt_condvar *cv, rt_tick_t timeout)
+{
+ int error;
+ int revokecnt = tp->t_revokecnt;
+
+ tty_lock_assert(tp, MA_OWNED | MA_NOTRECURSED);
+ MPASS(!tty_gone(tp));
+
+ error = cv_timedwait_sig(cv, tp->t_mtx, timeout);
+
+ /* Bail out when the device slipped away. */
+ if (tty_gone(tp))
+ return -ENXIO;
+
+ /* Restart the system call when we may have been revoked. */
+ if (tp->t_revokecnt != revokecnt)
+ return -ERESTART;
+
+ return error;
+}
+
+/* discard data in I/O buffers */
+void tty_flush(struct lwp_tty *tp, int flags)
+{
+ if (flags & FWRITE)
+ {
+ tp->t_flags &= ~TF_HIWAT_OUT;
+ ttyoutq_flush(&tp->t_outq);
+ tty_wakeup(tp, FWRITE);
+ if (!tty_gone(tp))
+ {
+ ttydevsw_outwakeup(tp);
+ ttydevsw_pktnotify(tp, TIOCPKT_FLUSHWRITE);
+ }
+ }
+ if (flags & FREAD)
+ {
+ tty_hiwat_in_unblock(tp);
+ ttyinq_flush(&tp->t_inq);
+ tty_wakeup(tp, FREAD);
+ if (!tty_gone(tp))
+ {
+ ttydevsw_inwakeup(tp);
+ ttydevsw_pktnotify(tp, TIOCPKT_FLUSHREAD);
+ }
+ }
+}
+
+void tty_set_winsize(struct lwp_tty *tp, const struct winsize *wsz)
+{
+ if (memcmp(&tp->t_winsize, wsz, sizeof(*wsz)) == 0)
+ return;
+ tp->t_winsize = *wsz;
+ lwp_tty_signal_pgrp(tp, SIGWINCH);
+}
+
+static int tty_generic_ioctl(struct lwp_tty *tp, rt_ubase_t cmd, void *data,
+ int fflag, struct rt_thread *td)
+{
+ int error;
+
+ switch (cmd)
+ {
+ /*
+ * Modem commands.
+ * The SER_* and TIOCM_* flags are the same, but one bit
+ * shifted. I don't know why.
+ */
+ case TIOCSDTR:
+ ttydevsw_modem(tp, SER_DTR, 0);
+ return 0;
+ case TIOCCDTR:
+ ttydevsw_modem(tp, 0, SER_DTR);
+ return 0;
+ case TIOCMSET: {
+ int bits = *(int *)data;
+ ttydevsw_modem(tp, (bits & (TIOCM_DTR | TIOCM_RTS)) >> 1,
+ ((~bits) & (TIOCM_DTR | TIOCM_RTS)) >> 1);
+ return 0;
+ }
+ case TIOCMBIS: {
+ int bits = *(int *)data;
+ ttydevsw_modem(tp, (bits & (TIOCM_DTR | TIOCM_RTS)) >> 1, 0);
+ return 0;
+ }
+ case TIOCMBIC: {
+ int bits = *(int *)data;
+ ttydevsw_modem(tp, 0, (bits & (TIOCM_DTR | TIOCM_RTS)) >> 1);
+ return 0;
+ }
+ case TIOCMGET:
+ *(int *)data = TIOCM_LE + (ttydevsw_modem(tp, 0, 0) << 1);
+ return 0;
+
+ case FIOASYNC:
+ if (*(int *)data)
+ tp->t_flags |= TF_ASYNC;
+ else
+ tp->t_flags &= ~TF_ASYNC;
+ return 0;
+ case FIONBIO:
+ /* This device supports non-blocking operation. */
+ return 0;
+ case FIONREAD:
+ *(int *)data = ttyinq_bytescanonicalized(&tp->t_inq);
+ return 0;
+ case FIONWRITE:
+ case TIOCOUTQ:
+ *(int *)data = ttyoutq_bytesused(&tp->t_outq);
+ return 0;
+#if BSD_USING_FIO_OWNERSHIP
+ case FIOSETOWN:
+ if (tp->t_session != NULL && !tty_is_ctty(tp, td->lwp))
+ /* Not allowed to set ownership. */
+ return -ENOTTY;
+
+ /* Temporarily unlock the TTY to set ownership. */
+ tty_unlock(tp);
+ error = fsetown(*(int *)data, &tp->t_sigio);
+ tty_lock(tp);
+ return (error);
+ case FIOGETOWN:
+ if (tp->t_session != NULL && !tty_is_ctty(tp, td->lwp))
+ /* Not allowed to set ownership. */
+ return -ENOTTY;
+
+ /* Get ownership. */
+ *(int *)data = fgetown(&tp->t_sigio);
+ return 0;
+#endif
+ case TIOCGETA:
+ /* Obtain terminal flags through tcgetattr(). */
+ *(struct termios *)data = tp->t_termios;
+ return 0;
+ case TIOCSETA:
+ case TIOCSETAW:
+ case TIOCSETAF: {
+ struct termios *t = data;
+
+ /*
+ * Who makes up these funny rules? According to POSIX,
+ * input baud rate is set equal to the output baud rate
+ * when zero.
+ */
+ if (t->__c_ispeed == 0)
+ t->__c_ispeed = t->__c_ospeed;
+
+ /* Discard any unsupported bits. */
+ t->c_iflag &= TTYSUP_IFLAG;
+ t->c_oflag &= TTYSUP_OFLAG;
+ t->c_lflag &= TTYSUP_LFLAG;
+ t->c_cflag &= TTYSUP_CFLAG;
+
+ /* Set terminal flags through tcsetattr(). */
+ if (cmd == TIOCSETAW || cmd == TIOCSETAF)
+ {
+ error = tty_drain(tp, 0);
+ if (error)
+ return (error);
+ if (cmd == TIOCSETAF)
+ tty_flush(tp, FREAD);
+ }
+
+ /*
+ * Only call param() when the flags really change.
+ */
+ if ((t->c_cflag & CIGNORE) == 0 &&
+ (tp->t_termios.c_cflag != t->c_cflag ||
+ ((tp->t_termios.c_iflag ^ t->c_iflag) &
+ (IXON | IXOFF | IXANY)) ||
+ tp->t_termios.__c_ispeed != t->__c_ispeed ||
+ tp->t_termios.__c_ospeed != t->__c_ospeed))
+ {
+ error = ttydevsw_param(tp, t);
+ if (error)
+ return (error);
+
+ /* XXX: CLOCAL? */
+
+ tp->t_termios.c_cflag = t->c_cflag & ~CIGNORE;
+ tp->t_termios.__c_ispeed = t->__c_ispeed;
+ tp->t_termios.__c_ospeed = t->__c_ospeed;
+
+ /* Baud rate has changed - update watermarks. */
+ error = tty_watermarks(tp);
+ if (error)
+ return (error);
+ }
+
+ /* Copy new non-device driver parameters. */
+ tp->t_termios.c_iflag = t->c_iflag;
+ tp->t_termios.c_oflag = t->c_oflag;
+ tp->t_termios.c_lflag = t->c_lflag;
+ memcpy(&tp->t_termios.c_cc, t->c_cc, sizeof t->c_cc);
+
+ ttydisc_optimize(tp);
+
+ if ((t->c_lflag & ICANON) == 0)
+ {
+ /*
+ * When in non-canonical mode, wake up all
+ * readers. Canonicalize any partial input. VMIN
+ * and VTIME could also be adjusted.
+ */
+ ttyinq_canonicalize(&tp->t_inq);
+ tty_wakeup(tp, FREAD);
+ }
+
+ /**
+ * For packet mode: notify the PTY consumer that VSTOP
+ * and VSTART may have been changed.
+ *
+ * TODO: change the _CONTROL('S') to a CSTOP?
+ */
+ if (tp->t_termios.c_iflag & IXON &&
+ tp->t_termios.c_cc[VSTOP] == _CONTROL('S') &&
+ tp->t_termios.c_cc[VSTART] == _CONTROL('Q'))
+ ttydevsw_pktnotify(tp, TIOCPKT_DOSTOP);
+ else
+ ttydevsw_pktnotify(tp, TIOCPKT_NOSTOP);
+ return 0;
+ }
+ case TIOCGETD:
+ /* For compatibility - we only support TTYDISC. */
+ *(int *)data = TTYDISC;
+ return 0;
+ case TIOCGPGRP:
+ if (!tty_is_ctty(tp, td->lwp))
+ return -ENOTTY;
+
+ if (tp->t_pgrp != NULL)
+ *(int *)data = tp->t_pgrp->pgid;
+ else
+ *(int *)data = NO_PID;
+ return 0;
+ case TIOCGSID:
+ if (!tty_is_ctty(tp, td->lwp))
+ return -ENOTTY;
+
+ MPASS(tp->t_session);
+ *(int *)data = tp->t_session->sid;
+ return 0;
+ case TIOCNOTTY:
+ return tty_drop_ctty(tp, td->lwp);
+ case TIOCSCTTY:
+ return lwp_tty_set_ctrl_proc(tp, td);
+ case TIOCSPGRP: {
+ int pgid;
+ if (lwp_in_user_space((void *)data))
+ {
+ if (lwp_get_from_user(&pgid, data, sizeof(int)) != sizeof(int))
+ return -EFAULT;
+ }
+ else
+ {
+ pgid = *(int *)data;
+ }
+ return lwp_tty_assign_foreground(tp, td, pgid);
+ }
+ case TIOCFLUSH: {
+ int flags = *(int *)data;
+
+ if (flags == 0)
+ flags = (FREAD | FWRITE);
+ else
+ flags &= (FREAD | FWRITE);
+ tty_flush(tp, flags);
+ return 0;
+ }
+ case TIOCDRAIN:
+ /* Drain TTY output. */
+ return tty_drain(tp, 0);
+ case TIOCGDRAINWAIT:
+ *(int *)data = tp->t_drainwait;
+ return 0;
+ case TIOCSDRAINWAIT:
+ error = priv_check(td, PRIV_TTY_DRAINWAIT);
+ if (error == 0)
+ tp->t_drainwait = *(int *)data;
+ return (error);
+ case TIOCCONS:
+ /* Set terminal as console TTY. */
+ if (*(int *)data)
+ {
+ error = priv_check(td, PRIV_TTY_CONSOLE);
+ if (error)
+ return (error);
+ error = constty_set(tp);
+ }
+ else
+ {
+ error = constty_clear(tp);
+ }
+ return (error);
+ case TIOCGWINSZ:
+ /* Obtain window size. */
+ *(struct winsize *)data = tp->t_winsize;
+ return 0;
+ case TIOCSWINSZ:
+ /* Set window size. */
+ tty_set_winsize(tp, data);
+ return 0;
+ case TIOCEXCL:
+ tp->t_flags |= TF_EXCLUDE;
+ return 0;
+ case TIOCNXCL:
+ tp->t_flags &= ~TF_EXCLUDE;
+ return 0;
+ case TIOCSTOP:
+ tp->t_flags |= TF_STOPPED;
+ ttydevsw_pktnotify(tp, TIOCPKT_STOP);
+ return 0;
+ case TIOCSTART:
+ tp->t_flags &= ~TF_STOPPED;
+ tp->t_termios.c_lflag &= ~FLUSHO;
+ ttydevsw_outwakeup(tp);
+ ttydevsw_pktnotify(tp, TIOCPKT_START);
+ return 0;
+ case TIOCSTAT:
+ tty_info(tp);
+ return 0;
+ case TIOCSTI:
+ if ((fflag & FREAD) == 0 && priv_check(td, PRIV_TTY_STI))
+ return -EPERM;
+ if (!tty_is_ctty(tp, td->lwp) && priv_check(td, PRIV_TTY_STI))
+ return -EACCES;
+ ttydisc_rint(tp, *(char *)data, 0);
+ ttydisc_rint_done(tp);
+ return 0;
+ }
+
+#ifdef COMPAT_43TTY
+ return tty_ioctl_compat(tp, cmd, data, fflag, td);
+#else /* !COMPAT_43TTY */
+ return -ENOIOCTL;
+#endif /* COMPAT_43TTY */
+}
+
+int tty_ioctl(struct lwp_tty *tp, rt_ubase_t cmd, void *data, int fflag,
+ struct rt_thread *td)
+{
+ int error;
+
+ tty_assert_locked(tp);
+
+ if (tty_gone(tp))
+ return -ENXIO;
+
+ error = ttydevsw_ioctl(tp, cmd, data, td);
+ if (error == -ENOIOCTL)
+ error = tty_generic_ioctl(tp, cmd, data, fflag, td);
+
+ return error;
+}
+
+int tty_checkoutq(struct lwp_tty *tp)
+{
+ /* 256 bytes should be enough to print a log message. */
+ return (ttyoutq_bytesleft(&tp->t_outq) >= 256);
+}
+
+void tty_hiwat_in_block(struct lwp_tty *tp)
+{
+ if ((tp->t_flags & TF_HIWAT_IN) == 0 && tp->t_termios.c_iflag & IXOFF &&
+ tp->t_termios.c_cc[VSTOP] != _POSIX_VDISABLE)
+ {
+ /*
+ * Input flow control. Only enter the high watermark when we
+ * can successfully store the VSTOP character.
+ */
+ if (ttyoutq_write_nofrag(&tp->t_outq, &tp->t_termios.c_cc[VSTOP], 1) ==
+ 0)
+ tp->t_flags |= TF_HIWAT_IN;
+ }
+ else
+ {
+ /* No input flow control. */
+ tp->t_flags |= TF_HIWAT_IN;
+ }
+}
+
+void tty_hiwat_in_unblock(struct lwp_tty *tp)
+{
+ if (tp->t_flags & TF_HIWAT_IN && tp->t_termios.c_iflag & IXOFF &&
+ tp->t_termios.c_cc[VSTART] != _POSIX_VDISABLE)
+ {
+ /*
+ * Input flow control. Only leave the high watermark when we
+ * can successfully store the VSTART character.
+ */
+ if (ttyoutq_write_nofrag(&tp->t_outq, &tp->t_termios.c_cc[VSTART], 1) ==
+ 0)
+ tp->t_flags &= ~TF_HIWAT_IN;
+ }
+ else
+ {
+ /* No input flow control. */
+ tp->t_flags &= ~TF_HIWAT_IN;
+ }
+
+ if (!tty_gone(tp))
+ ttydevsw_inwakeup(tp);
+}
diff --git a/components/lwp/terminal/freebsd/tty_compat.c b/components/lwp/terminal/freebsd/tty_compat.c
new file mode 100644
index 0000000000..0f2c70a5c5
--- /dev/null
+++ b/components/lwp/terminal/freebsd/tty_compat.c
@@ -0,0 +1,711 @@
+/*
+ * Copyright (c) 2006-2023, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * (tty_compat.c)
+ * The compatible layer which interacts with process management core (lwp)
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2023-11-13 Shell init ver.
+ */
+
+#define DBG_TAG "lwp.tty"
+#define DBG_LVL DBG_INFO
+#include <rtdbg.h>
+
+#include "../tty_config.h"
+#include "../tty_internal.h"
+#include "../terminal.h"
+
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 1994-1995 Søren Schmidt
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* is the tty and session leader already binding ? */
+static rt_bool_t _is_already_binding(lwp_tty_t tp, rt_lwp_t p)
+{
+ rt_bool_t rc;
+ rt_processgroup_t pgrp = p->pgrp;
+
+ /* lwp is already locked */
+ RT_ASSERT(pgrp);
+
+ /* Note: pgrp->session is constant after process group is created */
+ if (tp->t_session && tp->t_session == pgrp->session)
+ {
+ rc = RT_TRUE;
+ }
+ else
+ {
+ rc = RT_FALSE;
+ }
+ return rc;
+}
+
+static rt_bool_t _is_tty_or_sess_busy(lwp_tty_t tp, rt_lwp_t p)
+{
+ rt_bool_t rc;
+ rt_session_t sess = p->pgrp->session;
+
+ SESS_LOCK(sess);
+ if (sess->ctty)
+ {
+ rc = RT_TRUE;
+ }
+ else if (tp->t_session)
+ {
+ /**
+ * TODO: allow TTY stolen if the sess leader is killed while resource
+ * had not been collected
+ */
+ if (tp->t_session->leader == RT_NULL)
+ rc = RT_FALSE;
+ else
+ rc = RT_TRUE;
+ }
+ else
+ {
+ rc = RT_FALSE;
+ }
+ SESS_UNLOCK(sess);
+ return rc;
+}
+
+int lwp_tty_bg_stop(struct lwp_tty *tp, struct rt_condvar *cv)
+{
+ int error;
+ int revokecnt = tp->t_revokecnt;
+ rt_lwp_t self_lwp;
+ rt_thread_t header_thr;
+ rt_thread_t cur_thr = rt_thread_self();
+ int jobctl_stopped;
+
+ self_lwp = cur_thr->lwp;
+ RT_ASSERT(self_lwp);
+
+ jobctl_stopped = self_lwp->jobctl_stopped;
+
+ tty_lock_assert(tp, MA_OWNED | MA_NOTRECURSED);
+ MPASS(!tty_gone(tp));
+
+ LWP_LOCK(self_lwp);
+ header_thr = rt_list_entry(self_lwp->t_grp.prev, struct rt_thread, sibling);
+ if (!jobctl_stopped && header_thr == cur_thr &&
+ cur_thr->sibling.prev == &self_lwp->t_grp)
+ {
+ /* update lwp status */
+ jobctl_stopped = self_lwp->jobctl_stopped = RT_TRUE;
+ }
+ LWP_UNLOCK(self_lwp);
+
+ error = cv_wait(cv, tp->t_mtx);
+
+ if (jobctl_stopped)
+ {
+ self_lwp->jobctl_stopped = RT_FALSE;
+ }
+
+ /* Bail out when the device slipped away. */
+ if (tty_gone(tp))
+ return -ENXIO;
+
+ /* Restart the system call when we may have been revoked. */
+ if (tp->t_revokecnt != revokecnt)
+ return -ERESTART;
+
+ return error;
+}
+
+/* process management */
+int lwp_tty_set_ctrl_proc(lwp_tty_t tp, rt_thread_t td)
+{
+ int rc = -1;
+ struct rt_lwp *p = td->lwp;
+
+ tty_unlock(tp);
+ LWP_LOCK(p);
+ tty_lock(tp);
+
+ if (is_sess_leader(p))
+ {
+ if (_is_already_binding(tp, p))
+ {
+ rc = 0;
+ }
+ else if (_is_tty_or_sess_busy(tp, p))
+ {
+ rc = -EPERM;
+ }
+ else
+ {
+ /**
+ * Binding controlling process
+ * note: p->pgrp is protected by lwp lock;
+ * pgrp->session is always constant.
+ */
+ tp->t_session = p->pgrp->session;
+ tp->t_session->ctty = tp;
+ tp->t_sessioncnt++;
+
+ /* Assign foreground process group */
+ tp->t_pgrp = p->pgrp;
+ p->term_ctrlterm = RT_TRUE;
+
+ LOG_D("%s(sid=%d)", __func__, tp->t_session->sid);
+ rc = 0;
+ }
+ }
+ else
+ {
+ rc = -EPERM;
+ }
+
+ LWP_UNLOCK(p);
+
+ return rc;
+}
+
+int lwp_tty_assign_foreground(lwp_tty_t tp, rt_thread_t td, int pgid)
+{
+ struct rt_processgroup *pg;
+ rt_lwp_t cur_lwp = td->lwp;
+
+ tty_unlock(tp);
+ pg = lwp_pgrp_find_and_inc_ref(pgid);
+ if (pg == NULL || cur_lwp == NULL)
+ {
+ tty_lock(tp);
+ return -EPERM;
+ }
+ else
+ {
+ PGRP_LOCK(pg);
+
+ if (pg->sid != cur_lwp->sid)
+ {
+ PGRP_UNLOCK(pg);
+ lwp_pgrp_dec_ref(pg);
+ LOG_D("%s: NoPerm current process (pid=%d, pgid=%d, sid=%d), "
+ "tagget group (pgid=%d, sid=%d)", __func__,
+ cur_lwp->pid, cur_lwp->pgid, cur_lwp->sid, pgid, pg->sid);
+ tty_lock(tp);
+ return -EPERM;
+ }
+ }
+ tty_lock(tp);
+
+ /**
+ * Determine if this TTY is the controlling TTY after
+ * relocking the TTY.
+ */
+ if (!tty_is_ctty(tp, td->lwp))
+ {
+ PGRP_UNLOCK(pg);
+ LOG_D("%s: NoCTTY current process (pid=%d, pgid=%d, sid=%d), "
+ "tagget group (pgid=%d, sid=%d)", __func__,
+ cur_lwp->pid, cur_lwp->pgid, cur_lwp->sid, pgid, pg->sid);
+ return -ENOTTY;
+ }
+ tp->t_pgrp = pg;
+ PGRP_UNLOCK(pg);
+ lwp_pgrp_dec_ref(pg);
+
+ /* Wake up the background process groups. */
+ cv_broadcast(&tp->t_bgwait);
+
+ LOG_D("%s: Foreground group %p (pgid=%d)", __func__, tp->t_pgrp,
+ tp->t_pgrp ? tp->t_pgrp->pgid : -1);
+
+ return 0;
+}
+
+/**
+ * Signalling processes.
+ */
+
+void lwp_tty_signal_sessleader(struct lwp_tty *tp, int sig)
+{
+ struct rt_lwp *p;
+ struct rt_session *s;
+
+ tty_assert_locked(tp);
+ MPASS(sig >= 1 && sig < _LWP_NSIG);
+
+ /* Make signals start output again. */
+ tp->t_flags &= ~TF_STOPPED;
+ tp->t_termios.c_lflag &= ~FLUSHO;
+
+ /**
+ * Load s.leader exactly once to avoid race where s.leader is
+ * set to NULL by a concurrent invocation of killjobc() by the
+ * session leader. Note that we are not holding t_session's
+ * lock for the read.
+ */
+ if ((s = tp->t_session) != NULL &&
+ (p = (void *)rt_atomic_load((rt_atomic_t *)&s->leader)) != NULL)
+ {
+ lwp_signal_kill(p, sig, SI_KERNEL, 0);
+ }
+}
+
+void lwp_tty_signal_pgrp(struct lwp_tty *tp, int sig)
+{
+ tty_assert_locked(tp);
+ MPASS(sig >= 1 && sig < _LWP_NSIG);
+
+ /* Make signals start output again. */
+ tp->t_flags &= ~TF_STOPPED;
+ tp->t_termios.c_lflag &= ~FLUSHO;
+
+#ifdef USING_BSD_SIGINFO
+ if (sig == SIGINFO && !(tp->t_termios.c_lflag & NOKERNINFO))
+ tty_info(tp);
+#endif /* USING_BSD_SIGINFO */
+
+ if (tp->t_pgrp != NULL)
+ {
+ PGRP_LOCK(tp->t_pgrp);
+ lwp_pgrp_signal_kill(tp->t_pgrp, sig, SI_KERNEL, 0);
+ PGRP_UNLOCK(tp->t_pgrp);
+ }
+}
+
+/* bsd_ttydev_methods.d_ioctl */
+
+rt_inline size_t _copy_to_user(void *to, void *from, size_t n)
+{
+ return lwp_put_to_user(to, from, n) == n ? 0 : -EFAULT;
+}
+
+rt_inline size_t _copy_from_user(void *to, void *from, size_t n)
+{
+ return lwp_get_from_user(to, from, n) == n ? 0 : -EFAULT;
+}
+
+static void termios_to_termio(struct termios *tios, struct termio *tio)
+{
+ memset(tio, 0, sizeof(*tio));
+ tio->c_iflag = tios->c_iflag;
+ tio->c_oflag = tios->c_oflag;
+ tio->c_cflag = tios->c_cflag;
+ tio->c_lflag = tios->c_lflag;
+ tio->c_line = tios->c_line;
+ memcpy(tio->c_cc, tios->c_cc, NCC);
+}
+
+static void termio_to_termios(struct termio *tio, struct termios *tios)
+{
+ int i;
+
+ tios->c_iflag = tio->c_iflag;
+ tios->c_oflag = tio->c_oflag;
+ tios->c_cflag = tio->c_cflag;
+ tios->c_lflag = tio->c_lflag;
+ for (i = NCC; i < NCCS; i++)
+ tios->c_cc[i] = _POSIX_VDISABLE;
+ memcpy(tios->c_cc, tio->c_cc, NCC);
+}
+
+#define IOCTL(cmd, data, fflags, td) \
+ bsd_ttydev_methods.d_ioctl(tp, cmd, data, fflags, td)
+
+int lwp_tty_ioctl_adapter(lwp_tty_t tp, int cmd, int oflags, void *args, rt_thread_t td)
+{
+ long fflags = FFLAGS(oflags);
+ struct termios tios;
+ struct termio tio;
+ int error;
+
+ LOG_D("%s(cmd=0x%x, args=%p)", __func__, cmd, args);
+ switch (cmd & 0xffff)
+ {
+ case TCGETS:
+ error = IOCTL(TIOCGETA, (rt_caddr_t)&tios, fflags, td);
+ if (error)
+ break;
+ error = _copy_to_user(args, &tios, sizeof(tios));
+ break;
+
+ case TCSETS:
+ error = _copy_from_user(&tios, args, sizeof(tios));
+ if (error)
+ break;
+ error = (IOCTL(TIOCSETA, (rt_caddr_t)&tios, fflags, td));
+ break;
+
+ case TCSETSW:
+ error = _copy_from_user(&tios, args, sizeof(tios));
+ if (error)
+ break;
+ error = (IOCTL(TIOCSETAW, (rt_caddr_t)&tios, fflags, td));
+ break;
+
+ case TCSETSF:
+ error = _copy_from_user(&tios, args, sizeof(tios));
+ if (error)
+ break;
+ error = (IOCTL(TIOCSETAF, (rt_caddr_t)&tios, fflags, td));
+ break;
+
+ case TCGETA:
+ error = IOCTL(TIOCGETA, (rt_caddr_t)&tios, fflags, td);
+ if (error)
+ break;
+ termios_to_termio(&tios, &tio);
+ error = _copy_to_user((void *)args, &tio, sizeof(tio));
+ break;
+
+ case TCSETA:
+ error = _copy_from_user(&tio, (void *)args, sizeof(tio));
+ if (error)
+ break;
+ termio_to_termios(&tio, &tios);
+ error = (IOCTL(TIOCSETA, (rt_caddr_t)&tios, fflags, td));
+ break;
+
+ case TCSETAW:
+ error = _copy_from_user(&tio, (void *)args, sizeof(tio));
+ if (error)
+ break;
+ termio_to_termios(&tio, &tios);
+ error = (IOCTL(TIOCSETAW, (rt_caddr_t)&tios, fflags, td));
+ break;
+
+ case TCSETAF:
+ error = _copy_from_user(&tio, (void *)args, sizeof(tio));
+ if (error)
+ break;
+ termio_to_termios(&tio, &tios);
+ error = (IOCTL(TIOCSETAF, (rt_caddr_t)&tios, fflags, td));
+ break;
+
+ case TCSBRK:
+ if (args != 0)
+ {
+ /**
+ * Linux manual: SVr4, UnixWare, Solaris, and Linux treat
+ * tcsendbreak(fd,arg) with nonzero arg like tcdrain(fd).
+ */
+ error = IOCTL(TIOCDRAIN, (rt_caddr_t)&tios, fflags, td);
+ }
+ else
+ {
+ /**
+ * Linux manual: If the terminal is using asynchronous serial
+ * data transmission, and arg is zero, then send a break (a
+ * stream of zero bits) for between 0.25 and 0.5 seconds.
+ */
+ LOG_D("%s: ioctl TCSBRK arg 0 not implemented", __func__);
+ error = -ENOSYS;
+ }
+ break;
+
+#ifdef USING_BSD_IOCTL_EXT
+ /* Software flow control */
+ case TCXONC: {
+ switch (args->arg)
+ {
+ case TCOOFF:
+ args->cmd = TIOCSTOP;
+ break;
+ case TCOON:
+ args->cmd = TIOCSTART;
+ break;
+ case TCIOFF:
+ case TCION: {
+ int c;
+ struct write_args wr;
+ error = IOCTL(TIOCGETA, (rt_caddr_t)&tios, fflags,
+ td);
+ if (error)
+ break;
+ fdrop(fp, td);
+ c = (args->arg == TCIOFF) ? VSTOP : VSTART;
+ c = tios.c_cc[c];
+ if (c != _POSIX_VDISABLE)
+ {
+ wr.fd = args->fd;
+ wr.buf = &c;
+ wr.nbyte = sizeof(c);
+ return (sys_write(td, &wr));
+ }
+ else
+ return 0;
+ }
+ default:
+ fdrop(fp, td);
+ return -EINVAL;
+ }
+ args->arg = 0;
+ error = (sys_ioctl(td, (struct ioctl_args *)args));
+ break;
+ }
+#endif /* USING_BSD_IOCTL_EXT */
+ case TCFLSH: {
+ int val;
+ error = 0;
+ switch ((rt_base_t)args)
+ {
+ case TCIFLUSH:
+ val = FREAD;
+ break;
+ case TCOFLUSH:
+ val = FWRITE;
+ break;
+ case TCIOFLUSH:
+ val = FREAD | FWRITE;
+ break;
+ default:
+ error = -EINVAL;
+ break;
+ }
+ if (!error)
+ error = (IOCTL(TIOCFLUSH, (rt_caddr_t)&val, fflags, td));
+ break;
+ }
+
+#ifdef USING_BSD_IOCTL_EXT
+ case TIOCEXCL:
+ args->cmd = TIOCEXCL;
+ error = (sys_ioctl(td, (struct ioctl_args *)args));
+ break;
+
+ case TIOCNXCL:
+ args->cmd = TIOCNXCL;
+ error = (sys_ioctl(td, (struct ioctl_args *)args));
+ break;
+#endif /* USING_BSD_IOCTL_EXT */
+
+ /* Controlling terminal */
+ case TIOCSCTTY:
+ case TIOCNOTTY:
+
+ /* Process group and session ID */
+ case TIOCGPGRP:
+ case TIOCSPGRP:
+ case TIOCGSID:
+
+ /* TIOCOUTQ */
+ /* TIOCSTI */
+ case TIOCGWINSZ:
+ case TIOCSWINSZ:
+ error = IOCTL(cmd, (rt_caddr_t)args, fflags, td);
+ break;
+#ifdef USING_BSD_IOCTL_EXT
+ case TIOCMGET:
+ args->cmd = TIOCMGET;
+ error = (sys_ioctl(td, (struct ioctl_args *)args));
+ break;
+
+ case TIOCMBIS:
+ args->cmd = TIOCMBIS;
+ error = (sys_ioctl(td, (struct ioctl_args *)args));
+ break;
+
+ case TIOCMBIC:
+ args->cmd = TIOCMBIC;
+ error = (sys_ioctl(td, (struct ioctl_args *)args));
+ break;
+
+ case TIOCMSET:
+ args->cmd = TIOCMSET;
+ error = (sys_ioctl(td, (struct ioctl_args *)args));
+ break;
+
+ /* TIOCGSOFTCAR */
+ /* TIOCSSOFTCAR */
+
+ case FIONREAD: /* TIOCINQ */
+ args->cmd = FIONREAD;
+ error = (sys_ioctl(td, (struct ioctl_args *)args));
+ break;
+
+ /* TIOCLINUX */
+
+ case TIOCCONS:
+ args->cmd = TIOCCONS;
+ error = (sys_ioctl(td, (struct ioctl_args *)args));
+ break;
+
+ case TIOCGSERIAL: {
+ struct linux_serial_struct lss;
+
+ bzero(&lss, sizeof(lss));
+ lss.type = PORT_16550A;
+ lss.flags = 0;
+ lss.close_delay = 0;
+ error = copyout(&lss, (void *)args->arg, sizeof(lss));
+ break;
+ }
+
+ case TIOCSSERIAL: {
+ struct linux_serial_struct lss;
+ error = copyin((void *)args->arg, &lss, sizeof(lss));
+ if (error)
+ break;
+ /* XXX - It really helps to have an implementation that
+ * does nothing. NOT!
+ */
+ error = 0;
+ break;
+ }
+
+ case TIOCPKT:
+ args->cmd = TIOCPKT;
+ error = (sys_ioctl(td, (struct ioctl_args *)args));
+ break;
+
+ case FIONBIO:
+ args->cmd = FIONBIO;
+ error = (sys_ioctl(td, (struct ioctl_args *)args));
+ break;
+
+ case TIOCSETD: {
+ int line;
+ switch (args->arg)
+ {
+ case N_TTY:
+ line = TTYDISC;
+ break;
+ case N_SLIP:
+ line = SLIPDISC;
+ break;
+ case N_PPP:
+ line = PPPDISC;
+ break;
+ default:
+ fdrop(fp, td);
+ return -EINVAL;
+ }
+ error = (ioctl_emit(TIOCSETD, (rt_caddr_t)&line, fflags, td));
+ break;
+ }
+
+ case TIOCGETD: {
+ int linux_line;
+ int bsd_line = TTYDISC;
+ error =
+ ioctl_emit(TIOCGETD, (rt_caddr_t)&bsd_line, fflags, td);
+ if (error)
+ break;
+ switch (bsd_line)
+ {
+ case TTYDISC:
+ linux_line = N_TTY;
+ break;
+ case SLIPDISC:
+ linux_line = N_SLIP;
+ break;
+ case PPPDISC:
+ linux_line = N_PPP;
+ break;
+ default:
+ fdrop(fp, td);
+ return -EINVAL;
+ }
+ error = (copyout(&linux_line, (void *)args->arg, sizeof(int)));
+ break;
+ }
+
+ /* TCSBRKP */
+ /* TIOCTTYGSTRUCT */
+
+ case FIONCLEX:
+ args->cmd = FIONCLEX;
+ error = (sys_ioctl(td, (struct ioctl_args *)args));
+ break;
+
+ case FIOCLEX:
+ args->cmd = FIOCLEX;
+ error = (sys_ioctl(td, (struct ioctl_args *)args));
+ break;
+
+ case FIOASYNC:
+ args->cmd = FIOASYNC;
+ error = (sys_ioctl(td, (struct ioctl_args *)args));
+ break;
+
+ /* TIOCSERCONFIG */
+ /* TIOCSERGWILD */
+ /* TIOCSERSWILD */
+ /* TIOCGLCKTRMIOS */
+ /* TIOCSLCKTRMIOS */
+
+ case TIOCSBRK:
+ args->cmd = TIOCSBRK;
+ error = (sys_ioctl(td, (struct ioctl_args *)args));
+ break;
+
+ case TIOCCBRK:
+ args->cmd = TIOCCBRK;
+ error = (sys_ioctl(td, (struct ioctl_args *)args));
+ break;
+ case TIOCGPTN: {
+ int nb;
+
+ error = ioctl_emit(TIOCGPTN, (rt_caddr_t)&nb, fflags, td);
+ if (!error)
+ error = copyout(&nb, (void *)args->arg, sizeof(int));
+ break;
+ }
+ case TIOCGPTPEER:
+ linux_msg(td, "unsupported ioctl TIOCGPTPEER");
+ error = -ENOIOCTL;
+ break;
+ case TIOCSPTLCK:
+ /*
+ * Our unlockpt() does nothing. Check that fd refers
+ * to a pseudo-terminal master device.
+ */
+ args->cmd = TIOCPTMASTER;
+ error = (sys_ioctl(td, (struct ioctl_args *)args));
+ break;
+#endif /* USING_BSD_IOCTL_EXT */
+
+ /**
+ * those are for current implementation of devfs, and we dont want to
+ * log them
+ */
+ case F_DUPFD:
+ case F_DUPFD_CLOEXEC:
+ case F_GETFD:
+ case F_SETFD:
+ case F_GETFL:
+ case F_SETFL:
+ /* fall back to fs */
+ error = -ENOIOCTL;
+ break;
+ default:
+ LOG_I("%s: unhandle commands 0x%x", __func__, cmd);
+ error = -ENOSYS;
+ break;
+ }
+
+ return (error);
+}
diff --git a/components/lwp/terminal/freebsd/tty_inq.c b/components/lwp/terminal/freebsd/tty_inq.c
new file mode 100644
index 0000000000..1dc748d6c2
--- /dev/null
+++ b/components/lwp/terminal/freebsd/tty_inq.c
@@ -0,0 +1,507 @@
+/*
+ * Copyright (c) 2006-2023, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2023-11-13 Shell init ver.
+ */
+
+#include "../bsd_porting.h"
+#include "../terminal.h"
+
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2008 Ed Schouten <[email protected]>
+ * All rights reserved.
+ *
+ * Portions of this software were developed under sponsorship from Snow
+ * B.V., the Netherlands.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * TTY input queue buffering.
+ *
+ * Unlike the output queue, the input queue has more features that are
+ * needed to properly implement various features offered by the TTY
+ * interface:
+ *
+ * - Data can be removed from the tail of the queue, which is used to
+ * implement backspace.
+ * - Once in a while, input has to be `canonicalized'. When ICANON is
+ * turned on, this will be done after a CR has been inserted.
+ * Otherwise, it should be done after any character has been inserted.
+ * - The input queue can store one bit per byte, called the quoting bit.
+ * This bit is used by TTYDISC to make backspace work on quoted
+ * characters.
+ *
+ * In most cases, there is probably less input than output, so unlike
+ * the outq, we'll stick to 128 byte blocks here.
+ */
+
+static int ttyinq_flush_secure = 1;
+
+#define TTYINQ_QUOTESIZE (TTYINQ_DATASIZE / BMSIZE)
+#define BMSIZE 32
+#define GETBIT(tib, boff) \
+ ((tib)->tib_quotes[(boff) / BMSIZE] & (1 << ((boff) % BMSIZE)))
+#define SETBIT(tib, boff) \
+ ((tib)->tib_quotes[(boff) / BMSIZE] |= (1 << ((boff) % BMSIZE)))
+#define CLRBIT(tib, boff) \
+ ((tib)->tib_quotes[(boff) / BMSIZE] &= ~(1 << ((boff) % BMSIZE)))
+
+struct ttyinq_block
+{
+ struct ttyinq_block *tib_prev;
+ struct ttyinq_block *tib_next;
+ uint32_t tib_quotes[TTYINQ_QUOTESIZE];
+ char tib_data[TTYINQ_DATASIZE];
+};
+
+static uma_zone_t ttyinq_zone;
+
+#define TTYINQ_INSERT_TAIL(ti, tib) \
+ do \
+ { \
+ if (ti->ti_end == 0) \
+ { \
+ tib->tib_prev = NULL; \
+ tib->tib_next = ti->ti_firstblock; \
+ ti->ti_firstblock = tib; \
+ } \
+ else \
+ { \
+ tib->tib_prev = ti->ti_lastblock; \
+ tib->tib_next = ti->ti_lastblock->tib_next; \
+ ti->ti_lastblock->tib_next = tib; \
+ } \
+ if (tib->tib_next != NULL) \
+ tib->tib_next->tib_prev = tib; \
+ ti->ti_nblocks++; \
+ } while (0)
+
+#define TTYINQ_REMOVE_HEAD(ti) \
+ do \
+ { \
+ ti->ti_firstblock = ti->ti_firstblock->tib_next; \
+ if (ti->ti_firstblock != NULL) \
+ ti->ti_firstblock->tib_prev = NULL; \
+ ti->ti_nblocks--; \
+ } while (0)
+
+#define TTYINQ_RECYCLE(ti, tib) \
+ do \
+ { \
+ if (ti->ti_quota <= ti->ti_nblocks) \
+ uma_zfree(ttyinq_zone, tib); \
+ else \
+ TTYINQ_INSERT_TAIL(ti, tib); \
+ } while (0)
+
+int ttyinq_setsize(struct ttyinq *ti, struct lwp_tty *tp, size_t size)
+{
+ struct ttyinq_block *tib;
+
+ ti->ti_quota = howmany(size, TTYINQ_DATASIZE);
+
+ while (ti->ti_quota > ti->ti_nblocks)
+ {
+ /*
+ * List is getting bigger.
+ * Add new blocks to the tail of the list.
+ *
+ * We must unlock the TTY temporarily, because we need
+ * to allocate memory. This won't be a problem, because
+ * in the worst case, another thread ends up here, which
+ * may cause us to allocate too many blocks, but this
+ * will be caught by the loop below.
+ */
+ tty_unlock(tp);
+ tib = uma_zalloc(ttyinq_zone, M_WAITOK);
+ tty_lock(tp);
+
+ if (tty_gone(tp))
+ {
+ uma_zfree(ttyinq_zone, tib);
+ return -ENXIO;
+ }
+
+ TTYINQ_INSERT_TAIL(ti, tib);
+ }
+ return 0;
+}
+
+void ttyinq_free(struct ttyinq *ti)
+{
+ struct ttyinq_block *tib;
+
+ ttyinq_flush(ti);
+ ti->ti_quota = 0;
+
+ while ((tib = ti->ti_firstblock) != NULL)
+ {
+ TTYINQ_REMOVE_HEAD(ti);
+ uma_zfree(ttyinq_zone, tib);
+ }
+
+ MPASS(ti->ti_nblocks == 0);
+}
+
+int ttyinq_read_uio(struct ttyinq *ti, struct lwp_tty *tp, struct uio *uio,
+ size_t rlen, size_t flen)
+{
+ MPASS(rlen <= uio->uio_resid);
+
+ while (rlen > 0)
+ {
+ int error;
+ struct ttyinq_block *tib;
+ size_t cbegin, cend, clen;
+
+ /* See if there still is data. */
+ if (ti->ti_begin == ti->ti_linestart)
+ return 0;
+ tib = ti->ti_firstblock;
+ if (tib == NULL)
+ return 0;
+
+ /*
+ * The end address should be the lowest of these three:
+ * - The write pointer
+ * - The blocksize - we can't read beyond the block
+ * - The end address if we could perform the full read
+ */
+ cbegin = ti->ti_begin;
+ cend = MIN(MIN(ti->ti_linestart, ti->ti_begin + rlen), TTYINQ_DATASIZE);
+ clen = cend - cbegin;
+ MPASS(clen >= flen);
+ rlen -= clen;
+
+ /*
+ * We can prevent buffering in some cases:
+ * - We need to read the block until the end.
+ * - We don't need to read the block until the end, but
+ * there is no data beyond it, which allows us to move
+ * the write pointer to a new block.
+ */
+ if (cend == TTYINQ_DATASIZE || cend == ti->ti_end)
+ {
+ /*
+ * Fast path: zero copy. Remove the first block,
+ * so we can unlock the TTY temporarily.
+ */
+ TTYINQ_REMOVE_HEAD(ti);
+ ti->ti_begin = 0;
+
+ /*
+ * Because we remove the first block, we must
+ * fix up the block offsets.
+ */
+#define CORRECT_BLOCK(t) \
+ do \
+ { \
+ if (t <= TTYINQ_DATASIZE) \
+ t = 0; \
+ else \
+ t -= TTYINQ_DATASIZE; \
+ } while (0)
+ CORRECT_BLOCK(ti->ti_linestart);
+ CORRECT_BLOCK(ti->ti_reprint);
+ CORRECT_BLOCK(ti->ti_end);
+#undef CORRECT_BLOCK
+
+ /*
+ * Temporary unlock and copy the data to
+ * userspace. We may need to flush trailing
+ * bytes, like EOF characters.
+ */
+ tty_unlock(tp);
+ error = uiomove(tib->tib_data + cbegin, clen - flen, uio);
+ tty_lock(tp);
+
+ /* Block can now be readded to the list. */
+ TTYINQ_RECYCLE(ti, tib);
+ }
+ else
+ {
+ char ob[TTYINQ_DATASIZE - 1];
+
+ /*
+ * Slow path: store data in a temporary buffer.
+ */
+ memcpy(ob, tib->tib_data + cbegin, clen - flen);
+ ti->ti_begin += clen;
+ MPASS(ti->ti_begin < TTYINQ_DATASIZE);
+
+ /* Temporary unlock and copy the data to userspace. */
+ tty_unlock(tp);
+ error = uiomove(ob, clen - flen, uio);
+ tty_lock(tp);
+ }
+
+ if (error != 0)
+ return error;
+ if (tty_gone(tp))
+ return -ENXIO;
+ }
+
+ return 0;
+}
+
+rt_inline void ttyinq_set_quotes(struct ttyinq_block *tib, size_t offset,
+ size_t length, int value)
+{
+ if (value)
+ {
+ /* Set the bits. */
+ for (; length > 0; length--, offset++) SETBIT(tib, offset);
+ }
+ else
+ {
+ /* Unset the bits. */
+ for (; length > 0; length--, offset++) CLRBIT(tib, offset);
+ }
+}
+
+size_t ttyinq_write(struct ttyinq *ti, const void *buf, size_t nbytes,
+ int quote)
+{
+ const char *cbuf = buf;
+ struct ttyinq_block *tib;
+ unsigned int boff;
+ size_t l;
+
+ while (nbytes > 0)
+ {
+ boff = ti->ti_end % TTYINQ_DATASIZE;
+
+ if (ti->ti_end == 0)
+ {
+ /* First time we're being used or drained. */
+ MPASS(ti->ti_begin == 0);
+ tib = ti->ti_firstblock;
+ if (tib == NULL)
+ {
+ /* Queue has no blocks. */
+ break;
+ }
+ ti->ti_lastblock = tib;
+ }
+ else if (boff == 0)
+ {
+ /* We reached the end of this block on last write. */
+ tib = ti->ti_lastblock->tib_next;
+ if (tib == NULL)
+ {
+ /* We've reached the watermark. */
+ break;
+ }
+ ti->ti_lastblock = tib;
+ }
+ else
+ {
+ tib = ti->ti_lastblock;
+ }
+
+ /* Don't copy more than was requested. */
+ l = MIN(nbytes, TTYINQ_DATASIZE - boff);
+ MPASS(l > 0);
+ memcpy(tib->tib_data + boff, cbuf, l);
+
+ /* Set the quoting bits for the proper region. */
+ ttyinq_set_quotes(tib, boff, l, quote);
+
+ cbuf += l;
+ nbytes -= l;
+ ti->ti_end += l;
+ }
+
+ return (cbuf - (const char *)buf);
+}
+
+int ttyinq_write_nofrag(struct ttyinq *ti, const void *buf, size_t nbytes,
+ int quote)
+{
+ size_t ret __unused;
+
+ if (ttyinq_bytesleft(ti) < nbytes)
+ return -1;
+
+ /* We should always be able to write it back. */
+ ret = ttyinq_write(ti, buf, nbytes, quote);
+ MPASS(ret == nbytes);
+
+ return 0;
+}
+
+void ttyinq_canonicalize(struct ttyinq *ti)
+{
+ ti->ti_linestart = ti->ti_reprint = ti->ti_end;
+ ti->ti_startblock = ti->ti_reprintblock = ti->ti_lastblock;
+}
+
+size_t ttyinq_findchar(struct ttyinq *ti, const char *breakc, size_t maxlen,
+ char *lastc)
+{
+ struct ttyinq_block *tib = ti->ti_firstblock;
+ unsigned int boff = ti->ti_begin;
+ unsigned int bend =
+ MIN(MIN(TTYINQ_DATASIZE, ti->ti_linestart), ti->ti_begin + maxlen);
+
+ MPASS(maxlen > 0);
+
+ if (tib == NULL)
+ return 0;
+
+ while (boff < bend)
+ {
+ if (strchr(breakc, tib->tib_data[boff]) && !GETBIT(tib, boff))
+ {
+ *lastc = tib->tib_data[boff];
+ return (boff - ti->ti_begin + 1);
+ }
+ boff++;
+ }
+
+ /* Not found - just process the entire block. */
+ return (bend - ti->ti_begin);
+}
+
+void ttyinq_flush(struct ttyinq *ti)
+{
+ struct ttyinq_block *tib;
+
+ ti->ti_begin = 0;
+ ti->ti_linestart = 0;
+ ti->ti_reprint = 0;
+ ti->ti_end = 0;
+
+ /* Zero all data in the input queue to get rid of passwords. */
+ if (ttyinq_flush_secure)
+ {
+ for (tib = ti->ti_firstblock; tib != NULL; tib = tib->tib_next)
+ memset(&tib->tib_data, 0, sizeof tib->tib_data);
+ }
+}
+
+int ttyinq_peekchar(struct ttyinq *ti, char *c, int *quote)
+{
+ unsigned int boff;
+ struct ttyinq_block *tib = ti->ti_lastblock;
+
+ if (ti->ti_linestart == ti->ti_end)
+ return -1;
+
+ MPASS(ti->ti_end > 0);
+ boff = (ti->ti_end - 1) % TTYINQ_DATASIZE;
+
+ *c = tib->tib_data[boff];
+ *quote = GETBIT(tib, boff);
+
+ return 0;
+}
+
+void ttyinq_unputchar(struct ttyinq *ti)
+{
+ MPASS(ti->ti_linestart < ti->ti_end);
+
+ if (--ti->ti_end % TTYINQ_DATASIZE == 0)
+ {
+ /* Roll back to the previous block. */
+ ti->ti_lastblock = ti->ti_lastblock->tib_prev;
+ /*
+ * This can only fail if we are unputchar()'ing the
+ * first character in the queue.
+ */
+ MPASS((ti->ti_lastblock == NULL) == (ti->ti_end == 0));
+ }
+}
+
+void ttyinq_reprintpos_set(struct ttyinq *ti)
+{
+ ti->ti_reprint = ti->ti_end;
+ ti->ti_reprintblock = ti->ti_lastblock;
+}
+
+void ttyinq_reprintpos_reset(struct ttyinq *ti)
+{
+ ti->ti_reprint = ti->ti_linestart;
+ ti->ti_reprintblock = ti->ti_startblock;
+}
+
+static void ttyinq_line_iterate(struct ttyinq *ti,
+ ttyinq_line_iterator_t *iterator, void *data,
+ unsigned int offset, struct ttyinq_block *tib)
+{
+ unsigned int boff;
+
+ /* Use the proper block when we're at the queue head. */
+ if (offset == 0)
+ tib = ti->ti_firstblock;
+
+ /* Iterate all characters and call the iterator function. */
+ for (; offset < ti->ti_end; offset++)
+ {
+ boff = offset % TTYINQ_DATASIZE;
+ MPASS(tib != NULL);
+
+ /* Call back the iterator function. */
+ iterator(data, tib->tib_data[boff], GETBIT(tib, boff));
+
+ /* Last byte iterated - go to the next block. */
+ if (boff == TTYINQ_DATASIZE - 1)
+ tib = tib->tib_next;
+ }
+}
+
+void ttyinq_line_iterate_from_linestart(struct ttyinq *ti,
+ ttyinq_line_iterator_t *iterator,
+ void *data)
+{
+ ttyinq_line_iterate(ti, iterator, data, ti->ti_linestart,
+ ti->ti_startblock);
+}
+
+void ttyinq_line_iterate_from_reprintpos(struct ttyinq *ti,
+ ttyinq_line_iterator_t *iterator,
+ void *data)
+{
+ ttyinq_line_iterate(ti, iterator, data, ti->ti_reprint,
+ ti->ti_reprintblock);
+}
+
+static int ttyinq_startup(void)
+{
+ ttyinq_zone = uma_zcreate("ttyinq", sizeof(struct ttyinq_block), NULL, NULL,
+ NULL, NULL, UMA_ALIGN_PTR, 0);
+ return 0;
+}
+INIT_PREV_EXPORT(ttyinq_startup);
+
+#if 0
+
+SYSINIT(ttyinq, SI_SUB_DRIVERS, SI_ORDER_FIRST, ttyinq_startup, NULL);
+#endif
diff --git a/components/lwp/terminal/freebsd/tty_outq.c b/components/lwp/terminal/freebsd/tty_outq.c
new file mode 100644
index 0000000000..214309813e
--- /dev/null
+++ b/components/lwp/terminal/freebsd/tty_outq.c
@@ -0,0 +1,370 @@
+/*
+ * Copyright (c) 2006-2023, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2023-11-13 Shell init ver.
+ */
+
+#include "../bsd_porting.h"
+#include "../terminal.h"
+
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2008 Ed Schouten <[email protected]>
+ * All rights reserved.
+ *
+ * Portions of this software were developed under sponsorship from Snow
+ * B.V., the Netherlands.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * TTY output queue buffering.
+ *
+ * The previous design of the TTY layer offered the so-called clists.
+ * These clists were used for both the input queues and the output
+ * queue. We don't use certain features on the output side, like quoting
+ * bits for parity marking and such. This mechanism is similar to the
+ * old clists, but only contains the features we need to buffer the
+ * output.
+ */
+
+struct ttyoutq_block
+{
+ struct ttyoutq_block *tob_next;
+ char tob_data[TTYOUTQ_DATASIZE];
+};
+
+static uma_zone_t ttyoutq_zone;
+
+#define TTYOUTQ_INSERT_TAIL(to, tob) \
+ do \
+ { \
+ if (to->to_end == 0) \
+ { \
+ tob->tob_next = to->to_firstblock; \
+ to->to_firstblock = tob; \
+ } \
+ else \
+ { \
+ tob->tob_next = to->to_lastblock->tob_next; \
+ to->to_lastblock->tob_next = tob; \
+ } \
+ to->to_nblocks++; \
+ } while (0)
+
+#define TTYOUTQ_REMOVE_HEAD(to) \
+ do \
+ { \
+ to->to_firstblock = to->to_firstblock->tob_next; \
+ to->to_nblocks--; \
+ } while (0)
+
+#define TTYOUTQ_RECYCLE(to, tob) \
+ do \
+ { \
+ if (to->to_quota <= to->to_nblocks) \
+ uma_zfree(ttyoutq_zone, tob); \
+ else \
+ TTYOUTQ_INSERT_TAIL(to, tob); \
+ } while (0)
+
+void ttyoutq_flush(struct ttyoutq *to)
+{
+ to->to_begin = 0;
+ to->to_end = 0;
+}
+
+int ttyoutq_setsize(struct ttyoutq *to, struct lwp_tty *tp, size_t size)
+{
+ struct ttyoutq_block *tob;
+
+ to->to_quota = howmany(size, TTYOUTQ_DATASIZE);
+
+ while (to->to_quota > to->to_nblocks)
+ {
+ /*
+ * List is getting bigger.
+ * Add new blocks to the tail of the list.
+ *
+ * We must unlock the TTY temporarily, because we need
+ * to allocate memory. This won't be a problem, because
+ * in the worst case, another thread ends up here, which
+ * may cause us to allocate too many blocks, but this
+ * will be caught by the loop below.
+ */
+ tty_unlock(tp);
+ tob = uma_zalloc(ttyoutq_zone, M_WAITOK);
+ tty_lock(tp);
+
+ if (tty_gone(tp))
+ {
+ uma_zfree(ttyoutq_zone, tob);
+ return -ENXIO;
+ }
+
+ TTYOUTQ_INSERT_TAIL(to, tob);
+ }
+ return 0;
+}
+
+void ttyoutq_free(struct ttyoutq *to)
+{
+ struct ttyoutq_block *tob;
+
+ ttyoutq_flush(to);
+ to->to_quota = 0;
+
+ while ((tob = to->to_firstblock) != NULL)
+ {
+ TTYOUTQ_REMOVE_HEAD(to);
+ uma_zfree(ttyoutq_zone, tob);
+ }
+
+ MPASS(to->to_nblocks == 0);
+}
+
+size_t ttyoutq_read(struct ttyoutq *to, void *buf, size_t len)
+{
+ char *cbuf = buf;
+
+ while (len > 0)
+ {
+ struct ttyoutq_block *tob;
+ size_t cbegin, cend, clen;
+
+ /* See if there still is data. */
+ if (to->to_begin == to->to_end)
+ break;
+ tob = to->to_firstblock;
+ if (tob == NULL)
+ break;
+
+ /*
+ * The end address should be the lowest of these three:
+ * - The write pointer
+ * - The blocksize - we can't read beyond the block
+ * - The end address if we could perform the full read
+ */
+ cbegin = to->to_begin;
+ cend = MIN(MIN(to->to_end, to->to_begin + len), TTYOUTQ_DATASIZE);
+ clen = cend - cbegin;
+
+ /* Copy the data out of the buffers. */
+ memcpy(cbuf, tob->tob_data + cbegin, clen);
+ cbuf += clen;
+ len -= clen;
+
+ if (cend == to->to_end)
+ {
+ /* Read the complete queue. */
+ to->to_begin = 0;
+ to->to_end = 0;
+ }
+ else if (cend == TTYOUTQ_DATASIZE)
+ {
+ /* Read the block until the end. */
+ TTYOUTQ_REMOVE_HEAD(to);
+ to->to_begin = 0;
+ to->to_end -= TTYOUTQ_DATASIZE;
+ TTYOUTQ_RECYCLE(to, tob);
+ }
+ else
+ {
+ /* Read the block partially. */
+ to->to_begin += clen;
+ }
+ }
+
+ return cbuf - (char *)buf;
+}
+
+/*
+ * An optimized version of ttyoutq_read() which can be used in pseudo
+ * TTY drivers to directly copy data from the outq to userspace, instead
+ * of buffering it.
+ *
+ * We can only copy data directly if we need to read the entire block
+ * back to the user, because we temporarily remove the block from the
+ * queue. Otherwise we need to copy it to a temporary buffer first, to
+ * make sure data remains in the correct order.
+ */
+int ttyoutq_read_uio(struct ttyoutq *to, struct lwp_tty *tp, struct uio *uio)
+{
+ while (uio->uio_resid > 0)
+ {
+ int error;
+ struct ttyoutq_block *tob;
+ size_t cbegin, cend, clen;
+
+ /* See if there still is data. */
+ if (to->to_begin == to->to_end)
+ return 0;
+ tob = to->to_firstblock;
+ if (tob == NULL)
+ return 0;
+
+ /*
+ * The end address should be the lowest of these three:
+ * - The write pointer
+ * - The blocksize - we can't read beyond the block
+ * - The end address if we could perform the full read
+ */
+ cbegin = to->to_begin;
+ cend = MIN(MIN(to->to_end, to->to_begin + uio->uio_resid),
+ TTYOUTQ_DATASIZE);
+ clen = cend - cbegin;
+
+ /*
+ * We can prevent buffering in some cases:
+ * - We need to read the block until the end.
+ * - We don't need to read the block until the end, but
+ * there is no data beyond it, which allows us to move
+ * the write pointer to a new block.
+ */
+ if (cend == TTYOUTQ_DATASIZE || cend == to->to_end)
+ {
+ /*
+ * Fast path: zero copy. Remove the first block,
+ * so we can unlock the TTY temporarily.
+ */
+ TTYOUTQ_REMOVE_HEAD(to);
+ to->to_begin = 0;
+ if (to->to_end <= TTYOUTQ_DATASIZE)
+ to->to_end = 0;
+ else
+ to->to_end -= TTYOUTQ_DATASIZE;
+
+ /* Temporary unlock and copy the data to userspace. */
+ tty_unlock(tp);
+ error = uiomove(tob->tob_data + cbegin, clen, uio);
+ tty_lock(tp);
+
+ /* Block can now be readded to the list. */
+ TTYOUTQ_RECYCLE(to, tob);
+ }
+ else
+ {
+ char ob[TTYOUTQ_DATASIZE - 1];
+
+ /*
+ * Slow path: store data in a temporary buffer.
+ */
+ memcpy(ob, tob->tob_data + cbegin, clen);
+ to->to_begin += clen;
+ MPASS(to->to_begin < TTYOUTQ_DATASIZE);
+
+ /* Temporary unlock and copy the data to userspace. */
+ tty_unlock(tp);
+ error = uiomove(ob, clen, uio);
+ tty_lock(tp);
+ }
+
+ if (error != 0)
+ return error;
+ }
+
+ return 0;
+}
+
+size_t ttyoutq_write(struct ttyoutq *to, const void *buf, size_t nbytes)
+{
+ const char *cbuf = buf;
+ struct ttyoutq_block *tob;
+ unsigned int boff;
+ size_t l;
+
+ while (nbytes > 0)
+ {
+ boff = to->to_end % TTYOUTQ_DATASIZE;
+
+ if (to->to_end == 0)
+ {
+ /* First time we're being used or drained. */
+ MPASS(to->to_begin == 0);
+ tob = to->to_firstblock;
+ if (tob == NULL)
+ {
+ /* Queue has no blocks. */
+ break;
+ }
+ to->to_lastblock = tob;
+ }
+ else if (boff == 0)
+ {
+ /* We reached the end of this block on last write. */
+ tob = to->to_lastblock->tob_next;
+ if (tob == NULL)
+ {
+ /* We've reached the watermark. */
+ break;
+ }
+ to->to_lastblock = tob;
+ }
+ else
+ {
+ tob = to->to_lastblock;
+ }
+
+ /* Don't copy more than was requested. */
+ l = MIN(nbytes, TTYOUTQ_DATASIZE - boff);
+ MPASS(l > 0);
+ memcpy(tob->tob_data + boff, cbuf, l);
+
+ cbuf += l;
+ nbytes -= l;
+ to->to_end += l;
+ }
+
+ return (cbuf - (const char *)buf);
+}
+
+int ttyoutq_write_nofrag(struct ttyoutq *to, const void *buf, size_t nbytes)
+{
+ size_t ret __unused;
+
+ if (ttyoutq_bytesleft(to) < nbytes)
+ return -1;
+
+ /* We should always be able to write it back. */
+ ret = ttyoutq_write(to, buf, nbytes);
+ MPASS(ret == nbytes);
+
+ return 0;
+}
+
+static int ttyoutq_startup(void)
+{
+ ttyoutq_zone = uma_zcreate("ttyoutq", sizeof(struct ttyoutq_block), NULL,
+ NULL, NULL, NULL, UMA_ALIGN_PTR, 0);
+ return 0;
+}
+INIT_PREV_EXPORT(ttyoutq_startup);
+
+#if 0
+SYSINIT(ttyoutq, SI_SUB_DRIVERS, SI_ORDER_FIRST, ttyoutq_startup, NULL);
+#endif
diff --git a/components/lwp/terminal/freebsd/tty_pts.c b/components/lwp/terminal/freebsd/tty_pts.c
new file mode 100644
index 0000000000..2c75ff3009
--- /dev/null
+++ b/components/lwp/terminal/freebsd/tty_pts.c
@@ -0,0 +1,837 @@
+/*
+ * Copyright (c) 2006-2023, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2023-12-07 Shell init ver.
+ */
+
+#include <ipc/condvar.h>
+#include <rid_bitmap.h>
+#include <terminal/terminal.h>
+#include <terminal/tty_internal.h>
+#include <ptyfs.h>
+
+#include <rtthread.h>
+
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2008 Ed Schouten <[email protected]>
+ * All rights reserved.
+ *
+ * Portions of this software were developed under sponsorship from Snow
+ * B.V., the Netherlands.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+#define PTS_EXTERNAL
+
+/*
+ * Per-PTS structure.
+ *
+ * List of locks
+ * (t) locked by tty_lock()
+ * (c) const until freeing
+ */
+struct pts_softc
+{
+ int pts_unit; /* (c) Device unit number. */
+ unsigned int pts_flags; /* (t) Device flags. */
+#define PTS_PKT 0x1 /* Packet mode. */
+#define PTS_FINISHED 0x2 /* Return errors on read()/write(). */
+#define PTS_PTLOCKED 0x4 /* ioctl %TIOCSPTLCK/%TIOCGPTLCK */
+ char pts_pkt; /* (t) Unread packet mode data. */
+
+ struct rt_condvar pts_inwait; /* (t) Blocking write() on master. */
+ struct rt_wqueue pts_inpoll; /* (t) Select queue for write(). */
+ struct rt_condvar pts_outwait; /* (t) Blocking read() on master. */
+ struct rt_wqueue pts_outpoll; /* (t) Select queue for read(). */
+
+ struct ucred *pts_cred; /* (c) Resource limit. */
+ rt_device_t pts_master; /** (c) Master device.
+ * (Note: in rtsmart kernel, we support
+ * multi-instance ptmx )
+ */
+};
+
+/**
+ * Controller-side file operations.
+ * (P)seudo-(T)erminal (M)ultiple(X)er
+ */
+
+static int ptsdev_read(struct lwp_tty *tp, struct uio *uio,
+ struct ucred *active_cred, int oflags,
+ struct rt_thread *td)
+{
+ struct pts_softc *psc = tty_softc(tp);
+ int error = 0;
+ char pkt;
+
+ if (uio->uio_resid == 0)
+ return (0);
+
+ tty_lock(tp);
+
+ for (;;)
+ {
+ /*
+ * Implement packet mode. When packet mode is turned on,
+ * the first byte contains a bitmask of events that
+ * occurred (start, stop, flush, window size, etc).
+ */
+ if (psc->pts_flags & PTS_PKT && psc->pts_pkt)
+ {
+ pkt = psc->pts_pkt;
+ psc->pts_pkt = 0;
+ tty_unlock(tp);
+
+ error = uiomove(&pkt, 1, uio);
+ return (error);
+ }
+
+ /*
+ * Transmit regular data.
+ *
+ * XXX: We shouldn't use ttydisc_getc_poll()! Even
+ * though in this implementation, there is likely going
+ * to be data, we should just call ttydisc_getc_uio()
+ * and use its return value to sleep.
+ */
+ if (ttydisc_getc_poll(tp))
+ {
+ if (psc->pts_flags & PTS_PKT)
+ {
+ /*
+ * XXX: Small race. Fortunately PTY
+ * consumers aren't multithreaded.
+ */
+
+ tty_unlock(tp);
+ pkt = TIOCPKT_DATA;
+ error = uiomove(&pkt, 1, uio);
+ if (error)
+ return (error);
+ tty_lock(tp);
+ }
+
+ error = ttydisc_getc_uio(tp, uio);
+ break;
+ }
+
+ /* Maybe the device isn't used anyway. */
+ if (psc->pts_flags & PTS_FINISHED)
+ break;
+
+ /* Wait for more data. */
+ if (oflags & O_NONBLOCK)
+ {
+ error = EWOULDBLOCK;
+ break;
+ }
+ error = cv_wait_sig(&psc->pts_outwait, tp->t_mtx);
+ if (error != 0)
+ break;
+ }
+
+ tty_unlock(tp);
+
+ return (error);
+}
+
+static int ptsdev_write(struct lwp_tty *tp, struct uio *uio,
+ struct ucred *active_cred, int oflags,
+ struct rt_thread *td)
+{
+ struct pts_softc *psc = tty_softc(tp);
+ char ib[256], *ibstart;
+ size_t iblen, rintlen;
+ int error = 0;
+
+ if (uio->uio_resid == 0)
+ return (0);
+
+ for (;;)
+ {
+ ibstart = ib;
+ iblen = MIN(uio->uio_resid, sizeof ib);
+ error = uiomove(ib, iblen, uio);
+
+ tty_lock(tp);
+ if (error != 0)
+ {
+ iblen = 0;
+ goto done;
+ }
+
+ /*
+ * When possible, avoid the slow path. rint_bypass()
+ * copies all input to the input queue at once.
+ */
+ MPASS(iblen > 0);
+ do
+ {
+ rintlen = ttydisc_rint_simple(tp, ibstart, iblen);
+ ibstart += rintlen;
+ iblen -= rintlen;
+ if (iblen == 0)
+ {
+ /* All data written. */
+ break;
+ }
+
+ /* Maybe the device isn't used anyway. */
+ if (psc->pts_flags & PTS_FINISHED)
+ {
+ error = -EIO;
+ goto done;
+ }
+
+ /* Wait for more data. */
+ if (oflags & O_NONBLOCK)
+ {
+ error = -EWOULDBLOCK;
+ goto done;
+ }
+
+ /* Wake up users on the slave side. */
+ ttydisc_rint_done(tp);
+ error = cv_wait_sig(&psc->pts_inwait, tp->t_mtx);
+ if (error != 0)
+ goto done;
+ } while (iblen > 0);
+
+ if (uio->uio_resid == 0)
+ break;
+ tty_unlock(tp);
+ }
+
+done:
+ ttydisc_rint_done(tp);
+ tty_unlock(tp);
+
+ /*
+ * Don't account for the part of the buffer that we couldn't
+ * pass to the TTY.
+ */
+ uio->uio_resid += iblen;
+ return (error);
+}
+
+static int ptsdev_ioctl(struct lwp_tty *tp, rt_ubase_t cmd, void *data,
+ struct ucred *active_cred, int fflags,
+ struct rt_thread *td)
+{
+ struct pts_softc *psc = tty_softc(tp);
+ int error = 0, sig;
+
+ switch (cmd)
+ {
+#ifdef USING_BSD_IOCTL_EXT
+ case FIODTYPE:
+ *(int *)data = D_TTY;
+ return (0);
+#endif
+ case FIONBIO:
+ /* This device supports non-blocking operation. */
+ return (0);
+ case FIONREAD:
+ tty_lock(tp);
+ if (psc->pts_flags & PTS_FINISHED)
+ {
+ /* Force read() to be called. */
+ *(int *)data = 1;
+ }
+ else
+ {
+ *(int *)data = ttydisc_getc_poll(tp);
+ }
+ tty_unlock(tp);
+ return (0);
+#ifdef USING_BSD_IOCTL_EXT
+ case FIODGNAME:
+#ifdef COMPAT_FREEBSD32
+ case FIODGNAME_32:
+#endif
+ {
+ struct fiodgname_arg *fgn;
+ const char *p;
+ int i;
+
+ /* Reverse device name lookups, for ptsname() and ttyname(). */
+ fgn = data;
+ p = tty_devname(tp);
+ i = strlen(p) + 1;
+ if (i > fgn->len)
+ return -EINVAL;
+ return (copyout(p, fiodgname_buf_get_ptr(fgn, cmd), i));
+ }
+#endif
+ /*
+ * We need to implement TIOCGPGRP and TIOCGSID here again. When
+ * called on the pseudo-terminal master, it should not check if
+ * the terminal is the foreground terminal of the calling
+ * process.
+ *
+ * TIOCGETA is also implemented here. Various Linux PTY routines
+ * often call isatty(), which is implemented by tcgetattr().
+ */
+ case TIOCGETA:
+ /* Obtain terminal flags through tcgetattr(). */
+ tty_lock(tp);
+ *(struct termios *)data = tp->t_termios;
+ tty_unlock(tp);
+ return (0);
+ case TIOCSETAF:
+ case TIOCSETAW:
+ /*
+ * We must make sure we turn tcsetattr() calls of TCSAFLUSH and
+ * TCSADRAIN into something different. If an application would
+ * call TCSAFLUSH or TCSADRAIN on the master descriptor, it may
+ * deadlock waiting for all data to be read.
+ */
+ cmd = TIOCSETA;
+ break;
+ case TIOCGPTN:
+ /*
+ * Get the device unit number.
+ */
+ if (psc->pts_unit < 0)
+ return -ENOTTY;
+ *(unsigned int *)data = psc->pts_unit;
+ return (0);
+ case TIOCGPGRP:
+ /* Get the foreground process group ID. */
+ tty_lock(tp);
+ if (tp->t_pgrp != NULL)
+ *(int *)data = tp->t_pgrp->pgid;
+ else
+ *(int *)data = NO_PID;
+ tty_unlock(tp);
+ return (0);
+ case TIOCGSID:
+ /* Get the session leader process ID. */
+ tty_lock(tp);
+ if (tp->t_session == NULL)
+ error = -ENOTTY;
+ else
+ *(int *)data = tp->t_session->sid;
+ tty_unlock(tp);
+ return (error);
+#ifdef USING_BSD_IOCTL_EXT
+ case TIOCPTMASTER:
+ /* Yes, we are a pseudo-terminal master. */
+ return (0);
+#endif /* USING_BSD_IOCTL_EXT */
+ case TIOCSIG:
+ /* Signal the foreground process group. */
+ sig = *(int *)data;
+ if (sig < 1 || sig >= _LWP_NSIG)
+ return -EINVAL;
+
+ tty_lock(tp);
+ lwp_tty_signal_pgrp(tp, sig);
+ tty_unlock(tp);
+ return (0);
+ case TIOCPKT:
+ /* Enable/disable packet mode. */
+ tty_lock(tp);
+ if (*(int *)data)
+ psc->pts_flags |= PTS_PKT;
+ else
+ psc->pts_flags &= ~PTS_PKT;
+ tty_unlock(tp);
+ return (0);
+ }
+
+ /* Just redirect this ioctl to the slave device. */
+ tty_lock(tp);
+ error = tty_ioctl(tp, cmd, data, fflags, td);
+ tty_unlock(tp);
+ if (error == -ENOIOCTL)
+ error = -ENOTTY;
+
+ return error;
+}
+
+static int ptsdev_poll(struct lwp_tty *tp, struct rt_pollreq *req,
+ struct ucred *active_cred, struct rt_thread *td)
+{
+ struct pts_softc *psc = tty_softc(tp);
+ int revents = 0;
+ int events = req->_key;
+
+ tty_lock(tp);
+
+ if (psc->pts_flags & PTS_FINISHED)
+ {
+ /* Slave device is not opened. */
+ tty_unlock(tp);
+ return ((events & (POLLIN | POLLRDNORM)) | POLLHUP);
+ }
+
+ if (events & (POLLIN | POLLRDNORM))
+ {
+ /* See if we can getc something. */
+ if (ttydisc_getc_poll(tp) || (psc->pts_flags & PTS_PKT && psc->pts_pkt))
+ revents |= events & (POLLIN | POLLRDNORM);
+ }
+ if (events & (POLLOUT | POLLWRNORM))
+ {
+ /* See if we can rint something. */
+ if (ttydisc_rint_poll(tp))
+ revents |= events & (POLLOUT | POLLWRNORM);
+ }
+
+ /*
+ * No need to check for POLLHUP here. This device cannot be used
+ * as a callout device, which means we always have a carrier,
+ * because the master is.
+ */
+
+ if (revents == 0)
+ {
+ /*
+ * This code might look misleading, but the naming of
+ * poll events on this side is the opposite of the slave
+ * device.
+ */
+ if (events & (POLLIN | POLLRDNORM))
+ rt_poll_add(&psc->pts_outpoll, req);
+ if (events & (POLLOUT | POLLWRNORM))
+ rt_poll_add(&psc->pts_inpoll, req);
+ }
+
+ tty_unlock(tp);
+
+ return (revents);
+}
+
+#if USING_BSD_KQUEUE
+/*
+ * kqueue support.
+ */
+
+static void pts_kqops_read_detach(struct knote *kn)
+{
+ struct file *fp = kn->kn_fp;
+ struct lwp_tty *tp = fp->f_data;
+ struct pts_softc *psc = tty_softc(tp);
+
+ knlist_remove(&psc->pts_outpoll.si_note, kn, 0);
+}
+
+static int pts_kqops_read_event(struct knote *kn, long hint)
+{
+ struct file *fp = kn->kn_fp;
+ struct lwp_tty *tp = fp->f_data;
+ struct pts_softc *psc = tty_softc(tp);
+
+ if (psc->pts_flags & PTS_FINISHED)
+ {
+ kn->kn_flags |= EV_EOF;
+ return (1);
+ }
+ else
+ {
+ kn->kn_data = ttydisc_getc_poll(tp);
+ return (kn->kn_data > 0);
+ }
+}
+
+static void pts_kqops_write_detach(struct knote *kn)
+{
+ struct file *fp = kn->kn_fp;
+ struct lwp_tty *tp = fp->f_data;
+ struct pts_softc *psc = tty_softc(tp);
+
+ knlist_remove(&psc->pts_inpoll.si_note, kn, 0);
+}
+
+static int pts_kqops_write_event(struct knote *kn, long hint)
+{
+ struct file *fp = kn->kn_fp;
+ struct lwp_tty *tp = fp->f_data;
+ struct pts_softc *psc = tty_softc(tp);
+
+ if (psc->pts_flags & PTS_FINISHED)
+ {
+ kn->kn_flags |= EV_EOF;
+ return (1);
+ }
+ else
+ {
+ kn->kn_data = ttydisc_rint_poll(tp);
+ return (kn->kn_data > 0);
+ }
+}
+
+static struct filterops pts_kqops_read = {
+ .f_isfd = 1,
+ .f_detach = pts_kqops_read_detach,
+ .f_event = pts_kqops_read_event,
+};
+static struct filterops pts_kqops_write = {
+ .f_isfd = 1,
+ .f_detach = pts_kqops_write_detach,
+ .f_event = pts_kqops_write_event,
+};
+
+static int ptsdev_kqfilter(struct file *fp, struct knote *kn)
+{
+ struct lwp_tty *tp = fp->f_data;
+ struct pts_softc *psc = tty_softc(tp);
+ int error = 0;
+
+ tty_lock(tp);
+
+ switch (kn->kn_filter)
+ {
+ case EVFILT_READ:
+ kn->kn_fop = &pts_kqops_read;
+ knlist_add(&psc->pts_outpoll.si_note, kn, 1);
+ break;
+ case EVFILT_WRITE:
+ kn->kn_fop = &pts_kqops_write;
+ knlist_add(&psc->pts_inpoll.si_note, kn, 1);
+ break;
+ default:
+ error = EINVAL;
+ break;
+ }
+
+ tty_unlock(tp);
+ return (error);
+}
+#endif
+
+#if USING_BSD_STAT
+static int ptsdev_stat(struct file *fp, struct stat *sb,
+ struct ucred *active_cred)
+{
+ struct lwp_tty *tp = fp->f_data;
+#ifdef PTS_EXTERNAL
+ struct pts_softc *psc = tty_softc(tp);
+#endif /* PTS_EXTERNAL */
+ struct cdev *dev = tp->t_dev;
+
+ /*
+ * According to POSIX, we must implement an fstat(). This also
+ * makes this implementation compatible with Linux binaries,
+ * because Linux calls fstat() on the pseudo-terminal master to
+ * obtain st_rdev.
+ *
+ * XXX: POSIX also mentions we must fill in st_dev, but how?
+ */
+
+ bzero(sb, sizeof *sb);
+#ifdef PTS_EXTERNAL
+ if (psc->pts_cdev != NULL)
+ sb->st_ino = sb->st_rdev = dev2udev(psc->pts_cdev);
+ else
+#endif /* PTS_EXTERNAL */
+ sb->st_ino = sb->st_rdev = tty_udev(tp);
+
+ sb->st_atim = dev->si_atime;
+ sb->st_ctim = dev->si_ctime;
+ sb->st_mtim = dev->si_mtime;
+ sb->st_uid = dev->si_uid;
+ sb->st_gid = dev->si_gid;
+ sb->st_mode = dev->si_mode | S_IFCHR;
+
+ return (0);
+}
+#endif
+
+static int ptsdev_close(struct lwp_tty *tp, struct rt_thread *td)
+{
+ /* Deallocate TTY device. */
+ tty_lock(tp);
+ tty_rel_gone(tp);
+
+#ifdef USING_BSD_VNODE
+ /* TODO: consider the vnode operation on DFS */
+
+ /*
+ * Open of /dev/ptmx or /dev/ptyXX changes the type of file
+ * from DTYPE_VNODE to DTYPE_PTS. vn_open() increases vnode
+ * use count, we need to decrement it, and possibly do other
+ * required cleanup.
+ */
+ if (fp->f_vnode != NULL)
+ return (vnops.fo_close(fp, td));
+#endif /* USING_BSD_VNODE */
+
+ return 0;
+}
+
+#ifdef USING_BSD_KINFO
+static int ptsdev_fill_kinfo(struct file *fp, struct kinfo_file *kif,
+ struct filedesc *fdp)
+{
+ struct lwp_tty *tp;
+
+ kif->kf_type = KF_TYPE_PTS;
+ tp = fp->f_data;
+ kif->kf_un.kf_pts.kf_pts_dev = tty_udev(tp);
+ kif->kf_un.kf_pts.kf_pts_dev_freebsd11 =
+ kif->kf_un.kf_pts.kf_pts_dev; /* truncate */
+ strlcpy(kif->kf_path, tty_devname(tp), sizeof(kif->kf_path));
+ return (0);
+}
+#endif
+
+struct bsd_fileops bsd_ptsdev_methods = {
+ .fo_read = ptsdev_read,
+ .fo_write = ptsdev_write,
+ // .fo_truncate = invfo_truncate,
+ .fo_ioctl = ptsdev_ioctl,
+ .fo_poll = ptsdev_poll,
+ // .fo_kqfilter = ptsdev_kqfilter,
+ // .fo_stat = ptsdev_stat,
+ .fo_close = ptsdev_close,
+ // .fo_chmod = invfo_chmod,
+ // .fo_chown = invfo_chown,
+ // .fo_sendfile = invfo_sendfile,
+ // .fo_fill_kinfo = ptsdev_fill_kinfo,
+ .fo_flags = DFLAG_PASSABLE,
+};
+
+/*
+ * Driver-side hooks.
+ */
+
+static void ptsdrv_outwakeup(struct lwp_tty *tp)
+{
+ struct pts_softc *psc = tty_softc(tp);
+
+ cv_broadcast(&psc->pts_outwait);
+ rt_wqueue_wakeup_all(&psc->pts_outpoll, (void *)POLLIN);
+}
+
+static void ptsdrv_inwakeup(struct lwp_tty *tp)
+{
+ struct pts_softc *psc = tty_softc(tp);
+
+ cv_broadcast(&psc->pts_inwait);
+ rt_wqueue_wakeup_all(&psc->pts_inpoll, (void *)POLLOUT);
+}
+
+static int ptsdrv_open(struct lwp_tty *tp)
+{
+ struct pts_softc *psc = tty_softc(tp);
+
+ /* for ioctl(TIOCSPTLCK) */
+ if (psc->pts_flags & PTS_PTLOCKED)
+ return -EIO;
+
+ psc->pts_flags &= ~PTS_FINISHED;
+
+ return 0;
+}
+
+static void ptsdrv_close(struct lwp_tty *tp)
+{
+ struct pts_softc *psc = tty_softc(tp);
+
+ /* Wake up any blocked readers/writers. */
+ psc->pts_flags |= PTS_FINISHED;
+ ptsdrv_outwakeup(tp);
+ ptsdrv_inwakeup(tp);
+}
+
+static void ptsdrv_pktnotify(struct lwp_tty *tp, char event)
+{
+ struct pts_softc *psc = tty_softc(tp);
+
+ /*
+ * Clear conflicting flags.
+ */
+
+ switch (event)
+ {
+ case TIOCPKT_STOP:
+ psc->pts_pkt &= ~TIOCPKT_START;
+ break;
+ case TIOCPKT_START:
+ psc->pts_pkt &= ~TIOCPKT_STOP;
+ break;
+ case TIOCPKT_NOSTOP:
+ psc->pts_pkt &= ~TIOCPKT_DOSTOP;
+ break;
+ case TIOCPKT_DOSTOP:
+ psc->pts_pkt &= ~TIOCPKT_NOSTOP;
+ break;
+ }
+
+ psc->pts_pkt |= event;
+
+ /**
+ * Note: on smart, we don't wakeup master until it's willing to accept
+ * packet event. Because on poll, we setup POLLIN for PTS_PKT only. So There
+ * is a chance when we wakeup ipc but we can't wakeup user again. Since
+ * current wakeup will remove the wakequeue node on the meanwhile
+ */
+ if (psc->pts_flags & PTS_PKT)
+ ptsdrv_outwakeup(tp);
+}
+
+static void ptsdrv_free(void *softc)
+{
+ struct pts_softc *psc = softc;
+
+ /* Make device number available again. */
+ if (psc->pts_unit >= 0)
+ ptyfs_unregister_pts(psc->pts_master, psc->pts_unit);
+
+#ifdef USING_BSD_UCRED
+ chgptscnt(psc->pts_cred->cr_ruidinfo, -1, 0);
+ racct_sub_cred(psc->pts_cred, RACCT_NPTS, 1);
+ crfree(psc->pts_cred);
+#endif
+
+ rt_wqueue_wakeup_all(&psc->pts_inpoll, (void *)POLLHUP);
+ rt_wqueue_wakeup_all(&psc->pts_outpoll, (void *)POLLHUP);
+
+ rt_free(psc);
+}
+
+static struct lwp_ttydevsw pts_class = {
+ .tsw_flags = TF_NOPREFIX,
+ .tsw_outwakeup = ptsdrv_outwakeup,
+ .tsw_inwakeup = ptsdrv_inwakeup,
+ .tsw_open = ptsdrv_open,
+ .tsw_close = ptsdrv_close,
+ .tsw_pktnotify = ptsdrv_pktnotify,
+ .tsw_free = ptsdrv_free,
+};
+
+int pts_alloc(int fflags, struct rt_thread *td, struct dfs_file *ptm_file)
+{
+ int unit;
+ struct lwp_tty *tp;
+ struct pts_softc *psc;
+ char name_buf[DIRENT_NAME_MAX];
+ const char *rootpath;
+ rt_device_t ptmx_device = ptm_file->vnode->data;
+
+#ifdef USING_BSD_UCRED
+ struct rt_lwp *p = td->lwp;
+ int ok, error;
+ struct ucred *cred = td->td_ucred;
+#endif
+
+ /* Resource limiting. */
+#ifdef USING_BSD_UCRED
+ LWP_LOCK(p);
+ error = racct_add(p, RACCT_NPTS, 1);
+ if (error != 0)
+ {
+ LWP_UNLOCK(p);
+ return -EAGAIN;
+ }
+ ok = chgptscnt(cred->cr_ruidinfo, 1, lim_cur(td, RLIMIT_NPTS));
+ if (!ok)
+ {
+ racct_sub(p, RACCT_NPTS, 1);
+ LWP_UNLOCK(p);
+ return -EAGAIN;
+ }
+ LWP_UNLOCK(p);
+#endif
+
+ /* Allocate TTY and softc. */
+ psc = rt_calloc(1, sizeof(struct pts_softc));
+ cv_init(&psc->pts_inwait, "ptsin");
+ cv_init(&psc->pts_outwait, "ptsout");
+ rt_wqueue_init(&psc->pts_inpoll);
+ rt_wqueue_init(&psc->pts_outpoll);
+
+ psc->pts_master = ptmx_device;
+#ifdef USING_BSD_UCRED
+ psc->pts_cred = crhold(cred);
+#else
+ psc->pts_cred = 0;
+#endif
+
+ tp = lwp_tty_create(&pts_class, psc);
+ if (!tp)
+ {
+ rt_free(psc);
+ rt_condvar_detach(&psc->pts_inwait);
+ rt_condvar_detach(&psc->pts_outwait);
+ return -ENOMEM;
+ }
+
+ /* Try to allocate a new pts uint*/
+ unit = ptyfs_register_pts(ptmx_device, &tp->parent);
+ if (unit < 0)
+ {
+#ifdef USING_BSD_UCRED
+ racct_sub(p, RACCT_NPTS, 1);
+ chgptscnt(cred->cr_ruidinfo, -1, 0);
+#endif
+ lwp_tty_delete(tp);
+ return -EAGAIN;
+ }
+ psc->pts_unit = unit;
+
+ /* Expose the slave device as well. */
+#ifdef USING_BSD_UCRED
+ tty_makedev(tp, td->td_ucred, "pts/%u", psc->pts_unit);
+#else
+ rootpath = ptyfs_get_rootpath(ptmx_device);
+ RT_ASSERT(rootpath[strlen(rootpath) - 1] != '/');
+ snprintf(name_buf, DIRENT_NAME_MAX, "%s/%u", rootpath, psc->pts_unit);
+
+ /* setup the pts */
+ lwp_tty_register(tp, name_buf);
+
+ /* now this file operating on new pty */
+ ptm_file->vnode->data = tp;
+#endif
+
+ return 0;
+}
+
+void pts_set_lock(lwp_tty_t pts, rt_bool_t is_lock)
+{
+ struct pts_softc *psc = tty_softc(pts);
+ if (is_lock)
+ psc->pts_flags |= PTS_PTLOCKED;
+ else
+ psc->pts_flags &= ~PTS_PTLOCKED;
+}
+
+rt_bool_t pts_is_locked(lwp_tty_t pts)
+{
+ struct pts_softc *psc = tty_softc(pts);
+ return !!(psc->pts_flags & PTS_PTLOCKED);
+}
+
+int pts_get_pktmode(lwp_tty_t pts)
+{
+ struct pts_softc *psc = tty_softc(pts);
+ return !!(psc->pts_flags & PTS_PKT);
+}
diff --git a/components/lwp/terminal/freebsd/tty_ttydisc.c b/components/lwp/terminal/freebsd/tty_ttydisc.c
new file mode 100644
index 0000000000..932ef2ec00
--- /dev/null
+++ b/components/lwp/terminal/freebsd/tty_ttydisc.c
@@ -0,0 +1,1479 @@
+/*
+ * Copyright (c) 2006-2023, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2023-11-13 Shell init ver.
+ */
+#include "../bsd_porting.h"
+#include "../tty_config.h"
+#include "../tty_internal.h"
+#include "../terminal.h"
+
+#include <rtdef.h>
+
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2008 Ed Schouten <[email protected]>
+ * All rights reserved.
+ *
+ * Portions of this software were developed under sponsorship from Snow
+ * B.V., the Netherlands.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Standard TTYDISC `termios' line discipline.
+ */
+
+/* Statistics. */
+static rt_atomic_t tty_nin = 0;
+#ifdef USING_BSD_SYSCTL
+SYSCTL_ULONG(_kern, OID_AUTO, tty_nin, CTLFLAG_RD, &tty_nin, 0,
+ "Total amount of bytes received");
+#endif
+
+static rt_atomic_t tty_nout = 0;
+#ifdef USING_BSD_SYSCTL
+SYSCTL_ULONG(_kern, OID_AUTO, tty_nout, CTLFLAG_RD, &tty_nout, 0,
+ "Total amount of bytes transmitted");
+#endif
+
+/* termios comparison macro's. */
+#define CMP_CC(v, c) \
+ (tp->t_termios.c_cc[v] != _POSIX_VDISABLE && tp->t_termios.c_cc[v] == (c))
+#define CMP_FLAG(field, opt) (tp->t_termios.c_##field##flag & (opt))
+
+/* Characters that cannot be modified through c_cc. */
+#define CTAB '\t'
+#define CNL '\n'
+#define CCR '\r'
+
+/* Character is a control character. */
+#define CTL_VALID(c) ((c) == 0x7f || (unsigned char)(c) < 0x20)
+/* Control character should be processed on echo. */
+#define CTL_ECHO(c, q) \
+ (!(q) && ((c) == CERASE2 || (c) == CTAB || (c) == CNL || (c) == CCR))
+/* Control character should be printed using ^X notation. */
+#define CTL_PRINT(c, q) \
+ ((c) == 0x7f || \
+ ((unsigned char)(c) < 0x20 && ((q) || ((c) != CTAB && (c) != CNL))))
+/* Character is whitespace. */
+#define CTL_WHITE(c) ((c) == ' ' || (c) == CTAB)
+/* Character is alphanumeric. */
+#define CTL_ALNUM(c) \
+ (((c) >= '0' && (c) <= '9') || ((c) >= 'a' && (c) <= 'z') || \
+ ((c) >= 'A' && (c) <= 'Z'))
+/* Character is UTF8-encoded. */
+#define CTL_UTF8(c) (!!((c)&0x80))
+/* Character is a UTF8 continuation byte. */
+#define CTL_UTF8_CONT(c) (((c)&0xc0) == 0x80)
+
+#define TTY_STACKBUF 256
+#define UTF8_STACKBUF 4
+
+void ttydisc_open(struct lwp_tty *tp)
+{
+ ttydisc_optimize(tp);
+}
+
+void ttydisc_close(struct lwp_tty *tp)
+{
+ /* Clean up our flags when leaving the discipline. */
+ tp->t_flags &= ~(TF_STOPPED | TF_HIWAT | TF_ZOMBIE);
+ tp->t_termios.c_lflag &= ~FLUSHO;
+
+ /*
+ * POSIX states that we must drain output and flush input on
+ * last close. Draining has already been done if possible.
+ */
+ tty_flush(tp, FREAD | FWRITE);
+
+#ifdef USING_BSD_HOOK
+ if (ttyhook_hashook(tp, close))
+ ttyhook_close(tp);
+#endif
+}
+
+static int ttydisc_read_canonical(struct lwp_tty *tp, struct uio *uio,
+ int ioflag)
+{
+ char breakc[4] = {CNL}; /* enough to hold \n, VEOF and VEOL. */
+ int error;
+ size_t clen, flen = 0, n = 1;
+ unsigned char lastc = _POSIX_VDISABLE;
+
+#define BREAK_ADD(c) \
+ do \
+ { \
+ if (tp->t_termios.c_cc[c] != _POSIX_VDISABLE) \
+ breakc[n++] = tp->t_termios.c_cc[c]; \
+ } while (0)
+ /* Determine which characters we should trigger on. */
+ BREAK_ADD(VEOF);
+ BREAK_ADD(VEOL);
+#undef BREAK_ADD
+ breakc[n] = '\0';
+
+ do
+ {
+ error = tty_wait_background(tp, curthread, SIGTTIN);
+ if (error)
+ return error;
+
+ /*
+ * Quite a tricky case: unlike the old TTY
+ * implementation, this implementation copies data back
+ * to userspace in large chunks. Unfortunately, we can't
+ * calculate the line length on beforehand if it crosses
+ * ttyinq_block boundaries, because multiple reads could
+ * then make this code read beyond the newline.
+ *
+ * This is why we limit the read to:
+ * - The size the user has requested
+ * - The blocksize (done in tty_inq.c)
+ * - The amount of bytes until the newline
+ *
+ * This causes the line length to be recalculated after
+ * each block has been copied to userspace. This will
+ * cause the TTY layer to return data in chunks using
+ * the blocksize (except the first and last blocks).
+ */
+ clen =
+ ttyinq_findchar(&tp->t_inq, breakc, uio->uio_resid, (char *)&lastc);
+
+ /* No more data. */
+ if (clen == 0)
+ {
+ if (tp->t_flags & TF_ZOMBIE)
+ return 0;
+ else if (ioflag & IO_NDELAY)
+ return -EWOULDBLOCK;
+
+ error = tty_wait(tp, &tp->t_inwait);
+ if (error)
+ return error;
+ continue;
+ }
+
+ /* Don't send the EOF char back to userspace. */
+ if (CMP_CC(VEOF, lastc))
+ flen = 1;
+
+ MPASS(flen <= clen);
+
+ /* Read and throw away the EOF character. */
+ error = ttyinq_read_uio(&tp->t_inq, tp, uio, clen, flen);
+ if (error)
+ return error;
+
+ /** Notes: proceed when read buf frees and not reaching breaks */
+ } while (uio->uio_resid > 0 && lastc == _POSIX_VDISABLE);
+
+ return 0;
+}
+
+static int ttydisc_read_raw_no_timer(struct lwp_tty *tp, struct uio *uio,
+ int ioflag)
+{
+ size_t vmin = tp->t_termios.c_cc[VMIN];
+ ssize_t oresid = uio->uio_resid;
+ int error;
+
+ MPASS(tp->t_termios.c_cc[VTIME] == 0);
+
+ /*
+ * This routine implements the easy cases of read()s while in
+ * non-canonical mode, namely case B and D, where we don't have
+ * any timers at all.
+ */
+
+ for (;;)
+ {
+ error = tty_wait_background(tp, curthread, SIGTTIN);
+ if (error)
+ return error;
+
+ error = ttyinq_read_uio(&tp->t_inq, tp, uio, uio->uio_resid, 0);
+ if (error)
+ return error;
+ if (uio->uio_resid == 0 || (oresid - uio->uio_resid) >= vmin)
+ return 0;
+
+ /* We have to wait for more. */
+ if (tp->t_flags & TF_ZOMBIE)
+ return 0;
+ else if (ioflag & IO_NDELAY)
+ return -EWOULDBLOCK;
+
+ error = tty_wait(tp, &tp->t_inwait);
+ if (error)
+ return error;
+ }
+}
+
+static int ttydisc_read_raw_read_timer(struct lwp_tty *tp, struct uio *uio,
+ int ioflag, int oresid)
+{
+ size_t vmin = MAX(tp->t_termios.c_cc[VMIN], 1);
+ unsigned int vtime = tp->t_termios.c_cc[VTIME];
+ struct timeval end, now, left;
+ int error, hz;
+
+ MPASS(tp->t_termios.c_cc[VTIME] != 0);
+
+ /* Determine when the read should be expired. */
+ end.tv_sec = vtime / 10;
+ end.tv_usec = (vtime % 10) * 100000;
+ getmicrotime(&now);
+ timevaladd(&end, &now);
+
+ for (;;)
+ {
+ error = tty_wait_background(tp, curthread, SIGTTIN);
+ if (error)
+ return error;
+
+ error = ttyinq_read_uio(&tp->t_inq, tp, uio, uio->uio_resid, 0);
+ if (error)
+ return error;
+ if (uio->uio_resid == 0 || (oresid - uio->uio_resid) >= vmin)
+ return 0;
+
+ /* Calculate how long we should wait. */
+ getmicrotime(&now);
+ if (timevalcmp(&now, &end, >))
+ return 0;
+ left = end;
+ timevalsub(&left, &now);
+ hz = tvtohz(&left);
+
+ /*
+ * We have to wait for more. If the timer expires, we
+ * should return a 0-byte read.
+ */
+ if (tp->t_flags & TF_ZOMBIE)
+ return 0;
+ else if (ioflag & IO_NDELAY)
+ return -EWOULDBLOCK;
+
+ error = tty_timedwait(tp, &tp->t_inwait, hz);
+ if (error)
+ return (error == -EWOULDBLOCK ? 0 : error);
+ }
+
+ return 0;
+}
+
+static int ttydisc_read_raw_interbyte_timer(struct lwp_tty *tp, struct uio *uio,
+ int ioflag)
+{
+ size_t vmin = tp->t_termios.c_cc[VMIN];
+ ssize_t oresid = uio->uio_resid;
+ int error;
+
+ MPASS(tp->t_termios.c_cc[VMIN] != 0);
+ MPASS(tp->t_termios.c_cc[VTIME] != 0);
+
+ /*
+ * When using the interbyte timer, the timer should be started
+ * after the first byte has been received. We just call into the
+ * generic read timer code after we've received the first byte.
+ */
+
+ for (;;)
+ {
+ error = tty_wait_background(tp, curthread, SIGTTIN);
+ if (error)
+ return error;
+
+ error = ttyinq_read_uio(&tp->t_inq, tp, uio, uio->uio_resid, 0);
+ if (error)
+ return error;
+ if (uio->uio_resid == 0 || (oresid - uio->uio_resid) >= vmin)
+ return 0;
+
+ /*
+ * Not enough data, but we did receive some, which means
+ * we'll now start using the interbyte timer.
+ */
+ if (oresid != uio->uio_resid)
+ break;
+
+ /* We have to wait for more. */
+ if (tp->t_flags & TF_ZOMBIE)
+ return 0;
+ else if (ioflag & IO_NDELAY)
+ return -EWOULDBLOCK;
+
+ error = tty_wait(tp, &tp->t_inwait);
+ if (error)
+ return error;
+ }
+
+ return ttydisc_read_raw_read_timer(tp, uio, ioflag, oresid);
+}
+
+int ttydisc_read(struct lwp_tty *tp, struct uio *uio, int ioflag)
+{
+ int error;
+
+ tty_assert_locked(tp);
+
+ if (uio->uio_resid == 0)
+ return 0;
+
+ if (CMP_FLAG(l, ICANON))
+ error = ttydisc_read_canonical(tp, uio, ioflag);
+ else if (tp->t_termios.c_cc[VTIME] == 0)
+ error = ttydisc_read_raw_no_timer(tp, uio, ioflag);
+ else if (tp->t_termios.c_cc[VMIN] == 0)
+ error = ttydisc_read_raw_read_timer(tp, uio, ioflag, uio->uio_resid);
+ else
+ error = ttydisc_read_raw_interbyte_timer(tp, uio, ioflag);
+
+ if (ttyinq_bytesleft(&tp->t_inq) >= tp->t_inlow ||
+ ttyinq_bytescanonicalized(&tp->t_inq) == 0)
+ {
+ /* Unset the input watermark when we've got enough space. */
+ tty_hiwat_in_unblock(tp);
+ }
+
+ return error;
+}
+
+/* return the offset to special character from string start */
+rt_inline unsigned int ttydisc_findchar(const char *obstart, unsigned int oblen)
+{
+ const char *c = obstart;
+
+ while (oblen--)
+ {
+ if (CTL_VALID(*c))
+ break;
+ c++;
+ }
+
+ return (c - obstart);
+}
+
+static int ttydisc_write_oproc(struct lwp_tty *tp, char c)
+{
+ unsigned int scnt, error;
+
+ MPASS(CMP_FLAG(o, OPOST));
+ MPASS(CTL_VALID(c));
+
+#define PRINT_NORMAL() ttyoutq_write_nofrag(&tp->t_outq, &c, 1)
+ switch (c)
+ {
+ case CEOF:
+ /* End-of-text dropping. */
+ if (CMP_FLAG(o, ONOEOT))
+ return 0;
+ return PRINT_NORMAL();
+
+ case CERASE2:
+ /* Handle backspace to fix tab expansion. */
+ if (PRINT_NORMAL() != 0)
+ return (-1);
+ if (tp->t_column > 0)
+ tp->t_column--;
+ return 0;
+
+ case CTAB:
+ /* Tab expansion. */
+ scnt = 8 - (tp->t_column & 7);
+ if (CMP_FLAG(o, TAB3))
+ {
+ error = ttyoutq_write_nofrag(&tp->t_outq, " ", scnt);
+ }
+ else
+ {
+ error = PRINT_NORMAL();
+ }
+ if (error)
+ return (-1);
+
+ tp->t_column += scnt;
+ MPASS((tp->t_column % 8) == 0);
+ return 0;
+
+ case CNL:
+ /* Newline conversion. */
+ if (CMP_FLAG(o, ONLCR))
+ {
+ /* Convert \n to \r\n. */
+ error = ttyoutq_write_nofrag(&tp->t_outq, "\r\n", 2);
+ }
+ else
+ {
+ error = PRINT_NORMAL();
+ }
+ if (error)
+ return (-1);
+
+ if (CMP_FLAG(o, ONLCR | ONLRET))
+ {
+ tp->t_column = tp->t_writepos = 0;
+ ttyinq_reprintpos_set(&tp->t_inq);
+ }
+ return 0;
+
+ case CCR:
+ /* Carriage return to newline conversion. */
+ if (CMP_FLAG(o, OCRNL))
+ c = CNL;
+ /* Omit carriage returns on column 0. */
+ if (CMP_FLAG(o, ONOCR) && tp->t_column == 0)
+ return 0;
+ if (PRINT_NORMAL() != 0)
+ return (-1);
+
+ tp->t_column = tp->t_writepos = 0;
+ ttyinq_reprintpos_set(&tp->t_inq);
+ return 0;
+ }
+
+ /*
+ * Invisible control character. Print it, but don't
+ * increase the column count.
+ */
+ return PRINT_NORMAL();
+#undef PRINT_NORMAL
+}
+
+/*
+ * Just like the old TTY implementation, we need to copy data in chunks
+ * into a temporary buffer. One of the reasons why we need to do this,
+ * is because output processing (only TAB3 though) may allow the buffer
+ * to grow eight times.
+ */
+int ttydisc_write(struct lwp_tty *tp, struct uio *uio, int ioflag)
+{
+ char ob[TTY_STACKBUF];
+ char *obstart;
+ int error = 0;
+ unsigned int oblen = 0;
+
+ tty_assert_locked(tp);
+
+ if (tp->t_flags & TF_ZOMBIE)
+ return -EIO;
+
+ /*
+ * We don't need to check whether the process is the foreground
+ * process group or if we have a carrier. This is already done
+ * in ttydev_write().
+ */
+
+ while (uio->uio_resid > 0)
+ {
+ unsigned int nlen;
+
+ MPASS(oblen == 0);
+
+ if (CMP_FLAG(l, FLUSHO))
+ {
+ uio->uio_offset += uio->uio_resid;
+ uio->uio_resid = 0;
+ return 0;
+ }
+
+ /* Step 1: read data. */
+ obstart = ob;
+ nlen = MIN(uio->uio_resid, sizeof ob);
+ tty_unlock(tp);
+ error = uiomove(ob, nlen, uio);
+ tty_lock(tp);
+ if (error != 0)
+ break;
+ oblen = nlen;
+
+ if (tty_gone(tp))
+ {
+ error = -ENXIO;
+ break;
+ }
+
+ MPASS(oblen > 0);
+
+ /* Step 2: process data. */
+ do
+ {
+ unsigned int plen, wlen;
+
+ if (CMP_FLAG(l, FLUSHO))
+ {
+ uio->uio_offset += uio->uio_resid;
+ uio->uio_resid = 0;
+ return 0;
+ }
+
+ /* Search for special characters for post processing. */
+ if (CMP_FLAG(o, OPOST))
+ {
+ plen = ttydisc_findchar(obstart, oblen);
+ }
+ else
+ {
+ plen = oblen;
+ }
+
+ if (plen == 0)
+ {
+ /*
+ * We're going to process a character
+ * that needs processing
+ */
+ if (ttydisc_write_oproc(tp, *obstart) == 0)
+ {
+ obstart++;
+ oblen--;
+
+ tp->t_writepos = tp->t_column;
+ ttyinq_reprintpos_set(&tp->t_inq);
+ continue;
+ }
+ }
+ else
+ {
+ /* We're going to write regular data. */
+ wlen = ttyoutq_write(&tp->t_outq, obstart, plen);
+ obstart += wlen;
+ oblen -= wlen;
+ tp->t_column += wlen;
+
+ tp->t_writepos = tp->t_column;
+ ttyinq_reprintpos_set(&tp->t_inq);
+
+ if (wlen == plen)
+ continue;
+ }
+
+ /* Watermark reached. Try to sleep. */
+ tp->t_flags |= TF_HIWAT_OUT;
+
+ if (ioflag & IO_NDELAY)
+ {
+ error = EWOULDBLOCK;
+ goto done;
+ }
+
+ /*
+ * The driver may write back the data
+ * synchronously. Be sure to check the high
+ * water mark before going to sleep.
+ */
+ ttydevsw_outwakeup(tp);
+ if ((tp->t_flags & TF_HIWAT_OUT) == 0)
+ continue;
+
+ error = tty_wait(tp, &tp->t_outwait);
+ if (error)
+ goto done;
+
+ if (tp->t_flags & TF_ZOMBIE)
+ {
+ error = EIO;
+ goto done;
+ }
+ } while (oblen > 0);
+ }
+
+done:
+ if (!tty_gone(tp))
+ ttydevsw_outwakeup(tp);
+
+ /*
+ * Add the amount of bytes that we didn't process back to the
+ * uio counters. We need to do this to make sure write() doesn't
+ * count the bytes we didn't store in the queue.
+ */
+ uio->uio_resid += oblen;
+ return error;
+}
+
+/* set optimize flags if possible */
+void ttydisc_optimize(struct lwp_tty *tp)
+{
+ tty_assert_locked(tp);
+
+ if (ttyhook_hashook(tp, rint_bypass))
+ {
+ tp->t_flags |= TF_BYPASS;
+ }
+ else if (ttyhook_hashook(tp, rint))
+ {
+ tp->t_flags &= ~TF_BYPASS;
+ }
+ else if (!CMP_FLAG(i, ICRNL | IGNCR | IMAXBEL | INLCR | ISTRIP | IXON) &&
+ (!CMP_FLAG(i, BRKINT) || CMP_FLAG(i, IGNBRK)) &&
+ (!CMP_FLAG(i, PARMRK) ||
+ CMP_FLAG(i, IGNPAR | IGNBRK) == (IGNPAR | IGNBRK)) &&
+ !CMP_FLAG(l, ECHO | ICANON | IEXTEN | ISIG | PENDIN))
+ {
+ tp->t_flags |= TF_BYPASS;
+ }
+ else
+ {
+ tp->t_flags &= ~TF_BYPASS;
+ }
+}
+
+void ttydisc_modem(struct lwp_tty *tp, int open)
+{
+ tty_assert_locked(tp);
+
+ if (open)
+ cv_broadcast(&tp->t_dcdwait);
+
+ /*
+ * Ignore modem status lines when CLOCAL is turned on, but don't
+ * enter the zombie state when the TTY isn't opened, because
+ * that would cause the TTY to be in zombie state after being
+ * opened.
+ */
+ if (!tty_opened(tp) || CMP_FLAG(c, CLOCAL))
+ return;
+
+ if (open == 0)
+ {
+ /*
+ * Lost carrier.
+ */
+ tp->t_flags |= TF_ZOMBIE;
+
+ lwp_tty_signal_sessleader(tp, SIGHUP);
+ tty_flush(tp, FREAD | FWRITE);
+ }
+ else
+ {
+ /*
+ * Carrier is back again.
+ */
+
+ /* XXX: what should we do here? */
+ }
+}
+
+static int ttydisc_echo_force(struct lwp_tty *tp, char c, int quote)
+{
+ if (CMP_FLAG(l, FLUSHO))
+ return 0;
+
+ if (CMP_FLAG(o, OPOST) && CTL_ECHO(c, quote))
+ {
+ /*
+ * Only perform postprocessing when OPOST is turned on
+ * and the character is an unquoted BS/TB/NL/CR.
+ */
+ return ttydisc_write_oproc(tp, c);
+ }
+ else if (CMP_FLAG(l, ECHOCTL) && CTL_PRINT(c, quote))
+ {
+ /*
+ * Only use ^X notation when ECHOCTL is turned on and
+ * we've got an quoted control character.
+ *
+ * Print backspaces when echoing an end-of-file.
+ */
+ char ob[4] = "^?\b\b";
+
+ /* Print ^X notation. */
+ if (c != 0x7f)
+ ob[1] = c + 'A' - 1;
+
+ if (!quote && CMP_CC(VEOF, c))
+ {
+ return ttyoutq_write_nofrag(&tp->t_outq, ob, 4);
+ }
+ else
+ {
+ tp->t_column += 2;
+ return ttyoutq_write_nofrag(&tp->t_outq, ob, 2);
+ }
+ }
+ else
+ {
+ /* Can just be printed. */
+ tp->t_column++;
+ return ttyoutq_write_nofrag(&tp->t_outq, &c, 1);
+ }
+}
+
+static int ttydisc_echo(struct lwp_tty *tp, char c, int quote)
+{
+ /*
+ * Only echo characters when ECHO is turned on, or ECHONL when
+ * the character is an unquoted newline.
+ */
+ if (!CMP_FLAG(l, ECHO) && (!CMP_FLAG(l, ECHONL) || c != CNL || quote))
+ return 0;
+
+ return ttydisc_echo_force(tp, c, quote);
+}
+
+static void ttydisc_reprint_char(void *d, char c, int quote)
+{
+ struct lwp_tty *tp = d;
+
+ ttydisc_echo(tp, c, quote);
+}
+
+static void ttydisc_reprint(struct lwp_tty *tp)
+{
+ cc_t c;
+
+ /* Print ^R\n, followed by the line. */
+ c = tp->t_termios.c_cc[VREPRINT];
+ if (c != _POSIX_VDISABLE)
+ ttydisc_echo(tp, c, 0);
+ ttydisc_echo(tp, CNL, 0);
+ ttyinq_reprintpos_reset(&tp->t_inq);
+
+ ttyinq_line_iterate_from_linestart(&tp->t_inq, ttydisc_reprint_char, tp);
+}
+
+struct ttydisc_recalc_length
+{
+ struct lwp_tty *tp;
+ unsigned int curlen;
+};
+
+static void ttydisc_recalc_charlength(void *d, char c, int quote)
+{
+ struct ttydisc_recalc_length *data = d;
+ struct lwp_tty *tp = data->tp;
+
+ if (CTL_PRINT(c, quote))
+ {
+ if (CMP_FLAG(l, ECHOCTL))
+ data->curlen += 2;
+ }
+ else if (c == CTAB)
+ {
+ data->curlen += 8 - (data->curlen & 7);
+ }
+ else
+ {
+ data->curlen++;
+ }
+}
+
+static unsigned int ttydisc_recalc_linelength(struct lwp_tty *tp)
+{
+ struct ttydisc_recalc_length data = {tp, tp->t_writepos};
+
+ ttyinq_line_iterate_from_reprintpos(&tp->t_inq, ttydisc_recalc_charlength,
+ &data);
+ return (data.curlen);
+}
+
+static int ttydisc_rubchar(struct lwp_tty *tp)
+{
+ char c;
+ int quote;
+ unsigned int prevpos, tablen;
+
+ if (ttyinq_peekchar(&tp->t_inq, &c, &quote) != 0)
+ return (-1);
+ ttyinq_unputchar(&tp->t_inq);
+
+ if (CMP_FLAG(l, ECHO))
+ {
+ /*
+ * Remove the character from the screen. This is even
+ * safe for characters that span multiple characters
+ * (tabs, quoted, etc).
+ */
+ if (tp->t_writepos >= tp->t_column)
+ {
+ /* Retype the sentence. */
+ ttydisc_reprint(tp);
+ }
+ else if (CMP_FLAG(l, ECHOE))
+ {
+ if (CTL_PRINT(c, quote))
+ {
+ /* Remove ^X formatted chars. */
+ if (CMP_FLAG(l, ECHOCTL))
+ {
+ tp->t_column -= 2;
+ ttyoutq_write_nofrag(&tp->t_outq, "\b\b \b\b", 6);
+ }
+ }
+ else if (c == ' ')
+ {
+ /* Space character needs no rubbing. */
+ tp->t_column -= 1;
+ ttyoutq_write_nofrag(&tp->t_outq, "\b", 1);
+ }
+ else if (c == CTAB)
+ {
+ /*
+ * Making backspace work with tabs is
+ * quite hard. Recalculate the length of
+ * this character and remove it.
+ *
+ * Because terminal settings could be
+ * changed while the line is being
+ * inserted, the calculations don't have
+ * to be correct. Make sure we keep the
+ * tab length within proper bounds.
+ */
+ prevpos = ttydisc_recalc_linelength(tp);
+ if (prevpos >= tp->t_column)
+ tablen = 1;
+ else
+ tablen = tp->t_column - prevpos;
+ if (tablen > 8)
+ tablen = 8;
+
+ tp->t_column = prevpos;
+ ttyoutq_write_nofrag(&tp->t_outq, "\b\b\b\b\b\b\b\b", tablen);
+ return 0;
+ }
+#ifdef USING_BSD_UTF8
+ else if ((tp->t_termios.c_iflag & IUTF8) != 0 && CTL_UTF8(c))
+ {
+ uint8_t bytes[UTF8_STACKBUF] = {0};
+ int curidx = UTF8_STACKBUF - 1, cwidth = 1, nb = 0;
+ teken_char_t codepoint;
+
+ /* Save current byte. */
+ bytes[curidx] = c;
+ curidx--;
+ nb++;
+ /* Loop back through inq until we hit the
+ * leading byte. */
+ while (CTL_UTF8_CONT(c) && nb < UTF8_STACKBUF)
+ {
+ /*
+ * Check if we've reached the beginning
+ * of the line.
+ */
+ if (ttyinq_peekchar(&tp->t_inq, &c, &quote) != 0)
+ break;
+ ttyinq_unputchar(&tp->t_inq);
+ bytes[curidx] = c;
+ curidx--;
+ nb++;
+ }
+ /*
+ * Shift array so that the leading
+ * byte ends up at idx 0.
+ */
+ if (nb < UTF8_STACKBUF)
+ memmove(&bytes[0], &bytes[curidx + 1],
+ nb * sizeof(uint8_t));
+ /* Check for malformed UTF8 characters. */
+ if (nb == UTF8_STACKBUF && CTL_UTF8_CONT(bytes[0]))
+ {
+ /*
+ * Place all bytes back into the inq and
+ * delete the last byte only.
+ */
+ ttyinq_write(&tp->t_inq, bytes, UTF8_STACKBUF, 0);
+ ttyinq_unputchar(&tp->t_inq);
+ }
+ else
+ {
+ /* Find codepoint and width. */
+ codepoint = teken_utf8_bytes_to_codepoint(bytes, nb);
+ if (codepoint == TEKEN_UTF8_INVALID_CODEPOINT ||
+ (cwidth = teken_wcwidth(codepoint)) == -1)
+ {
+ /*
+ * Place all bytes back into the
+ * inq and fall back to
+ * default behaviour.
+ */
+ cwidth = 1;
+ ttyinq_write(&tp->t_inq, bytes, nb, 0);
+ ttyinq_unputchar(&tp->t_inq);
+ }
+ }
+ tp->t_column -= cwidth;
+ /*
+ * Delete character by punching
+ * 'cwidth' spaces over it.
+ */
+ if (cwidth == 1)
+ ttyoutq_write_nofrag(&tp->t_outq, "\b \b", 3);
+ else if (cwidth == 2)
+ ttyoutq_write_nofrag(&tp->t_outq, "\b\b \b\b", 6);
+ }
+#endif
+ else
+ {
+ /*
+ * Remove a regular character by
+ * punching a space over it.
+ */
+ tp->t_column -= 1;
+ ttyoutq_write_nofrag(&tp->t_outq, "\b \b", 3);
+ }
+ }
+ else
+ {
+ /* Don't print spaces. */
+ ttydisc_echo(tp, tp->t_termios.c_cc[VERASE], 0);
+ }
+ }
+
+ return 0;
+}
+
+static void ttydisc_rubword(struct lwp_tty *tp)
+{
+ char c;
+ int quote;
+#ifdef ALTWERASE
+ int alnum;
+#endif
+
+ /* Strip whitespace first. */
+ for (;;)
+ {
+ if (ttyinq_peekchar(&tp->t_inq, &c, &quote) != 0)
+ return;
+ if (!CTL_WHITE(c))
+ break;
+ ttydisc_rubchar(tp);
+ }
+
+ /*
+ * Record whether the last character from the previous iteration
+ * was alphanumeric or not. We need this to implement ALTWERASE.
+ */
+#ifdef ALTWERASE
+ alnum = CTL_ALNUM(c);
+#endif
+ for (;;)
+ {
+ ttydisc_rubchar(tp);
+
+ if (ttyinq_peekchar(&tp->t_inq, &c, &quote) != 0)
+ return;
+ if (CTL_WHITE(c))
+ return;
+#ifdef ALTWERASE
+ if (CMP_FLAG(l, ALTWERASE) && CTL_ALNUM(c) != alnum)
+ return;
+#endif
+ }
+}
+
+int ttydisc_rint(struct lwp_tty *tp, char c, int flags)
+{
+ int signal, quote = 0;
+ char ob[3] = {0xff, 0x00};
+ size_t ol;
+
+ tty_assert_locked(tp);
+
+ rt_atomic_add(&tty_nin, 1);
+
+#ifdef USING_BSD_HOOK
+ if (ttyhook_hashook(tp, rint))
+ return ttyhook_rint(tp, c, flags);
+#endif
+
+ if (tp->t_flags & TF_BYPASS)
+ goto processed;
+
+ if (flags)
+ {
+ if (flags & TRE_BREAK)
+ {
+ if (CMP_FLAG(i, IGNBRK))
+ {
+ /* Ignore break characters. */
+ return 0;
+ }
+ else if (CMP_FLAG(i, BRKINT))
+ {
+ /* Generate SIGINT on break. */
+ tty_flush(tp, FREAD | FWRITE);
+ lwp_tty_signal_pgrp(tp, SIGINT);
+ return 0;
+ }
+ else
+ {
+ /* Just print it. */
+ goto parmrk;
+ }
+ }
+ else if (flags & TRE_FRAMING ||
+ (flags & TRE_PARITY && CMP_FLAG(i, INPCK)))
+ {
+ if (CMP_FLAG(i, IGNPAR))
+ {
+ /* Ignore bad characters. */
+ return 0;
+ }
+ else
+ {
+ /* Just print it. */
+ goto parmrk;
+ }
+ }
+ }
+
+ /* Allow any character to perform a wakeup. */
+ if (CMP_FLAG(i, IXANY))
+ {
+ tp->t_flags &= ~TF_STOPPED;
+ tp->t_termios.c_lflag &= ~FLUSHO; /* FLUSHO: Output is being flushed */
+ }
+
+ /* Remove the top bit. */
+ if (CMP_FLAG(i, ISTRIP))
+ c &= ~0x80;
+
+ /* Skip input processing when we want to print it literally. */
+ if (tp->t_flags & TF_LITERAL)
+ {
+ tp->t_flags &= ~TF_LITERAL;
+ quote = 1;
+ goto processed;
+ }
+
+ /* Special control characters that are implementation dependent. */
+ if (CMP_FLAG(l, IEXTEN))
+ {
+ /* Accept the next character as literal. */
+ if (CMP_CC(VLNEXT, c))
+ {
+ if (CMP_FLAG(l, ECHO))
+ {
+ if (CMP_FLAG(l, ECHOE))
+ ttyoutq_write_nofrag(&tp->t_outq, "^\b", 2);
+ else
+ ttydisc_echo(tp, c, 0);
+ }
+ tp->t_flags |= TF_LITERAL;
+ return 0;
+ }
+ /* Discard processing */
+ if (CMP_CC(VDISCARD, c))
+ {
+ if (CMP_FLAG(l, FLUSHO))
+ {
+ tp->t_termios.c_lflag &= ~FLUSHO;
+ }
+ else
+ {
+ tty_flush(tp, FWRITE);
+ ttydisc_echo(tp, c, 0);
+ if (tp->t_inq.ti_end > 0)
+ ttydisc_reprint(tp);
+ tp->t_termios.c_lflag |= FLUSHO;
+ }
+ }
+ }
+
+ /*
+ * Handle signal processing.
+ */
+ if (CMP_FLAG(l, ISIG))
+ {
+#ifdef USING_BSD_SIGINFO
+ if (CMP_FLAG(l, ICANON | IEXTEN) == (ICANON | IEXTEN))
+ {
+ if (CMP_CC(VSTATUS, c))
+ {
+ tty_signal_pgrp(tp, SIGINFO);
+ return 0;
+ }
+ }
+#endif /* USING_BSD_SIGINFO */
+
+ /*
+ * When compared to the old implementation, this
+ * implementation also flushes the output queue. POSIX
+ * is really brief about this, but does makes us assume
+ * we have to do so.
+ */
+ signal = 0;
+ if (CMP_CC(VINTR, c))
+ {
+ signal = SIGINT;
+ }
+ else if (CMP_CC(VQUIT, c))
+ {
+ signal = SIGQUIT;
+ }
+ else if (CMP_CC(VSUSP, c))
+ {
+ signal = SIGTSTP;
+ }
+
+ if (signal != 0)
+ {
+ /*
+ * Echo the character before signalling the
+ * processes.
+ */
+ if (!CMP_FLAG(l, NOFLSH))
+ tty_flush(tp, FREAD | FWRITE);
+ ttydisc_echo(tp, c, 0);
+ lwp_tty_signal_pgrp(tp, signal);
+ return 0;
+ }
+ }
+
+ /*
+ * Handle start/stop characters.
+ */
+ if (CMP_FLAG(i, IXON))
+ {
+ if (CMP_CC(VSTOP, c))
+ {
+ /* Stop it if we aren't stopped yet. */
+ if ((tp->t_flags & TF_STOPPED) == 0)
+ {
+ tp->t_flags |= TF_STOPPED;
+ return 0;
+ }
+ /*
+ * Fallthrough:
+ * When VSTART == VSTOP, we should make this key
+ * toggle it.
+ */
+ if (!CMP_CC(VSTART, c))
+ return 0;
+ }
+ if (CMP_CC(VSTART, c))
+ {
+ tp->t_flags &= ~TF_STOPPED;
+ return 0;
+ }
+ }
+
+ /* Conversion of CR and NL. */
+ switch (c)
+ {
+ case CCR:
+ if (CMP_FLAG(i, IGNCR))
+ return 0;
+ if (CMP_FLAG(i, ICRNL))
+ c = CNL;
+ break;
+ case CNL:
+ if (CMP_FLAG(i, INLCR))
+ c = CCR;
+ break;
+ }
+
+ /* Canonical line editing. */
+ if (CMP_FLAG(l, ICANON))
+ {
+ if (CMP_CC(VERASE, c)
+#ifdef VERASE2
+ || CMP_CC(VERASE2, c)
+#endif
+ )
+ {
+ ttydisc_rubchar(tp);
+ return 0;
+ }
+ else if (CMP_CC(VKILL, c))
+ {
+ while (ttydisc_rubchar(tp) == 0)
+ ;
+ return 0;
+ }
+ else if (CMP_FLAG(l, IEXTEN))
+ {
+ if (CMP_CC(VWERASE, c))
+ {
+ ttydisc_rubword(tp);
+ return 0;
+ }
+ else if (CMP_CC(VREPRINT, c))
+ {
+ ttydisc_reprint(tp);
+ return 0;
+ }
+ }
+ }
+
+processed:
+ if (CMP_FLAG(i, PARMRK) && (unsigned char)c == 0xff)
+ {
+ /* Print 0xff 0xff. */
+ ob[1] = 0xff;
+ ol = 2;
+ quote = 1;
+ }
+ else
+ {
+ ob[0] = c;
+ ol = 1;
+ }
+
+ goto print;
+
+parmrk:
+ if (CMP_FLAG(i, PARMRK))
+ {
+ /* Prepend 0xff 0x00 0x.. */
+ ob[2] = c;
+ ol = 3;
+ quote = 1;
+ }
+ else
+ {
+ ob[0] = c;
+ ol = 1;
+ }
+
+print:
+ /* See if we can store this on the input queue. */
+ if (ttyinq_write_nofrag(&tp->t_inq, ob, ol, quote) != 0)
+ {
+ if (CMP_FLAG(i, IMAXBEL))
+ ttyoutq_write_nofrag(&tp->t_outq, "\a", 1);
+
+ /*
+ * Prevent a deadlock here. It may be possible that a
+ * user has entered so much data, there is no data
+ * available to read(), but the buffers are full anyway.
+ *
+ * Only enter the high watermark if the device driver
+ * can actually transmit something.
+ */
+ if (ttyinq_bytescanonicalized(&tp->t_inq) == 0)
+ return 0;
+
+ tty_hiwat_in_block(tp);
+ return (-1);
+ }
+
+ /*
+ * In raw mode, we canonicalize after receiving a single
+ * character. Otherwise, we canonicalize when we receive a
+ * newline, VEOL or VEOF, but only when it isn't quoted.
+ */
+ if (!CMP_FLAG(l, ICANON) ||
+ (!quote && (c == CNL || CMP_CC(VEOL, c) || CMP_CC(VEOF, c))))
+ {
+ ttyinq_canonicalize(&tp->t_inq);
+ }
+
+ ttydisc_echo(tp, c, quote);
+
+ return 0;
+}
+
+size_t ttydisc_rint_simple(struct lwp_tty *tp, const void *buf, size_t len)
+{
+ const char *cbuf;
+
+ if (ttydisc_can_bypass(tp))
+ return (ttydisc_rint_bypass(tp, buf, len));
+
+ for (cbuf = buf; len-- > 0; cbuf++)
+ {
+ if (ttydisc_rint(tp, *cbuf, 0) != 0)
+ break;
+ }
+
+ return (cbuf - (const char *)buf);
+}
+
+size_t ttydisc_rint_bypass(struct lwp_tty *tp, const void *buf, size_t len)
+{
+ size_t ret;
+
+ tty_assert_locked(tp);
+
+ MPASS(tp->t_flags & TF_BYPASS);
+
+ rt_atomic_add(&tty_nin, len);
+
+#ifdef USING_BSD_HOOK
+ if (ttyhook_hashook(tp, rint_bypass))
+ return ttyhook_rint_bypass(tp, buf, len);
+#endif
+
+ ret = ttyinq_write(&tp->t_inq, buf, len, 0);
+ ttyinq_canonicalize(&tp->t_inq);
+ if (ret < len)
+ tty_hiwat_in_block(tp);
+
+ return (ret);
+}
+
+void ttydisc_rint_done(struct lwp_tty *tp)
+{
+ tty_assert_locked(tp);
+
+#ifdef USING_BSD_HOOK
+ if (ttyhook_hashook(tp, rint_done))
+ ttyhook_rint_done(tp);
+#endif
+
+ /* Wake up readers. */
+ tty_wakeup(tp, FREAD);
+ /* Wake up driver for echo. */
+ ttydevsw_outwakeup(tp);
+}
+
+size_t ttydisc_rint_poll(struct lwp_tty *tp)
+{
+ size_t l;
+
+ tty_assert_locked(tp);
+
+#ifdef USING_BSD_HOOK
+ if (ttyhook_hashook(tp, rint_poll))
+ return ttyhook_rint_poll(tp);
+#endif
+
+ /*
+ * XXX: Still allow character input when there's no space in the
+ * buffers, but we haven't entered the high watermark. This is
+ * to allow backspace characters to be inserted when in
+ * canonical mode.
+ */
+ l = ttyinq_bytesleft(&tp->t_inq);
+ if (l == 0 && (tp->t_flags & TF_HIWAT_IN) == 0)
+ return (1);
+
+ return (l);
+}
+
+static void ttydisc_wakeup_watermark(struct lwp_tty *tp)
+{
+ size_t c;
+
+ c = ttyoutq_bytesleft(&tp->t_outq);
+ if (tp->t_flags & TF_HIWAT_OUT)
+ {
+ /* Only allow us to run when we're below the watermark. */
+ if (c < tp->t_outlow)
+ return;
+
+ /* Reset the watermark. */
+ tp->t_flags &= ~TF_HIWAT_OUT;
+ }
+ else
+ {
+ /* Only run when we have data at all. */
+ if (c == 0)
+ return;
+ }
+ tty_wakeup(tp, FWRITE);
+}
+
+size_t ttydisc_getc(struct lwp_tty *tp, void *buf, size_t len)
+{
+ tty_assert_locked(tp);
+
+ if (tp->t_flags & TF_STOPPED)
+ return 0;
+
+#ifdef USING_BSD_HOOK
+ if (ttyhook_hashook(tp, getc_inject))
+ return ttyhook_getc_inject(tp, buf, len);
+#endif
+
+ len = ttyoutq_read(&tp->t_outq, buf, len);
+
+#ifdef USING_BSD_HOOK
+ if (ttyhook_hashook(tp, getc_capture))
+ ttyhook_getc_capture(tp, buf, len);
+#endif
+
+ ttydisc_wakeup_watermark(tp);
+ rt_atomic_add(&tty_nout, len);
+
+ return (len);
+}
+
+int ttydisc_getc_uio(struct lwp_tty *tp, struct uio *uio)
+{
+ int error = 0;
+ ssize_t obytes = uio->uio_resid;
+ size_t len;
+ char buf[TTY_STACKBUF];
+
+ tty_assert_locked(tp);
+
+ if (tp->t_flags & TF_STOPPED)
+ return 0;
+
+ /*
+ * When a TTY hook is attached, we cannot perform unbuffered
+ * copying to userspace. Just call ttydisc_getc() and
+ * temporarily store data in a shadow buffer.
+ */
+ if (ttyhook_hashook(tp, getc_capture) || ttyhook_hashook(tp, getc_inject))
+ {
+ while (uio->uio_resid > 0)
+ {
+ /* Read to shadow buffer. */
+ len = ttydisc_getc(tp, buf, MIN(uio->uio_resid, sizeof buf));
+ if (len == 0)
+ break;
+
+ /* Copy to userspace. */
+ tty_unlock(tp);
+ error = uiomove(buf, len, uio);
+ tty_lock(tp);
+
+ if (error != 0)
+ break;
+ }
+ }
+ else
+ {
+ error = ttyoutq_read_uio(&tp->t_outq, tp, uio);
+
+ ttydisc_wakeup_watermark(tp);
+ rt_atomic_add(&tty_nout, obytes - uio->uio_resid);
+ }
+
+ return error;
+}
+
+size_t ttydisc_getc_poll(struct lwp_tty *tp)
+{
+ tty_assert_locked(tp);
+
+ if (tp->t_flags & TF_STOPPED)
+ return 0;
+
+#if USING_BSD_HOOK
+ if (ttyhook_hashook(tp, getc_poll))
+ return ttyhook_getc_poll(tp);
+#endif
+
+ return ttyoutq_bytesused(&tp->t_outq);
+}
diff --git a/components/lwp/terminal/terminal.h b/components/lwp/terminal/terminal.h
new file mode 100644
index 0000000000..df0dc04128
--- /dev/null
+++ b/components/lwp/terminal/terminal.h
@@ -0,0 +1,415 @@
+/*
+ * Copyright (c) 2006-2023, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2023-11-13 Shell init ver.
+ */
+
+#ifndef __LWP_TERMINAL_H__
+#define __LWP_TERMINAL_H__
+
+#include "bsd_ttyqueue.h"
+#include "bsd_ttydisc.h"
+
+#ifdef USING_BSD_HOOK
+#include "bsd_ttyhook.h"
+#endif
+
+#include <lwp.h>
+#include <rtdef.h>
+
+/* include kernel header for termios base definitions */
+#include <termios.h>
+/* for _POSIX_VDISABLE */
+#include <unistd.h>
+
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2008 Ed Schouten <[email protected]>
+ * All rights reserved.
+ *
+ * Portions of this software were developed under sponsorship from Snow
+ * B.V., the Netherlands.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+struct lwp_tty;
+
+/*
+ * Driver routines that are called from the line discipline to adjust
+ * hardware parameters and such.
+ */
+typedef int tsw_open_t(struct lwp_tty *tp);
+typedef void tsw_close_t(struct lwp_tty *tp);
+typedef void tsw_outwakeup_t(struct lwp_tty *tp);
+typedef void tsw_inwakeup_t(struct lwp_tty *tp);
+typedef int tsw_ioctl_t(struct lwp_tty *tp, rt_ubase_t cmd, rt_caddr_t data,
+ struct rt_thread *td);
+typedef int tsw_cioctl_t(struct lwp_tty *tp, int unit, rt_ubase_t cmd, rt_caddr_t data,
+ struct rt_thread *td);
+typedef int tsw_param_t(struct lwp_tty *tp, struct termios *t);
+typedef int tsw_modem_t(struct lwp_tty *tp, int sigon, int sigoff);
+typedef int tsw_mmap_t(struct lwp_tty *tp, vm_ooffset_t offset,
+ vm_paddr_t *paddr, int nprot, vm_memattr_t *memattr);
+
+typedef void tsw_pktnotify_t(struct lwp_tty *tp, char event);
+typedef void tsw_free_t(void *softc);
+typedef rt_bool_t tsw_busy_t(struct lwp_tty *tp);
+
+struct lwp_ttydevsw
+{
+ unsigned int tsw_flags; /* Default TTY flags. */
+
+ tsw_open_t *tsw_open; /* Device opening. */
+ tsw_close_t *tsw_close; /* Device closure. */
+
+ tsw_outwakeup_t *tsw_outwakeup; /* Output available. */
+ tsw_inwakeup_t *tsw_inwakeup; /* Input can be stored again. */
+
+ tsw_ioctl_t *tsw_ioctl; /* ioctl() hooks. */
+ tsw_cioctl_t *tsw_cioctl; /* ioctl() on control devices. */
+ tsw_param_t *tsw_param; /* TIOCSETA device parameter setting. */
+ tsw_modem_t *tsw_modem; /* Modem sigon/sigoff. */
+
+ tsw_mmap_t *tsw_mmap; /* mmap() hooks. */
+ tsw_pktnotify_t *tsw_pktnotify; /* TIOCPKT events. */
+
+ tsw_free_t *tsw_free; /* Destructor. */
+
+ tsw_busy_t *tsw_busy; /* Draining output. */
+
+ void *tsw_spare[3]; /* For future use. */
+};
+typedef struct lwp_ttydevsw *lwp_ttydevsw_t;
+
+struct lwp_tty
+{
+ struct rt_device parent; /* inherit from Class:RT_Device */
+ struct rt_mutex *t_mtx; /* TTY lock. */
+ struct rt_mutex t_mtxobj; /* Per-TTY lock (when not borrowing). */
+ rt_list_t t_list; /* (l) TTY list entry. */
+ int t_drainwait; /* (t) TIOCDRAIN timeout seconds. */
+ unsigned int t_flags; /* (t) Terminal option flags. */
+/* Keep flags in sync with db_show_tty and pstat(8). */
+#define TF_NOPREFIX 0x00001 /* Don't prepend "tty" to device name. */
+#define TF_INITLOCK 0x00002 /* Create init/lock state devices. */
+#define TF_CALLOUT 0x00004 /* Create "cua" devices. */
+#define TF_OPENED_IN 0x00008 /* "tty" node is in use. */
+#define TF_OPENED_OUT 0x00010 /* "cua" node is in use. */
+#define TF_OPENED_CONS 0x00020 /* Device in use as console. */
+#define TF_OPENED (TF_OPENED_IN | TF_OPENED_OUT | TF_OPENED_CONS)
+#define TF_GONE 0x00040 /* Device node is gone. */
+#define TF_OPENCLOSE 0x00080 /* Device is in open()/close(). */
+#define TF_ASYNC 0x00100 /* Asynchronous I/O enabled. */
+#define TF_LITERAL 0x00200 /* Accept the next character literally. */
+#define TF_HIWAT_IN 0x00400 /* We've reached the input watermark. */
+#define TF_HIWAT_OUT 0x00800 /* We've reached the output watermark. */
+#define TF_HIWAT (TF_HIWAT_IN | TF_HIWAT_OUT)
+#define TF_STOPPED 0x01000 /* Output flow control - stopped. */
+#define TF_EXCLUDE 0x02000 /* Exclusive access. */
+#define TF_BYPASS 0x04000 /* Optimized input path. */
+#define TF_ZOMBIE 0x08000 /* Modem disconnect received. */
+#define TF_HOOK 0x10000 /* TTY has hook attached. */
+#define TF_BUSY_IN 0x20000 /* Process busy in read() -- not supported. */
+#define TF_BUSY_OUT 0x40000 /* Process busy in write(). */
+#define TF_BUSY (TF_BUSY_IN | TF_BUSY_OUT)
+ unsigned int t_revokecnt; /* (t) revoke() count. */
+
+ /* Buffering mechanisms. */
+ struct ttyinq t_inq; /* (t) Input queue. */
+ size_t t_inlow; /* (t) Input low watermark. */
+ struct ttyoutq t_outq; /* (t) Output queue. */
+ size_t t_outlow; /* (t) Output low watermark. */
+
+ /* Sleeping mechanisms. */
+ struct rt_condvar t_inwait; /* (t) Input wait queue. */
+ struct rt_condvar t_outwait; /* (t) Output wait queue. */
+ struct rt_condvar t_outserwait; /* (t) Serial output wait queue. */
+ struct rt_condvar t_bgwait; /* (t) Background wait queue. */
+ struct rt_condvar t_dcdwait; /* (t) Carrier Detect wait queue. */
+
+ struct rt_wqueue t_inpoll; /* (t) Input poll queue. */
+ struct rt_wqueue t_outpoll; /* (t) Output poll queue. */
+
+#ifdef USING_BSD_AIO
+ struct sigio *t_sigio; /* (t) Asynchronous I/O. */
+#endif
+
+ struct termios t_termios; /* (t) I/O processing flags. */
+ struct winsize t_winsize; /* (t) Window size. */
+ unsigned int t_column; /* (t) Current cursor position. */
+ unsigned int t_writepos; /* (t) Where input was interrupted. */
+ int t_compatflags; /* (t) COMPAT_43TTY flags. */
+
+ /* Init/lock-state devices. */
+ struct termios t_termios_init_in; /* tty%s.init. */
+ struct termios t_termios_lock_in; /* tty%s.lock. */
+#ifdef USING_BSD_INIT_LOCK_DEVICE
+ struct termios t_termios_init_out; /* cua%s.init. */
+ struct termios t_termios_lock_out; /* cua%s.lock. */
+
+#endif /* USING_BSD_INIT_LOCK_DEVICE */
+
+ struct lwp_ttydevsw *t_devsw; /* (c) Driver hooks. */
+#ifdef USING_BSD_HOOK
+ struct lwp_ttyhook *t_hook; /* (t) Capture/inject hook. */
+#endif
+
+ /* Process signal delivery. */
+ struct rt_processgroup *t_pgrp; /* (t) Foreground process group. */
+ struct rt_session *t_session; /* (t) Associated session. */
+ unsigned int t_sessioncnt; /* (t) Backpointing sessions. */
+
+ void *t_devswsoftc; /* (c) Soft config, for drivers. */
+#ifdef USING_BSD_HOOK
+ void *t_hooksoftc; /* (t) Soft config, for hooks. */
+#endif
+#ifdef USING_BSD_CHAR_DEVICE
+ struct cdev *t_dev; /* (c) Primary character device. */
+#endif /* USING_BSD_CHAR_DEVICE */
+#ifdef USING_BSD_SIGINFO
+ size_t t_prbufsz; /* (t) SIGINFO buffer size. */
+ char t_prbuf[]; /* (t) SIGINFO buffer. */
+#endif /* USING_BSD_SIGINFO */
+};
+typedef struct lwp_tty *lwp_tty_t;
+
+/* Allocation and deallocation. */
+void tty_rel_pgrp(struct lwp_tty *tp, struct rt_processgroup *pgrp);
+void tty_rel_sess(struct lwp_tty *tp, struct rt_session *sess);
+void tty_rel_gone(struct lwp_tty *tp);
+
+/* tty locking mechanism */
+#define tty_getlock(tp) ((tp)->t_mtx)
+#define tty_lock(tp) rt_mutex_take(tty_getlock(tp), RT_WAITING_FOREVER);
+#define tty_unlock(tp) rt_mutex_release(tty_getlock(tp))
+#define tty_lock_owned(tp) \
+ (rt_mutex_get_owner(tty_getlock(tp)) == rt_thread_self())
+#define tty_lock_notrecused(tp) (rt_mutex_get_hold(tty_getlock(tp)) == 1)
+#define tty_assert_locked(tp) RT_ASSERT(tty_lock_owned(tp))
+#define tty_lock_assert(tp, option) \
+ (((option) == (MA_OWNED | MA_NOTRECURSED)) \
+ ? (tty_lock_owned(tp) && tty_lock_notrecused(tp)) \
+ : rt_assert_handler("Operation not allowed", __func__, __LINE__))
+
+/* System messages. */
+int tty_checkoutq(struct lwp_tty *tp);
+int tty_putchar(struct lwp_tty *tp, char c);
+int tty_putstrn(struct lwp_tty *tp, const char *p, size_t n);
+
+int tty_ioctl(struct lwp_tty *tp, rt_ubase_t cmd, void *data, int fflag,
+ struct rt_thread *td);
+int tty_ioctl_compat(struct lwp_tty *tp, rt_ubase_t cmd, rt_caddr_t data, int fflag,
+ struct rt_thread *td);
+void tty_set_winsize(struct lwp_tty *tp, const struct winsize *wsz);
+void tty_init_console(struct lwp_tty *tp, speed_t speed);
+void tty_flush(struct lwp_tty *tp, int flags);
+void tty_hiwat_in_block(struct lwp_tty *tp);
+void tty_hiwat_in_unblock(struct lwp_tty *tp);
+dev_t tty_udev(struct lwp_tty *tp);
+
+/* tesing on tty */
+#define tty_opened(tp) ((tp)->t_flags & TF_OPENED)
+#define tty_gone(tp) ((tp)->t_flags & TF_GONE)
+#define tty_softc(tp) ((tp)->t_devswsoftc)
+#define tty_devname(tp) ((tp)->parent.parent.name)
+
+/**
+ * @brief TTY registeration on device subsystem
+ *
+ * @warning It's the duty of the caller to ensure that the name is not
+ * identical to any existed registered devices.
+ *
+ * @param terminal the target tty device
+ * @param name name of the device (must be exclusive)
+ * @return rt_err_t RT_EOK on success
+ */
+rt_err_t lwp_tty_register(lwp_tty_t terminal, const char *name);
+
+/**
+ * @brief TTY allocation and deallocation. TTY devices can be deallocated when
+ * the driver doesn't use it anymore, when the TTY isn't a session's
+ * controlling TTY and when the device node isn't opened through devfs.
+ *
+ * @param handle device handle of tty
+ * @param softc device configuration binding on tty
+ * @param prefix device name prefix
+ * @param cutom_mtx the lock provided to protect tty
+ * @return lwp_tty_t NULL on failure
+ */
+lwp_tty_t lwp_tty_create_ext(lwp_ttydevsw_t handle, void *softc,
+ rt_mutex_t custom_mtx);
+
+/**
+ * @brief Handful version of lwp_tty_create_ext
+ *
+ * @param softc device configuration binding on tty
+ * @param cutom_mtx the lock provided to protect tty
+ * @param prefix device name prefix
+ * @return lwp_tty_t NULL on failure
+ */
+lwp_tty_t lwp_tty_create(lwp_ttydevsw_t handle, void *softc);
+
+void lwp_tty_delete(lwp_tty_t tp);
+
+void lwp_tty_signal_sessleader(struct lwp_tty *tp, int sig);
+
+void lwp_tty_signal_pgrp(struct lwp_tty *tp, int sig);
+
+/**
+ * @brief Create a new pseudo-terminal multiplexer
+ *
+ * @param root_path path of root mount point of ptyfs
+ * @return rt_device_t new device object if succeed, otherwise NULL
+ */
+rt_err_t lwp_ptmx_init(rt_device_t ptmx_device, const char *root_path);
+
+#define LWP_CONSOLE_LOWEST_PRIOR 0
+#define LWP_CONSOLE_HIGHEST_PRIO INT_MAX
+/**
+ * @brief Register an alternative backend tty device as console
+ */
+rt_err_t lwp_console_register_backend(struct rt_device *bakdev, int prio);
+
+rt_inline int ttydevsw_open(struct lwp_tty *tp)
+{
+ tty_assert_locked(tp);
+ MPASS(!tty_gone(tp));
+
+ return (tp->t_devsw->tsw_open(tp));
+}
+
+rt_inline void ttydevsw_close(struct lwp_tty *tp)
+{
+ tty_assert_locked(tp);
+ MPASS(!tty_gone(tp));
+
+ tp->t_devsw->tsw_close(tp);
+}
+
+rt_inline void ttydevsw_outwakeup(struct lwp_tty *tp)
+{
+ tty_assert_locked(tp);
+ MPASS(!tty_gone(tp));
+
+ /* Prevent spurious wakeups. */
+ if (ttydisc_getc_poll(tp) == 0)
+ return;
+
+ tp->t_devsw->tsw_outwakeup(tp);
+}
+
+rt_inline void ttydevsw_inwakeup(struct lwp_tty *tp)
+{
+ tty_assert_locked(tp);
+ MPASS(!tty_gone(tp));
+
+ /* Prevent spurious wakeups. */
+ if (tp->t_flags & TF_HIWAT_IN)
+ return;
+
+ tp->t_devsw->tsw_inwakeup(tp);
+}
+
+rt_inline int ttydevsw_ioctl(struct lwp_tty *tp, rt_ubase_t cmd, rt_caddr_t data,
+ struct rt_thread *td)
+{
+ tty_assert_locked(tp);
+ MPASS(!tty_gone(tp));
+
+ return (tp->t_devsw->tsw_ioctl(tp, cmd, data, td));
+}
+
+rt_inline int ttydevsw_cioctl(struct lwp_tty *tp, int unit, rt_ubase_t cmd,
+ rt_caddr_t data, struct rt_thread *td)
+{
+ tty_assert_locked(tp);
+ MPASS(!tty_gone(tp));
+
+ return (tp->t_devsw->tsw_cioctl(tp, unit, cmd, data, td));
+}
+
+rt_inline int ttydevsw_param(struct lwp_tty *tp, struct termios *t)
+{
+ MPASS(!tty_gone(tp));
+
+ return (tp->t_devsw->tsw_param(tp, t));
+}
+
+rt_inline int ttydevsw_modem(struct lwp_tty *tp, int sigon, int sigoff)
+{
+ MPASS(!tty_gone(tp));
+
+ return (tp->t_devsw->tsw_modem(tp, sigon, sigoff));
+}
+
+rt_inline int ttydevsw_mmap(struct lwp_tty *tp, vm_ooffset_t offset,
+ vm_paddr_t *paddr, int nprot, vm_memattr_t *memattr)
+{
+ MPASS(!tty_gone(tp));
+
+ return (tp->t_devsw->tsw_mmap(tp, offset, paddr, nprot, memattr));
+}
+
+rt_inline void ttydevsw_pktnotify(struct lwp_tty *tp, char event)
+{
+ tty_assert_locked(tp);
+ MPASS(!tty_gone(tp));
+
+ tp->t_devsw->tsw_pktnotify(tp, event);
+}
+
+rt_inline void ttydevsw_free(struct lwp_tty *tp)
+{
+ MPASS(tty_gone(tp));
+
+ tp->t_devsw->tsw_free(tty_softc(tp));
+}
+
+rt_inline rt_bool_t ttydevsw_busy(struct lwp_tty *tp)
+{
+ tty_assert_locked(tp);
+ MPASS(!tty_gone(tp));
+
+ return (tp->t_devsw->tsw_busy(tp));
+}
+
+rt_inline size_t ttydisc_read_poll(struct lwp_tty *tp)
+{
+ tty_assert_locked(tp);
+
+ return ttyinq_bytescanonicalized(&tp->t_inq);
+}
+
+rt_inline size_t ttydisc_write_poll(struct lwp_tty *tp)
+{
+ tty_assert_locked(tp);
+
+ return ttyoutq_bytesleft(&tp->t_outq);
+}
+
+#endif /* __LWP_TERMINAL_H__ */
diff --git a/components/lwp/terminal/tty_config.h b/components/lwp/terminal/tty_config.h
new file mode 100644
index 0000000000..a1fe75dc7a
--- /dev/null
+++ b/components/lwp/terminal/tty_config.h
@@ -0,0 +1,136 @@
+/*
+ * Copyright (c) 2006-2023, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2023-11-13 Shell init ver.
+ */
+
+#ifndef __TTY_CONFIG_H__
+#define __TTY_CONFIG_H__
+
+/* default buffer size of tty siginfo */
+#define LWP_TTY_PRBUF_SIZE 256
+
+/*
+ * System wide defaults for terminal state.
+ */
+
+/*-
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Copyright (c) 1982, 1986, 1993
+ * The Regents of the University of California. All rights reserved.
+ * (c) UNIX System Laboratories, Inc.
+ * All or some portions of this file are derived from material licensed
+ * to the University of California by American Telephone and Telegraph
+ * Co. or Unix System Laboratories, Inc. and are reproduced herein with
+ * the permission of UNIX System Laboratories, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)ttydefaults.h 8.4 (Berkeley) 1/21/94
+ */
+
+/*
+ * Defaults on "first" open.
+ */
+#define TTYDEF_IFLAG (BRKINT | ICRNL | IMAXBEL | IXON | IXANY | IUTF8)
+#define TTYDEF_OFLAG (OPOST | ONLCR)
+#define TTYDEF_LFLAG_NOECHO (ICANON | ISIG | IEXTEN)
+#define TTYDEF_LFLAG_ECHO \
+ (TTYDEF_LFLAG_NOECHO | ECHO | ECHOE | ECHOKE | ECHOCTL)
+#define TTYDEF_LFLAG TTYDEF_LFLAG_ECHO
+#define TTYDEF_CFLAG (CREAD | CS8 | HUPCL)
+#define TTYDEF_SPEED (B9600)
+
+/*
+ * Control Character Defaults
+ */
+/*
+ * XXX: A lot of code uses lowercase characters, but control-character
+ * conversion is actually only valid when applied to uppercase
+ * characters. We just treat lowercase characters as if they were
+ * inserted as uppercase.
+ */
+#define _CONTROL(c) \
+ ((c) >= 'a' && (c) <= 'z' ? ((c) - 'a' + 1) : (((c) - 'A' + 1) & 0x7f))
+#define CEOF _CONTROL('D')
+#define CEOL 0xff /* XXX avoid _POSIX_VDISABLE */
+#define CERASE 0x7f
+#define CERASE2 _CONTROL('H')
+#define CINTR _CONTROL('C')
+#define CSTATUS _CONTROL('T')
+#define CKILL _CONTROL('U')
+#define CMIN 1
+#define CQUIT _CONTROL('\\')
+#define CSUSP _CONTROL('Z')
+#define CTIME 0
+#define CDSUSP _CONTROL('Y')
+#define CSTART _CONTROL('Q')
+#define CSTOP _CONTROL('S')
+#define CLNEXT _CONTROL('V')
+#define CDISCARD _CONTROL('O')
+#define CWERASE _CONTROL('W')
+#define CREPRINT _CONTROL('R')
+#define CEOT CEOF
+/* compat */
+#define CBRK CEOL
+#define CRPRNT CREPRINT
+#define CFLUSH CDISCARD
+
+/* PROTECTED INCLUSION ENDS HERE */
+#endif /* !__TTY_CONFIG_H__ */
+
+/*
+ * #define TTY_CONF_INCLUDE_CCHARS to include an array of default control
+ * characters.
+ */
+#ifdef TTY_CONF_INCLUDE_CCHARS
+#include <rtdef.h>
+#include <termios.h>
+#include <unistd.h>
+
+static const cc_t tty_ctrl_charset[NCCS] = {
+ [VINTR] = CINTR,
+ [VQUIT] = CQUIT,
+ [VERASE] = CERASE,
+ [VKILL] = CKILL,
+ [VEOF] = CEOF,
+ [VSTART] = CSTART,
+ [VSTOP] = CSTOP,
+ [VSUSP] = CSUSP,
+ [VREPRINT] = CREPRINT,
+ [VDISCARD] = CDISCARD,
+ [VWERASE] = CWERASE,
+ [VLNEXT] = CLNEXT,
+ [VMIN] = CMIN
+#undef _CONTROL
+};
+
+#undef TTY_CONF_INCLUDE_CCHARS
+#endif /* __TTY_CONFIG_H__ */
diff --git a/components/lwp/terminal/tty_cons.c b/components/lwp/terminal/tty_cons.c
new file mode 100644
index 0000000000..42434f3568
--- /dev/null
+++ b/components/lwp/terminal/tty_cons.c
@@ -0,0 +1,128 @@
+/*
+ * Copyright (c) 2006-2023, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2023-12-11 Shell init ver.
+ */
+
+#define DBG_TAG "lwp.tty"
+#define DBG_LVL DBG_INFO
+#include <rtdbg.h>
+
+#include "tty_config.h"
+#include "tty_internal.h"
+#include "bsd_porting.h"
+#include "terminal.h"
+
+#include <fcntl.h>
+
+#ifdef RT_USING_DEVICE_OPS
+const static struct rt_device_ops cons_rtdev_ops;
+#endif
+
+struct backend_entry
+{
+ rt_list_t bakend_list_node;
+ int prio;
+
+ rt_device_t bakdev;
+};
+
+static rt_list_t _bakend_list;
+
+static void _bent_enqueue(struct backend_entry *bent)
+{
+ struct backend_entry *idx;
+ rt_bool_t inserted = RT_FALSE;
+ rt_list_for_each_entry(idx, &_bakend_list, bakend_list_node)
+ {
+ if (idx->prio < bent->prio)
+ {
+ rt_list_insert_before(&idx->bakend_list_node, &bent->bakend_list_node);
+ inserted = RT_TRUE;
+ break;
+ }
+ }
+
+ if (!inserted)
+ rt_list_insert_after(&_bakend_list, &bent->bakend_list_node);
+ return ;
+}
+
+rt_err_t lwp_console_register_backend(struct rt_device *bakdev, int prio)
+{
+ rt_err_t ret = RT_EOK;
+ struct backend_entry *bent;
+ bent = rt_malloc(sizeof(struct backend_entry));
+ if (bent)
+ {
+ rt_list_init(&bent->bakend_list_node);
+ bent->prio = prio;
+ bent->bakdev = bakdev;
+
+ _bent_enqueue(bent);
+ }
+ else
+ {
+ ret = -RT_ENOMEM;
+ }
+ return ret;
+}
+
+static struct rt_device _cons_rtdev;
+
+static int fops_open(struct dfs_file *file)
+{
+ return -EINVAL;
+}
+
+static struct dfs_file_ops _cons_fops = {
+ .open = fops_open,
+};
+
+static rt_err_t _cons_readlink(struct rt_device *dev, char *buf, int len)
+{
+ int rc = -EIO;
+ struct backend_entry *bent;
+ if (!rt_list_isempty(&_bakend_list))
+ {
+ bent = rt_list_first_entry(&_bakend_list, struct backend_entry, bakend_list_node);
+ if (bent)
+ {
+ RT_ASSERT(bent->bakdev);
+ strncpy(buf, bent->bakdev->parent.name, MIN(len, RT_NAME_MAX));
+ LOG_D("%s: backend device %s", __func__, buf);
+ rc = 0;
+ }
+ }
+
+ if (rc != 0)
+ {
+ LOG_W("%s: No backend device", __func__);
+ }
+
+ return rc;
+}
+
+static int _cons_init(void)
+{
+ rt_err_t rc;
+
+ rt_list_init(&_bakend_list);
+
+ /* setup system level device */
+ _cons_rtdev.type = RT_Device_Class_Char;
+ _cons_rtdev.ops = &cons_rtdev_ops;
+ rc = rt_device_register(&_cons_rtdev, "console", RT_DEVICE_FLAG_DYNAMIC);
+ if (rc == RT_EOK)
+ {
+ _cons_rtdev.readlink = &_cons_readlink;
+ _cons_rtdev.fops = &_cons_fops;
+ }
+
+ return rc;
+}
+INIT_DEVICE_EXPORT(_cons_init);
diff --git a/components/lwp/terminal/tty_ctty.c b/components/lwp/terminal/tty_ctty.c
new file mode 100644
index 0000000000..f0de0001f8
--- /dev/null
+++ b/components/lwp/terminal/tty_ctty.c
@@ -0,0 +1,106 @@
+/*
+ * Copyright (c) 2006-2023, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2023-11-30 Shell init ver.
+ */
+#define DBG_TAG "lwp.ctty"
+#define DBG_LVL DBG_INFO
+#include <rtdbg.h>
+
+#define TTY_CONF_INCLUDE_CCHARS
+#include "tty_config.h"
+#include "tty_internal.h"
+#include "terminal.h"
+
+static int fops_open(struct dfs_file *file)
+{
+ return -EINVAL;
+}
+
+static rt_err_t ctty_readlink(struct rt_device *dev, char *buf, int len)
+{
+ int rc = -ENXIO;
+ lwp_tty_t tp;
+ rt_session_t sess;
+ rt_processgroup_t pgrp;
+ rt_lwp_t lwp;
+
+ lwp = lwp_self();
+ if (lwp)
+ {
+ pgrp = lwp->pgrp;
+ if (pgrp)
+ {
+ sess = pgrp->session;
+ if (sess)
+ {
+ tp = sess->ctty;
+ if (tp)
+ {
+ tty_lock(tp);
+
+ if (lwp->pgrp == pgrp && pgrp->session == sess && sess->ctty == tp)
+ {
+ rt_strncpy(buf, tp->parent.parent.name, len);
+ rc = RT_EOK;
+ }
+
+ tty_unlock(tp);
+ }
+ }
+ }
+ }
+
+ return rc;
+}
+
+static struct dfs_file_ops ctty_file_ops = {
+ .open = fops_open,
+};
+
+/* character device for tty */
+#ifdef RT_USING_DEVICE_OPS
+const static struct rt_device_ops tty_dev_ops = {
+ /* IO directly through device is not allowed */
+};
+#else
+#error Must enable RT_USING_DEVICE_OPS in Kconfig
+#endif
+
+rt_inline void device_setup(rt_device_t ctty)
+{
+ ctty->type = RT_Device_Class_Char;
+#ifdef RT_USING_DEVICE_OPS
+ ctty->ops = &tty_dev_ops;
+#else
+#error Must enable RT_USING_DEVICE_OPS in Kconfig
+#endif
+}
+
+/* register device to DFS */
+static int lwp_ctty_register(rt_device_t ctty)
+{
+ rt_err_t rc = -RT_ENOMEM;
+ const char *tty_name = "tty";
+
+ device_setup(ctty);
+ rc = rt_device_register(ctty, tty_name, RT_DEVICE_FLAG_DYNAMIC);
+ if (rc == RT_EOK)
+ {
+ ctty->readlink = &ctty_readlink;
+ ctty->fops = &ctty_file_ops;
+ }
+ return rc;
+}
+
+static struct rt_device ctty;
+
+static int lwp_ctty_init(void)
+{
+ return lwp_ctty_register(&ctty);
+}
+INIT_DEVICE_EXPORT(lwp_ctty_init);
diff --git a/components/lwp/terminal/tty_device.c b/components/lwp/terminal/tty_device.c
new file mode 100644
index 0000000000..17364fabc1
--- /dev/null
+++ b/components/lwp/terminal/tty_device.c
@@ -0,0 +1,456 @@
+/*
+ * Copyright (c) 2006-2023, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2023-11-13 Shell init ver.
+ */
+
+#define DBG_TAG "lwp.tty"
+#define DBG_LVL DBG_INFO
+#include <rtdbg.h>
+
+#define TTY_CONF_INCLUDE_CCHARS
+#include "tty_config.h"
+#include "tty_internal.h"
+#include "terminal.h"
+
+/* configure option: timeout of tty drain wait */
+static int tty_drainwait = 5 * 60;
+
+#define TTY_NAME_PREFIX "tty"
+static char *alloc_device_name(const char *name)
+{
+ char *tty_dev_name;
+ long name_buf_len = (sizeof(TTY_NAME_PREFIX) - 1) /* raw prefix */
+ + rt_strlen(name) /* custom name */
+ + 1; /* tailing \0 */
+
+ tty_dev_name = rt_malloc(name_buf_len);
+ if (tty_dev_name)
+ sprintf(tty_dev_name, "%s%s", TTY_NAME_PREFIX, name);
+ return tty_dev_name;
+}
+
+/* character device for tty */
+#ifdef RT_USING_DEVICE_OPS
+const static struct rt_device_ops tty_dev_ops = {
+ /* IO directly through device is not allowed */
+};
+#else
+#error Must enable RT_USING_DEVICE_OPS in Kconfig
+#endif
+
+static int tty_fops_open(struct dfs_file *file)
+{
+ int rc;
+ lwp_tty_t tp;
+ rt_device_t device;
+ int devtype = 0; /* unused */
+
+ if (file->vnode && file->vnode->data)
+ {
+ if (file->vnode->ref_count != 1)
+ {
+ rc = 0;
+ }
+ else
+ {
+ device = (rt_device_t)file->vnode->data;
+ tp = rt_container_of(device, struct lwp_tty, parent);
+ rc = bsd_ttydev_methods.d_open(tp, file->flags, devtype,
+ rt_thread_self());
+ }
+ }
+ else
+ {
+ rc = -EINVAL;
+ }
+
+ return rc;
+}
+
+static int tty_fops_close(struct dfs_file *file)
+{
+ int rc;
+ lwp_tty_t tp;
+ rt_device_t device;
+ int fflags = FFLAGS(file->flags);
+ int devtype = 0; /* unused */
+
+ if (file->vnode && file->vnode->data)
+ {
+ if (file->vnode->ref_count != 1)
+ {
+ rc = 0;
+ }
+ else
+ {
+ device = (rt_device_t)file->vnode->data;
+ tp = rt_container_of(device, struct lwp_tty, parent);
+ rc = bsd_ttydev_methods.d_close(tp, fflags, devtype, rt_thread_self());
+ }
+ }
+ else
+ {
+ rc = -EINVAL;
+ }
+
+ return rc;
+}
+
+static int tty_fops_ioctl(struct dfs_file *file, int cmd, void *arg)
+{
+ int rc;
+ lwp_tty_t tp;
+ rt_device_t device;
+
+ if (file->vnode && file->vnode->data)
+ {
+ device = (rt_device_t)file->vnode->data;
+ tp = rt_container_of(device, struct lwp_tty, parent);
+ rc = lwp_tty_ioctl_adapter(tp, cmd, file->flags, arg, rt_thread_self());
+ }
+ else
+ {
+ rc = -EINVAL;
+ }
+
+ return rc;
+}
+
+static ssize_t tty_fops_read(struct dfs_file *file, void *buf, size_t count,
+ off_t *pos)
+{
+ ssize_t rc = 0;
+ int error;
+ struct uio uio;
+ struct iovec iov;
+ rt_device_t device;
+ struct lwp_tty *tp;
+ int ioflags;
+ int oflags = file->flags;
+
+ if (file->vnode && file->vnode->data)
+ {
+ device = (rt_device_t)file->vnode->data;
+ tp = rt_container_of(device, struct lwp_tty, parent);
+
+ /* setup ioflags */
+ ioflags = 0;
+ if (oflags & O_NONBLOCK)
+ ioflags |= IO_NDELAY;
+
+ /* setup uio parameters */
+ iov.iov_base = (void *)buf;
+ iov.iov_len = count;
+ uio.uio_offset = file->fpos;
+ uio.uio_resid = count;
+ uio.uio_iov = &iov;
+ uio.uio_iovcnt = 1;
+ uio.uio_rw = UIO_READ;
+
+ rc = count;
+ error = bsd_ttydev_methods.d_read(tp, &uio, ioflags);
+ rc -= uio.uio_resid;
+ if (error)
+ {
+ LOG_D("%s: failed to write %d bytes of data. error code %d",
+ __func__, uio.uio_resid, error);
+ rc = error;
+ }
+
+ /* reset file context */
+ file->fpos = uio.uio_offset;
+ }
+
+ if (rc)
+ LOG_D("%s(len=%d, buf=%c \"%d\")", __func__, rc, *((char *)buf),
+ *((char *)buf));
+ return rc;
+}
+
+static ssize_t tty_fops_write(struct dfs_file *file, const void *buf,
+ size_t count, off_t *pos)
+{
+ ssize_t rc = 0;
+ int error;
+ struct uio uio;
+ struct iovec iov;
+ rt_device_t device;
+ struct lwp_tty *tp;
+ int ioflags;
+ int oflags = file->flags;
+
+ if (file->vnode && file->vnode->data)
+ {
+ device = (rt_device_t)file->vnode->data;
+ tp = rt_container_of(device, struct lwp_tty, parent);
+
+ /* setup ioflags */
+ ioflags = 0;
+ if (oflags & O_NONBLOCK)
+ ioflags |= IO_NDELAY;
+
+ /* setup uio parameters */
+ iov.iov_base = (void *)buf;
+ iov.iov_len = count;
+ uio.uio_offset = file->fpos;
+ uio.uio_resid = count;
+ uio.uio_iov = &iov;
+ uio.uio_iovcnt = 1;
+ uio.uio_rw = UIO_WRITE;
+
+ rc = count;
+ error = bsd_ttydev_methods.d_write(tp, &uio, ioflags);
+ if (error)
+ {
+ rc = error;
+ LOG_D("%s: failed to write %d bytes of data. error code %d",
+ __func__, uio.uio_resid, error);
+ }
+ else
+ {
+ rc -= uio.uio_resid;
+ }
+
+ /* reset file context */
+ file->fpos = uio.uio_offset;
+ }
+ return rc;
+}
+
+static int tty_fops_flush(struct dfs_file *file)
+{
+ return -EINVAL;
+}
+
+static off_t tty_fops_lseek(struct dfs_file *file, off_t offset, int wherece)
+{
+ return -EINVAL;
+}
+
+static int tty_fops_truncate(struct dfs_file *file, off_t offset)
+{
+ /**
+ * regarding to POSIX.1, TRUNC is not supported for tty device.
+ * return 0 always to make filesystem happy
+ */
+ return 0;
+}
+
+static int tty_fops_poll(struct dfs_file *file, struct rt_pollreq *req)
+{
+ int rc;
+ rt_device_t device;
+ struct lwp_tty *tp;
+
+ if (file->vnode && file->vnode->data)
+ {
+ device = (rt_device_t)file->vnode->data;
+ tp = rt_container_of(device, struct lwp_tty, parent);
+
+ rc = bsd_ttydev_methods.d_poll(tp, req, rt_thread_self());
+ }
+ else
+ {
+ rc = -1;
+ }
+
+ return rc;
+}
+
+static int tty_fops_mmap(struct dfs_file *file, struct lwp_avl_struct *mmap)
+{
+ return -EINVAL;
+}
+
+static int tty_fops_lock(struct dfs_file *file, struct file_lock *flock)
+{
+ return -EINVAL;
+}
+
+static int tty_fops_flock(struct dfs_file *file, int operation, struct file_lock *flock)
+{
+ return -EINVAL;
+}
+
+static struct dfs_file_ops tty_file_ops = {
+ .open = tty_fops_open,
+ .close = tty_fops_close,
+ .ioctl = tty_fops_ioctl,
+ .read = tty_fops_read,
+ .write = tty_fops_write,
+ .flush = tty_fops_flush,
+ .lseek = tty_fops_lseek,
+ .truncate = tty_fops_truncate,
+ .poll = tty_fops_poll,
+ .mmap = tty_fops_mmap,
+ .lock = tty_fops_lock,
+ .flock = tty_fops_flock,
+};
+
+rt_inline void device_setup(lwp_tty_t terminal)
+{
+ terminal->parent.type = RT_Device_Class_Char;
+#ifdef RT_USING_DEVICE_OPS
+ terminal->parent.ops = &tty_dev_ops;
+#else
+#error Must enable RT_USING_DEVICE_OPS in Kconfig
+#endif
+}
+
+/* register TTY device */
+rt_err_t lwp_tty_register(lwp_tty_t terminal, const char *name)
+{
+ rt_err_t rc = -RT_ENOMEM;
+ const char *tty_name;
+ char *alloc_name;
+
+ if (terminal->t_devsw->tsw_flags & TF_NOPREFIX)
+ {
+ alloc_name = RT_NULL;
+ tty_name = name;
+ }
+ else
+ {
+ alloc_name = alloc_device_name(name);
+ tty_name = alloc_name;
+ }
+
+ if (tty_name)
+ {
+ device_setup(terminal);
+ rc = rt_device_register(&terminal->parent, tty_name, 0);
+ if (rc == RT_EOK)
+ {
+ terminal->parent.fops = &tty_file_ops;
+
+ LOG_D("%s() /dev/%s device registered", __func__, tty_name);
+ }
+
+ rt_free(alloc_name);
+ }
+ return rc;
+}
+
+static void tty_init_termios(lwp_tty_t tp)
+{
+ struct termios *t = &tp->t_termios_init_in;
+
+ t->c_cflag = TTYDEF_CFLAG;
+ t->c_iflag = TTYDEF_IFLAG;
+ t->c_lflag = TTYDEF_LFLAG;
+ t->c_oflag = TTYDEF_OFLAG;
+ t->__c_ispeed = TTYDEF_SPEED;
+ t->__c_ospeed = TTYDEF_SPEED;
+
+ memcpy(&t->c_cc, tty_ctrl_charset,
+ sizeof(tty_ctrl_charset) / sizeof(tty_ctrl_charset[0]));
+
+#ifdef USING_BSD_INIT_LOCK_DEVICE
+ tp->t_termios_init_out = *t;
+#endif /* USING_BSD_INIT_LOCK_DEVICE */
+}
+
+lwp_tty_t lwp_tty_create_ext(lwp_ttydevsw_t handle, void *softc,
+ rt_mutex_t custom_mtx)
+{
+ lwp_tty_t tp;
+
+ tp = rt_calloc(1, sizeof(struct lwp_tty)
+ #ifdef USING_BSD_SIGINFO
+ + LWP_TTY_PRBUF_SIZE
+ #endif
+ );
+
+ if (!tp)
+ return tp;
+
+ bsd_devsw_init(handle);
+
+#ifdef USING_BSD_SIGINFO
+ tp->t_prbufsz = LWP_TTY_PRBUF_SIZE;
+#endif
+ tp->t_devsw = handle;
+ tp->t_devswsoftc = softc;
+ tp->t_flags = handle->tsw_flags;
+ tp->t_drainwait = tty_drainwait;
+
+ tty_init_termios(tp);
+
+ cv_init(&tp->t_inwait, "ttyin");
+ cv_init(&tp->t_outwait, "ttyout");
+ cv_init(&tp->t_outserwait, "ttyosr");
+ cv_init(&tp->t_bgwait, "ttybg");
+ cv_init(&tp->t_dcdwait, "ttydcd");
+
+ rt_wqueue_init(&tp->t_inpoll);
+ rt_wqueue_init(&tp->t_outpoll);
+
+ /* Allow drivers to use a custom mutex to lock the TTY. */
+ if (custom_mtx != NULL)
+ {
+ tp->t_mtx = custom_mtx;
+ }
+ else
+ {
+ tp->t_mtx = &tp->t_mtxobj;
+ rt_mutex_init(&tp->t_mtxobj, "ttydev", RT_IPC_FLAG_PRIO);
+ }
+
+#ifdef USING_BSD_POLL
+ knlist_init_mtx(&tp->t_inpoll.si_note, tp->t_mtx);
+ knlist_init_mtx(&tp->t_outpoll.si_note, tp->t_mtx);
+#endif
+
+ return tp;
+}
+
+lwp_tty_t lwp_tty_create(lwp_ttydevsw_t handle, void *softc)
+{
+ return lwp_tty_create_ext(handle, softc, NULL);
+}
+
+void lwp_tty_delete(lwp_tty_t tp)
+{
+ /*
+ * ttyydev_leave() usually frees the i/o queues earlier, but it is
+ * not always called between queue allocation and here. The queues
+ * may be allocated by ioctls on a pty control device without the
+ * corresponding pty slave device ever being open, or after it is
+ * closed.
+ */
+ ttyinq_free(&tp->t_inq);
+ ttyoutq_free(&tp->t_outq);
+ rt_wqueue_wakeup_all(&tp->t_inpoll, (void *)POLLHUP);
+ rt_wqueue_wakeup_all(&tp->t_outpoll, (void *)POLLHUP);
+
+#ifdef USING_BSD_POLL
+ knlist_destroy(&tp->t_inpoll.si_note);
+ knlist_destroy(&tp->t_outpoll.si_note);
+#endif
+
+ cv_destroy(&tp->t_inwait);
+ cv_destroy(&tp->t_outwait);
+ cv_destroy(&tp->t_bgwait);
+ cv_destroy(&tp->t_dcdwait);
+ cv_destroy(&tp->t_outserwait);
+
+ if (tp->t_mtx == &tp->t_mtxobj)
+ rt_mutex_detach(&tp->t_mtxobj);
+ ttydevsw_free(tp);
+ rt_device_unregister(&tp->parent);
+ rt_free(tp);
+}
+
+/*
+ * Report on state of foreground process group.
+ */
+void tty_info(struct lwp_tty *tp)
+{
+ /* TODO */
+ return;
+}
diff --git a/components/lwp/terminal/tty_internal.h b/components/lwp/terminal/tty_internal.h
new file mode 100644
index 0000000000..8bf336cb89
--- /dev/null
+++ b/components/lwp/terminal/tty_internal.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2006-2023, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2023-11-13 Shell init ver.
+ */
+#ifndef __LWP_TTY_INTERNAL_H__
+#define __LWP_TTY_INTERNAL_H__
+
+#include "lwp.h"
+#include "terminal.h"
+
+extern struct cdevsw bsd_ttydev_methods;
+
+extern struct bsd_fileops bsd_ptsdev_methods;
+
+/* bsd devsw porting */
+void bsd_devsw_init(struct lwp_ttydevsw *tsw);
+
+/**
+ * Do not assert RTS or DTR automatically. If CNO_RTSDTR is set then the RTS and
+ * DTR lines will not be asserted when the device is opened. As a result, this
+ * flag is only useful on initial-state devices.
+ *
+ * Note: this feature is not using on smart system, so this flag is always 0.
+ */
+#define CNO_RTSDTR 0
+
+/* Waking up readers/writers. */
+int tty_wait(struct lwp_tty *tp, struct rt_condvar *cv);
+int tty_wait_background(struct lwp_tty *tp, struct rt_thread *td, int sig);
+int tty_timedwait(struct lwp_tty *tp, struct rt_condvar *cv, rt_tick_t timeout);
+void tty_wakeup(struct lwp_tty *tp, int flags);
+
+void tty_info(struct lwp_tty *tp);
+
+void pts_set_lock(lwp_tty_t pts, rt_bool_t is_lock);
+rt_bool_t pts_is_locked(lwp_tty_t pts);
+int pts_get_pktmode(lwp_tty_t pts);
+int pts_alloc(int fflags, struct rt_thread *td, struct dfs_file *ptm_file);
+
+int lwp_tty_ioctl_adapter(lwp_tty_t tp, int cmd, int oflags, void *args, rt_thread_t td);
+
+int lwp_tty_set_ctrl_proc(lwp_tty_t tp, rt_thread_t td);
+int lwp_tty_assign_foreground(lwp_tty_t tp, rt_thread_t td, int pgid);
+int lwp_tty_bg_stop(struct lwp_tty *tp, struct rt_condvar *cv);
+
+rt_inline rt_bool_t is_sess_leader(rt_lwp_t p)
+{
+ /**
+ * Note: a pgrp leader is never lose its group, so once it's
+ * true then it's always true
+ */
+ return p->pid == p->sid;
+}
+
+rt_inline int tty_is_ctty(struct lwp_tty *tp, struct rt_lwp *p)
+{
+ tty_assert_locked(tp);
+
+ return p->pgrp->session == tp->t_session && p->term_ctrlterm;
+}
+
+#endif /* __LWP_TTY_INTERNAL_H__ */
diff --git a/components/lwp/terminal/tty_ptmx.c b/components/lwp/terminal/tty_ptmx.c
new file mode 100644
index 0000000000..4158292251
--- /dev/null
+++ b/components/lwp/terminal/tty_ptmx.c
@@ -0,0 +1,350 @@
+/*
+ * Copyright (c) 2006-2023, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2023-12-07 Shell init ver.
+ */
+
+#define DBG_TAG "lwp.tty"
+#define DBG_LVL DBG_INFO
+#include <rtdbg.h>
+
+#include "tty_config.h"
+#include "tty_internal.h"
+#include "bsd_porting.h"
+#include "terminal.h"
+
+#include <fcntl.h>
+
+static struct dfs_file_ops ptm_fops;
+
+static int ptm_fops_open(struct dfs_file *file)
+{
+ int rc;
+ rt_uint32_t oflags = file->flags;
+ rt_thread_t cur_thr = rt_thread_self();
+
+ /* we don't check refcnt because each open will create a new device */
+ if (file->vnode && file->vnode->data)
+ {
+ /**
+ * Filter out illegal flags
+ */
+ if ((oflags & ~(O_RDWR | O_NOCTTY | O_CLOEXEC | O_LARGEFILE)) == 0)
+ {
+ rc = pts_alloc(FFLAGS(oflags & O_ACCMODE), cur_thr, file);
+
+ /* detached operation from devfs */
+ if (rc == 0)
+ file->vnode->fops = &ptm_fops;
+ }
+ else
+ {
+ rc = -EINVAL;
+ }
+ }
+ else
+ {
+ rc = -EINVAL;
+ }
+
+ return rc;
+}
+
+static int ptm_fops_close(struct dfs_file *file)
+{
+ int rc;
+ lwp_tty_t tp;
+ rt_device_t device;
+
+ if (file->vnode && file->vnode->data)
+ {
+ if (file->vnode->ref_count != 1)
+ {
+ rc = 0;
+ }
+ else
+ {
+ device = (rt_device_t)file->vnode->data;
+ tp = rt_container_of(device, struct lwp_tty, parent);
+ rc = bsd_ptsdev_methods.fo_close(tp, rt_thread_self());
+ }
+ }
+ else
+ {
+ rc = -EINVAL;
+ }
+
+ return rc;
+}
+
+static ssize_t ptm_fops_read(struct dfs_file *file, void *buf, size_t count,
+ off_t *pos)
+{
+ ssize_t rc = 0;
+ int error;
+ struct uio uio;
+ struct iovec iov;
+ rt_device_t device;
+ struct lwp_tty *tp;
+ int oflags = file->flags;
+
+ if (file->vnode && file->vnode->data)
+ {
+ device = (rt_device_t)file->vnode->data;
+ tp = rt_container_of(device, struct lwp_tty, parent);
+
+ /* setup uio parameters */
+ iov.iov_base = (void *)buf;
+ iov.iov_len = count;
+ uio.uio_offset = file->fpos;
+ uio.uio_resid = count;
+ uio.uio_iov = &iov;
+ uio.uio_iovcnt = 1;
+ uio.uio_rw = UIO_READ;
+
+ rc = count;
+ error =
+ bsd_ptsdev_methods.fo_read(tp, &uio, 0, oflags, rt_thread_self());
+ rc -= uio.uio_resid;
+ if (error)
+ {
+ rc = error;
+ }
+
+ /* reset file context */
+ file->fpos = uio.uio_offset;
+ }
+
+ return rc;
+}
+
+static ssize_t ptm_fops_write(struct dfs_file *file, const void *buf,
+ size_t count, off_t *pos)
+{
+ ssize_t rc = 0;
+ int error;
+ struct uio uio;
+ struct iovec iov;
+ rt_device_t device;
+ struct lwp_tty *tp;
+ int oflags = file->flags;
+
+ if (file->vnode && file->vnode->data)
+ {
+ device = (rt_device_t)file->vnode->data;
+ tp = rt_container_of(device, struct lwp_tty, parent);
+
+ /* setup uio parameters */
+ iov.iov_base = (void *)buf;
+ iov.iov_len = count;
+ uio.uio_offset = file->fpos;
+ uio.uio_resid = count;
+ uio.uio_iov = &iov;
+ uio.uio_iovcnt = 1;
+ uio.uio_rw = UIO_WRITE;
+
+ rc = count;
+ error =
+ bsd_ptsdev_methods.fo_write(tp, &uio, 0, oflags, rt_thread_self());
+ if (error)
+ {
+ rc = error;
+ }
+ else
+ {
+ rc -= uio.uio_resid;
+ }
+
+ /* reset file context */
+ file->fpos = uio.uio_offset;
+ }
+ return rc;
+}
+
+static int ptm_fops_ioctl(struct dfs_file *file, int cmd, void *arg)
+{
+ int rc;
+ lwp_tty_t tp;
+ rt_device_t device;
+ rt_ubase_t cmd_normal = (unsigned int)cmd;
+
+ if (file->vnode && file->vnode->data)
+ {
+ device = (rt_device_t)file->vnode->data;
+ tp = rt_container_of(device, struct lwp_tty, parent);
+
+ switch (cmd_normal)
+ {
+ case TIOCSPTLCK:
+ {
+ int is_lock;
+ if (lwp_get_from_user(&is_lock, arg, sizeof(int)) != sizeof(int))
+ return -EFAULT;
+ pts_set_lock(tp, is_lock);
+ rc = 0;
+ }
+ break;
+ case TIOCGPTLCK:
+ {
+ int is_lock = pts_is_locked(tp);
+ if (lwp_put_to_user(arg, &is_lock, sizeof(int)) != sizeof(int))
+ return -EFAULT;
+ rc = 0;
+ }
+ break;
+ case TIOCGPKT:
+ {
+ int pktmode = pts_get_pktmode(tp);
+ if (lwp_put_to_user(arg, &pktmode, sizeof(int)) != sizeof(int))
+ return -EFAULT;
+ rc = 0;
+ }
+ break;
+ default:
+ rc = bsd_ptsdev_methods.fo_ioctl(
+ tp, cmd_normal, arg, 0, FFLAGS(file->flags), rt_thread_self());
+ break;
+ }
+ }
+ else
+ {
+ rc = -EINVAL;
+ }
+
+ return rc;
+}
+
+static int ptm_fops_flush(struct dfs_file *file)
+{
+ return -EINVAL;
+}
+
+static off_t ptm_fops_lseek(struct dfs_file *file, off_t offset, int wherece)
+{
+ return -EINVAL;
+}
+
+static int ptm_fops_truncate(struct dfs_file *file, off_t offset)
+{
+ return -EINVAL;
+}
+
+static int ptm_fops_poll(struct dfs_file *file, struct rt_pollreq *req)
+{
+ int rc;
+ rt_device_t device;
+ struct lwp_tty *tp;
+
+ if (file->vnode && file->vnode->data)
+ {
+ device = (rt_device_t)file->vnode->data;
+ tp = rt_container_of(device, struct lwp_tty, parent);
+
+ rc = bsd_ptsdev_methods.fo_poll(tp, req, 0, rt_thread_self());
+ }
+ else
+ {
+ rc = -1;
+ }
+
+ return rc;
+}
+
+static int ptm_fops_mmap(struct dfs_file *file, struct lwp_avl_struct *mmap)
+{
+ return -EINVAL;
+}
+
+static int ptm_fops_lock(struct dfs_file *file, struct file_lock *flock)
+{
+ return -EINVAL;
+}
+
+static int ptm_fops_flock(struct dfs_file *file, int operation, struct file_lock *flock)
+{
+ return -EINVAL;
+}
+
+static struct dfs_file_ops ptm_fops = {
+ .open = ptm_fops_open,
+ .close = ptm_fops_close,
+ .ioctl = ptm_fops_ioctl,
+ .read = ptm_fops_read,
+ .write = ptm_fops_write,
+ .flush = ptm_fops_flush,
+ .lseek = ptm_fops_lseek,
+ .truncate = ptm_fops_truncate,
+ .poll = ptm_fops_poll,
+ .mmap = ptm_fops_mmap,
+ .lock = ptm_fops_lock,
+ .flock = ptm_fops_flock,
+};
+
+rt_err_t lwp_ptmx_init(rt_device_t ptmx_device, const char *root_path)
+{
+ char *device_name;
+ int root_len;
+ const char *dev_rel_path;
+ rt_err_t rc;
+
+ root_len = strlen(root_path);
+ dev_rel_path = "/ptmx";
+ device_name = rt_malloc(root_len + sizeof("/ptmx"));
+
+ if (device_name)
+ {
+ /* Register device */
+ sprintf(device_name, "%s%s", root_path, dev_rel_path);
+ rt_device_register(ptmx_device, device_name, 0);
+
+ /* Setup fops */
+ ptmx_device->fops = &ptm_fops;
+
+ rt_free(device_name);
+
+ rc = RT_EOK;
+ }
+ else
+ {
+ rc = -RT_ENOMEM;
+ }
+
+ return rc;
+}
+
+/* system level ptmx */
+static struct rt_device sysptmx;
+
+static struct dfs_file_ops sysptmx_file_ops;
+
+static rt_err_t sysptmx_readlink(struct rt_device *dev, char *buf, int len)
+{
+ int rc = 0;
+
+ /* TODO: support multi-root ? */
+ strncpy(buf, "pts/ptmx", len);
+
+ return rc;
+}
+
+static int _sys_ptmx_init(void)
+{
+ rt_err_t rc;
+ rt_device_t sysptmx_rtdev = &sysptmx;
+
+ /* setup system level device */
+ sysptmx_rtdev->type = RT_Device_Class_Char;
+ sysptmx_rtdev->ops = RT_NULL;
+ rc = rt_device_register(sysptmx_rtdev, "ptmx", RT_DEVICE_FLAG_DYNAMIC);
+ if (rc == RT_EOK)
+ {
+ sysptmx_rtdev->readlink = &sysptmx_readlink;
+ sysptmx_rtdev->fops = &sysptmx_file_ops;
+ }
+ return rc;
+}
+INIT_DEVICE_EXPORT(_sys_ptmx_init);