File:  [Coherent Logic Development] / freem / src / jobtab.c
Revision 1.5: download - view: text, annotated - select for diffs
Mon Mar 24 02:57:49 2025 UTC (8 days, 21 hours ago) by snw
Branches: MAIN
CVS tags: v0-62-3, HEAD
Shared memory compatibility fixes for OS/2

/*
 *   $Id: jobtab.c,v 1.5 2025/03/24 02:57:49 snw Exp $
 *    job table implementation
 *
 *  
 *   Author: Serena Willis <snw@coherent-logic.com>
 *    Copyright (C) 1998 MUG Deutschland
 *    Copyright (C) 2021, 2023, 2025 Coherent Logic Development LLC
 *
 *
 *   This file is part of FreeM.
 *
 *   FreeM is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU Affero Public License as published by
 *   the Free Software Foundation, either version 3 of the License, or
 *   (at your option) any later version.
 *
 *   FreeM is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU Affero Public License for more details.
 *
 *   You should have received a copy of the GNU Affero Public License
 *   along with FreeM.  If not, see <https://www.gnu.org/licenses/>.
 *
 *   $Log: jobtab.c,v $
 *   Revision 1.5  2025/03/24 02:57:49  snw
 *   Shared memory compatibility fixes for OS/2
 *
 *   Revision 1.4  2025/03/09 19:14:25  snw
 *   First phase of REUSE compliance and header reformat
 *
 *
 * SPDX-FileCopyrightText:  (C) 2025 Coherent Logic Development LLC
 * SPDX-License-Identifier: AGPL-3.0-or-later
 **/
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>

#include "mpsdef.h"
#include "shmmgr.h"
#include "jobtab.h"

#if !defined(__OpenBSD__) && !defined(__APPLE__) && !defined(__OS2__)
union semun {
    int              val;    /* Value for SETVAL */
    struct semid_ds *buf;    /* Buffer for IPC_STAT, IPC_SET */
    unsigned short  *array;  /* Array for GETALL, SETALL */
    struct seminfo  *__buf;  /* Buffer for IPC_INFO
                                (Linux-specific) */
};
#endif

int semid_jobtab;
short have_jobtab_sem = FALSE;

void jobtab_init(void)
{
    union semun arg;
    key_t jt_sk;

    jt_sk = ftok (config_file, 2);
    if (first_process) {

        semid_jobtab = semget (jt_sk, 1, 0666 | IPC_CREAT);
        if (semid_jobtab == -1) {
            fprintf (stderr, "jobtab_init:  failed to create job table semaphore\r\n");
            exit (1);
        }
        else {
            fprintf (stderr, "jobtab_init:  job table semaphore created with semid %d\r\n", semid_jobtab);
        }

        arg.val = 1;
        if (semctl (semid_jobtab, 0, SETVAL, arg) == -1) {
            fprintf (stderr, "jobtab_init:  failed to initialize job table semaphore\r\n");
            exit (1);
        }
        else {
            fprintf (stderr, "jobtab_init:  job table semaphore initialized\r\n");
        }
        
    }
    else {

        semid_jobtab = semget (jt_sk, 1, 0);
        if (semid_jobtab == -1) {
            fprintf (stderr, "jobtab_init:  could not attach to job table semaphore\r\n");
            exit (1);
        }
        
    }
    
    return;
}

short jobtab_get_sem(void)
{
    int tries;
    struct sembuf s = {0, -1, IPC_NOWAIT};

    if (have_jobtab_sem) {
//        fprintf (stderr, "jobtab_get_sem:  this process already owns the job table semaphore\r\n");
        return TRUE;
    }
    
    for (tries = 0; tries < 5; tries++) {

        if (semop (semid_jobtab, &s, 1) != -1) {
            have_jobtab_sem = TRUE;           
            return TRUE;
        }

        /* fprintf (stderr, "jobtab_get_sem:  sleeping for retry [tries = %d]\r\n", tries); */

        sleep (1);

    }
    fprintf (stderr, "jobtab_get_sem:  fail\r\n");
    
    have_jobtab_sem = FALSE;
    return FALSE;
}

