1: /*
2: * $Id: locktab.c,v 1.9 2025/05/15 02:49:11 snw Exp $
3: * lock table implementation
4: *
5: *
6: * Author: Serena Willis <snw@coherent-logic.com>
7: * Copyright (C) 1998 MUG Deutschland
8: * Copyright (C) 2021, 2025 Coherent Logic Development LLC
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: *
26: * $Log: locktab.c,v $
27: * Revision 1.9 2025/05/15 02:49:11 snw
28: * Fix linker error on errno symbol in locktab.c
29: *
30: * Revision 1.8 2025/05/14 15:28:55 snw
31: * Get basic job table and lock table functionality working with new shared memory architecture
32: *
33: * Revision 1.7 2025/05/01 21:02:31 snw
34: * Documentation updates
35: *
36: * Revision 1.6 2025/04/13 04:22:43 snw
37: * Fix snprintf calls
38: *
39: * Revision 1.5 2025/03/24 02:57:25 snw
40: * Shared memory compatibility fixes for OS/2
41: *
42: * Revision 1.4 2025/03/09 19:14:25 snw
43: * First phase of REUSE compliance and header reformat
44: *
45: *
46: * SPDX-FileCopyrightText: (C) 2025 Coherent Logic Development LLC
47: * SPDX-License-Identifier: AGPL-3.0-or-later
48: **/
49:
50: #include <stdio.h>
51: #include <stdlib.h>
52: #include <unistd.h>
53: #include <time.h>
54: #include <string.h>
55: #include <errno.h>
56:
57: #include "mpsdef.h"
58: #include "locktab.h"
59: #include "shmmgr.h"
60: #include "mref.h"
61: #include "transact.h"
62: #include "log.h"
63:
64: #if !defined(__OpenBSD__) && !defined(__APPLE__) && !defined(__OS2__)
65: union semun {
66: int val; /* Value for SETVAL */
67: struct semid_ds *buf; /* Buffer for IPC_STAT, IPC_SET */
68: unsigned short *array; /* Array for GETALL, SETALL */
69: struct seminfo *__buf; /* Buffer for IPC_INFO
70: (Linux-specific) */
71: };
72: #endif
73:
74: int semid_locktab;
75:
76: int locktab_list_count(char *key);
77:
78: void locktab_init(void)
79: {
80: union semun arg;
81: key_t lt_sk;
82:
83: lt_sk = ftok (config_file, 3);
84:
85: if (first_process) {
86:
87: semid_locktab = semget (lt_sk, 1, 0666 | IPC_CREAT);
88: if (semid_locktab == -1) {
89: logprintf (FM_LOG_FATAL, "locktab_init: failed to create lock table semaphore");
90: }
91:
92: arg.val = 1;
93: if (semctl (semid_locktab, 0, SETVAL, arg) == -1) {
94: logprintf (FM_LOG_FATAL, "locktab_init: failed to initialize lock table semaphore");
95: }
96:
97: }
98: else {
99:
100: semid_locktab = semget (lt_sk, 1, 0);
101: if (semid_locktab == -1) {
102: logprintf (FM_LOG_FATAL, "locktab_init: could not attach to lock table semaphore");
103: }
104:
105: }
106:
107: return;
108:
109: }
110:
111: short locktab_get_sem(void)
112: {
113: int tries;
114: struct sembuf s = {0, -1, 0};
115:
116: logprintf (FM_LOG_DEBUG, "locktab_get_sem: attempting to acquire lock table semaphore");
117:
118: for (tries = 0; tries < 5; tries++) {
119:
120: if (semop (semid_locktab, &s, 1) != -1) {
121: logprintf (FM_LOG_DEBUG, "locktab_get_sem: acquired semaphore by virtue of calling semop on it");
122: return TRUE;
123: }
124:
125: logprintf (FM_LOG_INFO, "locktab_get_sem: sleeping for retry [tries = %d; errno = %d; error = %s]", tries, errno, strerror (errno));
126: sleep (1);
127:
128: }
129: logprintf (FM_LOG_ERROR, "locktab_get_sem: failed to acquire job table semaphore");
130:
131: return FALSE;
132: }
133:
134: void locktab_release_sem(void)
135: {
136: struct sembuf s = {0, 1, 0};
137:
138: logprintf (FM_LOG_DEBUG, "locktab_release_sem: trying to release lock table semaphore");
139: if(semop (semid_locktab, &s, 1) != - 1) {
140: logprintf (FM_LOG_DEBUG, "locktab_release_sem: released lock table semaphore by means of semop");
141: }
142: else {
143: logprintf (FM_LOG_FATAL, "locktab_release_sem: failed to release lock table semaphore (error code %d [%s])", errno, strerror (errno));
144: }
145:
146: }
147:
148: void lock(char *lockarg, long time_out, char type)
149: {
150: char *key = &(lockarg[1]);
151: char a = lockarg[0];
152:
153: if (shm_config == NULL) {
154: fprintf (stderr, "lock: global LOCK operation attemped before shared memory available.\r\n");
155: return;
156: }
157:
158: switch (a) {
159:
160: case '+':
161: locktab_increment (key, time_out, FALSE);
162: break;
163:
164: case '-':
165: locktab_decrement (key, time_out);
166: break;
167:
168: case SP:
169: locktab_unlock_all ();
170: locktab_increment (key, time_out, TRUE);
171: break;
172:
173: default:
174: break;
175:
176: }
177:
178: return;
179:
180: }
181:
182: void locktab_increment(char *key, long lck_timeout, short old_lock)
183: {
184: short lck_action;
185: char chk_ns[256];
186: int nref_ct = locktab_list_count (key);
187:
188: if (old_lock) {
189: lck_action = lock_old;
190: }
191: else {
192: lck_action = lock_inc;
193: }
194:
195: strncpy (chk_ns, nsname, 256);
196:
197: switch (lck_timeout) {
198:
199: case -1: /* blocking lock (no timeout) */
200:
201: if (nref_ct > 1) {
202: /* this is a lock list */
203: char *nref;
204: char tmps[255];
205:
206: int i;
207: int successes = 0;
208: int attempts = 0;
209: int list_pos = 0;
210:
211: char *attempt_status = (char *) malloc (nref_ct * sizeof (char));
212: NULLPTRCHK(attempt_status,"locktab_increment");
213:
214: for (i = 0; i < nref_ct; i++) attempt_status[i] = (char) FALSE;
215:
216: stcpy (tmps, key);
217: stcnv_m2c (tmps);
218:
219: nref = strtok (tmps, "\001\201");
220:
221: do {
222:
223: list_pos = 0;
224: attempts = 0;
225: successes = 0;
226:
227: for (;;) {
228:
229: attempts++;
230:
231: if (attempt_status[list_pos] == FALSE) {
232:
233: if (locktab_insert (nref) != NULL) {
234: successes++;
235: attempt_status[list_pos] = TRUE;
236: }
237: else {
238: locktab_decrement (nref, -1L);
239: attempt_status[list_pos] = FALSE;
240: }
241:
242: }
243:
244: nref = strtok (NULL, "\001\201");
245: if (nref == NULL) break;
246:
247: list_pos++;
248:
249: }
250:
251: } while (successes < nref_ct);
252:
253: free (attempt_status);
254: if (tp_level > 0) tp_add_op (TRUE, lck_action, key, chk_ns);
255: return;
256:
257: }
258: else {
259: int sigint_ct = 0;
260:
261: for (;;) {
262:
263: if (locktab_insert (key) != NULL) {
264: if (tp_level > 0) tp_add_op (TRUE, lck_action, key, chk_ns);
265: return;
266: }
267: else {
268: if (merr () == INRPT) {
269: sigint_ct++;
270:
271: if (sigint_ct == 1) {
272: printf ("\r\nlocktab_insert: got Ctrl-C in blocking LOCK; Ctrl-C again to force the process to halt\r\n");
273: merr_clear ();
274: }
275: else {
276: printf ("\r\n");
277: logprintf (FM_LOG_FATAL, "locktab_insert: blocking LOCK was interrupted; forcing halt");
278: }
279: }
280: sleep (1);
281: }
282:
283: }
284: }
285:
286:
287: case 0: /* lock that returns immediately */
288:
289: if (nref_ct > 1) {
290: /* this is a lock list */
291: char *nref;
292: char tmps[255];
293:
294: int successes = 0;
295: int attempts = 0;
296:
297: stcpy (tmps, key);
298: stcnv_m2c (tmps);
299:
300: nref = strtok (tmps, "\001\201");
301:
302: for (;;) {
303: attempts++;
304:
305: if (locktab_insert (nref) != NULL) {
306: successes++;
307: }
308: else {
309: locktab_decrement (nref, 0L);
310: test = 0;
311: return;
312: }
313:
314: nref = strtok (NULL, "\001\201");
315: if (nref == NULL) break;
316: }
317:
318: test = 1;
319: if (tp_level > 0) tp_add_op (TRUE, lck_action, key, chk_ns);
320: return;
321:
322: }
323: else {
324:
325: if (locktab_insert (key) != NULL) {
326: test = 1;
327: if (tp_level > 0) tp_add_op (TRUE, lck_action, key, chk_ns);
328: }
329: else {
330: test = 0;
331: }
332:
333: }
334:
335: break;
336:
337: case 1: /* special case: lock with 1-second timeout */
338:
339: if (locktab_insert (key) != NULL) {
340: test = 1;
341: }
342: else {
343: sleep (1);
344:
345: if (locktab_insert (key) != NULL) {
346: test = 1;
347: if (tp_level > 0) tp_add_op (TRUE, lck_action, key, chk_ns);
348: }
349: else {
350: test = 0;
351: }
352: }
353:
354: return;
355:
356: default: /* lock with timeout */
357: {
358: time_t start_secs;
359: time_t end_secs;
360: time_t elapsed;
361: locktab_ent_t *lck = NULL;
362:
363: start_secs = time (NULL);
364:
365: if (nref_ct > 1) {
366: /* this is a lock-list */
367: printf ("lock-list with timeout\r\n");
368: }
369: else {
370:
371:
372: for (;;) {
373:
374:
375: lck = locktab_insert (key);
376:
377: end_secs = time (NULL);
378: elapsed = end_secs - start_secs;
379:
380: if (lck != NULL) {
381: test = 1;
382: return;
383: }
384:
385: if (elapsed >= lck_timeout) {
386:
387: if (lck == NULL) {
388: test = 0;
389: }
390: else {
391: if (tp_level > 0) tp_add_op (TRUE, lck_action, key, chk_ns);
392: test = 1;
393: }
394:
395: return;
396:
397: }
398:
399: sleep (1); /* prevent CPU pegging */
400:
401: } /* timeout loop */
402:
403: return;
404: }
405:
406: } /* lock with timeout */
407: } /* switch (timeout) */
408:
409:
410: }
411:
412: void locktab_decrement(char *key, long lck_timeout)
413: {
414:
415: locktab_ent_t *lck = locktab_find (key);
416:
417: if (lck != NULL) {
418:
419: if (tp_level > lck->tp_level) {
420: merr_raise (M41);
421: return;
422: }
423:
424: if (lck->ct > 0) lck->ct--;
425:
426: if (lck->ct == 0) {
427: lck->owner_job = 0;
428: strcpy (lck->namespace, "<REUSABLE>");
429:
430: ssvn_lock_remove (lck->nref);
431: }
432:
433: if (lck->owner_job != 0) {
434: ssvn_lock_add (lck->nref, lck->owner_job, lck->ct);
435: }
436:
437: }
438:
439: if (lck_timeout > -1) test = 1;
440:
441: }
442:
443: void locktab_unlock_all(void)
444: {
445: locktab_unlock_all_by_pid (pid);
446: }
447:
448: void locktab_unlock_all_by_pid(const pid_t target_pid)
449: {
450: locktab_ent_t *lck;
451: int remove_ct;
452:
453: remove_ct = 0;
454: if (target_pid != pid) {
455: logprintf (FM_LOG_INFO, "locktab_unlock_all_by_pid: removing all locks for pid %ld", target_pid);
456: }
457:
458:
459: for (lck = SOA(shm_config->hdr->locktab_head); lck != NULL; lck = SOA(lck->next)) {
460:
461: if (lck->owner_job == target_pid) {
462: remove_ct++;
463:
464: if (tp_level > lck->tp_level) {
465: merr_raise (M41);
466: return;
467: }
468:
469: lck->ct = 0;
470: lck->owner_job = 0;
471: strcpy (lck->namespace, "<REUSABLE>");
472:
473: ssvn_lock_remove (lck->nref);
474:
475: }
476:
477: }
478:
479: if (target_pid != pid) {
480: logprintf (FM_LOG_INFO, "locktab_unlock_all_by_pid: removed %ld lock(s) for pid %ld", remove_ct, target_pid);
481: }
482:
483:
484: }
485:
486: locktab_ent_t *locktab_find(char *key)
487: {
488:
489: locktab_ent_t *lck;
490: char chk_ns[255];
491:
492: if (key[1] == '%') {
493: snprintf (chk_ns, sizeof (chk_ns) - 1, "SYSTEM");
494: }
495: else {
496: snprintf (chk_ns, sizeof (chk_ns) - 1, "%s", nsname);
497: }
498:
499: for (lck = SOA(shm_config->hdr->locktab_head); lck != NULL; lck = SOA(lck->next)) {
500:
501: if ((stcmp (lck->nref, key) == 0) && (strcmp (lck->namespace, chk_ns) == 0)) {
502:
503: if (lck->owner_job != pid) {
504: return (locktab_ent_t *) NULL;
505: }
506: else {
507: return lck;
508: }
509:
510: }
511:
512: }
513:
514: return (locktab_ent_t *) NULL;
515:
516: }
517:
518: locktab_ent_t *locktab_insert(char *key)
519: {
520: locktab_ent_t *l;
521: char chk_ns[255];
522:
523: freem_ref_t *ik;
524: freem_ref_t *ok;
525:
526: ik = malloc (sizeof (freem_ref_t));
527: NULLPTRCHK(ik,"locktab_insert");
528:
529: ik = mref_init (ik, MREF_RT_GLOBAL, "");
530:
531: ok = malloc (sizeof (freem_ref_t));
532: NULLPTRCHK(ok,"locktab_insert");
533:
534: ik = internal_to_mref (ik, key);
535:
536: if (key[1] == '%') {
537: snprintf (chk_ns, sizeof (chk_ns) - 1, "SYSTEM");
538: }
539: else {
540: snprintf (chk_ns, sizeof (chk_ns) - 1, "%s", nsname);
541: }
542:
543: for (l = SOA(shm_config->hdr->locktab_head); l != NULL; l = SOA(l->next)) {
544: ok = mref_init (ok, MREF_RT_GLOBAL, "");
545: ok = internal_to_mref (ok, l->nref);
546:
547: if (((stcmp (l->nref, key) == 0) || (mref_is_descendant (ok, ik) == TRUE)) && (strcmp (l->namespace, chk_ns) == 0)) {
548:
549: /* nref already owned by another job */
550: if ((l->owner_job != pid) && (l->ct > 0)) {
551: free (ik);
552: free (ok);
553: return NULL;
554: }
555: else {
556: if ((mref_is_descendant (ok, ik)) && (pid == l->owner_job)) {
557: if (locktab_find (key) == NULL) goto new_insert;
558: }
559:
560: /* increment the lock and return */
561: l->ct++;
562:
563: /* if this was a lock with a counter of zero belonging to another pid,
564: * re-use it and take ownership of it.
565: */
566: if (l->owner_job != pid) l->owner_job = pid;
567:
568: l->tp_level = tp_level;
569:
570: ssvn_lock_add (l->nref, l->owner_job, l->ct);
571:
572: free (ik);
573: free (ok);
574:
575: return l;
576: }
577:
578: }
579:
580: }
581:
582: new_insert:
583: /* no lock exists for key: this is a new insert */
584: l = (locktab_ent_t *) shm_alloc (sizeof (locktab_ent_t));
585: if (l == (locktab_ent_t *) NULL) {
586: free (ik);
587: free (ok);
588: return (locktab_ent_t *) NULL;
589: }
590:
591: stcpy (l->nref, key);
592: snprintf (l->namespace, sizeof (l->namespace) - 1, "%s", chk_ns);
593:
594: l->owner_job = pid;
595: l->ct = 1;
596:
597: l->next = SBM(SOA(shm_config->hdr->locktab_head));
598: shm_config->hdr->locktab_head = SBM(l);
599:
600: ssvn_lock_add (l->nref, l->owner_job, l->ct);
601:
602: free (ik);
603: free (ok);
604:
605: return l;
606: }
607:
608:
609: int locktab_count(char *key)
610: {
611: locktab_ent_t *l;
612: int ct = 0;
613:
614: for (l = SOA(shm_config->hdr->locktab_head); l != NULL; l = SOA(l->next)) {
615: if (stcmp (l->nref, key) == 0) ct++;
616: }
617:
618: return ct;
619: }
620:
621: int locktab_list_count(char *key)
622: {
623: int i;
624: int lct = 0;
625:
626: for (i = 0; i < stlen (key); i++) {
627: if (key[i] == '\001') lct++;
628: }
629:
630: return lct;
631: }
632:
633: unsigned long locktab_pages(void)
634: {
635:
636: locktab_ent_t *l;
637: unsigned long bytes = 0;
638: unsigned long pages = 0;
639: float extra;
640:
641: for (l = SOA(shm_config->hdr->locktab_head); l != NULL; l = SOA(l->next)) {
642: bytes += sizeof (locktab_ent_t);
643: }
644:
645: pages = bytes / PG_SIZE;
646: extra = bytes % PG_SIZE;
647:
648: if (extra > 0) {
649: pages++;
650: }
651:
652: return pages;
653:
654: }
655:
656: unsigned long locktab_bytes(void)
657: {
658:
659: locktab_ent_t *l;
660: unsigned int ct = 0;
661: unsigned long bytes = 0;
662:
663: for (l = SOA(shm_config->hdr->locktab_head); l != NULL; l = SOA(l->next)) {
664: ct++;
665: bytes += sizeof (locktab_ent_t);
666: }
667:
668: return bytes;
669:
670: }
671:
672: void locktab_dump(void)
673: {
674:
675: locktab_ent_t *l;
676: unsigned long ct = 0;
677: unsigned long tot = 0;
678: freem_ref_t *r;
679: char *ref_ext;
680:
681: r = (freem_ref_t *) malloc (sizeof (freem_ref_t));
682: NULLPTRCHK(r,"locktab_dump");
683:
684: ref_ext = (char *) malloc (STRLEN * sizeof (char));
685: NULLPTRCHK(ref_ext,"locktab_dump");
686:
687: printf ("%-20s%-20s%-20s%s\r\n", "NAMESPACE", "PID", "COUNT", "KEY");
688: printf ("%-20s%-20s%-20s%s\r\n", "---------", "---", "-----", "---");
689:
690: if (SOA(shm_config->hdr->locktab_head) == NULL) {
691: printf ("\r\n*** lock table empty ***\r\n");
692: free (r);
693: return;
694: }
695:
696:
697: for (l = SOA(shm_config->hdr->locktab_head); l != NULL; l = SOA(l->next)) {
698:
699: mref_init (r, MREF_RT_GLOBAL, "");
700: internal_to_mref (r, l->nref);
701: mref_to_external (r, ref_ext);
702:
703: if (l->owner_job) {
704: printf ("%-20s%-20d%-20d%s\r\n", l->namespace, l->owner_job, l->ct, ref_ext);
705: ct++;
706: }
707:
708: tot++;
709:
710: }
711:
712: printf ("\r\n\tActive LOCK table entries: %ld\r\n", ct);
713: printf ( "\tReusable LOCK table entries: %ld\r\n", tot - ct);
714: printf ( "\tShared memory pages: %ld\r\n", locktab_pages ());
715: printf ( "\tShared memory bytes: %ld\r\n", locktab_bytes ());
716:
717: free (r);
718: free (ref_ext);
719:
720: return;
721:
722: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>