File:  [Coherent Logic Development] / freem / src / jobtab.c
Revision 1.13: download - view: text, annotated - select for diffs
Mon Jun 23 20:53:30 2025 UTC (5 weeks, 5 days ago) by snw
Branches: MAIN
CVS tags: HEAD
Documentation updates

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

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>