1: /*
2: * $Id: shmmgr.c,v 1.6 2025/04/04 19:43:18 snw Exp $
3: * shared memory manager
4: *
5: *
6: * Author: Serena Willis <snw@coherent-logic.com>
7: * Copyright (C) 1998 MUG Deutschland
8: * Copyright (C) 2020, 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: shmmgr.c,v $
27: * Revision 1.6 2025/04/04 19:43:18 snw
28: * Switch to using environment catalog to determine user and group for environment, and remove -u and -g flags from freem
29: *
30: * Revision 1.5 2025/03/24 02:56:50 snw
31: * Shared memory compatibility fixes for OS/2
32: *
33: * Revision 1.4 2025/03/09 19:50:47 snw
34: * Second phase of REUSE compliance and header reformat
35: *
36: *
37: * SPDX-FileCopyrightText: (C) 2025 Coherent Logic Development LLC
38: * SPDX-License-Identifier: AGPL-3.0-or-later
39: **/
40:
41: #include <stdlib.h>
42: #include <stdio.h>
43: #include <assert.h>
44: #include <string.h>
45: #include <unistd.h>
46: #include <errno.h>
47: #include <signal.h>
48:
49: #include "shmmgr.h"
50: #include "mpsdef.h"
51: #include "locktab.h"
52:
53: #include <sys/types.h>
54: #include <sys/ipc.h>
55: #include <sys/sem.h>
56:
57: #if !defined(__OpenBSD__) && !defined(__APPLE__) && !defined(__OS2__)
58: union semun {
59: int val; /* Value for SETVAL */
60: struct semid_ds *buf; /* Buffer for IPC_STAT, IPC_SET */
61: unsigned short *array; /* Array for GETALL, SETALL */
62: struct seminfo *__buf; /* Buffer for IPC_INFO
63: (Linux-specific) */
64: };
65: #endif
66:
67: int semid_shm;
68: extern int semid_locktab;
69: extern int semid_jobtab;
70: extern int semid_tp;
71: extern int semid_symtab;
72:
73: void shm_daemon_init(void);
74:
75: shm_config_t *shm_config = (shm_config_t *) NULL;
76:
77: short shm_init(const size_t seg_size)
78: {
79: union semun arg;
80: size_t alloc_map_size;
81: long pg_size;
82: register int i;
83: key_t shm_sk;
84:
85: shm_sk = ftok (config_file, 5);
86: pg_size = sysconf (_SC_PAGESIZE);
87:
88: shm_config = (shm_config_t *) malloc (sizeof (shm_config_t));
89: NULLPTRCHK(shm_config,"shm_init");
90:
91: /* figure out how many pages we can fit in the segment, accounting for header size */
92: shm_config->pgct = (seg_size / pg_size) - sizeof (shm_hdr_t);
93:
94: /* how big will the alloc map be? */
95: alloc_map_size = shm_config->pgct * sizeof (shm_page_t);
96:
97: shm_config->segsiz = seg_size + alloc_map_size + pg_size;
98: shm_config->key = ftok (config_file, 1);
99: shm_config->pgsiz = pg_size;
100:
101: shm_config->seg_id = shmget (shm_config->key, shm_config->segsiz, 0770 | IPC_CREAT);
102: if (shm_config->seg_id == -1) {
103: if (errno == 22) {
104: fprintf (stderr, "shm_init: cannot get shared memory segment of %ld bytes\r\n\r\n", (unsigned long) shm_config->segsiz);
105: fprintf (stderr, "You may need to tune your kernel parameters, or manually set a smaller shared memory segment size in both the FreeM daemon and each interpreter process by using the `-S` command-line flag.\r\n\r\nPlease refer to the FreeM Platform Notes for your operating system for details.\r\n\r\n");
106: }
107: return SHMS_GET_ERR;
108: }
109:
110: #if !defined(__arm__)
111: shm_config->dta = shmat (shm_config->seg_id, NULL, 0);
112: #else
113: shm_config->dta = shmat (shm_config->seg_id, (void *) 0x1000000, 0);
114: #endif
115:
116: if (shm_config->dta == (void *) -1) {
117: return SHMS_ATTACH_ERR;
118: }
119: /* view the first sizeof (shm_hdr_t) bytes of the data area as an shm_hdr_t */
120: shm_config->hdr = (shm_hdr_t *) shm_config->dta;
121:
122: if (shm_config->hdr->magic != shm_config->key) {
123:
124: /* the shm segment is brand new */
125: first_process = TRUE;
126:
127: shm_daemon_init ();
128:
129: }
130: else {
131:
132: /* this shared mem segment was initialized before */
133: int daemon_chk;
134:
135: /* check if the daemon recorded in the header is actually running */
136: daemon_chk = kill (shm_config->hdr->first_process, 0);
137:
138: if (daemon_chk == -1 && errno == ESRCH) {
139:
140: fprintf (stderr, "shm_init: recovering from crashed daemon pid %d\r\n", shm_config->hdr->first_process);
141:
142: first_process = TRUE;
143:
144: shm_daemon_init ();
145:
146: }
147: else {
148:
149:
150: first_process = FALSE;
151:
152: semid_shm = semget (shm_sk, 1, 0);
153: if (semid_shm == -1) {
154: fprintf (stderr, "shm_init: could not attach to shared memory semaphore\r\n");
155: exit (1);
156: }
157:
158: /* we are NOT the initial process. if addresses don't match, re-attach! */
159: /* (again, borrowed from RSM) */
160: if (shm_config->hdr->shmad != shm_config->dta) {
161:
162: /* grab the pointers we need */
163: void *old_addr = shm_config->dta;
164: void *new_addr = shm_config->hdr->shmad;
165:
166: /* detach and reattach */
167: if (shmdt (old_addr) == -1) {
168: fprintf (stderr, "shm_init: detach failed during detach/reattach [shmdt error %s]\r\n", strerror (errno));
169: exit (1);
170: }
171:
172: shm_config->dta = shmat (shm_config->seg_id, new_addr, 0);
173:
174: if (shm_config->dta == (void *) -1) {
175: fprintf (stderr, "shm_init: fatal error attaching shared memory segment [shmat error '%s']\r\n", strerror (errno));
176: exit (1);
177: }
178:
179: shm_config->hdr = (shm_hdr_t *) shm_config->dta;
180:
181: /* allocator buffer at the next page-aligned address after the header and allocation map */
182: shm_config->buf = SHMALIGN(shm_config->dta + (sizeof (shm_hdr_t) * shm_config->pgct));
183: }
184: else {
185: shm_config->buf = SHMALIGN(shm_config->dta + (sizeof (shm_hdr_t) * shm_config->pgct));
186: }
187:
188: }
189:
190: }
191:
192: locktab_init ();
193:
194: assert(shm_address_to_page_num(shm_page_num_to_address(20)) == 20);
195:
196:
197: return TRUE;
198: }
199:
200: void shm_daemon_init(void)
201: {
202: union semun arg;
203: size_t alloc_map_size;
204: key_t shm_sk;
205: register int i;
206:
207: shm_sk = ftok (config_file, 5);
208:
209: semid_shm = semget (shm_sk, 1, 0660 | IPC_CREAT);
210: if (semid_shm == -1) {
211: fprintf (stderr, "shm_init: failed to create shared memory semaphore\r\n");
212: exit (1);
213: }
214:
215: arg.val = 1;
216: if (semctl (semid_shm, 0, SETVAL, arg) == -1) {
217: fprintf (stderr, "shm_init: failed to initialize shared memory semaphore\r\n");
218: exit (1);
219: }
220:
221: /* zero out the segment */
222: memset (shm_config->dta, 0, shm_config->segsiz);
223:
224: /* we are the process that created the segment: initialize everything */
225: shm_config->hdr->magic = shm_config->key;
226: shm_config->hdr->first_process = pid;
227:
228: /* store the address we got into the shm_hdr (borrowed from RSM) */
229: shm_config->hdr->shmad = shm_config->dta;
230: shm_config->hdr->maintenance_mode = 0;
231:
232: /* alloc_map comes after the header */
233: /*
234: shm_config->alloc_map = (shm_page_t *) (shm_config->dta + sizeof (shm_hdr_t));
235: */
236: shm_config->buf = SHMALIGN(shm_config->dta + (sizeof (shm_hdr_t) * shm_config->pgct));
237: printf ("shm_daemon_init: allocator buffer aligned at %p (system page size %ld)\r\n", shm_config->buf, sysconf (_SC_PAGESIZE));
238:
239: for (i = 0; i < shm_config->pgct; i++) {
240: shm_config->hdr->alloc_map[i].is_first = FALSE;
241: shm_config->hdr->alloc_map[i].is_last = FALSE;
242: shm_config->hdr->alloc_map[i].pg_state = PG_FREE;
243: }
244:
245: }
246:
247: short shm_exit(void)
248: {
249: int res;
250: union semun arg;
251:
252: res = shmdt (shm_config->dta);
253:
254: if (res == -1) {
255: fprintf (stderr, "shm_exit: failure in shmdt()\r\n");
256: return FALSE;
257: }
258:
259: if (first_process) {
260:
261: res = shmctl (shm_config->seg_id, IPC_RMID, 0);
262:
263: if (res == -1) {
264: fprintf (stderr, "shm_exit: failure in shmctl()\r\n");
265: return FALSE;
266: }
267:
268: semctl (semid_shm, 0, IPC_RMID, arg);
269: semctl (semid_locktab, 0, IPC_RMID, arg);
270: semctl (semid_jobtab, 0, IPC_RMID, arg);
271: semctl (semid_tp, 0, IPC_RMID, arg);
272: semctl (semid_symtab, 0, IPC_RMID, arg);
273:
274: }
275:
276: return TRUE;
277:
278: }
279:
280: short shm_get_sem(void)
281: {
282:
283: int tries;
284: struct sembuf s = {0, -1, 0};
285:
286: for (tries = 0; tries < 3; tries++) {
287:
288: if (semop (semid_shm, &s, 1) != -1) {
289: return TRUE;
290: }
291:
292: sleep (1);
293:
294: }
295:
296: return FALSE;
297:
298: }
299:
300: short shm_release_sem(void)
301: {
302: struct sembuf s = {0, 1, 0};
303:
304: if (semop (semid_shm, &s, 1) != -1) {
305: return TRUE;
306: }
307:
308: return FALSE;
309: }
310:
311: void shm_set_maintenance_mode (const short maintenance_mode)
312: {
313: if (shm_get_sem () == TRUE) {
314: shm_config->hdr->maintenance_mode = maintenance_mode;
315:
316: shm_release_sem ();
317: }
318: }
319:
320: shm_page_t *shm_get_alloc_map_entry(const int page_number)
321: {
322: return &(shm_config->hdr->alloc_map[page_number]);
323: }
324:
325: void *shm_page_num_to_address(const int page_num)
326: {
327: return (void *) shm_config->buf + (shm_config->pgsiz * page_num);
328: }
329:
330: int shm_address_to_page_num(const void *address)
331: {
332: unsigned long val = (unsigned long) address - (unsigned long) shm_config->buf;
333: unsigned long new_val = val / shm_config->pgsiz;
334:
335: return (int) new_val;
336: }
337:
338: void *shm_alloc_pages(const int page_count)
339: {
340:
341: register int i;
342: register int j;
343:
344: int candidate_page = 0;
345: int free_pages_gotten = 0;
346:
347: shm_page_t *pg;
348:
349: if (shm_get_sem () == FALSE) {
350: fprintf (stderr, "shm_alloc_pages: could not get exclusive access to shared memory\r\n");
351: exit (1);
352: }
353:
354:
355: for (i = 0; i < shm_config->pgct; i++) {
356:
357: pg = shm_get_alloc_map_entry (i);
358: NULLPTRCHK(pg,"shm_alloc_pages");
359:
360: free_pages_gotten = 0;
361:
362: if (pg->pg_state == PG_FREE) {
363:
364: candidate_page = i;
365:
366: for (j = i; ((j < (i + page_count)) && (j < shm_config->pgct)); j++) {
367: pg = shm_get_alloc_map_entry (j);
368:
369: if (pg->pg_state == PG_FREE) free_pages_gotten++;
370:
371: }
372:
373: if (free_pages_gotten == page_count) {
374:
375: for (j = candidate_page; j < (candidate_page + page_count); j++) {
376: pg = shm_get_alloc_map_entry (j);
377:
378: pg->pg_state = PG_ALLOC;
379: pg->pid = pid;
380:
381: if (j == candidate_page) {
382: pg->is_first = TRUE;
383: }
384:
385: if (j == candidate_page + (page_count - 1)) {
386: pg->is_last = TRUE;
387: }
388:
389: }
390:
391: shm_release_sem ();
392:
393: return (void *) shm_config->buf + (shm_config->pgsiz * candidate_page);
394:
395: }
396:
397: }
398:
399: }
400:
401: shm_release_sem ();
402:
403: return (void *) NULL;
404:
405: }
406:
407:
408: void *shm_alloc(const size_t bytes)
409: {
410: int pages_needed = bytes / shm_config->pgsiz;
411: float extra = bytes % shm_config->pgsiz;
412:
413: if (extra > 0) {
414: pages_needed++;
415: }
416:
417: return shm_alloc_pages (pages_needed);
418: }
419:
420: void shm_free_page(const int page_number)
421: {
422: register int i;
423: shm_page_t *a = shm_get_alloc_map_entry (page_number);
424:
425: if (a->is_first == FALSE) {
426: fprintf (stderr, "shm_free_page: attempt to free page in the middle of allocation chain\r\n");
427: return;
428: }
429:
430: if (a->pg_state == PG_FREE) {
431: fprintf (stderr, "shm_free_page: double free attempted in page %d\r\n", page_number);
432: exit (1);
433: }
434:
435: if (shm_get_sem () == FALSE) {
436: fprintf (stderr, "shm_free_page: could not get exclusive access to shared memory\r\n");
437: exit (1);
438: }
439:
440:
441: for (i = page_number; i < shm_config->pgct; i++) {
442:
443: a = shm_get_alloc_map_entry (i);
444:
445: if (a->is_last) {
446: a->is_first = FALSE;
447: a->pg_state = PG_FREE;
448: a->pid = 0;
449: a->is_last = FALSE;
450:
451: shm_release_sem ();
452:
453: return;
454: }
455: else {
456: a->is_first = FALSE;
457: a->pg_state = PG_FREE;
458: a->pid = 0;
459: a->is_last = FALSE;
460: }
461:
462: }
463:
464: shm_release_sem ();
465:
466: }
467:
468: void shm_free(const void *addr)
469: {
470: shm_free_page (shm_address_to_page_num (addr));
471: }
472:
473: void shm_dump(void)
474: {
475:
476: printf ("SHARED MEMORY CONFIGURATION\r\n");
477: printf (" pgsiz %ld\r\n", (unsigned long) shm_config->pgsiz);
478: printf (" pgct %d\r\n", shm_config->pgct);
479: printf (" key %ld\r\n", shm_config->key);
480: printf (" segid %d\r\n", shm_config->seg_id);
481: printf (" sizeof shm_page_t %ld\r\n", (long) sizeof (shm_page_t));
482: printf (" segsiz %ld\r\n", (long) shm_config->segsiz);
483: printf (" shm address %p\r\n", shm_config->dta);
484: printf (" alloc_map size %ld\r\n", (unsigned long) sizeof (shm_page_t) * shm_config->pgct);
485: printf (" buf address %p\r\n", shm_config->buf);
486: }
487:
488: void shm_dump_pages(void)
489: {
490:
491: register int i;
492: shm_page_t *p;
493:
494: printf ("%-10s%-10s%-10s%-10s%-10s\r\n", "PAGE", "PID", "BMHEAD", "BMTAIL", "STATE");
495:
496: for (i = 0; i < shm_config->pgct; i++) {
497:
498: p = shm_get_alloc_map_entry (i);
499:
500: printf ("%-10d%-10d%-10s%-10s%-10s\r\n",
501: i,
502: p->pid,
503: (p->is_first == TRUE) ? "Y" : "N",
504: (p->is_last == TRUE) ? "Y" : "N",
505: (p->pg_state == PG_FREE) ? "PG_FREE" : "PG_ALLOC");
506:
507: }
508:
509: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>