void jobtab_release_sem(void)
{
    struct sembuf s = {0, 1, 0};

    semop (semid_jobtab, &s, 1);

    have_jobtab_sem = FALSE;
}


job_slot_t *job_init(short is_fmadm)
{

    job_slot_t *s;
        
    if (jobtab_get_sem () == FALSE) {
        fprintf (stderr, "job_init:  failed to get job table semaphore\r\n");
        exit (1);
    }

    for (s = shm_config->hdr->jobtab_head; s != NULL; s = s->next) {

        if (((s->flags & JFLG_DEFUNCT) == JFLG_DEFUNCT) || (s->pid == pid)) {
            goto skip_alloc;
        }
        
    }

    s = (job_slot_t *) shm_alloc (sizeof (job_slot_t));
    NULLPTRCHK(s,"job_init");

    
skip_alloc:    
    
    s->pid = pid;    
    s->ipc_head = (ipc_slot_t *) NULL;
    s->status = JSTAT_IDLE;
    s->start_time = time (NULL);
    s->stop_requested = 0;
    
    if (first_process) {
        s->flags = JFLG_DAEMON | JFLG_NEW;
    }
    else {
        if (is_fmadm == TRUE) {
            s->flags = JFLG_NEW | JFLG_FMADM;
        }
        else {
            s->flags = JFLG_NEW;
        }
    }
    
    s->next = shm_config->hdr->jobtab_head;
    shm_config->hdr->jobtab_head = s;

    jobtab_release_sem ();
    
    return s;
    
}

void job_remove(const pid_t pid)
{
    
    job_slot_t *t = shm_config->hdr->jobtab_head;
    job_slot_t *p = NULL;

    if (jobtab_get_sem() == FALSE) {
        fprintf (stderr, "job_remove:  failed to get job table semaphore\r\n");
        exit (1);
    }
    
    if ((t != (job_slot_t *) NULL) && (t->pid == pid)) {

        shm_config->hdr->jobtab_head = t->next;
        shm_free (t);

        jobtab_release_sem ();

        return;
        
    }

    while ((t != NULL) && (t->pid != pid)) {
        p = t;
        t = t->next;
    }

    if (t == NULL) {
        jobtab_release_sem ();
        return;
    }

    p->next = t->next;
    shm_free (t);

    jobtab_release_sem ();
    
}

void job_request_stop(const pid_t target_pid)
{

    job_slot_t *s;

    if (jobtab_get_sem() == FALSE) {
        fprintf (stderr, "job_request_stop:  failed to get job table semaphore\r\n");
        exit (1);
    }
    
    for (s = shm_config->hdr->jobtab_head; s != NULL; s = s->next) {

        if (s->pid == target_pid) {
            s->stop_requested = pid;

            jobtab_release_sem ();
            job_set_status (target_pid, JSTAT_SHUTDOWN);
            
            return;
        }
        
    }

    jobtab_release_sem ();
    
}

void job_set_ecode(const pid_t target_pid, const char *ecode)
{

    job_slot_t *s;

    if (jobtab_get_sem() == FALSE) {
        fprintf (stderr, "job_set_ecode:  failed to get job table semaphore\r\n");
        exit (1);
    }
    
    for (s = shm_config->hdr->jobtab_head; s != NULL; s = s->next) {

        if (s->pid == target_pid) {
            strncpy (s->last_ecode, ecode, 20);
            jobtab_release_sem ();
            return;
        }
        
    }

    jobtab_release_sem ();
    
}

pid_t job_stop_requested (const pid_t target_pid)
{

    job_slot_t *s;

    
    for (s = shm_config->hdr->jobtab_head; s != NULL; s = s->next) {
        if (s->pid == target_pid) {
            return s->stop_requested;
        }
    }
    
    return (pid_t) 0;
    
}

