@@ -56,9 +56,9 @@ struct vector {
5656};
5757
5858constexpr okm_long nr_fchdir = 13 ;
59- // openkal 0.11: a started program that forms a job of its own .
59+ // openkal 0.11: the unit a started program joins .
6060constexpr okm_long nr_setpgid = 82 ;
61- constexpr okm_long nr_getpgid = 151 ;
61+
6262
6363} // namespace
6464
@@ -100,8 +100,7 @@ int kal_process_spawn(const kal_spawn* how,
100100 // outright, and what this system offers instead is a WATCH, which needs a
101101 // live context to notice. Composing it would move the defect from "refused"
102102 // to "works except when it matters".
103- constexpr kal_uintptr can = KAL_SPAWN_OWN_JOB ;
104- if (how->flags & ~can) return kal_err_not_supported;
103+ if (how->flags != 0 ) return kal_err_not_supported;
105104
106105 okm::terminated p (path, path_len);
107106 if (!p.ok ) return kal_err_invalid;
@@ -143,7 +142,11 @@ int kal_process_spawn(const kal_spawn* how,
143142 const okm_long ou = streams ? static_cast <okm_long>(streams->out .h ) : 0 ;
144143 const okm_long er = streams ? static_cast <okm_long>(streams->err .h ) : 0 ;
145144
146- const bool job = (how->flags & KAL_SPAWN_OWN_JOB ) != 0 ;
145+ // ⭐ The unit, named here by a process group --- which is to say by whichever
146+ // program formed it first. Zero for the first member; a later one is given
147+ // the number to join.
148+ const okm_long join = how->job ? static_cast <okm_long>(how->job ->h ) : 0 ;
149+ const bool unit = how->job != nullptr ;
147150
148151 bool is_duplicate = false ;
149152 const okm_long child = okm::duplicate (is_duplicate);
@@ -167,14 +170,18 @@ int kal_process_spawn(const kal_spawn* how,
167170 // is named from, so this no longer has to serve both.
168171 okm::sys (nr_fchdir, w);
169172
170- if (job ) okm::sys (nr_setpgid, 0 , 0 );
173+ if (unit ) okm::sys (nr_setpgid, 0 , join );
171174
172175 okm::sys (okm::nr_execve, reinterpret_cast <okm_long>(whole),
173176 reinterpret_cast <okm_long>(args.slots ),
174177 reinterpret_cast <okm_long>(envs.slots ));
175178 for (;;) okm::sys (okm::nr_exit, 127 );
176179 }
177180
181+ // Written only after the start succeeded, and only when the unit was new:
182+ // the first member's identifier IS the group's.
183+ if (unit && join == 0 ) how->job ->h = static_cast <kal_uintptr>(child);
184+
178185 *out = kal_process{ static_cast <kal_uintptr>(child) };
179186 return kal_ok;
180187}
@@ -221,24 +228,29 @@ void kal_process_channel_close(kal_stream s) {
221228
222229// Starting a program that receives exactly the directories named.
223230
224- // ⭐ REACHES THE WHOLE JOB WHEN THERE IS ONE, AND THE HANDLE CARRIES NOTHING TO
225- // SAY SO. A program started with KAL_SPAWN_OWN_JOB called `setpgid(0, 0)', so its
226- // group identifier is its own; one started without it inherited this
227- // implementation's, which is some other process. `getpgid(pid) == pid'
228- // distinguishes them exactly.
229- //
230- // ⚠️ Without this the flag would do nothing a caller could see: forming the job
231- // matters only because terminating then reaches what the started program itself
232- // started.
231+ // One program, whatever unit it is in --- the unit has its own operation below,
232+ // so this one's meaning never turns on how the program was started.
233233int kal_process_terminate (kal_process h) {
234234 if (h.h == 0 ) return kal_err_invalid;
235- const okm_long pid = static_cast <okm_long>(h.h );
236- const okm_long pgid = okm::sys (nr_getpgid, pid);
237- const okm_long target = (!okm::failed (pgid) && pgid == pid) ? -pid : pid;
238- const okm_long r = okm::sys (okm::nr_kill, target, 15 /* SIGTERM */ );
235+ const okm_long r = okm::sys (okm::nr_kill, static_cast <okm_long>(h.h ), 15 /* SIGTERM */ );
236+ return okm::failed (r) ? okm::translate (r) : kal_ok;
237+ }
238+
239+ // Every program in the unit, including ones never held as a handle.
240+ //
241+ // ⚠️ A group is named by a process identifier, and those are reused: once the
242+ // program that formed it has ended and the numbers have wrapped, this can reach
243+ // a different group. That is what this system does, and it is recorded rather
244+ // than hidden.
245+ int kal_process_job_terminate (kal_job j) {
246+ if (j.h == 0 ) return kal_err_invalid;
247+ const okm_long r = okm::sys (okm::nr_kill, -static_cast <okm_long>(j.h ), 15 /* SIGTERM */ );
239248 return okm::failed (r) ? okm::translate (r) : kal_ok;
240249}
241250
251+ // A group here is a number and not a resource, so there is nothing to release.
252+ void kal_process_job_close (kal_job) { }
253+
242254// Releasing the handle does not affect the program. A program that has not been
243255// waited for continues, and this environment collects it when the caller exits.
244256void kal_process_close (kal_process) { }
@@ -250,6 +262,6 @@ kal_uintptr kal_process_props(void) { return
250262 KAL_PROCESS_PROP_TERMINATE | KAL_PROCESS_PROP_STREAM_PASSING
251263 | KAL_PROCESS_PROP_EXIT_STATUS
252264 | KAL_PROCESS_PROP_CHANNEL | KAL_PROCESS_PROP_GRANT_DIR
253- | KAL_PROCESS_PROP_OWN_JOB ; }
265+ | KAL_PROCESS_PROP_JOB ; }
254266
255267}
0 commit comments