The Pedigree Project 0.1
PosixProcess-job-control.cc
1/* Copyright (c) 2026, Pedigree Developers. */
2#include "pedigree/kernel/syscallError.h"
3#include "pedigree/kernel/utilities/Pointers.h"
4#include "pedigree/kernel/utilities/assert.h"
5
6#include "PosixProcess.h"
7#include "TerminalControl.h"
8
9ProcessGroup::~ProcessGroup() {
10 RecursingLockGuard<Spinlock> guard(ProcessGroupManager::instance().lock());
11 assert(!firstMember && !memberCount);
12 if (registered)
13 ProcessGroupManager::instance().unregisterGroup(processGroupId, this);
14}
15
16void PosixProcess::initializeJobControl(Process* parent) {
17 LockGuard<Mutex> terminalGuard(TerminalControl::lock());
18 if (parent && parent->getType() == Posix) {
19 inheritProcessGroup(static_cast<PosixProcess*>(parent));
20 // Core construction precedes POSIX session staging. Re-snapshot both
21 // authorities under the same policy lock used by setsid and terminal claims.
22 setCttyContext(parent->acquireCttyContext());
23 return;
24 }
25 auto* group = new ProcessGroup;
26 if (!group)
27 return;
28 group->processGroupId = getUserspaceId(Process::rootPidNamespace().get());
29 group->sessionId = getUserspaceId(Process::rootPidNamespace().get());
30 group->Leader = this;
31 group->identity = pidIdentity();
32 group->sessionIdentity = pidIdentity();
33 setProcessGroup(group);
34}
35
36void PosixProcess::setProcessGroup(ProcessGroup* group) {
37 RecursingLockGuard<Spinlock> guard(ProcessGroupManager::instance().lock());
38 ProcessGroup* old = m_pProcessGroup;
39 if (old == group)
40 return;
41 if (old) {
42 if (m_GroupPrevious)
43 m_GroupPrevious->m_GroupNext = m_GroupNext;
44 else
45 old->firstMember = m_GroupNext;
46 if (m_GroupNext)
47 m_GroupNext->m_GroupPrevious = m_GroupPrevious;
48 --old->memberCount;
49 if (old->Leader == this)
50 old->Leader = nullptr;
51 }
52 m_pProcessGroup = group;
53 m_GroupPrevious = m_GroupNext = nullptr;
54 m_GroupMembership = NoGroup;
55 if (group) {
56 if (!group->sessionId)
57 group->sessionId = m_SessionId;
58 m_SessionId = group->sessionId;
59 m_GroupNext = group->firstMember;
60 if (m_GroupNext)
61 m_GroupNext->m_GroupPrevious = this;
62 group->firstMember = this;
63 ++group->memberCount;
64 m_GroupMembership = static_cast<size_t>(group->processGroupId) ==
65 getUserspaceId(Process::rootPidNamespace().get())
66 ? Leader
67 : Member;
68 if (!group->registered)
69 ProcessGroupManager::instance().registerGroup(group->processGroupId, group);
70 }
71 if (old && !old->memberCount)
72 delete old;
73}
74
75void PosixProcess::inheritProcessGroup(PosixProcess* parent) {
76 if (!parent)
77 return;
78 RecursingLockGuard<Spinlock> guard(ProcessGroupManager::instance().lock());
79 setProcessGroup(parent->m_pProcessGroup);
80}
81
82ProcessGroup* PosixProcess::getProcessGroup() const {
83 return m_pProcessGroup;
84}
85
86bool PosixProcess::getProcessGroupId(size_t& id, const UserspacePidNamespace* space) const {
87 RecursingLockGuard<Spinlock> guard(ProcessGroupManager::instance().lock());
88 if (!m_pProcessGroup)
89 return false;
90 id = space ? m_pProcessGroup->identity->id(space) : m_pProcessGroup->processGroupId;
91 return true;
92}
93
94void PosixProcess::leaveProcessGroup() {
95 setProcessGroup(nullptr);
96}
97
98void PosixProcess::setGroupMembership(Membership type) {
99 RecursingLockGuard<Spinlock> guard(ProcessGroupManager::instance().lock());
100 assert(type == m_GroupMembership);
101}
102
103PosixProcess::Membership PosixProcess::getGroupMembership() const {
104 RecursingLockGuard<Spinlock> guard(ProcessGroupManager::instance().lock());
105 return m_GroupMembership;
106}
107
108size_t PosixProcess::getSessionId(const UserspacePidNamespace* space) const {
109 RecursingLockGuard<Spinlock> guard(ProcessGroupManager::instance().lock());
110 return space && m_pProcessGroup ? m_pProcessGroup->sessionIdentity->id(space) : m_SessionId;
111}
112
113bool PosixProcess::sharesSession(const PosixProcess& other) const {
114 RecursingLockGuard<Spinlock> guard(ProcessGroupManager::instance().lock());
115 return m_SessionId && m_SessionId == other.m_SessionId;
116}
117
118bool PosixProcess::jobControlReady() const {
119 RecursingLockGuard<Spinlock> guard(ProcessGroupManager::instance().lock());
120 return m_SessionId && m_pProcessGroup;
121}
122
123void PosixProcess::markExecCommitted() {
124 RecursingLockGuard<Spinlock> guard(ProcessGroupManager::instance().lock());
125 m_ExecCommitted = true;
126}
127
128bool PosixProcess::hasExecCommitted() const {
129 RecursingLockGuard<Spinlock> guard(ProcessGroupManager::instance().lock());
130 return m_ExecCommitted;
131}
132
133int PosixProcess::createSession() {
134 LockGuard<Mutex> terminalGuard(TerminalControl::lock());
136 if (!prepared) {
137 ERROR("PosixProcess::createSession - out of memory!");
138 SYSCALL_ERROR(OutOfMemory);
139 return -1;
140 }
141 int error = 0;
142 {
143 RecursingLockGuard<Spinlock> guard(ProcessGroupManager::instance().lock());
144 if (ProcessGroupManager::instance().findGroup(
145 getUserspaceId(Process::rootPidNamespace().get()))) {
146 error = Error::NotEnoughPermissions;
147 } else {
148 prepared.get()->processGroupId = getUserspaceId(Process::rootPidNamespace().get());
149 prepared.get()->sessionId = getUserspaceId(Process::rootPidNamespace().get());
150 prepared.get()->Leader = this;
151 prepared.get()->identity = pidIdentity();
152 prepared.get()->sessionIdentity = pidIdentity();
153 setProcessGroup(prepared.releaseOwnership());
154 }
155 }
156 if (!error)
158 syscallError(error);
159 return error ? -1 : static_cast<int>(getUserspaceId());
160}
161
162int PosixProcess::changeProcessGroup(PosixProcess& caller, int id) {
163 {
164 RecursingLockGuard<Spinlock> guard(ProcessGroupManager::instance().lock());
165 auto* visible = ProcessGroupManager::instance().findGroup(id, caller.pidNamespace().get());
166 if (visible) {
167 id = visible->processGroupId;
168 } else if (static_cast<size_t>(id) == getUserspaceId(caller.pidNamespace().get())) {
169 id = getUserspaceId(Process::rootPidNamespace().get());
170 } else {
171 syscallError(Error::NotEnoughPermissions);
172 return -1;
173 }
174 }
175 // Both the registry and memberships are intrusive: commit cannot allocate
176 // while the group spinlock is held, or partially leave the original group.
178 if (static_cast<size_t>(id) == getUserspaceId(Process::rootPidNamespace().get())) {
180 }
181 int error = 0;
182 {
183 RecursingLockGuard<Spinlock> guard(ProcessGroupManager::instance().lock());
184 ProcessGroup* existing = ProcessGroupManager::instance().findGroup(id);
185 if (this != &caller && getParent() != &caller)
186 error = Error::NoSuchProcess;
187 else if (m_SessionId != caller.m_SessionId)
188 error = Error::NotEnoughPermissions;
189 else if (this != &caller && m_ExecCommitted)
190 error = Error::PermissionDenied;
191 else if (m_SessionId == getUserspaceId(Process::rootPidNamespace().get())) {
192 error = Error::NotEnoughPermissions;
193 } else if (existing && existing->sessionId != m_SessionId) {
194 error = Error::NotEnoughPermissions;
195 } else if (existing)
196 setProcessGroup(existing);
197 else if (static_cast<size_t>(id) != getUserspaceId(Process::rootPidNamespace().get())) {
198 error = Error::NotEnoughPermissions;
199 } else if (!prepared) {
200 error = Error::OutOfMemory;
201 } else {
202 prepared.get()->processGroupId = id;
203 prepared.get()->sessionId = m_SessionId;
204 prepared.get()->Leader = this;
205 prepared.get()->identity = pidIdentity();
206 prepared.get()->sessionIdentity = m_pProcessGroup->sessionIdentity;
207 setProcessGroup(prepared.releaseOwnership());
208 }
209 }
210 syscallError(error);
211 return error ? -1 : 0;
212}
void registerGroup(size_t gid, ProcessGroup *group)
ProcessGroup * findGroup(size_t gid) const
size_t getUserspaceId() const
Definition Process.h:504
Process * getParent()
Definition Process.h:620
T * get() const
T * releaseOwnership() noexcept
Definition Pointers.h:106