1 /*
2 OWFS -- One-Wire filesystem
3 OWHTTPD -- One-Wire Web Server
4 Written 2003 Paul H Alfille
5 email: paul.alfille@gmail.com
6 Released under the GPL
7 See the header file: ow.h for full attribution
8 1wire/iButton system from Dallas Semiconductor
9 */
10
11 /* ow_server talks to the server, sending and recieving messages */
12 /* this is an alternative to direct bus communication */
13
14 #include <config.h>
15 #include "owfs_config.h"
16 #include "ow.h"
17 #include "ow_connection.h"
18 #include "ow_standard.h" // for FS_?_alias
19
20 struct server_connection_state {
21 FILE_DESCRIPTOR_OR_ERROR file_descriptor ;
22 enum persistent_state { persistent_yes, persistent_no, } persistence ;
23 struct connection_in * in ;
24 } ;
25
26 struct directory_element_structure {
27 const struct parsedname * pn_whole_directory ;
28 struct dirblob db ;
29 void (*dirfunc) (void *, const struct parsedname * const) ;
30 void * v ;
31 } ;
32
33 static uint32_t SetupControlFlags(const struct parsedname *pn);
34
35 static ZERO_OR_ERROR ServerDIRALL(void (*dirfunc) (void *, const struct parsedname * const), void *v, const struct parsedname *pn_whole_directory, uint32_t * flags);
36 static ZERO_OR_ERROR ServerDIR(void (*dirfunc) (void *, const struct parsedname * const), void *v, const struct parsedname *pn_whole_directory, uint32_t * flags);
37
38 static void Directory_Element_Init( struct directory_element_structure * des );
39 static void Directory_Element_Finish( struct directory_element_structure * des );
40 static ZERO_OR_ERROR Directory_Element( char * current_file, struct directory_element_structure * des );
41
42 static void Close_Persistent( struct server_connection_state * scs) ;
43 static void Release_Persistent( struct server_connection_state * scs, int granted ) ;
44
45 static GOOD_OR_BAD To_Server( struct server_connection_state * scs, struct server_msg * sm, struct serverpackage *sp) ;
46 static SIZE_OR_ERROR WriteToServer(int file_descriptor, struct server_msg *sm, struct serverpackage *sp);
47
48 static SIZE_OR_ERROR From_Server( struct server_connection_state * scs, struct client_msg *cm, char *msg, size_t size) ;
49 static void *From_ServerAlloc(struct server_connection_state * scs, struct client_msg *cm) ;
50
51
52 // Send to an owserver using the READ message
ServerRead(struct one_wire_query * owq)53 SIZE_OR_ERROR ServerRead(struct one_wire_query *owq)
54 {
55 struct server_msg sm;
56 struct client_msg cm;
57 struct parsedname *pn_file_entry = PN(owq);
58 struct serverpackage sp = { pn_file_entry->path_to_server, NULL, 0, pn_file_entry->tokenstring,
59 pn_file_entry->tokens,
60 };
61 struct server_connection_state scs ;
62
63 // initialization
64 scs.in = pn_file_entry->selected_connection ;
65 memset(&sm, 0, sizeof(struct server_msg));
66 memset(&cm, 0, sizeof(struct client_msg));
67 sm.type = msg_read;
68 sm.size = OWQ_size(owq);
69 sm.offset = OWQ_offset(owq);
70
71 // Alias should show local understanding except if bus.x specified
72 if ( (pn_file_entry->selected_filetype != NULL) && (pn_file_entry->selected_filetype->format == ft_alias && ! SpecifiedRemoteBus(pn_file_entry) )) {
73 ignore_result = FS_r_alias( owq ) ;
74 return OWQ_length(owq) ;
75 }
76
77 LEVEL_CALL("SERVER(%d) path=%s", pn_file_entry->selected_connection->index, SAFESTRING(pn_file_entry->path_to_server));
78
79 // Send to owserver
80 sm.control_flags = SetupControlFlags(pn_file_entry);
81 if ( BAD( To_Server( &scs, &sm, &sp) ) ) {
82 Release_Persistent( &scs, 0);
83 return -EIO ;
84 }
85
86 // Receive from owserver
87 if ( From_Server( &scs, &cm, OWQ_buffer(owq), OWQ_size(owq)) < 0 ) {
88 Release_Persistent( &scs, 0);
89 return -EIO ;
90 }
91 Release_Persistent( &scs, cm.control_flags & PERSISTENT_MASK);
92 return cm.ret;
93 }
94
95 // Send to an owserver using the PRESENT message
ServerPresence(struct parsedname * pn_file_entry)96 INDEX_OR_ERROR ServerPresence( struct parsedname *pn_file_entry)
97 {
98 struct server_msg sm;
99 struct client_msg cm;
100 struct serverpackage sp = { pn_file_entry->path_to_server, NULL, 0, pn_file_entry->tokenstring,
101 pn_file_entry->tokens,
102 };
103 BYTE * serial_number ;
104 struct server_connection_state scs ;
105
106 // initialization
107 scs.in = pn_file_entry->selected_connection ;
108 memset(&sm, 0, sizeof(struct server_msg));
109 memset(&cm, 0, sizeof(struct client_msg));
110 sm.type = msg_presence;
111
112 LEVEL_CALL("SERVER(%d) path=%s", pn_file_entry->selected_connection->index, SAFESTRING(pn_file_entry->path_to_server));
113
114 // Send to owserver
115 sm.control_flags = SetupControlFlags( pn_file_entry);
116 if ( BAD( To_Server( &scs, &sm, &sp) ) ) {
117 Release_Persistent( &scs, 0 ) ;
118 return INDEX_BAD ;
119 }
120
121 // Receive from owserver
122 serial_number = (BYTE *) From_ServerAlloc( &scs, &cm) ;
123 if (cm.ret < 0) {
124 Release_Persistent(&scs, 0 );
125 return INDEX_BAD ;
126 }
127
128 // Newer owservers return the serial number -- relevant for alias propagation
129 if ( serial_number ) {
130 memcpy( pn_file_entry->sn, serial_number, SERIAL_NUMBER_SIZE ) ;
131 owfree( serial_number) ;
132 }
133
134 Release_Persistent(&scs, cm.control_flags & PERSISTENT_MASK);
135 return INDEX_VALID(cm.ret) ? pn_file_entry->selected_connection->index : INDEX_BAD;
136 }
137
138 // Send to an owserver using the WRITE message
ServerWrite(struct one_wire_query * owq)139 ZERO_OR_ERROR ServerWrite(struct one_wire_query *owq)
140 {
141 struct server_msg sm;
142 struct client_msg cm;
143 struct parsedname *pn_file_entry = PN(owq);
144 struct serverpackage sp = { pn_file_entry->path_to_server, (BYTE *) OWQ_buffer(owq),
145 OWQ_size(owq), pn_file_entry->tokenstring, pn_file_entry->tokens,
146 };
147 struct server_connection_state scs ;
148
149 // initialization
150 scs.in = pn_file_entry->selected_connection ;
151 memset(&sm, 0, sizeof(struct server_msg));
152 memset(&cm, 0, sizeof(struct client_msg));
153 sm.type = msg_write;
154 sm.size = OWQ_size(owq);
155 sm.offset = OWQ_offset(owq);
156
157 LEVEL_CALL("SERVER(%d) path=%s", pn_file_entry->selected_connection->index, SAFESTRING(pn_file_entry->path_to_server));
158
159 // Send to owserver
160 sm.control_flags = SetupControlFlags( pn_file_entry);
161 if ( BAD( To_Server( &scs, &sm, &sp) ) ) {
162 Release_Persistent( &scs, 0 ) ;
163 return -EIO ;
164 }
165
166 // Receive from owserver
167 if ( From_Server( &scs, &cm, NULL, 0) < 0) {
168 Release_Persistent( &scs, 0 ) ;
169 return -EIO ;
170 }
171 {
172 int32_t control_flags = cm.control_flags & ~(SHOULD_RETURN_BUS_LIST | PERSISTENT_MASK | SAFEMODE);
173 // keep current safemode
174 control_flags |= LocalControlFlags & SAFEMODE ;
175 CONTROLFLAGSLOCK;
176 if (LocalControlFlags != control_flags) {
177 // replace control flags (except safemode persists)
178 //printf("ServerRead: cm.control_flags changed! controlflags=%X cm.control_flags=%X\n", SemiGlobal, cm.control_flags);
179 LocalControlFlags = control_flags;
180 }
181 CONTROLFLAGSUNLOCK;
182 }
183
184 Release_Persistent(&scs, cm.control_flags & PERSISTENT_MASK);
185 return cm.ret;
186 }
187
188 // Send to an owserver using either the DIR or DIRALL message
ServerDir(void (* dirfunc)(void *,const struct parsedname * const),void * v,const struct parsedname * pn_whole_directory,uint32_t * flags)189 ZERO_OR_ERROR ServerDir(void (*dirfunc) (void *, const struct parsedname * const), void *v, const struct parsedname *pn_whole_directory, uint32_t * flags)
190 {
191 ZERO_OR_ERROR ret;
192 struct connection_in * in = pn_whole_directory->selected_connection ;
193
194 // Do we know this server doesn't support DIRALL?
195 if (in->master.server.no_dirall) {
196 return ServerDIR(dirfunc, v, pn_whole_directory, flags);
197 }
198 // Did we ask for no DIRALL explicitly?
199 if (Globals.no_dirall) {
200 return ServerDIR(dirfunc, v, pn_whole_directory, flags);
201 }
202 // device directories have lower latency with DIR
203 if (IsRealDir(pn_whole_directory) || IsAlarmDir(pn_whole_directory)) {
204 return ServerDIR(dirfunc, v, pn_whole_directory, flags);
205 }
206 // try DIRALL and see if supported
207 if ((ret = ServerDIRALL(dirfunc, v, pn_whole_directory, flags)) == -ENOMSG) {
208 in->master.server.no_dirall = 1;
209 return ServerDIR(dirfunc, v, pn_whole_directory, flags);
210 }
211 return ret;
212 }
213
214 // Send to an owserver using the DIR message
ServerDIR(void (* dirfunc)(void *,const struct parsedname * const),void * v,const struct parsedname * pn_whole_directory,uint32_t * flags)215 static ZERO_OR_ERROR ServerDIR(void (*dirfunc) (void *, const struct parsedname * const), void *v, const struct parsedname *pn_whole_directory, uint32_t * flags)
216 {
217 struct server_msg sm;
218 struct client_msg cm;
219 struct serverpackage sp = { pn_whole_directory->path_to_server, NULL, 0,
220 pn_whole_directory->tokenstring, pn_whole_directory->tokens,
221 };
222 struct server_connection_state scs ;
223 struct directory_element_structure des ;
224
225 char *return_path;
226
227 // initialization
228 scs.in = pn_whole_directory->selected_connection ;
229 memset(&sm, 0, sizeof(struct server_msg));
230 memset(&cm, 0, sizeof(struct client_msg));
231 sm.type = msg_dir;
232
233 LEVEL_CALL("SERVER(%d) path=%s path_to_server=%s",
234 scs.in->index, SAFESTRING(pn_whole_directory->path), SAFESTRING(pn_whole_directory->path_to_server));
235
236 // Send to owserver
237 sm.control_flags = SetupControlFlags( pn_whole_directory);
238 if ( BAD( To_Server( &scs, &sm, &sp) ) ) {
239 Release_Persistent( &scs, 0 ) ;
240 return -EIO ;
241 }
242
243 des.dirfunc = dirfunc ;
244 des.v = v ;
245 des.pn_whole_directory = pn_whole_directory ;
246 Directory_Element_Init( &des ) ;
247
248 // Receive from owserver -- in a loop for each directory entry
249 while ( (return_path = From_ServerAlloc(&scs, &cm)) != NO_PATH ) {
250 ZERO_OR_ERROR ret;
251
252 return_path[cm.payload - 1] = '\0'; /* Ensure trailing null */
253
254 ret = Directory_Element( return_path, &des ) ;
255
256 owfree(return_path);
257
258 if (ret) {
259 cm.ret = ret;
260 break;
261 }
262 }
263
264 Directory_Element_Finish( &des ) ;
265
266 DIRLOCK;
267 /* flags are sent back in "offset" of final blank entry */
268 flags[0] |= cm.offset;
269 DIRUNLOCK;
270
271 Release_Persistent(&scs, cm.control_flags & PERSISTENT_MASK);
272 return cm.ret;
273 }
274
275 // Send to an owserver using the DIRALL message
ServerDIRALL(void (* dirfunc)(void *,const struct parsedname * const),void * v,const struct parsedname * pn_whole_directory,uint32_t * flags)276 static ZERO_OR_ERROR ServerDIRALL(void (*dirfunc) (void *, const struct parsedname * const), void *v, const struct parsedname *pn_whole_directory, uint32_t * flags)
277 {
278 ASCII *comma_separated_list;
279 struct server_msg sm;
280 struct client_msg cm;
281 struct serverpackage sp = { pn_whole_directory->path_to_server, NULL, 0,
282 pn_whole_directory->tokenstring, pn_whole_directory->tokens,
283 };
284 struct connection_in * in = pn_whole_directory->selected_connection ;
285 struct server_connection_state scs ;
286
287 // initialization
288 scs.in = in ;
289 memset(&sm, 0, sizeof(struct server_msg));
290 memset(&cm, 0, sizeof(struct client_msg));
291 sm.type = msg_dirall;
292
293 LEVEL_CALL("SERVER(%d) path=%s path_to_server=%s",
294 in->index, SAFESTRING(pn_whole_directory->path), SAFESTRING(pn_whole_directory->path_to_server));
295
296 // Send to owserver
297 sm.control_flags = SetupControlFlags( pn_whole_directory);
298 if ( BAD( To_Server( &scs, &sm, &sp) ) ) {
299 Release_Persistent( &scs, 0 ) ;
300 return -EIO ;
301 }
302
303 // Receive from owserver
304 comma_separated_list = From_ServerAlloc(&scs, &cm);
305 LEVEL_DEBUG("got %s", SAFESTRING(comma_separated_list));
306 if (cm.ret == 0) {
307 ASCII *current_file;
308 ASCII *rest_of_comma_list = comma_separated_list;
309 struct directory_element_structure des ;
310
311 des.dirfunc = dirfunc ;
312 des.v = v ;
313 des.pn_whole_directory = pn_whole_directory ;
314 Directory_Element_Init( &des ) ;
315
316 while ((current_file = strsep(&rest_of_comma_list, ",")) != NULL) {
317 ZERO_OR_ERROR ret = Directory_Element( current_file, &des );
318 if (ret) {
319 cm.ret = ret;
320 break;
321 }
322 }
323
324 Directory_Element_Finish( &des ) ;
325
326 DIRLOCK;
327 /* flags are sent back in "offset" */
328 flags[0] |= cm.offset;
329 DIRUNLOCK;
330
331 }
332 // free the allocated memory
333 SAFEFREE(comma_separated_list) ;
334
335 Release_Persistent( &scs, cm.control_flags & PERSISTENT_MASK );
336 return cm.ret;
337 }
338
Directory_Element_Init(struct directory_element_structure * des)339 static void Directory_Element_Init( struct directory_element_structure * des )
340 {
341 /* If cacheable, try to allocate a blob for storage */
342 /* only for "read devices" and not alarm */
343 DirblobInit(&(des->db) );
344 if (IsRealDir(des->pn_whole_directory)
345 && NotAlarmDir(des->pn_whole_directory)
346 && !SpecifiedBus(des->pn_whole_directory)
347 && des->pn_whole_directory->selected_device == NO_DEVICE) {
348 if (RootNotBranch(des->pn_whole_directory)) { /* root dir */
349 BUSLOCK(des->pn_whole_directory);
350 des->db.allocated = des->pn_whole_directory->selected_connection->last_root_devs; // root dir estimated length
351 BUSUNLOCK(des->pn_whole_directory);
352 }
353 } else {
354 DirblobPoison( &(des->db) ); // don't cache other directories
355 }
356 }
357
Directory_Element_Finish(struct directory_element_structure * des)358 static void Directory_Element_Finish( struct directory_element_structure * des )
359 {
360 /* Add to the cache (full list as a single element */
361 if ( DirblobPure( &(des->db) ) ) {
362 Cache_Add_Dir( &(des->db), des->pn_whole_directory); // end with a null entry
363 if (RootNotBranch(des->pn_whole_directory)) {
364 BUSLOCK(des->pn_whole_directory);
365 des->pn_whole_directory->selected_connection->last_root_devs = DirblobElements( &(des->db) ); // root dir estimated length
366 BUSUNLOCK(des->pn_whole_directory);
367 }
368 }
369 DirblobClear( & (des->db) );
370 }
371
Directory_Element(char * current_file,struct directory_element_structure * des)372 static ZERO_OR_ERROR Directory_Element( char * current_file, struct directory_element_structure * des )
373 {
374 struct parsedname s_pn_directory_element;
375 struct parsedname * pn_directory_element = & s_pn_directory_element ;
376 struct connection_in * in = des->pn_whole_directory->selected_connection ;
377 ZERO_OR_ERROR ret ;
378
379 LEVEL_DEBUG("got=[%s]", current_file);
380
381 if (SpecifiedRemoteBus(des->pn_whole_directory)) {
382 // Specified remote bus, add the bus to the path (in front)
383 int path_length = strlen(current_file);
384 char BigBuffer[path_length + 12];
385 int sn_ret ;
386 char * no_leading_slash = ¤t_file[(current_file[0] == '/') ? 1 : 0] ;
387 UCLIBCLOCK ;
388 sn_ret = snprintf(BigBuffer, path_length + 11, "/bus.%d/%s",in->index, no_leading_slash ) ;
389 UCLIBCUNLOCK ;
390 if (sn_ret < 0) {
391 return -EINVAL ;
392 }
393 ret = FS_ParsedName_BackFromRemote(BigBuffer, pn_directory_element);
394 } else {
395 ret = FS_ParsedName_BackFromRemote(current_file, pn_directory_element);
396 }
397
398 if ( ret < 0 ) {
399 DirblobPoison( &(des->db) ); // don't cache a mistake
400 return ret;
401 }
402
403 /* we got a device on bus_nr = pn_whole_directory->selected_connection->index. Cache it so we
404 find it quicker next time we want to do read values from the
405 the actual device
406 */
407 if (IsRealDir(des->pn_whole_directory)) {
408 /* If we get a device then cache the bus_nr */
409 Cache_Add_Device(in->index, pn_directory_element->sn);
410 }
411 /* Add to cache Blob -- snlist is also a flag for cachable */
412 if (DirblobPure( &(des->db) ) ) { /* only add if there is a blob allocated successfully */
413 DirblobAdd(pn_directory_element->sn, &(des->db) );
414 }
415
416 // Now actually do the directory function on this element
417 FS_dir_entry_aliased(des->dirfunc,des->v,pn_directory_element) ;
418
419 // Cleanup
420 FS_ParsedName_destroy(pn_directory_element); // destroy the last parsed name
421
422 return 0 ;
423 }
424
425 /* read from server, free return pointer if not Null */
426 /* Adds an extra null byte at end */
From_ServerAlloc(struct server_connection_state * scs,struct client_msg * cm)427 static void *From_ServerAlloc(struct server_connection_state * scs, struct client_msg *cm)
428 {
429 BYTE *msg;
430 struct timeval tv = { Globals.timeout_network + 1, 0, };
431 size_t actual_size ;
432
433 do { /* loop until non delay message (payload>=0) */
434 tcp_read(scs->file_descriptor, (BYTE *) cm, sizeof(struct client_msg), &tv, &actual_size);
435 if (actual_size != sizeof(struct client_msg)) {
436 memset(cm, 0, sizeof(struct client_msg));
437 cm->ret = -EIO;
438 return NO_PATH;
439 }
440 cm->payload = ntohl(cm->payload);
441 cm->size = ntohl(cm->size);
442 cm->ret = ntohl(cm->ret);
443 cm->control_flags = ntohl(cm->control_flags);
444 cm->offset = ntohl(cm->offset);
445 } while (cm->payload < 0);
446
447 if (cm->payload == 0) {
448 return NO_PATH;
449 }
450 if (cm->ret < 0) {
451 return NO_PATH;
452 }
453 if (cm->payload > MAX_OWSERVER_PROTOCOL_PAYLOAD_SIZE) {
454 return NO_PATH;
455 }
456
457 msg = owmalloc((size_t) cm->payload + 1) ;
458 if ( msg != NO_PATH ) {
459 tcp_read(scs->file_descriptor, msg, (size_t) (cm->payload), &tv, &actual_size);
460 if ((ssize_t)actual_size != cm->payload) {
461 cm->payload = 0;
462 cm->offset = 0;
463 cm->ret = -EIO;
464 owfree(msg);
465 msg = NO_PATH;
466 }
467 }
468 if(msg != NO_PATH) {
469 msg[cm->payload] = '\0'; // safety NULL
470 }
471 return msg;
472 }
473
474 /* Read from server -- return negative on error,
475 return 0 or positive giving size of data element */
From_Server(struct server_connection_state * scs,struct client_msg * cm,char * msg,size_t size)476 static SIZE_OR_ERROR From_Server( struct server_connection_state * scs, struct client_msg *cm, char *msg, size_t size)
477 {
478 size_t rtry;
479 size_t actual_read ;
480 struct timeval tv1 = { Globals.timeout_network + 1, 0, };
481 struct timeval tv2 = { Globals.timeout_network + 1, 0, };
482
483 do { // read regular header, or delay (delay when payload<0)
484 tcp_read(scs->file_descriptor, (BYTE *) cm, sizeof(struct client_msg), &tv1, &actual_read);
485 if (actual_read != sizeof(struct client_msg)) {
486 cm->size = 0;
487 cm->ret = -EIO;
488 return -EIO;
489 }
490
491 cm->payload = ntohl(cm->payload);
492 cm->size = ntohl(cm->size);
493 cm->ret = ntohl(cm->ret);
494 cm->control_flags = ntohl(cm->control_flags);
495 cm->offset = ntohl(cm->offset);
496 } while (cm->payload < 0); // flag to show a delay message
497
498 if (cm->payload == 0) {
499 return 0; // No payload, done.
500 }
501 rtry = cm->payload < (ssize_t) size ? (size_t) cm->payload : size;
502 tcp_read(scs->file_descriptor, (BYTE *) msg, rtry, &tv2, &actual_read); // read expected payload now.
503 if (actual_read != rtry) {
504 LEVEL_DEBUG("Read only %d of %d\n",(int)actual_read,(int)rtry) ;
505 cm->ret = -EIO;
506 return -EIO;
507 }
508 if (cm->payload > (ssize_t) size) { // Uh oh. payload bigger than expected. close the connection
509 Close_Persistent( scs ) ;
510 return size;
511 }
512 return cm->payload;
513 }
514
To_Server(struct server_connection_state * scs,struct server_msg * sm,struct serverpackage * sp)515 static GOOD_OR_BAD To_Server( struct server_connection_state * scs, struct server_msg * sm, struct serverpackage *sp)
516 {
517 struct connection_in * in = scs->in ; // for convenience
518 struct port_in * pin = in->pown ;
519 BYTE test_read[1] ;
520 int old_flags ;
521 ssize_t rcv_value ;
522 int saved_errno ;
523
524 // initialize the variables
525 scs->file_descriptor = FILE_DESCRIPTOR_BAD ;
526 scs->persistence = Globals.no_persistence ? persistent_no : persistent_yes ;
527
528 // First set up the file descriptor based on persistent state
529 if (scs->persistence == persistent_no) {
530 // no persistence wanted
531 scs->file_descriptor = ClientConnect(in);
532 } else {
533 // Persistence desired
534 BUSLOCKIN(in);
535 switch ( pin->file_descriptor ) {
536 case FILE_DESCRIPTOR_PERSISTENT_IN_USE:
537 // Currently in use, so make new non-persistent connection
538 scs->file_descriptor = ClientConnect(in);
539 scs->persistence = persistent_no ;
540 break ;
541 case FILE_DESCRIPTOR_BAD:
542 // no conection currently, so make a new one
543 scs->file_descriptor = ClientConnect(in);
544 if ( FILE_DESCRIPTOR_VALID( scs->file_descriptor ) ) {
545 pin->file_descriptor = FILE_DESCRIPTOR_PERSISTENT_IN_USE ;
546 }
547 break ;
548 default:
549 // persistent connection idle and waiting for use
550 // connection_in is locked so this is safe
551 scs->file_descriptor = pin->file_descriptor;
552 pin->file_descriptor = FILE_DESCRIPTOR_PERSISTENT_IN_USE;
553 break ;
554 }
555 BUSUNLOCKIN(in);
556 }
557
558 // Check if the server closed the connection
559 // This is contributed by Jacob Joseph to fix a timeout problem.
560 // http://permalink.gmane.org/gmane.comp.file-systems.owfs.devel/7306
561 //rcv_value = recv(scs->file_descriptor, test_read, 1, MSG_DONTWAIT | MSG_PEEK) ;
562 old_flags = fcntl( scs->file_descriptor, F_GETFL, 0 ) ; // save socket flags
563 if ( old_flags == -1 ) {
564 rcv_value = -2 ;
565 } else if ( fcntl( scs->file_descriptor, F_SETFL, old_flags | O_NONBLOCK ) == -1 ) { // set non-blocking
566 rcv_value = -2 ;
567 } else {
568 rcv_value = recv(scs->file_descriptor, test_read, 1, MSG_PEEK) ; // test read the socket to see if closed
569 saved_errno = errno ;
570 if ( fcntl( scs->file_descriptor, F_SETFL, old_flags ) == -1 ) { // restore socket flags
571 rcv_value = -2 ;
572 }
573 }
574
575 switch ( rcv_value ) {
576 case -1:
577 if ( saved_errno==EAGAIN || saved_errno==EWOULDBLOCK ) {
578 // No data to be read -- so connection healthy
579 break ;
580 }
581 // real error, fall through to close connection case
582 __attribute__ ((fallthrough));
583 case -2:
584 // fnctl error, fall through again
585 ;
586 __attribute__ ((fallthrough));
587 case 0:
588 LEVEL_DEBUG("Server connection was closed. Reconnecting.");
589 Close_Persistent( scs);
590 scs->file_descriptor = ClientConnect(in);
591 if ( FILE_DESCRIPTOR_VALID( scs->file_descriptor ) ) {
592 in->pown->file_descriptor = FILE_DESCRIPTOR_PERSISTENT_IN_USE ;
593 }
594 break ;
595 default:
596 // data to be read, so a good connection
597 break ;
598 }
599
600 // Now test
601 if ( FILE_DESCRIPTOR_NOT_VALID( scs->file_descriptor ) ) {
602 STAT_ADD1(in->reconnect_state);
603 Close_Persistent( scs ) ;
604 return gbBAD ;
605 }
606
607 // Do the real work
608 if (WriteToServer(scs->file_descriptor, sm, sp) >= 0) {
609 // successful message
610 return gbGOOD;
611 }
612
613 // This is where it gets a bit tricky. For non-persistent conections we're done'
614 if ( scs->persistence == persistent_no ) {
615 // not persistent, so no reconnection needed
616 Close_Persistent( scs ) ;
617 return gbBAD ;
618 }
619
620 // perhaps the persistent connection is stale?
621 // Make a new one
622 scs->file_descriptor = ClientConnect(in) ;
623
624 // Now retest
625 if ( FILE_DESCRIPTOR_NOT_VALID( scs->file_descriptor ) ) {
626 // couldn't make that new connection -- free everything
627 STAT_ADD1(in->reconnect_state);
628 Close_Persistent( scs ) ;
629 return gbBAD ;
630 }
631
632 // Leave in->file_descriptor = FILE_DESCRIPTOR_PERSISTENT_IN_USE
633
634 // Second attempt at the write, now with new connection
635 if (WriteToServer(scs->file_descriptor, sm, sp) >= 0) {
636 // successful message
637 return gbGOOD;
638 }
639
640 // bad write the second time -- clear everything
641 Close_Persistent( scs ) ;
642 return gbBAD ;
643 }
644
Close_Persistent(struct server_connection_state * scs)645 static void Close_Persistent( struct server_connection_state * scs)
646 {
647 // First set up the file descriptor based on persistent state
648 if (scs->persistence == persistent_yes) {
649 // no persistence wanted
650 BUSLOCKIN(scs->in);
651 scs->in->pown->file_descriptor = FILE_DESCRIPTOR_BAD ;
652 BUSUNLOCKIN(scs->in);
653 }
654
655 scs->persistence = persistent_no ;
656 Test_and_Close( &(scs->file_descriptor) ) ;
657 }
658
659 // should be const char * data but iovec has problems with const arguments
WriteToServer(int file_descriptor,struct server_msg * sm,struct serverpackage * sp)660 static SIZE_OR_ERROR WriteToServer(int file_descriptor, struct server_msg *sm, struct serverpackage *sp)
661 {
662 int payload = 0;
663 int tokens = 0;
664 int server_type = Globals.program_type==program_type_server || Globals.program_type==program_type_external ;
665
666 // We use vector write -- several ranges
667 int nio = 0;
668 struct iovec io[5] = { {NULL, 0}, {NULL, 0}, {NULL, 0}, {NULL, 0}, {NULL, 0}, };
669
670 struct server_msg net_sm ;
671
672 // Set the version
673 sm->version = MakeServerprotocol(OWSERVER_PROTOCOL_VERSION);
674
675 // First block to send, the header
676 // We'll do this last since the header values (e.g. payload) change
677 nio++;
678
679 // Next block, the path
680 if (sp->path != 0) { // send path (if not null)
681 // writev should take const data pointers, but I can't fix the library
682 #if ( __GNUC__ > 4 ) || (__GNUC__ == 4 && __GNUC_MINOR__ > 4 )
683 #pragma GCC diagnostic push
684 #pragma GCC diagnostic ignored "-Wcast-qual"
685 io[nio].iov_base = (char *) sp->path ; // gives a spurious compiler error -- constant is OK HERE!
686 #pragma GCC diagnostic pop
687 #else
688 io[nio].iov_base = (char *) sp->path ; // gives a spurious compiler error -- constant is OK HERE!
689 #endif
690 io[nio].iov_len = strlen(sp->path) + 1; // we're adding the null (though it isn't required post 2.9p3)
691 payload = io[nio].iov_len;
692 nio++;
693 }
694
695 // Next block, data (only for writes)
696 if ((sp->datasize>0) && (sp->data!=NULL)) { // send data only for writes (if datasize not zero)
697 io[nio].iov_base = sp->data ;
698 io[nio].iov_len = sp->datasize ;
699 payload += sp->datasize;
700 nio++;
701 }
702
703 if ( server_type ) {
704 tokens = sp->tokens;
705
706 // Next block prior tokens (if an owserver)
707 if (tokens > 0) { // owserver: send prior tags
708 io[nio].iov_base = sp->tokenstring;
709 io[nio].iov_len = tokens * sizeof(struct antiloop);
710 nio++;
711 }
712 // Final block, new token (if an owserver)
713
714 ++tokens;
715
716 if ( tokens == MakeServermessage ) {
717 LEVEL_DEBUG( "Too long a list of tokens -- %d owservers in the chain",tokens);
718 return -ELOOP ;
719 }
720
721 io[nio].iov_base = &(Globals.Token); // owserver: add our tag
722 io[nio].iov_len = sizeof(struct antiloop);
723
724 // put token information in header (lower 17 bits of version)
725 sm->version |= MakeServermessage; // bit 17
726 sm->version |= MakeServertokens(tokens); // lower 16 bits
727 nio++;
728 }
729
730 // First block to send, the header
731 // revisit now that the header values are set
732
733 // encode in network order (just the header)
734 net_sm.version = htonl( sm->version );
735 net_sm.payload = htonl( payload );
736 net_sm.size = htonl( sm->size );
737 net_sm.type = htonl( sm->type );
738 net_sm.control_flags = htonl( sm->control_flags );
739 net_sm.offset = htonl( sm->offset );
740
741 // set the header into the first (index=0) block
742 io[0].iov_base = &net_sm;
743 io[0].iov_len = sizeof(struct server_msg);
744
745 // debug data on packet
746 LEVEL_DEBUG("version=%u payload=%d size=%d type=%d SG=%X offset=%d",sm->version,payload,sm->size,sm->type,sm->control_flags,sm->offset);
747
748 // Here is some traffic display code
749 {
750 int traffic_counter ;
751 traffic_counter = 0 ;
752 TrafficOutFD("write header" ,io[traffic_counter].iov_base,io[traffic_counter].iov_len,file_descriptor);
753 ++traffic_counter;
754 TrafficOutFD("write path" ,io[traffic_counter].iov_base,io[traffic_counter].iov_len,file_descriptor);
755 if ((sp->datasize>0) && (sp->data!=NULL)) { // send data only for writes (if datasize not zero)
756 ++traffic_counter;
757 TrafficOutFD("write data" ,io[traffic_counter].iov_base,io[traffic_counter].iov_len,file_descriptor);
758 }
759 if ( server_type ) {
760 if (sp->tokens > 0) { // owserver: send prior tags
761 ++traffic_counter;
762 TrafficOutFD("write old tokens" ,io[traffic_counter].iov_base,io[traffic_counter].iov_len,file_descriptor);
763 }
764 ++traffic_counter;
765 TrafficOutFD("write new tokens" ,io[traffic_counter].iov_base,io[traffic_counter].iov_len,file_descriptor);
766 }
767 }
768 // End traffic display code
769
770 // Actual write of data to owserver
771 return writev(file_descriptor, io, nio) != (ssize_t) (payload + sizeof(struct server_msg) + tokens * sizeof(struct antiloop));
772 }
773
774 /* flag the sg for "virtual root" -- the remote bus was specifically requested */
SetupControlFlags(const struct parsedname * pn)775 static uint32_t SetupControlFlags(const struct parsedname *pn)
776 {
777 uint32_t control_flags = pn->control_flags;
778
779 control_flags &= ~PERSISTENT_MASK;
780 if (Globals.no_persistence == 0) {
781 control_flags |= PERSISTENT_MASK;
782 }
783
784 /* from owlib to owserver never wants alias */
785 control_flags &= ~ALIAS_REQUEST ;
786
787 control_flags &= ~SHOULD_RETURN_BUS_LIST;
788 if (SpecifiedBus(pn)) {
789 control_flags |= SHOULD_RETURN_BUS_LIST;
790 }
791
792 return control_flags;
793 }
794
795 /* Clean up at end of routine,
796 either leave connection open and persistent flag available,
797 or close
798 */
Release_Persistent(struct server_connection_state * scs,int granted)799 static void Release_Persistent( struct server_connection_state * scs, int granted )
800 {
801 if ( granted == 0 ) {
802 Close_Persistent( scs ) ;
803 return ;
804 }
805
806 if ( FILE_DESCRIPTOR_NOT_VALID( scs->file_descriptor) ) {
807 Close_Persistent( scs ) ;
808 return ;
809 }
810
811 if (scs->persistence == persistent_no) {
812 // non-persistence from the start
813 Close_Persistent( scs ) ;
814 return ;
815 }
816
817 // mark as available
818 BUSLOCKIN(scs->in);
819 scs->in->pown->file_descriptor = scs->file_descriptor;
820 BUSUNLOCKIN(scs->in);
821 scs->persistence = persistent_no ; // we no longer own this connection
822 scs->file_descriptor = FILE_DESCRIPTOR_BAD ;
823 }
824