00001 /* 00002 SMI - System V shared memory 00003 AUP2, Sec. 7.13.3 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_shm.h" 00023 #include <sys/sem.h> 00024 #include <sys/shm.h> 00025 /*[define]*/ 00026 #define SEMI_READ 0 00027 #define SEMI_WRITE 1 00028 #define SEMI_POST 1 00029 #define SEMI_WAIT -1 00030 /*[SMIQ_SHM]*/ 00031 typedef struct { 00032 SMIENTITY sq_entity; /* entity */ 00033 int sq_semid_server; /* server sem */ 00034 int sq_semid_client; /* client sem (client only) */ 00035 int sq_shmid_server; /* server shm ID */ 00036 int sq_shmid_client; /* client shm ID (client only) */ 00037 struct smi_msg *msg_server; /* ptr to server shm */ 00038 struct smi_msg *msg_client; /* ptr to client shm (client only) */ 00039 char sq_name[SERVER_NAME_MAX];/* server name */ 00040 struct client_id sq_client; /* client identification (server only)*/ 00041 } SMIQ_SHM; 00042 /*[]*/ 00043 #ifndef BSD_DERIVED 00044 union semun { /* not in sys/sem.h, except in FreeBSD and Darwin */ 00045 int val; 00046 struct semid_ds *buf; 00047 unsigned short *array; 00048 }; 00049 #endif 00050 00051 /*[mkshm_name_server]*/ 00052 static void mkshm_name_server(const SMIQ_SHM *p, char *shmname, 00053 size_t shmname_max) 00054 { 00055 snprintf(shmname, shmname_max, "/tmp/smishm-%s", p->sq_name); 00056 } 00057 /*[op_semi]*/ 00058 static int op_semi(int semid, int sem_num, int sem_op) 00059 { 00060 struct sembuf sbuf; 00061 int r; 00062 00063 sbuf.sem_num = sem_num; 00064 sbuf.sem_op = sem_op; 00065 sbuf.sem_flg = 0; 00066 ec_neg1( r = semop(semid, &sbuf, 1) ) 00067 return r; 00068 00069 EC_CLEANUP_BGN 00070 return -1; 00071 EC_CLEANUP_END 00072 } 00073 /*[init_semi]*/ 00074 static int init_semi(int semid) 00075 { 00076 union semun arg; 00077 int r; 00078 00079 arg.val = 0; 00080 semctl(semid, SEMI_WRITE, SETVAL, arg); 00081 semctl(semid, SEMI_READ, SETVAL, arg); 00082 /* Following call will set otime, allowing clients to proceed. */ 00083 ec_neg1( r = op_semi(semid, SEMI_WRITE, SEMI_POST) ) 00084 return r; 00085 00086 EC_CLEANUP_BGN 00087 return -1; 00088 EC_CLEANUP_END 00089 } 00090 /*[]*/ 00091 /* 00092 Following function used only by server. 00093 */ 00094 /*[getaddr]*/ 00095 #define MAX_ATTACHED 20 00096 00097 static void *getaddr(int shmid) 00098 { 00099 static struct { 00100 int shmid; 00101 void *addr; 00102 long time; 00103 } mapped[MAX_ATTACHED]; 00104 static long clock; 00105 int i, avail, oldest, r; 00106 static bool first = true; 00107 00108 if (first) { 00109 first = false; 00110 for (i = 0; i < MAX_ATTACHED; i++) 00111 mapped[i].shmid = -1; 00112 } 00113 if (shmid == -1) { /* final cleanup */ 00114 for (i = 0; i < MAX_ATTACHED; i++) 00115 if (mapped[i].shmid != -1) { 00116 (void)shmdt(mapped[i].addr); 00117 mapped[i].shmid = -1; 00118 } 00119 return NULL; 00120 } 00121 clock++; 00122 avail = -1; 00123 for (i = 0; i < MAX_ATTACHED; i++) { 00124 if (mapped[i].shmid == shmid) { 00125 mapped[i].time = clock; 00126 return mapped[i].addr; 00127 } 00128 if (mapped[i].shmid == -1 && avail == -1) 00129 avail = i; 00130 } 00131 if (avail == -1) { /* all fds in use; find oldest */ 00132 oldest = -1; 00133 for (i = 0; i < MAX_ATTACHED; i++) 00134 if (oldest == -1 || mapped[i].time < oldest) { 00135 oldest = mapped[i].time; 00136 avail = i; 00137 } 00138 r = shmdt(mapped[avail].addr); 00139 ec_neg1( r ) 00140 } 00141 mapped[avail].addr = shmat(shmid, NULL, 0); 00142 ec_neg1( mapped[avail].addr ) 00143 mapped[avail].time = clock; 00144 mapped[avail].shmid = shmid; 00145 return mapped[avail].addr; 00146 00147 EC_CLEANUP_BGN 00148 return NULL; 00149 EC_CLEANUP_END 00150 } 00151 /*[]*/ 00152 /* cleanup needs to be more complete than just free(p) */ 00153 /*[smi_open_shm]*/ 00154 SMIQ *smi_open_shm(const char *name, SMIENTITY entity, size_t msgsize) 00155 { 00156 SMIQ_SHM *p = NULL; 00157 char shmname[FILENAME_MAX]; 00158 int i; 00159 key_t key; 00160 00161 ec_null( p = calloc(1, sizeof(SMIQ_SHM)) ) 00162 p->sq_entity = entity; 00163 if (strlen(name) >= SERVER_NAME_MAX) { 00164 errno = ENAMETOOLONG; 00165 EC_FAIL 00166 } 00167 strcpy(p->sq_name, name); 00168 mkshm_name_server(p, shmname, sizeof(shmname)); 00169 (void)close(open(shmname, O_WRONLY | O_CREAT, 0)); 00170 ec_neg1( key = ftok(shmname, 1) ) 00171 if (p->sq_entity == SMI_SERVER) { 00172 if ((p->sq_semid_server = semget(key, 2, PERM_FILE)) != -1) 00173 (void)shmctl(p->sq_semid_server, IPC_RMID, NULL); 00174 ec_neg1( p->sq_semid_server = semget(key, 2, 00175 PERM_FILE | IPC_CREAT) ) 00176 p->sq_semid_client = -1; 00177 if ((p->sq_shmid_server = shmget(key, 0, PERM_FILE)) != -1) 00178 (void)shmctl(p->sq_shmid_server, IPC_RMID, NULL); 00179 ec_neg1( p->sq_shmid_server = shmget(key, msgsize, 00180 PERM_FILE | IPC_CREAT) ) 00181 p->sq_shmid_client = -1; 00182 ec_neg1( init_semi(p->sq_semid_server) ) 00183 } 00184 else { 00185 ec_neg1( p->sq_semid_server = semget(key, 2, PERM_FILE) ) 00186 ec_neg1( p->sq_semid_client = semget(IPC_PRIVATE, 2, 00187 PERM_FILE | IPC_CREAT) ) 00188 ec_neg1( p->sq_shmid_server = shmget(key, msgsize, PERM_FILE) ) 00189 ec_neg1( p->sq_shmid_client = shmget(IPC_PRIVATE, msgsize, 00190 PERM_FILE | IPC_CREAT) ) 00191 ec_neg1( p->msg_client = shmat(p->sq_shmid_client, NULL, 0) ) 00192 ec_neg1( init_semi(p->sq_semid_client) ) 00193 /*[]*/ 00194 /* 00195 Wait until server has initialized its semaphores. 00196 00197 Two problems with this loop: 00198 1. Wait of 1 sec or more messes up performance testing. 00199 2. Some systems (e.g., FreeBSD) seem not to update otime. 00200 00201 Solutions: 00202 1. Mode that skips the loop. 00203 2. Loop for at most 10 sec. If it hasn't happened by then, 00204 it most likely never will. 00205 */ 00206 /*[smi_open_shm2]*/ 00207 for (i = 0; !smi_client_nowait && i < 10; i++) { 00208 union semun arg; 00209 struct semid_ds ds; 00210 00211 arg.buf = &ds; 00212 ec_neg1( semctl(p->sq_semid_server, SEMI_WRITE, IPC_STAT, 00213 arg) ) 00214 if (ds.sem_otime > 0) 00215 break; 00216 sleep(1); 00217 } 00218 } 00219 ec_neg1( p->msg_server = shmat(p->sq_shmid_server, NULL, 0) ) 00220 return (SMIQ *)p; 00221 00222 EC_CLEANUP_BGN 00223 free(p); 00224 return NULL; 00225 EC_CLEANUP_END 00226 } 00227 /*[smi_close_shm]*/ 00228 bool smi_close_shm(SMIQ *sqp) 00229 { 00230 SMIQ_SHM *p = (SMIQ_SHM *)sqp; 00231 00232 if (p->sq_entity == SMI_SERVER) { 00233 char shmname[FILENAME_MAX]; 00234 00235 (void)getaddr(-1); 00236 #ifdef DARWIN /* not in book */ 00237 { 00238 union semun dummy; 00239 ec_neg1( semctl(p->sq_semid_server, 0, IPC_RMID, dummy) ) 00240 } 00241 #else 00242 ec_neg1( semctl(p->sq_semid_server, 0, IPC_RMID) ) 00243 #endif 00244 (void)shmdt(p->msg_server); 00245 (void)shmctl(p->sq_shmid_server, IPC_RMID, NULL); 00246 mkshm_name_server(p, shmname, sizeof(shmname)); 00247 (void)unlink(shmname); 00248 } 00249 else { 00250 /* following call to semctl causes msg_time to hang on last client with FreeBSD */ 00251 #ifndef FREEBSD 00252 #ifdef DARWIN /* not in book */ 00253 { 00254 union semun dummy; 00255 ec_neg1( semctl(p->sq_semid_client, 0, IPC_RMID, dummy) ) 00256 } 00257 #else 00258 ec_neg1( semctl(p->sq_semid_client, 0, IPC_RMID) ) 00259 #endif 00260 #endif 00261 (void)shmdt(p->msg_server); 00262 (void)shmdt(p->msg_client); 00263 (void)shmctl(p->sq_shmid_client, IPC_RMID, NULL); 00264 } 00265 free(p); 00266 return true; 00267 00268 EC_CLEANUP_BGN 00269 return false; 00270 EC_CLEANUP_END 00271 } 00272 /*[]*/ 00273 /*[smi_send_getaddr_shm]*/ 00274 bool smi_send_getaddr_shm(SMIQ *sqp, struct client_id *client, void **addr) 00275 { 00276 SMIQ_SHM *p = (SMIQ_SHM *)sqp; 00277 int semid_receiver; 00278 00279 if (p->sq_entity == SMI_SERVER) { 00280 semid_receiver = client->c_id2; 00281 p->sq_client = *client; 00282 } 00283 else 00284 semid_receiver = p->sq_semid_server; 00285 ec_neg1( op_semi(semid_receiver, SEMI_WRITE, SEMI_WAIT) ) 00286 if (p->sq_entity == SMI_SERVER) 00287 ec_null( *addr = getaddr(client->c_id1) ) 00288 else { 00289 *addr = p->msg_server; 00290 ((struct smi_msg *)*addr)->smi_client.c_id1 = p->sq_shmid_client; 00291 ((struct smi_msg *)*addr)->smi_client.c_id2 = p->sq_semid_client; 00292 } 00293 return true; 00294 00295 EC_CLEANUP_BGN 00296 return false; 00297 EC_CLEANUP_END 00298 } 00299 /*[smi_send_release_shm]*/ 00300 bool smi_send_release_shm(SMIQ *sqp) 00301 { 00302 SMIQ_SHM *p = (SMIQ_SHM *)sqp; 00303 int semid_receiver; 00304 00305 if (p->sq_entity == SMI_SERVER) 00306 semid_receiver = p->sq_client.c_id2; 00307 else 00308 semid_receiver = p->sq_semid_server; 00309 ec_neg1( op_semi(semid_receiver, SEMI_READ, SEMI_POST) ) 00310 return true; 00311 00312 EC_CLEANUP_BGN 00313 return false; 00314 EC_CLEANUP_END 00315 } 00316 /*[smi_receive_getaddr_shm]*/ 00317 bool smi_receive_getaddr_shm(SMIQ *sqp, void **addr) 00318 { 00319 SMIQ_SHM *p = (SMIQ_SHM *)sqp; 00320 int semid_receiver; 00321 00322 if (p->sq_entity == SMI_SERVER) 00323 semid_receiver = p->sq_semid_server; 00324 else 00325 semid_receiver = p->sq_semid_client; 00326 ec_neg1( op_semi(semid_receiver, SEMI_READ, SEMI_WAIT) ) 00327 if (p->sq_entity == SMI_SERVER) 00328 *addr = p->msg_server; 00329 else 00330 *addr = p->msg_client; 00331 return true; 00332 00333 EC_CLEANUP_BGN 00334 return false; 00335 EC_CLEANUP_END 00336 } 00337 /*[smi_receive_release_shm]*/ 00338 bool smi_receive_release_shm(SMIQ *sqp) 00339 { 00340 SMIQ_SHM *p = (SMIQ_SHM *)sqp; 00341 int semid_receiver; 00342 00343 if (p->sq_entity == SMI_SERVER) 00344 semid_receiver = p->sq_semid_server; 00345 else 00346 semid_receiver = p->sq_semid_client; 00347 ec_neg1( op_semi(semid_receiver, SEMI_WRITE, SEMI_POST) ) 00348 return true; 00349 00350 EC_CLEANUP_BGN 00351 return false; 00352 EC_CLEANUP_END 00353 } 00354 /*[]*/