00001 /* 00002 SMI - FIFOs 00003 AUP2, Sec. 7.03.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 "../c4/setblock.h" 00023 #include "smi_fifo.h" 00024 00025 /* See comment in smi_receive_fifo about poor handling of too many clients. */ 00026 /* 00027 client uses only sq_clients[0] and sq_clients[1] and doesn't use 00028 sq_fd_server_w 00029 */ 00030 /*[SMIQ_FIFO]*/ 00031 #define MAX_CLIENTS 20 00032 00033 typedef struct { 00034 SMIENTITY sq_entity; /* entity */ 00035 int sq_fd_server; /* server read and ... */ 00036 int sq_fd_server_w; /* ... write file descriptors */ 00037 char sq_name[SERVER_NAME_MAX]; /* server name */ 00038 struct { 00039 int cl_fd; /* client file descriptor */ 00040 pid_t cl_pid; /* client process ID */ 00041 } sq_clients[MAX_CLIENTS]; 00042 struct client_id sq_client; /* client ID */ 00043 size_t sq_msgsize; /* msg size */ 00044 struct smi_msg *sq_msg; /* msg buffer */ 00045 } SMIQ_FIFO; 00046 /*[clients_bgn]*/ 00047 static void clients_bgn(SMIQ_FIFO *p) 00048 { 00049 int i; 00050 00051 for (i = 0; i < MAX_CLIENTS; i++) 00052 p->sq_clients[i].cl_fd = -1; 00053 } 00054 /*[clients_find]*/ 00055 static int clients_find(SMIQ_FIFO *p, pid_t pid) 00056 { 00057 int i, avail = -1; 00058 00059 for (i = 0; i < MAX_CLIENTS; i++) { 00060 if (p->sq_clients[i].cl_pid == pid) 00061 return i; 00062 if (p->sq_clients[i].cl_fd == -1 && avail == -1) 00063 avail = i; 00064 } 00065 if (avail != -1) 00066 p->sq_clients[avail].cl_pid = pid; 00067 return avail; 00068 } 00069 /*[clients_close_all]*/ 00070 static void clients_close_all(SMIQ_FIFO *p) 00071 { 00072 int i; 00073 00074 for (i = 0; i < MAX_CLIENTS; i++) 00075 if (p->sq_clients[i].cl_fd != -1) { 00076 (void)close(p->sq_clients[i].cl_fd); 00077 p->sq_clients[i].cl_fd = -1; 00078 } 00079 } 00080 /*[clients_end]*/ 00081 static void clients_end(SMIQ_FIFO *p) 00082 { 00083 clients_close_all(p); 00084 } 00085 /*[make_fifo_name_]*/ 00086 static void make_fifo_name_server(const SMIQ_FIFO *p, char *fifoname, 00087 size_t fifoname_max) 00088 { 00089 snprintf(fifoname, fifoname_max, "/tmp/smififo-%s", p->sq_name); 00090 } 00091 00092 static void make_fifo_name_client(pid_t pid, char *fifoname, 00093 size_t fifoname_max) 00094 { 00095 snprintf(fifoname, fifoname_max, "/tmp/smififo%ld", (long)pid); 00096 } 00097 /*[]*/ 00098 /* 00099 Instead of freeing allocated structure on error, another idea is to build local structure and allocate only at end, copying local structure to allocated one. 00100 */ 00101 /*[smi_open_fifo]*/ 00102 SMIQ *smi_open_fifo(const char *name, SMIENTITY entity, size_t msgsize) 00103 { 00104 SMIQ_FIFO *p = NULL; 00105 char fifoname[SERVER_NAME_MAX + 50]; 00106 00107 ec_null( p = calloc(1, sizeof(SMIQ_FIFO)) ) 00108 p->sq_msgsize = msgsize + offsetof(struct smi_msg, smi_data); 00109 ec_null( p->sq_msg = calloc(1, p->sq_msgsize) ) 00110 p->sq_entity = entity; 00111 if (strlen(name) >= SERVER_NAME_MAX) { 00112 errno = ENAMETOOLONG; 00113 EC_FAIL 00114 } 00115 strcpy(p->sq_name, name); 00116 make_fifo_name_server(p, fifoname, sizeof(fifoname)); 00117 if (p->sq_entity == SMI_SERVER) { 00118 clients_bgn(p); 00119 if (mkfifo(fifoname, PERM_FILE) == -1 && errno != EEXIST) 00120 EC_FAIL 00121 ec_neg1( p->sq_fd_server = open(fifoname, O_RDONLY) ) 00122 ec_neg1( p->sq_fd_server_w = open(fifoname, O_WRONLY) ) 00123 } 00124 else { 00125 ec_neg1( p->sq_fd_server = open(fifoname, O_WRONLY) ) 00126 make_fifo_name_client(getpid(), fifoname, sizeof(fifoname)); 00127 (void)unlink(fifoname); 00128 ec_neg1( mkfifo(fifoname, PERM_FILE) ) 00129 ec_neg1( p->sq_clients[0].cl_fd = 00130 open(fifoname, O_RDONLY | O_NONBLOCK) ) 00131 ec_false( setblock(p->sq_clients[0].cl_fd, true) ) 00132 ec_neg1( p->sq_clients[1].cl_fd = open(fifoname, O_WRONLY) ) 00133 } 00134 return (SMIQ *)p; 00135 00136 EC_CLEANUP_BGN 00137 if (p != NULL) { 00138 free(p->sq_msg); 00139 free(p); 00140 } 00141 return NULL; 00142 EC_CLEANUP_END 00143 } 00144 /*[smi_close_fifo]*/ 00145 bool smi_close_fifo(SMIQ *sqp) 00146 { 00147 SMIQ_FIFO *p = (SMIQ_FIFO *)sqp; 00148 00149 clients_end(p); 00150 (void)close(p->sq_fd_server); 00151 /* 00152 Leaving server FIFO around allows future sq_clients to be started before server. 00153 */ 00154 if (p->sq_entity == SMI_CLIENT) { 00155 char fifoname[SERVER_NAME_MAX + 50]; 00156 00157 make_fifo_name_client(getpid(), fifoname, sizeof(fifoname)); 00158 (void)unlink(fifoname); 00159 } 00160 else 00161 (void)close(p->sq_fd_server_w); 00162 free(p->sq_msg); 00163 free(p); 00164 return true; 00165 } 00166 /*[]*/ 00167 /* Assumes whole message can be sent at once. */ 00168 /*[smi_send_getaddr_fifo]*/ 00169 bool smi_send_getaddr_fifo(SMIQ *sqp, struct client_id *client, 00170 void **addr) 00171 { 00172 SMIQ_FIFO *p = (SMIQ_FIFO *)sqp; 00173 00174 if (p->sq_entity == SMI_SERVER) 00175 p->sq_client = *client; 00176 *addr = p->sq_msg; 00177 return true; 00178 } 00179 /*[smi_send_release_fifo]*/ 00180 bool smi_send_release_fifo(SMIQ *sqp) 00181 { 00182 SMIQ_FIFO *p = (SMIQ_FIFO *)sqp; 00183 ssize_t nwrite; 00184 00185 if (p->sq_entity == SMI_SERVER) { 00186 int nclient = clients_find(p, p->sq_client.c_id1); 00187 if (nclient == -1 || p->sq_clients[nclient].cl_fd == -1) { 00188 errno = EADDRNOTAVAIL; 00189 EC_FAIL 00190 } 00191 ec_neg1( nwrite = write(p->sq_clients[nclient].cl_fd, p->sq_msg, 00192 p->sq_msgsize) ) 00193 } 00194 else { 00195 p->sq_msg->smi_client.c_id1 = (long)getpid(); 00196 ec_neg1( nwrite = write(p->sq_fd_server, p->sq_msg, 00197 p->sq_msgsize) ) 00198 } 00199 return true; 00200 00201 EC_CLEANUP_BGN 00202 return false; 00203 EC_CLEANUP_END 00204 } 00205 /*[smi_receive_getaddr_fifo]*/ 00206 bool smi_receive_getaddr_fifo(SMIQ *sqp, void **addr) 00207 { 00208 SMIQ_FIFO *p = (SMIQ_FIFO *)sqp; 00209 ssize_t nread; 00210 00211 if (p->sq_entity == SMI_SERVER) { 00212 int nclient; 00213 char fifoname[SERVER_NAME_MAX + 50]; 00214 00215 while (true) { 00216 ec_neg1( nread = read(p->sq_fd_server, p->sq_msg, 00217 p->sq_msgsize) ) 00218 if (nread == 0) { 00219 errno = ENETDOWN; 00220 EC_FAIL 00221 } 00222 if (nread < offsetof(struct smi_msg, smi_data)) { 00223 errno = E2BIG; 00224 EC_FAIL 00225 } 00226 if ((nclient = clients_find(p, 00227 (pid_t)p->sq_msg->smi_client.c_id1)) == -1) { 00228 continue; /* client not notified */ 00229 } 00230 if (p->sq_clients[nclient].cl_fd == -1) { 00231 make_fifo_name_client((pid_t)p->sq_msg->smi_client.c_id1, 00232 fifoname, sizeof(fifoname)); 00233 ec_neg1( p->sq_clients[nclient].cl_fd = 00234 open(fifoname, O_WRONLY) ) 00235 } 00236 break; 00237 } 00238 } 00239 else 00240 ec_neg1( nread = read(p->sq_clients[0].cl_fd, p->sq_msg, 00241 p->sq_msgsize) ) 00242 *addr = p->sq_msg; 00243 return true; 00244 00245 EC_CLEANUP_BGN 00246 return false; 00247 EC_CLEANUP_END 00248 } 00249 /*[smi_receive_release_fifo]*/ 00250 bool smi_receive_release_fifo(SMIQ *sqp) 00251 { 00252 return true; 00253 } 00254 /*[]*/