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