00001 /* 00002 SMI - POSIX shared memory 00003 AUP2, Sec. 7.14.2 00004 00005 Copyright 2003 by Marc J. Rochkind. All rights reserved. 00006 May be copied only for purposes and under conditions described 00007 on the Web page www.basepath.com/aup/copyright.htm. 00008 00009 The Example Files are provided "as is," without any warranty; 00010 without even the implied warranty of merchantability or fitness 00011 for a particular purpose. The author and his publisher are not 00012 responsible for any damages, direct or incidental, resulting 00013 from the use or non-use of these Example Files. 00014 00015 The Example Files may contain defects, and some contain deliberate 00016 coding mistakes that were included for educational reasons. 00017 You are responsible for determining if and how the Example Files 00018 are to be used. 00019 00020 */ 00021 #include "defs.h" 00022 #include "smi_pshm.h" 00023 /* 00024 There may exist a POSIX semaphore implementation for these systems, 00025 but I haven't located it yet. Change next line if you find one. 00026 and then please email me at aup@basepath.com. 00027 */ 00028 #if !defined(FREEBSD) && !defined(LINUX) 00029 00030 #include <semaphore.h> 00031 #define _P1003_1B_VISIBLE 00032 #include <sys/mman.h> 00033 00034 /* 00035 Shared memory with separate memory segment for server and all clients. 00036 Uses POSIX memory-based semaphores and POSIX shared memory. 00037 00038 Missing is a way for a client to tell the server to make its slot available. It isn't possible for a client's slot to be dropped and then reused, as the memory semaphores have to persist undisturbed. 00039 */ 00040 00041 /* Structure must contain no pointers. */ 00042 /*[shared_mem]*/ 00043 struct shared_mem { 00044 sem_t sm_sem_w; 00045 sem_t sm_sem_r; 00046 struct smi_msg sm_msg; /* variable size -- must be last */ 00047 }; 00048 /*[SMIQ_PSHM]*/ 00049 #define MAX_CLIENTS 50 00050 00051 typedef struct { 00052 SMIENTITY sq_entity; /* entity */ 00053 char sq_name[SERVER_NAME_MAX]; /* server name */ 00054 int sq_srv_fd; /* server shm file descriptor */ 00055 struct shared_mem *sq_srv_mem; /* server mapped shm segment */ 00056 struct client { 00057 pid_t cl_pid; /* client process ID */ 00058 int cl_fd; /* client shm file descriptor */ 00059 struct shared_mem *cl_mem; /* client mapped shm segment */ 00060 } sq_clients[MAX_CLIENTS]; /* client uses only [0] */ 00061 struct client_id sq_client; /* client ident. (server only)*/ 00062 size_t sq_msgsize; /* message size */ 00063 } SMIQ_PSHM; 00064 /*[MEM_SIZE]*/ 00065 #define MEM_SIZE(s)\ 00066 (sizeof(struct shared_mem) - sizeof(struct smi_msg) + (s)) 00067 /*[mkshm_name]*/ 00068 static void mkshm_name_server(const SMIQ_PSHM *p, char *shmname, 00069 size_t shmname_max) 00070 { 00071 snprintf(shmname, shmname_max, "/smipshm-%s", p->sq_name); 00072 } 00073 00074 static void mkshm_name_client(pid_t pid, char *shmname, 00075 size_t shmname_max) 00076 { 00077 snprintf(shmname, shmname_max, "/smipshm-%d", pid); 00078 } 00079 /*[op_semi]*/ 00080 #define SEMI_READ 0 00081 #define SEMI_WRITE 1 00082 #define SEMI_DESTROY 2 00083 #define SEMI_POST 1 00084 #define SEMI_WAIT -1 00085 00086 static int op_semi(struct shared_mem *m, int sem_num, int sem_op) 00087 { 00088 sem_t *sem_p = NULL; 00089 00090 if (sem_num == SEMI_WRITE) 00091 sem_p = &m->sm_sem_w; 00092 else 00093 sem_p = &m->sm_sem_r; 00094 switch (sem_op) { 00095 case SEMI_WAIT: 00096 ec_neg1( sem_wait(sem_p) ) 00097 break; 00098 case SEMI_POST: 00099 ec_neg1( sem_post(sem_p) ) 00100 break; 00101 case SEMI_DESTROY: 00102 ec_neg1( sem_destroy(sem_p) ) 00103 } 00104 return 0; 00105 00106 EC_CLEANUP_BGN 00107 return -1; 00108 EC_CLEANUP_END 00109 } 00110 /*[get_client]*/ 00111 static struct client *get_client(SMIQ_PSHM *p, pid_t pid) 00112 { 00113 int i, avail = -1; 00114 char shmname[SERVER_NAME_MAX + 50]; 00115 00116 for (i = 0; i < MAX_CLIENTS; i++) { 00117 if (p->sq_clients[i].cl_pid == pid) 00118 return &p->sq_clients[i]; 00119 if (p->sq_clients[i].cl_pid == 0 && avail == -1) 00120 avail = i; 00121 } 00122 if (avail == -1) { 00123 errno = EADDRNOTAVAIL; 00124 EC_FAIL 00125 } 00126 p->sq_clients[avail].cl_pid = pid; 00127 mkshm_name_client(pid, shmname, sizeof(shmname)); 00128 ec_neg1( p->sq_clients[avail].cl_fd = shm_open(shmname, O_RDWR, 00129 PERM_FILE) ) 00130 p->sq_clients[avail].cl_mem = mmap(NULL, MEM_SIZE(p->sq_msgsize), 00131 PROT_READ | PROT_WRITE, MAP_SHARED, p->sq_clients[avail].cl_fd, 00132 0); 00133 ec_cmp(p->sq_clients[avail].cl_mem, MAP_FAILED) 00134 return &p->sq_clients[avail]; 00135 00136 EC_CLEANUP_BGN 00137 return NULL; 00138 EC_CLEANUP_END 00139 } 00140 /*[]*/ 00141 /* cleanup needs to be more complete than just free(p) */ 00142 /*[smi_open_pshm]*/ 00143 SMIQ *smi_open_pshm(const char *name, SMIENTITY entity, size_t msgsize) 00144 { 00145 SMIQ_PSHM *p = NULL; 00146 char shmname[SERVER_NAME_MAX + 50]; 00147 00148 ec_null( p = calloc(1, sizeof(SMIQ_PSHM)) ) 00149 p->sq_entity = entity; 00150 p->sq_msgsize = msgsize; 00151 if (strlen(name) >= SERVER_NAME_MAX) { 00152 errno = ENAMETOOLONG; 00153 EC_FAIL 00154 } 00155 strcpy(p->sq_name, name); 00156 mkshm_name_server(p, shmname, sizeof(shmname)); 00157 if (p->sq_entity == SMI_SERVER) { 00158 if ((p->sq_srv_fd = shm_open(shmname, O_RDWR, PERM_FILE)) != -1) { 00159 (void)shm_unlink(shmname); 00160 (void)close(p->sq_srv_fd); 00161 } 00162 ec_neg1( p->sq_srv_fd = shm_open(shmname, O_RDWR | O_CREAT, 00163 PERM_FILE) ) 00164 ec_neg1( ftruncate(p->sq_srv_fd, MEM_SIZE(msgsize)) ) 00165 p->sq_srv_mem = mmap(NULL, MEM_SIZE(msgsize), 00166 PROT_READ | PROT_WRITE, MAP_SHARED, p->sq_srv_fd, 0); 00167 ec_cmp(p->sq_srv_mem, MAP_FAILED) 00168 ec_neg1( sem_init(&p->sq_srv_mem->sm_sem_w, true, 1) ) 00169 ec_neg1( sem_init(&p->sq_srv_mem->sm_sem_r, true, 0) ) 00170 } 00171 else { 00172 ec_neg1( p->sq_srv_fd = shm_open(shmname, O_RDWR, PERM_FILE) ) 00173 p->sq_srv_mem = mmap(NULL, MEM_SIZE(msgsize), 00174 PROT_READ | PROT_WRITE, MAP_SHARED, p->sq_srv_fd, 0); 00175 ec_cmp(p->sq_srv_mem, MAP_FAILED) 00176 mkshm_name_client(getpid(), shmname, sizeof(shmname)); 00177 if ((p->sq_clients[0].cl_fd = shm_open(shmname, O_RDWR, PERM_FILE)) 00178 != -1) { 00179 (void)shm_unlink(shmname); 00180 (void)close(p->sq_clients[0].cl_fd); 00181 } 00182 ec_neg1( p->sq_clients[0].cl_fd = shm_open(shmname, 00183 O_RDWR | O_CREAT, PERM_FILE) ) 00184 ec_neg1( ftruncate(p->sq_clients[0].cl_fd, MEM_SIZE(msgsize)) ) 00185 p->sq_clients[0].cl_mem = mmap(NULL, MEM_SIZE(msgsize), 00186 PROT_READ | PROT_WRITE, MAP_SHARED, p->sq_clients[0].cl_fd, 0); 00187 ec_cmp(p->sq_clients[0].cl_mem, MAP_FAILED) 00188 ec_neg1( sem_init(&p->sq_clients[0].cl_mem->sm_sem_w, true, 00189 1) ) // not sure if 1 is correct 00190 ec_neg1( sem_init(&p->sq_clients[0].cl_mem->sm_sem_r, true, 00191 0) ) 00192 } 00193 return (SMIQ *)p; 00194 00195 EC_CLEANUP_BGN 00196 if (p != NULL) 00197 (void)smi_close_pshm((SMIQ *)p); 00198 return NULL; 00199 EC_CLEANUP_END 00200 } 00201 /*[smi_close_pshm]*/ 00202 bool smi_close_pshm(SMIQ *sqp) 00203 { 00204 SMIQ_PSHM *p = (SMIQ_PSHM *)sqp; 00205 int i; 00206 char shmname[SERVER_NAME_MAX + 50]; 00207 00208 if (p->sq_entity == SMI_SERVER) { 00209 (void)op_semi(p->sq_srv_mem, SEMI_WRITE, SEMI_DESTROY); 00210 (void)op_semi(p->sq_srv_mem, SEMI_READ, SEMI_DESTROY); 00211 } 00212 (void)munmap(p->sq_srv_mem, MEM_SIZE(p->sq_msgsize)); 00213 if (p->sq_srv_fd > 0) 00214 (void)close(p->sq_srv_fd); 00215 if (p->sq_entity == SMI_SERVER) { 00216 mkshm_name_server(p, shmname, sizeof(shmname)); 00217 (void)shm_unlink(shmname); 00218 } 00219 for (i = 0; i < MAX_CLIENTS; i++) 00220 if (p->sq_clients[i].cl_mem != NULL) { 00221 if (p->sq_entity == SMI_CLIENT) { 00222 (void)op_semi(p->sq_clients[i].cl_mem, SEMI_WRITE, 00223 SEMI_DESTROY); 00224 (void)op_semi(p->sq_clients[i].cl_mem, SEMI_READ, 00225 SEMI_DESTROY); 00226 } 00227 (void)munmap(p->sq_clients[i].cl_mem, MEM_SIZE(p->sq_msgsize)); 00228 if (p->sq_clients[i].cl_fd > 0) 00229 (void)close(p->sq_clients[i].cl_fd); 00230 if (p->sq_entity == SMI_CLIENT) { 00231 mkshm_name_client(p->sq_clients[i].cl_pid, shmname, 00232 sizeof(shmname)); 00233 (void)shm_unlink(shmname); 00234 } 00235 } 00236 free(p); 00237 return true; 00238 } 00239 /*[]*/ 00240 /*[smi_send_getaddr_pshm]*/ 00241 bool smi_send_getaddr_pshm(SMIQ *sqp, struct client_id *client, 00242 void **addr) 00243 { 00244 SMIQ_PSHM *p = (SMIQ_PSHM *)sqp; 00245 struct client *cp; 00246 struct shared_mem *sm; 00247 00248 if (p->sq_entity == SMI_SERVER) { 00249 p->sq_client = *client; 00250 ec_null( cp = get_client(p, client->c_id1) ) 00251 sm = cp->cl_mem; 00252 } 00253 else 00254 sm = p->sq_srv_mem; 00255 ec_neg1( op_semi(sm, SEMI_WRITE, SEMI_WAIT) ) 00256 if (p->sq_entity == SMI_CLIENT) 00257 sm->sm_msg.smi_client.c_id1 = getpid(); 00258 *addr = &sm->sm_msg; 00259 return true; 00260 00261 EC_CLEANUP_BGN 00262 return false; 00263 EC_CLEANUP_END 00264 } 00265 /*[smi_send_release_pshm]*/ 00266 bool smi_send_release_pshm(SMIQ *sqp) 00267 { 00268 SMIQ_PSHM *p = (SMIQ_PSHM *)sqp; 00269 struct client *cp; 00270 struct shared_mem *sm; 00271 00272 if (p->sq_entity == SMI_SERVER) { 00273 ec_null( cp = get_client(p, p->sq_client.c_id1) ) 00274 sm = cp->cl_mem; 00275 } 00276 else 00277 sm = p->sq_srv_mem; 00278 ec_neg1( op_semi(sm, SEMI_READ, SEMI_POST) ) 00279 return true; 00280 00281 EC_CLEANUP_BGN 00282 return false; 00283 EC_CLEANUP_END 00284 } 00285 /*[smi_receive_getaddr_pshm]*/ 00286 bool smi_receive_getaddr_pshm(SMIQ *sqp, void **addr) 00287 { 00288 SMIQ_PSHM *p = (SMIQ_PSHM *)sqp; 00289 struct shared_mem *sm; 00290 00291 if (p->sq_entity == SMI_SERVER) 00292 sm = p->sq_srv_mem; 00293 else 00294 sm = p->sq_clients[0].cl_mem; 00295 ec_neg1( op_semi(sm, SEMI_READ, SEMI_WAIT) ) 00296 *addr = &sm->sm_msg; 00297 return true; 00298 00299 EC_CLEANUP_BGN 00300 return false; 00301 EC_CLEANUP_END 00302 } 00303 /*[smi_receive_release_pshm]*/ 00304 bool smi_receive_release_pshm(SMIQ *sqp) 00305 { 00306 SMIQ_PSHM *p = (SMIQ_PSHM *)sqp; 00307 struct shared_mem *sm; 00308 00309 if (p->sq_entity == SMI_SERVER) 00310 sm = p->sq_srv_mem; 00311 else 00312 sm = p->sq_clients[0].cl_mem; 00313 ec_neg1( op_semi(sm, SEMI_WRITE, SEMI_POST) ) 00314 return true; 00315 00316 EC_CLEANUP_BGN 00317 return false; 00318 EC_CLEANUP_END 00319 } 00320 /*[]*/ 00321 00322 #else /* FREEBSD or LINUX */ 00323 00324 SMIQ *smi_open_pshm(const char *name, SMIENTITY entity, size_t msgsize) 00325 { 00326 errno = ENOSYS; 00327 return NULL; 00328 } 00329 00330 bool smi_close_pshm(SMIQ *sqp) 00331 { 00332 return true; 00333 } 00334 00335 bool smi_send_getaddr_pshm(SMIQ *sqp, struct client_id *client, 00336 void **addr) 00337 { 00338 errno = ENOSYS; 00339 return false; 00340 } 00341 00342 bool smi_send_release_pshm(SMIQ *sqp) 00343 { 00344 errno = ENOSYS; 00345 return false; 00346 } 00347 00348 bool smi_receive_getaddr_pshm(SMIQ *sqp, void **addr) 00349 { 00350 errno = ENOSYS; 00351 return false; 00352 } 00353 00354 bool smi_receive_release_pshm(SMIQ *sqp) 00355 { 00356 errno = ENOSYS; 00357 return false; 00358 } 00359 00360 #endif /* FREEBSD or LINUX */