Annotation of freem/src/jobtab.c, revision 1.13

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

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