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