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 = &current_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