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>