void job_request_all_stop(void)
{

    job_slot_t *s;

    
    for (s = shm_config->hdr->jobtab_head; s != NULL; s = s->next) {

        if ((s->flags & JFLG_DAEMON) != JFLG_DAEMON) {
            job_request_stop (s->pid);
        }

    }

    
}

void job_signal_all(const int sig)
{

    job_slot_t *s;

    for (s = shm_config->hdr->jobtab_head; s != NULL; s = s->next) {

        if ((s->flags & JFLG_DAEMON) != JFLG_DAEMON) {
            kill (s->pid, sig);
        }

    }
    
}
    

int job_count(void)
{

    job_slot_t *s;
    int ct = 0;

    for (s = shm_config->hdr->jobtab_head; s != NULL; s = s->next) {

        if ((s->flags & JFLG_DEFUNCT) != JFLG_DEFUNCT) {
            ct++;
        }

    }

    return ct;

}

job_slot_t *job_set_status(const pid_t target_pid, const unsigned short status)
{

    job_slot_t *s;

    if (jobtab_get_sem() == FALSE) {
        fprintf (stderr, "job_set_status:  failed to get job table semaphore\r\n");
        exit (1);
    }    
    
    for (s = shm_config->hdr->jobtab_head; s != NULL; s = s->next) {

        if (s->pid == target_pid) {
            s->status = status;
            jobtab_release_sem ();
            return s;
        }
        
    }

    jobtab_release_sem ();

    return (job_slot_t *) NULL;

}

void job_gc_mark(void)
{

    job_slot_t *s;

    if (jobtab_get_sem () == FALSE) {
        fprintf (stderr, "job_gc_mark:  failed to get job table semaphore\r\n");
        exit (1);
    }

    for (s = shm_config->hdr->jobtab_head; s != NULL; s = s->next) {
       
        if (kill (s->pid, 0) != 0) {
            fprintf (stderr, "job_gc_mark:  marking pid %ld DEFUNCT\r\n", (long) s->pid);
            s->flags = JFLG_DEFUNCT;
        }       

        if ((s->flags & JFLG_NEW) == JFLG_NEW) {
            if ((s->flags & JFLG_DAEMON) == JFLG_DAEMON) {
                fprintf (stderr, "job_gc_mark:  registering new daemon %ld\r\n", (long) s->pid);
                s->flags = JFLG_ALIVE | JFLG_DAEMON;
            }
            else {
                if ((s->flags & JFLG_FMADM) == JFLG_FMADM) {
                    fprintf (stderr, "job_gc_mark:  registering new fmadm process %ld\r\n", (long) s->pid);
                    s->flags = JFLG_ALIVE | JFLG_FMADM;
                }
                else {
                    fprintf (stderr, "job_gc_mark:  registering new interpreter process %ld\r\n", (long) s->pid);
                    s->flags = JFLG_ALIVE;
                }
            }
        }
        
    }

    jobtab_release_sem ();
    
}

void job_gc_sweep(void)
{

    job_slot_t *s;
    char *k_buf = (char *) malloc (STRLEN * sizeof (char));
    NULLPTRCHK(k_buf,"job_gc_sweep");

    if (jobtab_get_sem () == FALSE) {
        fprintf (stderr, "job_gc_sweep:  failed to get job table semaphore\r\n");
        exit (1);
    }

    
    for (s = shm_config->hdr->jobtab_head; s != NULL; s = s->next) {

        if ((s->flags & JFLG_DEFUNCT) == JFLG_DEFUNCT) {

            fprintf (stderr, "job_gc_sweep:  sweeping DEFUNCT pid %ld\r\n", (long) s->pid);

            snprintf (k_buf, STRLEN - 1, "^$JOB\202%d\201", s->pid);

            symtab_shm (kill_sym, k_buf, " \201");
            merr_clear ();
            
            job_remove (s->pid);

            free (k_buf);

            jobtab_release_sem ();
            return;                     
            
        }
        
    }

    free (k_buf);

    jobtab_release_sem ();
    
}

