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