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>