1: /*
2: * $Id: jobtab.c,v 1.13 2025/06/23 20:53:30 snw Exp $
3: * job table implementation
4: *
5: *
6: * Author: Serena Willis <snw@coherent-logic.com>
7: * Copyright (C) 1998 MUG Deutschland
8: * Copyright (C) 2021, 2023, 2025 Coherent Logic Development LLC
9: *
10: *
11: * This file is part of FreeM.
12: *
13: * FreeM is free software: you can redistribute it and/or modify
14: * it under the terms of the GNU Affero Public License as published by
15: * the Free Software Foundation, either version 3 of the License, or
16: * (at your option) any later version.
17: *
18: * FreeM is distributed in the hope that it will be useful,
19: * but WITHOUT ANY WARRANTY; without even the implied warranty of
20: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21: * GNU Affero Public License for more details.
22: *
23: * You should have received a copy of the GNU Affero Public License
24: * along with FreeM. If not, see <https://www.gnu.org/licenses/>.
25: *
26: * $Log: jobtab.c,v $
27: * Revision 1.13 2025/06/23 20:53:30 snw
28: * Documentation updates
29: *
30: * Revision 1.12 2025/05/14 15:28:55 snw
31: * Get basic job table and lock table functionality working with new shared memory architecture
32: *
33: * Revision 1.11 2025/05/14 12:22:04 snw
34: * Further work on shared memory
35: *
36: * Revision 1.10 2025/05/12 18:18:00 snw
37: * Further work on shared memory
38: *
39: * Revision 1.9 2025/05/01 21:02:31 snw
40: * Documentation updates
41: *
42: * Revision 1.8 2025/04/30 20:03:09 snw
43: * Work on entryref parser
44: *
45: * Revision 1.7 2025/04/17 00:34:04 snw
46: * More logging improvements
47: *
48: * Revision 1.6 2025/04/10 01:24:38 snw
49: * Remove C++ style comments
50: *
51: * Revision 1.5 2025/03/24 02:57:49 snw
52: * Shared memory compatibility fixes for OS/2
53: *
54: * Revision 1.4 2025/03/09 19:14:25 snw
55: * First phase of REUSE compliance and header reformat
56: *
57: *
58: * SPDX-FileCopyrightText: (C) 2025 Coherent Logic Development LLC
59: * SPDX-License-Identifier: AGPL-3.0-or-later
60: **/
61: #include <stdlib.h>
62: #include <string.h>
63: #include <time.h>
64: #include <unistd.h>
65: #include <errno.h>
66:
67: #include "mpsdef.h"
68: #include "shmmgr.h"
69: #include "jobtab.h"
70: #include "log.h"
71:
72: #if !defined(__OpenBSD__) && !defined(__APPLE__) && !defined(__OS2__)
73: union semun {
74: int val; /* Value for SETVAL */
75: struct semid_ds *buf; /* Buffer for IPC_STAT, IPC_SET */
76: unsigned short *array; /* Array for GETALL, SETALL */
77: struct seminfo *__buf; /* Buffer for IPC_INFO
78: (Linux-specific) */
79: };
80: #endif
81:
82: int semid_jobtab;
83: short have_jobtab_sem = FALSE;
84:
85: void jobtab_init(void)
86: {
87: union semun arg;
88: key_t jt_sk;
89:
90: jt_sk = ftok (config_file, 2);
91: if (first_process) {
92:
93: semid_jobtab = semget (jt_sk, 1, 0666 | IPC_CREAT);
94: if (semid_jobtab == -1) {
95: logprintf (FM_LOG_FATAL, "jobtab_init: failed to create job table semaphore (error code %d [%s])", errno, strerror (errno));
96: }
97: else {
98: logprintf (FM_LOG_INFO, "jobtab_init: job table semaphore created with semid %d", semid_jobtab);
99: }
100:
101: arg.val = 1;
102: if (semctl (semid_jobtab, 0, SETVAL, arg) == -1) {
103: logprintf (FM_LOG_FATAL, "jobtab_init: failed to initialize job table semaphore (error code %d [%s])", errno, strerror (errno));
104: }
105: else {
106: logprintf (FM_LOG_INFO, "jobtab_init: job table semaphore initialized");
107: }
108:
109: }
110: else {
111:
112: semid_jobtab = semget (jt_sk, 1, 0);
113: if (semid_jobtab == -1) {
114: logprintf (FM_LOG_FATAL, "jobtab_init: could not attach to job table semaphore (error code %d [%s])", errno, strerror (errno));
115: }
116: else {
117: logprintf (FM_LOG_INFO, "jobtab_init: attached to job table semaphore (semid %d)", semid_jobtab);
118: }
119:
120: }
121:
122: return;
123: }
124:
125: short jobtab_get_sem(void)
126: {
127: int tries;
128: struct sembuf s = {0, -1, IPC_NOWAIT};
129:
130: logprintf (FM_LOG_DEBUG, "jobtab_get_sem: attempting to acquire job table semaphore");
131:
132: if (have_jobtab_sem) {
133: logprintf (FM_LOG_DEBUG, "jobtab_get_sem: acquired semaphore by virtue of already owning it");
134: return TRUE;
135: }
136:
137: for (tries = 0; tries < 5; tries++) {
138:
139: if (semop (semid_jobtab, &s, 1) != -1) {
140: logprintf (FM_LOG_DEBUG, "jobtab_get_sem: acquired semaphore by virtue of calling semop on it");
141: have_jobtab_sem = TRUE;
142: return TRUE;
143: }
144:
145: logprintf (FM_LOG_INFO, "jobtab_get_sem: sleeping for retry [tries = %d; errno = %d; error = %s]", tries, errno, strerror (errno));
146: sleep (1);
147:
148: }
149: logprintf (FM_LOG_ERROR, "jobtab_get_sem: failed to acquire job table semaphore");
150:
151: have_jobtab_sem = FALSE;
152: return FALSE;
153: }
154:
155: void jobtab_release_sem(void)
156: {
157: struct sembuf s = {0, 1, 0};
158:
159: logprintf (FM_LOG_DEBUG, "jobtab_release_sem: trying to release job table semaphore");
160: if (semop (semid_jobtab, &s, 1) != -1) {
161: logprintf (FM_LOG_DEBUG, "jobtab_release_sem: released job table semaphore by means of semop");
162: have_jobtab_sem = FALSE;
163: }
164: else {
165: logprintf (FM_LOG_FATAL, "jobtab_release_sem: failed to release job table semaphore (error code %d [%s])", errno, strerror (errno));
166: }
167: }
168:
169:
170: job_slot_t *job_init(short is_fmadm)
171: {
172:
173: job_slot_t *s;
174:
175: if (jobtab_get_sem () == FALSE) {
176: logprintf (FM_LOG_FATAL, "job_init: failed to get job table semaphore");
177: }
178:
179: for (s = SOA(shm_config->hdr->jobtab_head); s != NULL; s = SOA(s->next)) {
180:
181: if (((s->flags & JFLG_DEFUNCT) == JFLG_DEFUNCT) || (s->pid == pid)) {
182: goto skip_alloc;
183: }
184:
185: }
186:
187: s = (job_slot_t *) shm_alloc (sizeof (job_slot_t));
188: NULLPTRCHK(s,"job_init");
189:
190:
191: skip_alloc:
192:
193: s->pid = pid;
194: s->ipc_head = (ipc_slot_t *) NULL;
195: s->status = JSTAT_IDLE;
196: s->start_time = time (NULL);
197: s->stop_requested = 0;
198:
199: if (first_process) {
200: s->flags = JFLG_DAEMON | JFLG_NEW;
201: }
202: else {
203: if (is_fmadm == TRUE) {
204: s->flags = JFLG_NEW | JFLG_FMADM;
205: }
206: else {
207: s->flags = JFLG_NEW;
208: }
209: }
210:
211: s->next = SBM(SOA(shm_config->hdr->jobtab_head));
212: shm_config->hdr->jobtab_head = SBM(s);
213:
214: jobtab_release_sem ();
215:
216: return s;
217:
218: }
219:
220: void job_remove(const pid_t pid)
221: {
222:
223: job_slot_t *t = SOM(shm_config->hdr->jobtab_head);
224: job_slot_t *p = NULL;
225:
226: if (jobtab_get_sem () == FALSE) {
227: logprintf (FM_LOG_FATAL, "job_remove: failed to get job table semaphore");
228: }
229:
230: if ((t != (job_slot_t *) NULL) && (t->pid == pid)) {
231:
232: shm_config->hdr->jobtab_head = SBM(SOA(t->next));
233: shm_free (t);
234:
235: jobtab_release_sem ();
236:
237: return;
238:
239: }
240:
241: while ((t != NULL) && (t->pid != pid)) {
242: p = t;
243: t = SOA(t->next);
244: }
245:
246: if (t == NULL) {
247: jobtab_release_sem ();
248: return;
249: }
250:
251: p->next = SBA(t->next);
252: shm_free (t);
253:
254: jobtab_release_sem ();
255:
256: locktab_unlock_all_by_pid (pid);
257: }
258:
259: void job_request_stop(const pid_t target_pid)
260: {
261:
262: job_slot_t *s;
263:
264: if (jobtab_get_sem() == FALSE) {
265: logprintf (FM_LOG_FATAL, "job_request_stop: failed to get job table semaphore");
266: }
267:
268: for (s = SOA(shm_config->hdr->jobtab_head); s != NULL; s = SOA(s->next)) {
269:
270: if (s->pid == target_pid) {
271: s->stop_requested = pid;
272:
273: jobtab_release_sem ();
274: job_set_status (target_pid, JSTAT_SHUTDOWN);
275:
276: return;
277: }
278:
279: }
280:
281: logprintf (FM_LOG_ERROR, "job_request_stop: could not find pid %ld in job table", target_pid);
282:
283: jobtab_release_sem ();
284:
285: }
286:
287: void job_set_ecode(const pid_t target_pid, const char *ecode)
288: {
289:
290: job_slot_t *s;
291:
292: if (jobtab_get_sem () == FALSE) {
293: logprintf (FM_LOG_FATAL, "job_set_ecode: failed to get job table semaphore");
294: }
295:
296: for (s = SOA(shm_config->hdr->jobtab_head); s != NULL; s = SOA(s->next)) {
297:
298: if (s->pid == target_pid) {
299: strncpy (s->last_ecode, ecode, 20);
300: jobtab_release_sem ();
301: return;
302: }
303:
304: }
305:
306: jobtab_release_sem ();
307:
308: }
309:
310: pid_t job_stop_requested (const pid_t target_pid)
311: {
312:
313: job_slot_t *s;
314:
315: for (s = SOA(shm_config->hdr->jobtab_head); s != NULL; s = SOA(s->next)) {
316: if (s->pid == target_pid) {
317: return s->stop_requested;
318: }
319: }
320:
321: return (pid_t) 0;
322:
323: }
324:
325: void job_request_all_stop(void)
326: {
327:
328: job_slot_t *s;
329:
330:
331: for (s = SOA(shm_config->hdr->jobtab_head); s != NULL; s = SOA(s->next)) {
332:
333: if ((s->flags & JFLG_DAEMON) != JFLG_DAEMON) {
334: job_request_stop (s->pid);
335: }
336:
337: }
338:
339:
340: }
341:
342: void job_signal_all(const int sig)
343: {
344:
345: job_slot_t *s;
346:
347: for (s = SOA(shm_config->hdr->jobtab_head); s != NULL; s = SOA(s->next)) {
348:
349: if ((s->flags & JFLG_DAEMON) != JFLG_DAEMON) {
350: kill (s->pid, sig);
351: }
352:
353: }
354:
355: }
356:
357:
358: int job_count(void)
359: {
360:
361: job_slot_t *s;
362: int ct = 0;
363:
364: for (s = SOA(shm_config->hdr->jobtab_head); s != NULL; s = SOA(s->next)) {
365:
366: if ((s->flags & JFLG_DEFUNCT) != JFLG_DEFUNCT) {
367: ct++;
368: }
369:
370: }
371:
372: return ct;
373:
374: }
375:
376: job_slot_t *job_set_status(const pid_t target_pid, const unsigned short status)
377: {
378:
379: job_slot_t *s;
380:
381: if (jobtab_get_sem () == FALSE) {
382: logprintf (FM_LOG_FATAL, "job_set_status: failed to get job table semaphore");
383: }
384:
385: for (s = SOA(shm_config->hdr->jobtab_head); s != NULL; s = SOA(s->next)) {
386:
387: if (s->pid == target_pid) {
388: s->status = status;
389: jobtab_release_sem ();
390: return s;
391: }
392:
393: }
394:
395: jobtab_release_sem ();
396:
397: return (job_slot_t *) NULL;
398:
399: }
400:
401: void job_gc_mark(void)
402: {
403:
404: job_slot_t *s;
405: int jobstat;
406:
407: if (jobtab_get_sem () == FALSE) {
408: logprintf (FM_LOG_FATAL, "job_gc_mark: failed to get job table semaphore");
409: }
410:
411: for (s = SOA(shm_config->hdr->jobtab_head); s != NULL; s = SOA(s->next)) {
412: logprintf (FM_LOG_WARNING, "job_gc_mark looking at pid %ld", s->pid);
413: if ((jobstat = kill (s->pid, 0)) != 0) {
414:
415: switch (jobstat) {
416:
417: case EPERM:
418: logprintf (FM_LOG_WARNING, "job_gc_mark: environment daemon lacks permissions to pid %ld", (long) s->pid);
419: break;
420:
421: case ESRCH:
422: logprintf (FM_LOG_INFO, "job_gc_mark: marking pid %ld DEFUNCT", (long) s->pid);
423: s->flags = JFLG_DEFUNCT;
424: break;
425:
426: }
427:
428: }
429:
430: if ((s->flags & JFLG_NEW) == JFLG_NEW) {
431: if ((s->flags & JFLG_DAEMON) == JFLG_DAEMON) {
432: logprintf (FM_LOG_INFO, "job_gc_mark: registering new daemon %ld", (long) s->pid);
433: s->flags = JFLG_ALIVE | JFLG_DAEMON;
434: }
435: else {
436: if ((s->flags & JFLG_FMADM) == JFLG_FMADM) {
437: logprintf (FM_LOG_INFO, "job_gc_mark: registering new fmadm process %ld", (long) s->pid);
438: s->flags = JFLG_ALIVE | JFLG_FMADM;
439: }
440: else {
441: logprintf (FM_LOG_INFO, "job_gc_mark: registering new interpreter process %ld", (long) s->pid);
442: s->flags = JFLG_ALIVE;
443: }
444: }
445: }
446:
447: }
448:
449: jobtab_release_sem ();
450:
451: }
452:
453: void job_gc_sweep(void)
454: {
455:
456: job_slot_t *s;
457: char *k_buf = (char *) malloc (STRLEN * sizeof (char));
458: NULLPTRCHK(k_buf,"job_gc_sweep");
459:
460: if (jobtab_get_sem () == FALSE) {
461: logprintf (FM_LOG_FATAL, "job_gc_sweep: failed to get job table semaphore");
462: }
463:
464:
465: for (s = SOA(shm_config->hdr->jobtab_head); s != NULL; s = SOA(s->next)) {
466:
467: if ((s->flags & JFLG_DEFUNCT) == JFLG_DEFUNCT) {
468:
469: logprintf (FM_LOG_INFO, "job_gc_sweep: sweeping DEFUNCT pid %ld", (long) s->pid);
470:
471: snprintf (k_buf, STRLEN - 1, "^$JOB\202%d\201", s->pid);
472:
473: symtab_shm (kill_sym, k_buf, " \201");
474: merr_clear ();
475:
476: job_remove (s->pid);
477:
478: free (k_buf);
479:
480: jobtab_release_sem ();
481: return;
482:
483: }
484:
485: }
486:
487: free (k_buf);
488:
489: jobtab_release_sem ();
490:
491: }
492:
493: ipc_slot_t *job_send_ipc(const pid_t receiver_pid, const void *object)
494: {
495:
496: job_slot_t *j;
497: ipc_slot_t *s = (ipc_slot_t *) shm_alloc (sizeof (ipc_slot_t));
498: NULLPTRCHK(s,"job_send_ipc");
499:
500: if (jobtab_get_sem () == FALSE) {
501: logprintf (FM_LOG_FATAL, "job_send_ipc: failed to get job table semaphore");
502: }
503:
504:
505: for (j = SOA(shm_config->hdr->jobtab_head); j != NULL; j = SOA(j->next)) {
506:
507: if (j->pid == receiver_pid) {
508:
509: s->flags = JIPCFLG_PENDING;
510: s->sender_pid = pid;
511: s->object = (void *) object;
512:
513: s->next = j->ipc_head;
514: j->ipc_head = s;
515:
516: jobtab_release_sem ();
517:
518: return s;
519:
520: }
521:
522: }
523:
524: shm_free (s);
525:
526: jobtab_release_sem ();
527:
528: return (ipc_slot_t *) NULL;
529:
530: }
531:
532: job_slot_t *job_get(const pid_t target_pid)
533: {
534:
535: job_slot_t *s;
536:
537: for (s = SOA(shm_config->hdr->jobtab_head); s != NULL; s = SOA(s->next)) {
538: if (s->pid == target_pid) return s;
539: }
540:
541: return (job_slot_t *) NULL;
542:
543: }
544:
545: void job_dump(void)
546: {
547: char time_buf[20];
548: char *flag_s = (char *) malloc (256 * sizeof (char));
549: char *stat_s = (char *) malloc (256 * sizeof (char));
550:
551: job_slot_t *s;
552:
553: flag_s[0] = '\0';
554: stat_s[0] = '\0';
555:
556: printf ("%-10s%-15s%-20s%-22s%s\r\n", "PID", "STATUS", "LAST ECODE", "STARTED", "FLAGS");
557: printf ("%-10s%-15s%-20s%-22s%s\r\n", "---", "------", "----------", "-------", "-----");
558:
559: for (s = SOA(shm_config->hdr->jobtab_head); s != NULL; s = SOA(s->next)) {
560:
561: strftime (time_buf, 20, "%Y-%m-%d %H:%M:%S", localtime (&(s->start_time)));
562:
563: flag_s[0] = '\0';
564: stat_s[0] = '\0';
565:
566: if ((s->flags & JFLG_ALIVE) == JFLG_ALIVE) strcat (flag_s, "ALIVE ");
567: if ((s->flags & JFLG_DEFUNCT) == JFLG_DEFUNCT) strcat (flag_s, "DEFUNCT ");
568: if ((s->flags & JFLG_REPLSENDER) == JFLG_REPLSENDER) strcat (flag_s, "REPLSENDER ");
569: if ((s->flags & JFLG_REPLRECEIVER) == JFLG_REPLRECEIVER) strcat (flag_s, "REPLRECEIVER ");
570: if ((s->flags & JFLG_FMADM) == JFLG_FMADM) strcat (flag_s, "FMADM ");
571: if ((s->flags & JFLG_NEW) == JFLG_NEW) strcat (flag_s, "NEW ");
572: if ((s->flags & JFLG_DAEMON) == JFLG_DAEMON) strcat (flag_s, "DAEMON ");
573:
574: if (s->status == JSTAT_IDLE) strcat (stat_s, "IDLE");
575: if (s->status == JSTAT_INTERPRETER) strcat (stat_s, "INTERPRETER");
576: if (s->status == JSTAT_HOUSEKEEPING) strcat (stat_s, "HOUSEKEEPING");
577: if (s->status == JSTAT_DIRECTMODE) strcat (stat_s, "DIRECTMODE");
578: if (s->status == JSTAT_ERROR) strcat (stat_s, "ERROR");
579: if (s->status == JSTAT_SHUTDOWN) strcat (stat_s, "SHUTDOWN");
580:
581: printf ("%-10d%-15s%-20s%-22s%s\r\n", s->pid, stat_s, s->last_ecode, time_buf, flag_s);
582:
583:
584: }
585:
586: free (stat_s);
587: free (flag_s);
588:
589: }
590:
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>