File:  [Coherent Logic Development] / freem / src / shmmgr.c
Revision 1.6: download - view: text, annotated - select for diffs
Fri Apr 4 19:43:18 2025 UTC (5 months, 3 weeks ago) by snw
Branches: MAIN
CVS tags: HEAD
Switch to using environment catalog to determine user and group for environment, and remove -u and -g flags from freem

    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>