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