Annotation of freem/src/journal.c, revision 1.1.1.1
1.1 snw 1: /*
2: * *
3: * * *
4: * * *
5: * ***************
6: * * * * *
7: * * MUMPS *
8: * * * * *
9: * ***************
10: * * *
11: * * *
12: * *
13: *
14: * journal.c
15: * Implementation of FreeM journaling
16: *
17: *
18: * Author: Serena Willis <jpw@coherent-logic.com>
19: * Copyright (C) 1998 MUG Deutschland
20: * Copyright (C) 2020 Coherent Logic Development LLC
21: *
22: *
23: * This file is part of FreeM.
24: *
25: * FreeM is free software: you can redistribute it and/or modify
26: * it under the terms of the GNU Affero Public License as published by
27: * the Free Software Foundation, either version 3 of the License, or
28: * (at your option) any later version.
29: *
30: * FreeM is distributed in the hope that it will be useful,
31: * but WITHOUT ANY WARRANTY; without even the implied warranty of
32: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
33: * GNU Affero Public License for more details.
34: *
35: * You should have received a copy of the GNU Affero Public License
36: * along with FreeM. If not, see <https://www.gnu.org/licenses/>.
37: *
38: **/
39:
40: #include <stdio.h>
41: #include <string.h>
42: #include <stdlib.h>
43:
44: #include <sys/types.h>
45: #include <sys/stat.h>
46: #include <fcntl.h>
47: #include <time.h>
48:
49: #include <unistd.h>
50: #include <errno.h>
51:
52: #include "mpsdef.h"
53: #include "journal.h"
54: #include "transact.h"
55: #include "iniconf.h"
56: #include "shmmgr.h"
57:
58: unsigned long jnl_tran_id; /* transaction id for journaling */
59: unsigned long jnl_cut_threshold; /* byte limit of journal file before cutting new */
60: char jnl_file_path[PATH_MAX]; /* path to journal file */
61: char jnl_host_id[256]; /* host ID (configured at install time) */
62: short jnl_locked = FALSE; /* is the journal locked? */
63: int jnl_desc = 0; /* journal file descriptor */
64:
65: short jnl_enabled = FALSE;
66:
67: void jnl_cut(void);
68: void jnl_panic(char *msg);
69: void jnl_update_tid(void);
70: void jnl_lock(void);
71: void jnl_unlock(void);
72:
73: short jnl_init(char *jnlfile, char *hostid, unsigned long cut_threshold, unsigned long tran_id)
74: {
75:
76: jnl_hdr_t hdr;
77: char tmsg[256];
78:
79: char m[5] = "FRMJL";
80:
81: strncpy (jnl_host_id, hostid, 255);
82: jnl_cut_threshold = cut_threshold;
83:
84: /* cannot re-init in a running process */
85: if ((jnl_desc) && (tran_id == 0)) return FALSE;
86:
87: strncpy (jnl_file_path, jnlfile, PATH_MAX - 1);
88:
89: if (!file_exists (jnl_file_path)) {
90:
91: /* this is a new journal file */
92: jnl_tran_id = tran_id;
93:
94: jnl_desc = open (jnl_file_path, O_CREAT | O_APPEND | O_RDWR, S_IRWXU | S_IRWXG | S_IRWXO);
95:
96: if (jnl_desc == -1) jnl_panic ("error creating new journal file");
97:
98: jnl_lock ();
99:
100: memcpy (hdr.magic, m, 5);
101: hdr.fmt_version = FRM_JNL_VERSION;
102: snprintf (hdr.host_triplet, 40, "%s", HOST);
103:
104: if (write (jnl_desc, &hdr, sizeof (jnl_hdr_t)) == -1) {
105: snprintf (tmsg, 255, "error %d writing to journal file", errno);
106: jnl_panic (tmsg);
107: }
108:
109: jnl_unlock ();
110:
111: close (jnl_desc);
112:
113: }
114: else {
115:
116: /* this journal file already exists */
117:
118: jnl_desc = open (jnl_file_path, O_APPEND | O_RDWR);
119:
120: lseek (jnl_desc, 0L, SEEK_SET);
121:
122: jnl_lock ();
123:
124: read (jnl_desc, &hdr, sizeof (jnl_hdr_t));
125:
126: if (strncmp (hdr.magic, m, 5) != 0) {
127:
128: set_io (UNIX);
129: fprintf (stderr, "%s is not a valid FreeM journal file.\n", jnl_file_path);
130: set_io (MUMPS);
131:
132: return FALSE;
133:
134: }
135:
136: if (hdr.fmt_version != FRM_JNL_VERSION) {
137:
138: set_io (UNIX);
139: fprintf (stderr, "Journal file version mismatch.\n");
140: set_io (MUMPS);
141:
142: return FALSE;
143:
144: }
145:
146: //strncpy (jnl_host_id, hdr.)
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:
229: snprintf (msg, 255, "ran out of disk space while attempting journal write");
230: break;
231:
232: default:
233: snprintf (msg, 255, strerror (errno));
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>