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>