ipc_slot_t *job_send_ipc(const pid_t receiver_pid, const void *object)
{

    job_slot_t *j;
    ipc_slot_t *s = (ipc_slot_t *) shm_alloc (sizeof (ipc_slot_t));
    NULLPTRCHK(s,"job_send_ipc");

    if (jobtab_get_sem() == FALSE) {
        fprintf (stderr, "job_send_ipc:  failed to get job table semaphore\r\n");
        exit (1);
    }

    
    for (j = shm_config->hdr->jobtab_head; j != NULL; j = j->next) {

        if (j->pid == receiver_pid) {
            
            s->flags = JIPCFLG_PENDING;
            s->sender_pid = pid;
            s->object = (void *) object;

            s->next = j->ipc_head;
            j->ipc_head = s;

            jobtab_release_sem ();
            
            return s;
            
        }

    }

    shm_free (s);

    jobtab_release_sem ();
    
    return (ipc_slot_t *) NULL;
    
}

job_slot_t *job_get(const pid_t target_pid)
{
    
    job_slot_t *s;

    for (s = shm_config->hdr->jobtab_head; s != NULL; s = s->next) {
        if (s->pid == target_pid) return s;        
    }

    return (job_slot_t *) NULL;

}

void job_dump(void)
{
    char time_buf[20];
    char *flag_s = (char *) malloc (256 * sizeof (char));
    char *stat_s = (char *) malloc (256 * sizeof (char));    
    
    job_slot_t *s;

    flag_s[0] = '\0';
    stat_s[0] = '\0';
    
    printf ("%-10s%-15s%-20s%-22s%s\r\n", "PID", "STATUS", "LAST ECODE", "STARTED", "FLAGS");
    printf ("%-10s%-15s%-20s%-22s%s\r\n", "---", "------", "----------", "-------", "-----");
    
    for (s = shm_config->hdr->jobtab_head; s != NULL; s = s->next) {

        strftime (time_buf, 20, "%Y-%m-%d %H:%M:%S", localtime (&(s->start_time)));
        
        flag_s[0] = '\0';
        stat_s[0] = '\0';
        
        if ((s->flags & JFLG_ALIVE) == JFLG_ALIVE) strcat (flag_s, "ALIVE ");
        if ((s->flags & JFLG_DEFUNCT) == JFLG_DEFUNCT) strcat (flag_s, "DEFUNCT ");
        if ((s->flags & JFLG_REPLSENDER) == JFLG_REPLSENDER) strcat (flag_s, "REPLSENDER ");
        if ((s->flags & JFLG_REPLRECEIVER) == JFLG_REPLRECEIVER) strcat (flag_s, "REPLRECEIVER ");
        if ((s->flags & JFLG_FMADM) == JFLG_FMADM) strcat (flag_s, "FMADM ");
        if ((s->flags & JFLG_NEW) == JFLG_NEW) strcat (flag_s, "NEW ");
        if ((s->flags & JFLG_DAEMON) == JFLG_DAEMON) strcat (flag_s, "DAEMON ");

        if (s->status == JSTAT_IDLE) strcat (stat_s, "IDLE");
        if (s->status == JSTAT_INTERPRETER) strcat (stat_s, "INTERPRETER");
        if (s->status == JSTAT_HOUSEKEEPING) strcat (stat_s, "HOUSEKEEPING");
        if (s->status == JSTAT_DIRECTMODE) strcat (stat_s, "DIRECTMODE");
        if (s->status == JSTAT_ERROR) strcat (stat_s, "ERROR");
        if (s->status == JSTAT_SHUTDOWN) strcat (stat_s, "SHUTDOWN");

        printf ("%-10d%-15s%-20s%-22s%s\r\n", s->pid, stat_s, s->last_ecode, time_buf, flag_s);
       
        
    }

    free (stat_s);
    free (flag_s);
    
}


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