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