File:  [Coherent Logic Development] / freem / src / journal.c
Revision 1.4: download - view: text, annotated - select for diffs
Sun Mar 9 19:14:25 2025 UTC (3 weeks, 2 days ago) by snw
Branches: MAIN
CVS tags: v0-62-3, v0-62-2, v0-62-1, v0-62-0, HEAD
First phase of REUSE compliance and header reformat

/*
 *   $Id: journal.c,v 1.4 2025/03/09 19:14:25 snw Exp $
 *    Implementation of FreeM journaling
 *
 *  
 *   Author: Serena Willis <snw@coherent-logic.com>
 *    Copyright (C) 1998 MUG Deutschland
 *    Copyright (C) 2020, 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: journal.c,v $
 *   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 <stdio.h>
#include <string.h>
#include <stdlib.h>

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <time.h>

#include <unistd.h>
#include <errno.h>

#include "mpsdef.h"
#include "journal.h"
#include "transact.h"
#include "iniconf.h"
#include "shmmgr.h"

unsigned long jnl_tran_id;       /* transaction id for journaling */
unsigned long jnl_cut_threshold; /* byte limit of journal file before cutting new */
char jnl_file_path[PATH_MAX];    /* path to journal file */
char jnl_host_id[256];           /* host ID (configured at install time) */
short jnl_locked = FALSE;        /* is the journal locked? */
int jnl_desc = 0;                /* journal file descriptor */

short jnl_enabled = FALSE;

void jnl_cut(void);
void jnl_panic(char *msg);
void jnl_update_tid(void);
void jnl_lock(void);
void jnl_unlock(void);

short jnl_init(char *jnlfile, char *hostid, unsigned long cut_threshold, unsigned long tran_id)
{

    jnl_hdr_t hdr;
    char tmsg[256];
    
    char m[5] = "FRMJL";

    strncpy (jnl_host_id, hostid, 255);
    jnl_cut_threshold = cut_threshold;

    /* cannot re-init in a running process */
    if ((jnl_desc) && (tran_id == 0)) return FALSE;

    strncpy (jnl_file_path, jnlfile, PATH_MAX - 1);
    
    if (!file_exists (jnl_file_path)) {

        /* this is a new journal file */
        jnl_tran_id = tran_id;

        jnl_desc = open (jnl_file_path, O_CREAT | O_APPEND | O_RDWR, S_IRWXU | S_IRWXG | S_IRWXO);

        if (jnl_desc == -1) jnl_panic ("error creating new journal file");
        
        jnl_lock ();

        memcpy (hdr.magic, m, 5);
        hdr.fmt_version = FRM_JNL_VERSION;
        snprintf (hdr.host_triplet, 40, "%s", HOST);
        
        if (write (jnl_desc, &hdr, sizeof (jnl_hdr_t)) == -1) {
            snprintf (tmsg, 255, "error %d writing to journal file", errno);
            jnl_panic (tmsg);
        }

        jnl_unlock ();

        close (jnl_desc);

    }
    else {

        /* this journal file already exists */

        jnl_desc = open (jnl_file_path, O_APPEND | O_RDWR);

        lseek (jnl_desc, 0L, SEEK_SET);
        
        jnl_lock ();

        read (jnl_desc, &hdr, sizeof (jnl_hdr_t));

        if (strncmp (hdr.magic, m, 5) != 0) {
            
            set_io (UNIX);
            fprintf (stderr, "%s is not a valid FreeM journal file.\n", jnl_file_path);
            set_io (MUMPS);

            return FALSE;

        }

        if (hdr.fmt_version != FRM_JNL_VERSION) {
            
            set_io (UNIX);
            fprintf (stderr, "Journal file version mismatch.\n");
            set_io (MUMPS);

            return FALSE;

        }

        //strncpy (jnl_host_id, hdr.)
        
        jnl_unlock ();

        close (jnl_desc);

    }

    jnl_desc = open (jnl_file_path, O_APPEND | O_RDWR);

    lseek (jnl_desc, 0L, SEEK_END);

    jnl_lock ();
    jnl_update_tid ();
    jnl_unlock ();

    jnl_enabled = TRUE;

    return TRUE;

}

void jnl_cleanup(void)
{

    if (jnl_desc) {
        jnl_unlock ();
        close (jnl_desc);
    }

    return;

}

