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