Annotation of freem/src/journal.c, revision 1.7

1.1       snw         1: /*
1.7     ! snw         2:  *   $Id: journal.c,v 1.6 2025/04/10 01:24:38 snw Exp $
1.1       snw         3:  *    Implementation of FreeM journaling
                      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) 2020, 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: journal.c,v $
1.7     ! snw        27:  *   Revision 1.6  2025/04/10 01:24:38  snw
        !            28:  *   Remove C++ style comments
        !            29:  *
1.6       snw        30:  *   Revision 1.5  2025/04/03 20:48:14  snw
                     31:  *   Improve daemon error diagnostics and bump to 0.63.0-rc3
                     32:  *
1.5       snw        33:  *   Revision 1.4  2025/03/09 19:14:25  snw
                     34:  *   First phase of REUSE compliance and header reformat
                     35:  *
1.4       snw        36:  *
                     37:  * SPDX-FileCopyrightText:  (C) 2025 Coherent Logic Development LLC
                     38:  * SPDX-License-Identifier: AGPL-3.0-or-later
1.1       snw        39:  **/
                     40: 
                     41: #include <stdio.h>
                     42: #include <string.h>
                     43: #include <stdlib.h>
                     44: 
                     45: #include <sys/types.h>
                     46: #include <sys/stat.h>
                     47: #include <fcntl.h>
                     48: #include <time.h>
                     49: 
                     50: #include <unistd.h>
                     51: #include <errno.h>
                     52: 
                     53: #include "mpsdef.h"
                     54: #include "journal.h"
                     55: #include "transact.h"
                     56: #include "iniconf.h"
                     57: #include "shmmgr.h"
                     58: 
                     59: unsigned long jnl_tran_id;       /* transaction id for journaling */
                     60: unsigned long jnl_cut_threshold; /* byte limit of journal file before cutting new */
                     61: char jnl_file_path[PATH_MAX];    /* path to journal file */
                     62: char jnl_host_id[256];           /* host ID (configured at install time) */
                     63: short jnl_locked = FALSE;        /* is the journal locked? */
                     64: int jnl_desc = 0;                /* journal file descriptor */
                     65: 
                     66: short jnl_enabled = FALSE;
                     67: 
                     68: void jnl_cut(void);
                     69: void jnl_panic(char *msg);
                     70: void jnl_update_tid(void);
                     71: void jnl_lock(void);
                     72: void jnl_unlock(void);
                     73: 
                     74: short jnl_init(char *jnlfile, char *hostid, unsigned long cut_threshold, unsigned long tran_id)
                     75: {
                     76: 
                     77:     jnl_hdr_t hdr;
                     78:     char tmsg[256];
                     79:     
                     80:     char m[5] = "FRMJL";
                     81: 
                     82:     strncpy (jnl_host_id, hostid, 255);
                     83:     jnl_cut_threshold = cut_threshold;
                     84: 
                     85:     /* cannot re-init in a running process */
                     86:     if ((jnl_desc) && (tran_id == 0)) return FALSE;
                     87: 
                     88:     strncpy (jnl_file_path, jnlfile, PATH_MAX - 1);
                     89:     
                     90:     if (!file_exists (jnl_file_path)) {
                     91: 
                     92:         /* this is a new journal file */
                     93:         jnl_tran_id = tran_id;
                     94: 
                     95:         jnl_desc = open (jnl_file_path, O_CREAT | O_APPEND | O_RDWR, S_IRWXU | S_IRWXG | S_IRWXO);
                     96: 
1.7     ! snw        97:         snprintf (tmsg, sizeof (tmsg) - 1, "error creating new journal file '%s' [errno %d: '%s']", jnl_file_path, errno, strerror (errno));
1.5       snw        98:         if (jnl_desc == -1) jnl_panic (tmsg);
1.1       snw        99:         
                    100:         jnl_lock ();
                    101: 
                    102:         memcpy (hdr.magic, m, 5);
                    103:         hdr.fmt_version = FRM_JNL_VERSION;
1.7     ! snw       104:         snprintf (hdr.host_triplet, sizeof (hdr.host_triplet) - 1, "%s", HOST);
1.1       snw       105:         
                    106:         if (write (jnl_desc, &hdr, sizeof (jnl_hdr_t)) == -1) {
1.7     ! snw       107:             snprintf (tmsg, sizeof (tmsg) - 1, "error %d writing to journal file", errno);
1.1       snw       108:             jnl_panic (tmsg);
                    109:         }
                    110: 
                    111:         jnl_unlock ();
                    112: 
                    113:         close (jnl_desc);
                    114: 
                    115:     }
                    116:     else {
                    117: 
                    118:         /* this journal file already exists */
                    119: 
                    120:         jnl_desc = open (jnl_file_path, O_APPEND | O_RDWR);
                    121: 
                    122:         lseek (jnl_desc, 0L, SEEK_SET);
                    123:         
                    124:         jnl_lock ();
                    125: 
                    126:         read (jnl_desc, &hdr, sizeof (jnl_hdr_t));
                    127: 
                    128:         if (strncmp (hdr.magic, m, 5) != 0) {
                    129:             
                    130:             set_io (UNIX);
                    131:             fprintf (stderr, "%s is not a valid FreeM journal file.\n", jnl_file_path);
                    132:             set_io (MUMPS);
                    133: 
                    134:             return FALSE;
                    135: 
                    136:         }
                    137: 
                    138:         if (hdr.fmt_version != FRM_JNL_VERSION) {
                    139:             
                    140:             set_io (UNIX);
                    141:             fprintf (stderr, "Journal file version mismatch.\n");
                    142:             set_io (MUMPS);
                    143: 
                    144:             return FALSE;
                    145: 
                    146:         }
                    147: 
                    148:         jnl_unlock ();
                    149: 
                    150:         close (jnl_desc);
                    151: 
                    152:     }
                    153: 
                    154:     jnl_desc = open (jnl_file_path, O_APPEND | O_RDWR);
                    155: 
                    156:     lseek (jnl_desc, 0L, SEEK_END);
                    157: 
                    158:     jnl_lock ();
                    159:     jnl_update_tid ();
                    160:     jnl_unlock ();
                    161: 
                    162:     jnl_enabled = TRUE;
                    163: 
                    164:     return TRUE;
                    165: 
                    166: }
                    167: 
                    168: void jnl_cleanup(void)
                    169: {
                    170: 
                    171:     if (jnl_desc) {
                    172:         jnl_unlock ();
                    173:         close (jnl_desc);
                    174:     }
                    175: 
                    176:     return;
                    177: 
                    178: }
                    179: 
                    180: short jnl_ent_write(short action, char *key, char *data)
                    181: {
                    182: 
                    183:     jnl_ent_t ent;
                    184:     size_t siz;
                    185:     char msg[256];
                    186: 
                    187:     jnl_lock ();
                    188: 
                    189:     if ((tp_level == 0) && (action != JNLA_TSTART)) {
                    190:         /* make sure we have the latest transaction ID */
                    191:         jnl_update_tid ();
                    192:     }
                    193: 
                    194:     siz = lseek (jnl_desc, 0L, SEEK_END);
                    195: 
                    196:     if ((siz + sizeof (jnl_ent_t)) >= jnl_cut_threshold) jnl_cut ();
                    197: 
                    198:     /* only increment the transaction ID if we're NOT in a transaction
                    199:        or this action begins one. */
                    200:     if ((tp_level == 0) || action == JNLA_TSTART) {
                    201:         if (tp_get_sem () == FALSE) {
                    202:             jnl_panic ("could not get transaction processing semaphore");
                    203:         }
                    204:         else {
                    205:             jnl_tran_id++;
                    206:             shm_config->hdr->tp_serial_number = jnl_tran_id;
                    207: 
                    208:             tp_release_sem ();
                    209:         }
                    210:     }
                    211: 
                    212:     ent.tran_id = jnl_tran_id;
                    213:     ent.ts = time (NULL);
                    214:     ent.pid = (pid_t) pid;
                    215:     ent.action = action;
                    216: 
                    217:     strncpy (ent.host_id, jnl_host_id, 255);
                    218:     strncpy (ent.key, key, 1023);
                    219:     strncpy (ent.data, data, 1023);
                    220: 
                    221:     lseek (jnl_desc, 0L, SEEK_END);
                    222: 
                    223:     errno = 0;
                    224:     if ((siz = write (jnl_desc, &ent, sizeof (jnl_ent_t))) < sizeof (jnl_ent_t)) {
                    225: 
                    226:         switch (errno) {
                    227: 
                    228:             case ENOSPC:
1.7     ! snw       229:                 snprintf (msg, sizeof (msg) - 1, "ran out of disk space while attempting journal write");
1.1       snw       230:                 break;
                    231: 
                    232:             default:
1.7     ! snw       233:                 snprintf (msg, sizeof (msg) - 1, "%s", strerror (errno));
1.1       snw       234:                 break;
                    235: 
                    236:         }
                    237: 
                    238:         jnl_panic (msg);
                    239:                 
                    240:     }
                    241: 
                    242:     jnl_unlock ();
                    243: 
                    244:     return 1;
                    245:     
                    246: }
                    247: 
                    248: void jnl_update_tid(void)
                    249: {
                    250:     jnl_ent_t ent;
                    251: 
                    252:     if  (tp_get_sem () == TRUE) {
                    253:     
                    254:         if (first_process == TRUE) {
                    255:     
                    256:             if (!jnl_desc) return;
                    257:             
                    258:             lseek (jnl_desc, 0L, SEEK_END);
                    259:             lseek (jnl_desc, -sizeof (jnl_ent_t), SEEK_CUR);
                    260: 
                    261:             read (jnl_desc, &ent, sizeof (jnl_ent_t));
                    262:             
                    263:             jnl_tran_id = ent.tran_id;
                    264: 
                    265:             shm_config->hdr->tp_serial_number = ent.tran_id;
                    266: 
                    267:         }
                    268:         else {
                    269:             jnl_tran_id = shm_config->hdr->tp_serial_number;
                    270:         }
                    271: 
                    272:         tp_release_sem ();
                    273: 
                    274:     }
                    275:     else {
                    276:         jnl_panic ("jnl_update_tid:  could not acquire transaction processing sempahore");
                    277:     }
                    278: 
                    279: }
                    280: 
                    281: inline void jnl_lock(void) 
                    282: {
                    283:     struct flock lock;
                    284: 
                    285:     lock.l_type = F_WRLCK;
                    286:     lock.l_whence = SEEK_SET;
                    287:     lock.l_start = 0;
                    288:     lock.l_len = 0;
                    289: 
                    290:     fcntl (jnl_desc, F_SETLK, &lock);
                    291: 
                    292:     jnl_locked = TRUE;
                    293: 
                    294:     return;
                    295: }
                    296: 
                    297: inline void jnl_unlock(void)
                    298: {
                    299:     struct flock lock;
                    300: 
                    301:     lock.l_type = F_UNLCK;
                    302:     lock.l_whence = SEEK_SET;
                    303:     lock.l_start = 0;
                    304:     lock.l_len = 0;
                    305: 
                    306:     fcntl (jnl_desc, F_SETLK, &lock);
                    307: 
                    308:     jnl_locked = FALSE;
                    309: 
                    310:     return;
                    311: }
                    312: 
                    313: void jnl_cut(void)
                    314: {
                    315:     char cutname[PATH_MAX];
                    316: 
                    317: 
                    318:     if (jnl_desc) {
                    319: 
                    320:         jnl_lock ();
                    321: 
                    322:         jnl_update_tid ();
                    323: 
                    324:         snprintf (cutname, PATH_MAX - 1, "%s.%ld", jnl_file_path, jnl_tran_id);
                    325:         close (jnl_desc);
                    326: 
                    327:         rename (jnl_file_path, cutname);
                    328: 
                    329:         if(tp_level == 0) {
                    330:             jnl_init (jnl_file_path, jnl_host_id, jnl_cut_threshold, ++jnl_tran_id);
                    331:         }
                    332:         else {
                    333:             jnl_init (jnl_file_path, jnl_host_id, jnl_cut_threshold, jnl_tran_id);
                    334:         }
                    335: 
                    336:         jnl_unlock();
                    337: 
                    338:     }
                    339: 
                    340:     return;
                    341: 
                    342: }
                    343: 
                    344: void jnl_panic(char *msg)
                    345: {
                    346:     set_io (UNIX);
                    347: 
                    348:     if (tp_level > 0) {
                    349:         fprintf (stderr, "journal error:  [%s] (rolling back all transactions)\n", msg);
                    350:         tp_trollback (tp_level);
                    351:     }
                    352:     else {
                    353:         fprintf (stderr, "journal error:  [%s]\n", msg);
                    354:     }
                    355: 
                    356:     jnl_cleanup ();
                    357: 
                    358:     exit (1);
                    359: }

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