1 /* 2 * Copyright (c)2004 Cat's Eye Technologies. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 8 * Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 11 * Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in 13 * the documentation and/or other materials provided with the 14 * distribution. 15 * 16 * Neither the name of Cat's Eye Technologies nor the names of its 17 * contributors may be used to endorse or promote products derived 18 * from this software without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 23 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 24 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 25 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 26 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 27 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 29 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 30 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 31 * OF THE POSSIBILITY OF SUCH DAMAGE. 32 */ 33 34 /* 35 * conn_npipe.c 36 * $Id: conn_npipe.c,v 1.13 2005/02/06 19:53:19 cpressey Exp $ 37 */ 38 39 #include "system.h" 40 #ifdef HAS_NPIPE 41 42 #include <sys/types.h> 43 #include <sys/stat.h> 44 #include <sys/time.h> 45 #include <sys/errno.h> 46 47 #include <err.h> 48 #include <errno.h> 49 #include <stdarg.h> 50 #include <stdio.h> 51 #include <stdlib.h> 52 #include <string.h> 53 #include <unistd.h> 54 55 #include <libaura/buffer.h> 56 #include <libaura/fspred.h> 57 58 #define NEEDS_DFUI_STRUCTURE_DEFINITIONS 59 #include "dfui.h" 60 #undef NEEDS_DFUI_STRUCTURE_DEFINITIONS 61 #include "encoding.h" 62 #include "dump.h" 63 #include "conn_npipe.h" 64 65 /***** BACKEND ******/ 66 67 /** High Level **/ 68 69 /* 70 * Connect to the frontend. 71 */ 72 dfui_err_t 73 dfui_npipe_be_start(struct dfui_connection *c) 74 { 75 asprintf(&T_NPIPE(c)->out_pipename, "/tmp/dfui.%s.to_fe", c->rendezvous); 76 asprintf(&T_NPIPE(c)->in_pipename, "/tmp/dfui.%s.from_fe", c->rendezvous); 77 78 /* 79 * Create the named pipes. 80 */ 81 errno = 0; 82 if (mkfifo(T_NPIPE(c)->in_pipename, 0600) < 0) { 83 if (errno != EEXIST) { 84 warn("mkfifo (to_be)"); 85 return(DFUI_FAILURE); 86 } 87 } 88 errno = 0; 89 if (mkfifo(T_NPIPE(c)->out_pipename, 0600) < 0) { 90 if (errno != EEXIST) { 91 warn("mkfifo (to_fe)"); 92 return(DFUI_FAILURE); 93 } 94 } 95 dfui_debug("opening pipes...\n"); 96 if ((T_NPIPE(c)->out = fopen(T_NPIPE(c)->out_pipename, "w")) == NULL) { 97 return(DFUI_FAILURE); 98 } 99 dfui_debug("opened to_fe pipe\n"); 100 setvbuf(T_NPIPE(c)->out, NULL, _IONBF, 0); 101 if ((T_NPIPE(c)->in = fopen(T_NPIPE(c)->in_pipename, "r")) == NULL) { 102 fclose(T_NPIPE(c)->out); 103 return(DFUI_FAILURE); 104 } 105 dfui_debug("opened to_be pipe\n"); 106 return(DFUI_SUCCESS); 107 } 108 109 /* 110 * Tell the frontend that we're done and disconnect from it. 111 */ 112 dfui_err_t 113 dfui_npipe_be_stop(struct dfui_connection *c) 114 { 115 if (dfui_npipe_be_ll_exchange(c, DFUI_BE_MSG_STOP, "")) { 116 fclose(T_NPIPE(c)->in); 117 fclose(T_NPIPE(c)->out); 118 return(DFUI_SUCCESS); 119 } else 120 return(DFUI_FAILURE); 121 } 122 123 /** Low Level **/ 124 125 /* 126 * Exchange a message with the frontend. This involves two receive()/reply() 127 * cycles: one to provide our message, one to get a reply from the frontend. 128 * 129 * Note that this does not immediately send the message to the frontend - 130 * it can't, because we're a service and it's a client. What it does is 131 * keep the message handy and wait for a frontend request to come in. It 132 * then replies to that request with our message. 133 * 134 * The protocol looks something like the following, using the PRESENT and 135 * SUBMIT exchange as an example: 136 * 137 * frontend (client) | backend (service) 138 * ------------------+------------------ 139 * 140 * [stage 1] 141 * READY --> ll_receive() 142 * <-- PRESENT(form) ll_reply() 143 * 144 * [stage 2] 145 * SUBMIT(form) --> ll_receive() 146 * <-- READY ll_reply() 147 * 148 * Each of those exchanges is a pair of calls, on our end, to 149 * dfui_npipe_be_ll_receive() and dfui_npipe_be_ll_reply(). 150 * 151 * The set of messages that the client can pass us is determined by 152 * the conversation state: 153 * 154 * o In stage 1, only READY and ABORT are meaningful. 155 * o After a PRESENT, the messages SUBMIT and ABORT are meaningul 156 * in stage 2. 157 * o During a PROG_*, the messages CONTINUE, CANCEL, and ABORT 158 * are meaningful in stage 2. 159 * 160 * If the frontend sends us with READY in stage 2, we assume it has 161 * fallen out of sync, so we send the same initial reply again, going 162 * back to stage 1 as it were. 163 * 164 * After this call, the message is available in c->ebuf. 165 */ 166 dfui_err_t 167 dfui_npipe_be_ll_exchange(struct dfui_connection *c, char msgtype, const char *msg) 168 { 169 char *fmsg; 170 171 /* 172 * Construct our message to send. 173 */ 174 175 fmsg = malloc(strlen(msg) + 2); 176 fmsg[0] = msgtype; 177 strcpy(fmsg + 1, msg); 178 179 /* 180 * Get the frontend's message. 181 */ 182 183 dfui_npipe_be_ll_receive(c); 184 185 /* 186 * Frontend message should have been either READY or ABORT. 187 * If ABORT, we get out of here pronto. 188 */ 189 190 if (aura_buffer_buf(c->ebuf)[0] == DFUI_FE_MSG_ABORT) { 191 free(fmsg); 192 return(DFUI_FAILURE); 193 } 194 195 /* XXX if (!READY) ??? */ 196 197 do { 198 dfui_npipe_be_ll_reply(c, fmsg); 199 200 /* 201 * Here, the frontend has picked up our request and is 202 * processing it. We have to wait for the response. 203 */ 204 205 dfui_npipe_be_ll_receive(c); 206 207 /* 208 * Did we get READY from this? 209 * If so, loop! 210 */ 211 212 } while (aura_buffer_buf(c->ebuf)[0] == DFUI_FE_MSG_READY); 213 214 fmsg[0] = DFUI_BE_MSG_READY; 215 fmsg[1] = '\0'; 216 dfui_npipe_be_ll_reply(c, fmsg); 217 218 free(fmsg); 219 return(DFUI_SUCCESS); 220 } 221 222 /* 223 * Receive a message from the frontend. 224 * This call is synchronous. 225 * After this call, the NUL-terminated message is available in 226 * c->ebuf. 227 */ 228 dfui_err_t 229 dfui_npipe_be_ll_receive(struct dfui_connection *c) 230 { 231 int length; 232 char *buf; 233 234 dfui_debug("WAITING<<>>\n"); 235 236 fread(&length, 4, 1, T_NPIPE(c)->in); 237 238 dfui_debug("LENGTH<<%d>>\n", length); 239 240 buf = malloc(length + 1); 241 fread(buf, length, 1, T_NPIPE(c)->in); 242 aura_buffer_set(c->ebuf, buf, length); 243 free(buf); 244 245 dfui_debug("RECEIVED<<%s>>\n", aura_buffer_buf(c->ebuf)); 246 247 return(DFUI_SUCCESS); 248 } 249 250 /* 251 * Send a NUL-terminated reply to the frontend. 252 */ 253 dfui_err_t 254 dfui_npipe_be_ll_reply(struct dfui_connection *c, const char *fmsg) 255 { 256 int length; 257 258 dfui_debug("SEND<<%s>>\n", fmsg); 259 260 length = strlen(fmsg); 261 262 fwrite(&length, 4, 1, T_NPIPE(c)->out); 263 fwrite(fmsg, length, 1, T_NPIPE(c)->out); 264 265 return(DFUI_SUCCESS); 266 } 267 268 /******** FRONTEND ********/ 269 270 /** High Level **/ 271 272 dfui_err_t 273 dfui_npipe_fe_connect(struct dfui_connection *c) 274 { 275 asprintf(&T_NPIPE(c)->in_pipename, "/tmp/dfui.%s.to_fe", c->rendezvous); 276 asprintf(&T_NPIPE(c)->out_pipename, "/tmp/dfui.%s.from_fe", c->rendezvous); 277 278 dfui_debug("waiting for named pipes...\n"); 279 280 /* 281 * Wait for named pipes to be created. 282 */ 283 if (!is_named_pipe("%s", T_NPIPE(c)->in_pipename)) { 284 while (!is_named_pipe("%s", T_NPIPE(c)->in_pipename)) { 285 sleep(1); 286 } 287 sleep(1); 288 } 289 290 dfui_debug("opening inflow pipe...\n"); 291 292 if ((T_NPIPE(c)->in = fopen(T_NPIPE(c)->in_pipename, "r")) == NULL) { 293 return(DFUI_FAILURE); 294 } 295 296 dfui_debug("opening outflow pipe...\n"); 297 298 if ((T_NPIPE(c)->out = fopen(T_NPIPE(c)->out_pipename, "w")) == NULL) { 299 fclose(T_NPIPE(c)->in); 300 return(DFUI_FAILURE); 301 } 302 303 dfui_debug("making outflow pipe raw...\n"); 304 305 setvbuf(T_NPIPE(c)->out, NULL, _IONBF, 0); 306 return(DFUI_SUCCESS); 307 } 308 309 dfui_err_t 310 dfui_npipe_fe_disconnect(struct dfui_connection *c) 311 { 312 fclose(T_NPIPE(c)->in); 313 fclose(T_NPIPE(c)->out); 314 return(DFUI_SUCCESS); 315 } 316 317 /** Low Level **/ 318 319 /* 320 * Ask for, and subsequently receieve, a message from the backend. 321 * msgtype should be one of the DFUI_FE_MSG_* constants. 322 * This call is synchronous. 323 * After this call, the null-terminated, encoded message is 324 * available in T_NPIPE(c)->buf. 325 */ 326 dfui_err_t 327 dfui_npipe_fe_ll_request(struct dfui_connection *c, char msgtype, const char *msg) 328 { 329 char *fmsg, *buf; 330 int length; 331 332 /* 333 * First, assert that the connection is open. 334 */ 335 336 if (c == NULL || T_NPIPE(c)->in == NULL || T_NPIPE(c)->out == NULL) 337 return(DFUI_FAILURE); 338 339 /* 340 * Construct a message. 341 */ 342 343 fmsg = malloc(strlen(msg) + 2); 344 fmsg[0] = msgtype; 345 strcpy(fmsg + 1, msg); 346 347 dfui_debug("SEND<<%s>>\n", fmsg); 348 349 /* 350 * Send a NUL-terminated message to the backend. 351 */ 352 353 length = strlen(fmsg); 354 fwrite(&length, 4, 1, T_NPIPE(c)->out); 355 fwrite(fmsg, length, 1, T_NPIPE(c)->out); 356 357 /* 358 * Receive a reply from the backend. 359 * If our message was a READY, this should be a message like PRESENT. 360 * Otherwise it should simply be a READY. 361 */ 362 363 dfui_debug("WAITING<<>>\n"); 364 365 fread(&length, 4, 1, T_NPIPE(c)->in); 366 buf = malloc(length + 1); 367 fread(buf, length, 1, T_NPIPE(c)->in); 368 aura_buffer_set(c->ebuf, buf, length); 369 free(buf); 370 371 dfui_debug("RECV<<%s>>\n", aura_buffer_buf(c->ebuf)); 372 373 free(fmsg); 374 375 return(DFUI_SUCCESS); 376 } 377 378 #endif /* HAS_NPIPE */ 379