/*
* $Id: locktab.c,v 1.5 2025/03/24 02:57:25 snw Exp $
* lock table implementation
*
*
* Author: Serena Willis <snw@coherent-logic.com>
* Copyright (C) 1998 MUG Deutschland
* Copyright (C) 2021, 2025 Coherent Logic Development LLC
*
*
* This file is part of FreeM.
*
* FreeM is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* FreeM is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero Public License for more details.
*
* You should have received a copy of the GNU Affero Public License
* along with FreeM. If not, see <https://www.gnu.org/licenses/>.
*
* $Log: locktab.c,v $
* Revision 1.5 2025/03/24 02:57:25 snw
* Shared memory compatibility fixes for OS/2
*
* Revision 1.4 2025/03/09 19:14:25 snw
* First phase of REUSE compliance and header reformat
*
*
* SPDX-FileCopyrightText: (C) 2025 Coherent Logic Development LLC
* SPDX-License-Identifier: AGPL-3.0-or-later
**/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <time.h>
#include <string.h>
#include "mpsdef.h"
#include "locktab.h"
#include "shmmgr.h"
#include "mref.h"
#include "transact.h"
#if !defined(__OpenBSD__) && !defined(__APPLE__) && !defined(__OS2__)
union semun {
int val; /* Value for SETVAL */
struct semid_ds *buf; /* Buffer for IPC_STAT, IPC_SET */
unsigned short *array; /* Array for GETALL, SETALL */
struct seminfo *__buf; /* Buffer for IPC_INFO
(Linux-specific) */
};
#endif
int semid_locktab;
int locktab_list_count(char *key);
void locktab_init(void)
{
union semun arg;
key_t lt_sk;
lt_sk = ftok (config_file, 3);
if (first_process) {
semid_locktab = semget (lt_sk, 1, 0666 | IPC_CREAT);
if (semid_locktab == -1) {
fprintf (stderr, "locktab_init: failed to create lock table semaphore\r\n");
exit (1);
}
arg.val = 1;
if (semctl (semid_locktab, 0, SETVAL, arg) == -1) {
fprintf (stderr, "locktab_init: failed to initialize lock table semaphore\r\n");
exit (1);
}
}
else {
semid_locktab = semget (lt_sk, 1, 0);
if (semid_locktab == -1) {
fprintf (stderr, "locktab_init: could not attach to lock table semaphore\r\n");
exit (1);
}
}
return;
}
short locktab_get_sem(void)
{
int tries;
struct sembuf s = {0, -1, 0};
for (tries = 0; tries < 5; tries++) {
if (semop (semid_locktab, &s, 1) != -1) {
return TRUE;
}
sleep (1);
}
return FALSE;
}
void locktab_release_sem(void)
{
struct sembuf s = {0, 1, 0};
semop (semid_locktab, &s, 1);
}
void lock(char *lockarg, long time_out, char type)
{
char *key = &(lockarg[1]);
char a = lockarg[0];
if (shm_config == NULL) {
fprintf (stderr, "lock: global LOCK operation attemped before shared memory available.\r\n");
return;
}
switch (a) {
case '+':
locktab_increment (key, time_out, FALSE);
break;
case '-':
locktab_decrement (key, time_out);
break;
case SP:
locktab_unlock_all ();
locktab_increment (key, time_out, TRUE);
break;
default:
break;
}
return;
}
void locktab_increment(char *key, long lck_timeout, short old_lock)
{
short lck_action;
char chk_ns[256];
int nref_ct = locktab_list_count (key);
if (old_lock) {
lck_action = lock_old;
}
else {
lck_action = lock_inc;
}
strncpy (chk_ns, nsname, 256);
switch (lck_timeout) {
case -1: /* blocking lock (no timeout) */
if (nref_ct > 1) {
/* this is a lock list */
char *nref;
char tmps[255];
int i;
int successes = 0;
int attempts = 0;
int list_pos = 0;
char *attempt_status = (char *) malloc (nref_ct * sizeof (char));
NULLPTRCHK(attempt_status,"locktab_increment");
for (i = 0; i < nref_ct; i++) attempt_status[i] = (char) FALSE;
stcpy (tmps, key);
stcnv_m2c (tmps);
nref = strtok (tmps, "\001\201");
do {
list_pos = 0;
attempts = 0;
successes = 0;
for (;;) {
attempts++;
if (attempt_status[list_pos] == FALSE) {
if (locktab_insert (nref) != NULL) {
successes++;
attempt_status[list_pos] = TRUE;
}
else {
locktab_decrement (nref, -1L);
attempt_status[list_pos] = FALSE;
}
}
nref = strtok (NULL, "\001\201");
if (nref == NULL) break;
list_pos++;
}
} while (successes < nref_ct);
free (attempt_status);
if (tp_level > 0) tp_add_op (TRUE, lck_action, key, chk_ns);
return;
}
else {
for (;;) {
if (locktab_insert (key) != NULL) {
if (tp_level > 0) tp_add_op (TRUE, lck_action, key, chk_ns);
return;
}
else {
sleep (1);
}
}
}
case 0: /* lock that returns immediately */
if (nref_ct > 1) {
/* this is a lock list */
char *nref;
char tmps[255];
int successes = 0;
int attempts = 0;
stcpy (tmps, key);
stcnv_m2c (tmps);
nref = strtok (tmps, "\001\201");
for (;;) {
attempts++;
if (locktab_insert (nref) != NULL) {
successes++;
}
else {
locktab_decrement (nref, 0L);
test = 0;
return;
}
nref = strtok (NULL, "\001\201");
if (nref == NULL) break;
}
test = 1;
if (tp_level > 0) tp_add_op (TRUE, lck_action, key, chk_ns);
return;
}
else {
if (locktab_insert (key) != NULL) {
test = 1;
if (tp_level > 0) tp_add_op (TRUE, lck_action, key, chk_ns);
}
else {
test = 0;
}
}
break;
case 1: /* special case: lock with 1-second timeout */
if (locktab_insert (key) != NULL) {
test = 1;
}
else {
sleep (1);
if (locktab_insert (key) != NULL) {
test = 1;
if (tp_level > 0) tp_add_op (TRUE, lck_action, key, chk_ns);
}
else {
test = 0;
}
}
return;
default: /* lock with timeout */
{
time_t start_secs;
time_t end_secs;
time_t elapsed;
locktab_ent_t *lck = NULL;
start_secs = time (NULL);
if (nref_ct > 1) {
/* this is a lock-list */
printf ("lock-list with timeout\r\n");
}
else {
for (;;) {
lck = locktab_insert (key);
end_secs = time (NULL);
elapsed = end_secs - start_secs;
if (lck != NULL) {
test = 1;
return;
}
if (elapsed >= lck_timeout) {
if (lck == NULL) {
test = 0;
}
else {
if (tp_level > 0) tp_add_op (TRUE, lck_action, key, chk_ns);
test = 1;
}
return;
}
sleep (1); /* prevent CPU pegging */
} /* timeout loop */
return;
}
} /* lock with timeout */
} /* switch (timeout) */
}
void locktab_decrement(char *key, long lck_timeout)
{
locktab_ent_t *lck = locktab_find (key);
if (lck != NULL) {
if (tp_level > lck->tp_level) {
merr_raise (M41);
return;
}
if (lck->ct > 0) lck->ct--;
if (lck->ct == 0) {
lck->owner_job = 0;
strcpy (lck->namespace, "<REUSABLE>");
ssvn_lock_remove (lck->nref);
}
if (lck->owner_job != 0) {
ssvn_lock_add (lck->nref, lck->owner_job, lck->ct);
}
}
if (lck_timeout > -1) test = 1;
}
void locktab_unlock_all(void)
{
locktab_ent_t *lck;
for (lck = shm_config->hdr->locktab_head; lck != NULL; lck = lck->next) {
if (lck->owner_job == pid) {
if (tp_level > lck->tp_level) {
merr_raise (M41);
return;
}
lck->ct = 0;
lck->owner_job = 0;
strcpy (lck->namespace, "<REUSABLE>");
ssvn_lock_remove (lck->nref);
}
}
}
locktab_ent_t *locktab_find(char *key)
{
locktab_ent_t *lck;
char chk_ns[255];
if (key[1] == '%') {
snprintf (chk_ns, 255, "SYSTEM");
}
else {
snprintf (chk_ns, 255, "%s", nsname);
}
for (lck = shm_config->hdr->locktab_head; lck != NULL; lck = lck->next) {
if ((stcmp (lck->nref, key) == 0) && (strcmp (lck->namespace, chk_ns) == 0)) {
if (lck->owner_job != pid) {
return (locktab_ent_t *) NULL;
}
else {
return lck;
}
}
}
return (locktab_ent_t *) NULL;
}
locktab_ent_t *locktab_insert(char *key)
{
locktab_ent_t *l;
char chk_ns[255];
freem_ref_t *ik;
freem_ref_t *ok;
ik = malloc (sizeof (freem_ref_t));
NULLPTRCHK(ik,"locktab_insert");
ik = mref_init (ik, MREF_RT_GLOBAL, "");
ok = malloc (sizeof (freem_ref_t));
NULLPTRCHK(ok,"locktab_insert");
ik = internal_to_mref (ik, key);
if (key[1] == '%') {
snprintf (chk_ns, 255, "SYSTEM");
}
else {
snprintf (chk_ns, 255, "%s", nsname);
}
for (l = shm_config->hdr->locktab_head; l != NULL; l = l->next) {
ok = mref_init (ok, MREF_RT_GLOBAL, "");
ok = internal_to_mref (ok, l->nref);
if (((stcmp (l->nref, key) == 0) || (mref_is_descendant (ok, ik) == TRUE)) && (strcmp (l->namespace, chk_ns) == 0)) {
/* nref already owned by another job */
if ((l->owner_job != pid) && (l->ct > 0)) {
free (ik);
free (ok);
return NULL;
}
else {
if ((mref_is_descendant (ok, ik)) && (pid == l->owner_job)) {
if (locktab_find (key) == NULL) goto new_insert;
}
/* increment the lock and return */
l->ct++;
/* if this was a lock with a counter of zero belonging to another pid,
* re-use it and take ownership of it.
*/
if (l->owner_job != pid) l->owner_job = pid;
l->tp_level = tp_level;
ssvn_lock_add (l->nref, l->owner_job, l->ct);
free (ik);
free (ok);
return l;
}
}
}
new_insert:
/* no lock exists for key: this is a new insert */
l = (locktab_ent_t *) shm_alloc (sizeof (locktab_ent_t));
if (l == (locktab_ent_t *) NULL) {
free (ik);
free (ok);
return (locktab_ent_t *) NULL;
}
stcpy (l->nref, key);
snprintf (l->namespace, 255, "%s", chk_ns);
l->owner_job = pid;
l->ct = 1;
l->next = shm_config->hdr->locktab_head;
shm_config->hdr->locktab_head = l;
ssvn_lock_add (l->nref, l->owner_job, l->ct);
free (ik);
free (ok);
return l;
}
int locktab_count(char *key)
{
locktab_ent_t *l;
int ct = 0;
for (l = shm_config->hdr->locktab_head; l != NULL; l = l->next) {
if (stcmp (l->nref, key) == 0) ct++;
}
return ct;
}
int locktab_list_count(char *key)
{
int i;
int lct = 0;
for (i = 0; i < stlen (key); i++) {
if (key[i] == '\001') lct++;
}
return lct;
}
unsigned long locktab_pages(void)
{
locktab_ent_t *l;
unsigned long bytes = 0;
unsigned long pages = 0;
float extra;
for (l = shm_config->hdr->locktab_head; l != NULL; l = l->next) {
bytes += sizeof (locktab_ent_t);
}
pages = bytes / PG_SIZE;
extra = bytes % PG_SIZE;
if (extra > 0) {
pages++;
}
return pages;
}
unsigned long locktab_bytes(void)
{
locktab_ent_t *l;
unsigned int ct = 0;
unsigned long bytes = 0;
for (l = shm_config->hdr->locktab_head; l != NULL; l = l->next) {
ct++;
bytes += sizeof (locktab_ent_t);
}
return bytes;
}
void locktab_dump(void)
{
locktab_ent_t *l;
unsigned long ct = 0;
unsigned long tot = 0;
freem_ref_t *r;
char *ref_ext;
r = (freem_ref_t *) malloc (sizeof (freem_ref_t));
NULLPTRCHK(r,"locktab_dump");
ref_ext = (char *) malloc (STRLEN * sizeof (char));
NULLPTRCHK(ref_ext,"locktab_dump");
printf ("%-20s%-20s%-20s%s\r\n", "NAMESPACE", "PID", "COUNT", "KEY");
printf ("%-20s%-20s%-20s%s\r\n", "---------", "---", "-----", "---");
if (shm_config->hdr->locktab_head == NULL) {
printf ("\r\n*** lock table empty ***\r\n");
free (r);
return;
}
for (l = shm_config->hdr->locktab_head; l != NULL; l = l->next) {
mref_init (r, MREF_RT_GLOBAL, "");
internal_to_mref (r, l->nref);
mref_to_external (r, ref_ext);
if (l->owner_job) {
printf ("%-20s%-20d%-20d%s\r\n", l->namespace, l->owner_job, l->ct, ref_ext);
ct++;
}
tot++;
}
printf ("\r\n\tActive LOCK table entries: %ld\r\n", ct);
printf ( "\tReusable LOCK table entries: %ld\r\n", tot - ct);
printf ( "\tShared memory pages: %ld\r\n", locktab_pages ());
printf ( "\tShared memory bytes: %ld\r\n", locktab_bytes ());
free (r);
free (ref_ext);
return;
}
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>