short jnl_ent_write(short action, char *key, char *data)
{

    jnl_ent_t ent;
    size_t siz;
    char msg[256];

    jnl_lock ();

    if ((tp_level == 0) && (action != JNLA_TSTART)) {
        /* make sure we have the latest transaction ID */
        jnl_update_tid ();
    }

    siz = lseek (jnl_desc, 0L, SEEK_END);

    if ((siz + sizeof (jnl_ent_t)) >= jnl_cut_threshold) jnl_cut ();

    /* only increment the transaction ID if we're NOT in a transaction
       or this action begins one. */
    if ((tp_level == 0) || action == JNLA_TSTART) {
        if (tp_get_sem () == FALSE) {
            jnl_panic ("could not get transaction processing semaphore");
        }
        else {
            jnl_tran_id++;
            shm_config->hdr->tp_serial_number = jnl_tran_id;

            tp_release_sem ();
        }
    }

    ent.tran_id = jnl_tran_id;
    ent.ts = time (NULL);
    ent.pid = (pid_t) pid;
    ent.action = action;

    strncpy (ent.host_id, jnl_host_id, 255);
    strncpy (ent.key, key, 1023);
    strncpy (ent.data, data, 1023);

    lseek (jnl_desc, 0L, SEEK_END);

    errno = 0;
    if ((siz = write (jnl_desc, &ent, sizeof (jnl_ent_t))) < sizeof (jnl_ent_t)) {

        switch (errno) {

            case ENOSPC:
                snprintf (msg, 255, "ran out of disk space while attempting journal write");
                break;

            default:
                snprintf (msg, 255, "%s", strerror (errno));
                break;

        }

        jnl_panic (msg);
                
    }

    jnl_unlock ();

    return 1;
    
}

void jnl_update_tid(void)
{
    jnl_ent_t ent;

    if  (tp_get_sem () == TRUE) {
    
        if (first_process == TRUE) {
    
            if (!jnl_desc) return;
            
            lseek (jnl_desc, 0L, SEEK_END);
            lseek (jnl_desc, -sizeof (jnl_ent_t), SEEK_CUR);

            read (jnl_desc, &ent, sizeof (jnl_ent_t));
            
            jnl_tran_id = ent.tran_id;

            shm_config->hdr->tp_serial_number = ent.tran_id;

        }
        else {
            jnl_tran_id = shm_config->hdr->tp_serial_number;
        }

        tp_release_sem ();

    }
    else {
        jnl_panic ("jnl_update_tid:  could not acquire transaction processing sempahore");
    }

}

inline void jnl_lock(void) 
{
    struct flock lock;

    lock.l_type = F_WRLCK;
    lock.l_whence = SEEK_SET;
    lock.l_start = 0;
    lock.l_len = 0;

    fcntl (jnl_desc, F_SETLK, &lock);

    jnl_locked = TRUE;

    return;
}

inline void jnl_unlock(void)
{
    struct flock lock;

    lock.l_type = F_UNLCK;
    lock.l_whence = SEEK_SET;
    lock.l_start = 0;
    lock.l_len = 0;

    fcntl (jnl_desc, F_SETLK, &lock);

    jnl_locked = FALSE;

    return;
}

void jnl_cut(void)
{
    char cutname[PATH_MAX];


    if (jnl_desc) {

        jnl_lock ();

        jnl_update_tid ();

        snprintf (cutname, PATH_MAX - 1, "%s.%ld", jnl_file_path, jnl_tran_id);
        close (jnl_desc);

        rename (jnl_file_path, cutname);

        if(tp_level == 0) {
            jnl_init (jnl_file_path, jnl_host_id, jnl_cut_threshold, ++jnl_tran_id);
        }
        else {
            jnl_init (jnl_file_path, jnl_host_id, jnl_cut_threshold, jnl_tran_id);
        }

        jnl_unlock();

    }

    return;

}

void jnl_panic(char *msg)
{
    set_io (UNIX);

    if (tp_level > 0) {
        fprintf (stderr, "journal error:  [%s] (rolling back all transactions)\n", msg);
        tp_trollback (tp_level);
    }
    else {
        fprintf (stderr, "journal error:  [%s]\n", msg);
    }

    jnl_cleanup ();

    exit (1);
}

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