#ifndef TASK_H #define TASK_H #include #include "emu/cpu.h" #include "kernel/mm.h" #include "kernel/fs.h" #include "kernel/signal.h" #include "kernel/resource.h" #include "fs/sockrestart.h" #include "util/list.h" #include "util/timer.h" #include "util/sync.h" // everything here is private to the thread executing this task and needs no // locking, unless otherwise specified struct task { struct cpu_state cpu; struct mm *mm; // locked by general_lock struct mem *mem; // pointer to mm.mem, for convenience pthread_t thread; uint64_t threadid; struct tgroup *group; // immutable struct list group_links; pid_t_ pid, tgid; // immutable uid_t_ uid, gid; uid_t_ euid, egid; uid_t_ suid, sgid; #define MAX_GROUPS 32 unsigned ngroups; uid_t_ groups[MAX_GROUPS]; char comm[16] __strncpy_safe; // locked by general_lock bool did_exec; // for that one annoying setsid edge case struct fdtable *files; struct fs_info *fs; // locked by sighand->lock struct sighand *sighand; sigset_t_ blocked; sigset_t_ pending; sigset_t_ waiting; // if nonzero, an ongoing call to sigtimedwait is waiting on these struct list queue; cond_t pause; // please don't signal this // private sigset_t_ saved_mask; bool has_saved_mask; struct { // Locks all ptrace-related things lock_t lock; cond_t cond; bool traced; bool stopped; int signal; struct siginfo_ info; int trap_event; } ptrace; // locked by pids_lock struct task *parent; struct list children; struct list siblings; addr_t clear_tid; addr_t robust_list; // locked by pids_lock dword_t exit_code; bool zombie; bool exiting; // this structure is allocated on the stack of the parent's clone() call struct vfork_info { bool done; cond_t cond; lock_t lock; } *vfork; int exit_signal; // lock for anything that needs locking but is not covered by some other lock // specifically: comm, mm lock_t general_lock; struct task_sockrestart sockrestart; // current condition/lock, so it can be notified in case of a signal cond_t *waiting_cond; lock_t *waiting_lock; lock_t waiting_cond_lock; }; // current will always give the process that is currently executing // if I have to stop using __thread, current will become a macro extern __thread struct task *current; static inline void task_set_mm(struct task *task, struct mm *mm) { task->mm = mm; task->mem = &task->mm->mem; task->cpu.mmu = &task->mem->mmu; } // Creates a new process, initializes most fields from the parent. Specify // parent as NULL to create the init process. Returns NULL if out of memory. // Ends with an underscore because there's a mach function by the same name struct task *task_create_(struct task *parent); // Removes the process from the process table and frees it. Must be called with pids_lock. void task_destroy(struct task *task); // misc void vfork_notify(struct task *task); pid_t_ task_setsid(struct task *task); void task_leave_session(struct task *task); struct posix_timer { struct timer *timer; int_t timer_id; struct tgroup *tgroup; pid_t_ thread_pid; int_t signal; union sigval_ sig_value; }; // struct thread_group is way too long to type comfortably struct tgroup { struct list threads; // locked by pids_lock, by majority vote struct task *leader; // immutable struct rusage_ rusage; // locked by pids_lock pid_t_ sid, pgid; struct list session; struct list pgroup; bool stopped; cond_t stopped_cond; struct tty *tty; struct timer *itimer; #define TIMERS_MAX 16 struct posix_timer posix_timers[TIMERS_MAX]; struct rlimit_ limits[RLIMIT_NLIMITS_]; // From https://twitter.com/tblodt/status/957706819236904960 // > there are two distinct ways for a p̶r̶o̶c̶e̶s̶s̶ thread group to exit: // > // > - each thread calls exit // > wait will return the exit code for the group leader // > // > - any thread calls exit_group // > the SIGNAL_GROUP_EXIT flag will be set and wait will return the status passed to exit_group // // TODO locking bool doing_group_exit; dword_t group_exit_code; struct rusage_ children_rusage; cond_t child_exit; dword_t personality; // for everything in this struct not locked by something else lock_t lock; }; static inline bool task_is_leader(struct task *task) { return task->group->leader == task; } struct pid { dword_t id; struct task *task; struct list session; struct list pgroup; }; // synchronizes obtaining a pointer to a task and freeing that task extern lock_t pids_lock; // these functions must be called with pids_lock struct pid *pid_get(dword_t pid); struct task *pid_get_task(dword_t pid); struct task *pid_get_task_zombie(dword_t id); // don't return null if the task exists as a zombie #define MAX_PID (1 << 15) // oughta be enough // TODO document void task_start(struct task *task); void task_run_current(void); extern void (*exit_hook)(struct task *task, int code); #define superuser() (current != NULL && current->euid == 0) // Update the thread name to match the current task, in the format "comm-pid". // Will ensure that the -pid part always fits, then will fit as much of comm as possible. void update_thread_name(void); #endif