/*
* $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>