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 // regex
12 
13 #include <config.h>
14 #include "owfs_config.h"
15 #include "ow_devices.h"
16 #include "ow_counters.h"
17 #include "ow_connection.h"
18 #include "ow_dirblob.h"
19 #include "ow.h"
20 #include "ow_external.h"
21 
22 static enum search_status PossiblyLockedBusCall( enum search_status (* first_next)(struct device_search *, const struct parsedname *), struct device_search * ds, const struct parsedname * pn ) ;
23 
24 static ZERO_OR_ERROR FS_dir_both(void (*dirfunc) (void *, const struct parsedname *), void *v, const struct parsedname *pn_directory, uint32_t * flags);
25 static ZERO_OR_ERROR FS_dir_all_connections(void (*dirfunc) (void *, const struct parsedname *), void *v, const struct parsedname *pn_directory, uint32_t * flags);
26 static ZERO_OR_ERROR FS_devdir(void (*dirfunc) (void *, const struct parsedname * const), void *v, const struct parsedname *pn2);
27 static ZERO_OR_ERROR FS_structdevdir(void (*dirfunc) (void *, const struct parsedname *), void *v, const struct parsedname *pn_device_directory);
28 static ZERO_OR_ERROR FS_alarmdir(void (*dirfunc) (void *, const struct parsedname * const), void *v, const struct parsedname *pn2);
29 static ZERO_OR_ERROR FS_typedir(void (*dirfunc) (void *, const struct parsedname * const), void *v, const struct parsedname *pn_type_directory);
30 static ZERO_OR_ERROR FS_realdir(void (*dirfunc) (void *, const struct parsedname * const), void *v, const struct parsedname *pn2, uint32_t * flags);
31 static ZERO_OR_ERROR FS_cache_or_real(void (*dirfunc) (void *, const struct parsedname * const), void *v, const struct parsedname *pn2, uint32_t * flags);
32 static ZERO_OR_ERROR FS_busdir(void (*dirfunc) (void *, const struct parsedname *), void *v, const struct parsedname *pn_directory);
33 
34 static void FS_stype_dir(void (*dirfunc) (void *, const struct parsedname *), void *v, const struct parsedname *pn_root_directory);
35 static ZERO_OR_ERROR FS_externaldir(void (*dirfunc) (void *, const struct parsedname *), void *v, const struct parsedname *pn_external_directory);
36 static void FS_interface_dir(void (*dirfunc) (void *, const struct parsedname *), void *v, const struct parsedname *pn_root_directory);
37 static void FS_alarm_entry(void (*dirfunc) (void *, const struct parsedname *), void *v, const struct parsedname *pn_root_directory);
38 static void FS_simultaneous_entry(void (*dirfunc) (void *, const struct parsedname *), void *v, const struct parsedname *pn_root_directory);
39 static void FS_uncached_dir(void (*dirfunc) (void *, const struct parsedname *), void *v, const struct parsedname *pn_root_directory);
40 static ZERO_OR_ERROR FS_dir_plus(void (*dirfunc) (void *, const struct parsedname *), void *v, uint32_t * flags, const struct parsedname *pn_directory, const char *file) ;
41 
42 /* Calls dirfunc() for each element in directory */
43 /* void * data is arbitrary user data passed along -- e.g. output file descriptor */
44 /* pn_directory -- input:
45     pn_directory->selected_device == NO_DEVICE -- root directory, give list of all devices
46     pn_directory->selected_device non-null, -- device directory, give all properties
47     branch aware
48     cache aware
49 
50    pn_directory -- output (with each call to dirfunc)
51     ROOT
52     pn_directory->selected_device set
53     pn_directory->sn set appropriately
54     pn_directory->selected_filetype not set
55 
56     DEVICE
57     pn_directory->selected_device and pn_directory->sn still set
58     pn_directory->selected_filetype loops through
59 */
60 
61 /* FS_dir produces the "invariant" portion of the directory, passing on to
62    FS_dir_all_connections the variable part */
FS_dir(void (* dirfunc)(void *,const struct parsedname *),void * v,struct parsedname * pn_directory)63 ZERO_OR_ERROR FS_dir(void (*dirfunc) (void *, const struct parsedname *), void *v, struct parsedname *pn_directory)
64 {
65 	/* applies 'dirfunc' to each directory element of pn_directory */
66 	/* void * v is extra information passed along */
67 
68 	uint32_t flags;
69 	LEVEL_DEBUG("path=%s", pn_directory->path);
70 	pn_directory->control_flags |= ALIAS_REQUEST ; // All local directory queries want alias translation
71 
72 	return FS_dir_both(dirfunc, v, pn_directory, &flags);
73 }
74 
75 /* path is the path which "pn_directory" parses */
76 /* FS_dir_remote is the entry into FS_dir_all_connections from ServerDir */
77 /* More checking is done, and the flags are returned */
FS_dir_remote(void (* dirfunc)(void *,const struct parsedname *),void * v,const struct parsedname * pn_directory,uint32_t * flags)78 ZERO_OR_ERROR FS_dir_remote(void (*dirfunc) (void *, const struct parsedname *), void *v, const struct parsedname *pn_directory, uint32_t * flags)
79 {
80 	LEVEL_DEBUG("path=%s", pn_directory->path);
81 	return FS_dir_both(dirfunc, v, pn_directory, flags);
82 }
83 
84 
FS_dir_both(void (* dirfunc)(void *,const struct parsedname *),void * v,const struct parsedname * pn_raw_directory,uint32_t * flags)85 static ZERO_OR_ERROR FS_dir_both(void (*dirfunc) (void *, const struct parsedname *), void *v, const struct parsedname *pn_raw_directory, uint32_t * flags)
86 {
87 	ZERO_OR_ERROR ret = 0;
88 
89 	/* initialize flags */
90 	flags[0] = 0;
91 
92 	/* pn_raw_directory->selected_connection Could be NULL here...
93 	 * It will then return a root-directory containing
94 	 * /uncached,/settings,/system,/statistics,/structure
95 	 * instead of an empty directory.
96 	 */
97 	if (pn_raw_directory == NO_PARSEDNAME) {
98 		LEVEL_CALL("return ENODEV pn_raw_directory=%p selected_connection=%p",
99 			pn_raw_directory,
100 			(pn_raw_directory ? pn_raw_directory->selected_connection : NO_CONNECTION));
101 		return -ENODEV;
102 	}
103 
104 	LEVEL_CALL("path=%s", SAFESTRING(pn_raw_directory->path));
105 
106 	STATLOCK;
107 	AVERAGE_IN(&dir_avg);
108 	AVERAGE_IN(&all_avg);
109 	STATUNLOCK;
110 
111 	FSTATLOCK;
112 	StateInfo.dir_time = NOW_TIME;	// protected by mutex
113 	FSTATUNLOCK;
114 
115 	if (pn_raw_directory->selected_filetype != NO_FILETYPE) {
116 		// File, not directory
117 		ret = -ENOTDIR;
118 
119 	} else if (SpecifiedVeryRemoteBus(pn_raw_directory)) {
120 		//printf("SPECIFIED_BUS BUS_IS_SERVER (very remote)\n");
121 		// Send remotely only (all evaluation done there)
122 		ret = ServerDir(dirfunc, v, pn_raw_directory, flags);
123 
124 	} else if (SpecifiedRemoteBus(pn_raw_directory)) {
125 		//printf("SPECIFIED_BUS BUS_IS_SERVER (just remote)\n");
126 		//printf("Add extra INTERFACE\n");
127 		FS_interface_dir(dirfunc, v, pn_raw_directory);
128 		// Send remotely only (all evaluation done there)
129 		ret = ServerDir(dirfunc, v, pn_raw_directory, flags);
130 
131 	} else if (pn_raw_directory->selected_device != NO_DEVICE) {
132 		//printf("YES SELECTED_DEVICE\n");
133 		// device directory -- not bus-specific
134 		if ( IsInterfaceDir(pn_raw_directory) ) {
135 			ret = FS_devdir(dirfunc, v, pn_raw_directory);
136 		} else if ( BusIsServer( pn_raw_directory->selected_connection) ) {
137 			ret = ServerDir(dirfunc, v, pn_raw_directory, flags);
138 		} else if ( IsStructureDir( pn_raw_directory ) ) {
139 			ret = FS_structdevdir( dirfunc, v, pn_raw_directory ) ;
140 		} else {
141 			ret = FS_devdir(dirfunc, v, pn_raw_directory);
142 		}
143 
144 	} else if (NotRealDir(pn_raw_directory)) {
145 		//printf("NOT_REAL_DIR\n");
146 		// structure, statistics, system or settings dir -- not bus-specific
147 		ret = FS_typedir(dirfunc, v, pn_raw_directory);
148 
149 	} else if (SpecifiedLocalBus(pn_raw_directory)) {
150 		if (IsAlarmDir(pn_raw_directory)) {	/* root or branch directory -- alarm state */
151 			ret = FS_alarmdir(dirfunc, v, pn_raw_directory);
152 		} else {
153 			if (pn_raw_directory->ds2409_depth == 0) {
154 				// only add funny directories for non-micro hub (DS2409) branches
155 				FS_interface_dir(dirfunc, v, pn_raw_directory);
156 			}
157 			/* Now get the actual devices */
158 			ret = FS_cache_or_real(dirfunc, v, pn_raw_directory, flags);
159 			/* simultaneous directory */
160 			if (flags[0] & (DEV_temp | DEV_volt)) {
161 				FS_simultaneous_entry(dirfunc, v, pn_raw_directory);
162 			}
163 			if (flags[0] & DEV_alarm) {
164 				FS_alarm_entry(dirfunc, v, pn_raw_directory);
165 			}
166 		}
167 
168 	} else if ( IsAlarmDir(pn_raw_directory)) { // alarm for all busses
169 		// Not specified bus, so scan through all and print union
170 		ret = FS_dir_all_connections(dirfunc, v, pn_raw_directory, flags);
171 		// add no chaff to alarm directory -- no "uncached", "bus.x" etc
172 	} else { // standard directory search -- all busses
173 		// Not specified bus, so scan through all and print union
174 		int server_type = Globals.program_type==program_type_server || Globals.program_type==program_type_external ;
175 		ret = FS_dir_all_connections(dirfunc, v, pn_raw_directory, flags);
176 		if ( !server_type || ShouldReturnBusList(pn_raw_directory)) {
177 			if (pn_raw_directory->ds2409_depth == 0) {
178 				// only add funny directories for non-micro hub (DS2409) branches
179 				FS_busdir(dirfunc, v, pn_raw_directory);
180 				FS_uncached_dir(dirfunc, v, pn_raw_directory);
181 				FS_stype_dir(dirfunc, v, pn_raw_directory);
182 			}
183 			/* simultaneous directory */
184 			if (flags[0] & (DEV_temp | DEV_volt)) {
185 				FS_simultaneous_entry(dirfunc, v, pn_raw_directory);
186 			}
187 			if (flags[0] & DEV_alarm) {
188 				FS_alarm_entry(dirfunc, v, pn_raw_directory);
189 			}
190 		}
191 
192 	}
193 
194 	STATLOCK;
195 	AVERAGE_OUT(&dir_avg);
196 	AVERAGE_OUT(&all_avg);
197 	STATUNLOCK;
198 
199 	LEVEL_DEBUG("ret=%d", ret);
200 	return ret;
201 }
202 
203 /* directories about the internal pogram state and configuration rather than device data */
FS_stype_dir(void (* dirfunc)(void *,const struct parsedname *),void * v,const struct parsedname * pn_root_directory)204 static void FS_stype_dir(void (*dirfunc) (void *, const struct parsedname *), void *v, const struct parsedname *pn_root_directory)
205 {
206 	uint32_t ignoreflag = 0;
207 	FS_dir_plus(dirfunc, v, &ignoreflag, pn_root_directory, ePN_name[ePN_settings]);
208 	FS_dir_plus(dirfunc, v, &ignoreflag, pn_root_directory, ePN_name[ePN_system]);
209 	FS_dir_plus(dirfunc, v, &ignoreflag, pn_root_directory, ePN_name[ePN_statistics]);
210 	FS_dir_plus(dirfunc, v, &ignoreflag, pn_root_directory, ePN_name[ePN_structure]);
211 }
212 
213 /* interface (only for bus directories) -- actually generated by the layer ABOVE that bus. */
FS_interface_dir(void (* dirfunc)(void *,const struct parsedname *),void * v,const struct parsedname * pn_root_directory)214 static void FS_interface_dir(void (*dirfunc) (void *, const struct parsedname *), void *v, const struct parsedname *pn_root_directory)
215 {
216 	uint32_t ignoreflag = 0;
217 	FS_dir_plus(dirfunc, v, &ignoreflag, pn_root_directory, ePN_name[ePN_interface]);
218 }
219 
220 /* Some devices have a special state when certain events are found called the ALARM state */
FS_alarm_entry(void (* dirfunc)(void *,const struct parsedname *),void * v,const struct parsedname * pn_root_directory)221 static void FS_alarm_entry(void (*dirfunc) (void *, const struct parsedname *), void *v, const struct parsedname *pn_root_directory)
222 {
223 	uint32_t ignoreflag = 0 ;
224 	FS_dir_plus(dirfunc, v, &ignoreflag, pn_root_directory, "alarm");
225 }
226 
227 /* Add the "uncached" directory as a mnenomic aid to top level directory listings. */
FS_uncached_dir(void (* dirfunc)(void *,const struct parsedname *),void * v,const struct parsedname * pn_root_directory)228 static void FS_uncached_dir(void (*dirfunc) (void *, const struct parsedname *), void *v, const struct parsedname *pn_root_directory)
229 {
230 	uint32_t ignoreflag = 0 ;
231 
232 	if (IsUncachedDir(pn_root_directory)) {	/* already uncached */
233 		return;
234 	}
235 
236 	FS_dir_plus(dirfunc, v, &ignoreflag, pn_root_directory, "uncached");
237 }
238 
239 /* Some temperature and voltage measurements can be triggered globally for considerable speed improvements */
FS_simultaneous_entry(void (* dirfunc)(void *,const struct parsedname *),void * v,const struct parsedname * pn_root_directory)240 static void FS_simultaneous_entry(void (*dirfunc) (void *, const struct parsedname *), void *v, const struct parsedname *pn_root_directory)
241 {
242 	uint32_t ignoreflag = 0 ;
243 	FS_dir_plus(dirfunc, v, &ignoreflag, pn_root_directory, "simultaneous");
244 }
245 
246 /* path is the path which "pn_directory" parses */
247 /* FS_dir_all_connections produces the data that can vary: device lists, etc. */
248 
249 struct dir_all_connections_struct {
250 	struct port_in * pin ;
251 	struct connection_in * cin ;
252 	struct parsedname pn_directory;
253 	void (*dirfunc) (void *, const struct parsedname *);
254 	void *v;
255 	uint32_t flags;
256 	ZERO_OR_ERROR ret;
257 };
258 
259 /* Embedded function */
260 /* Directory on a particular port's channel */
FS_dir_all_connections_callback_conn(struct dir_all_connections_struct * dacs)261 static void FS_dir_all_connections_callback_conn( struct dir_all_connections_struct * dacs )
262 {
263 	if ( dacs->cin == NO_CONNECTION ) {
264 		return ;
265 	}
266 
267 	SetKnownBus(dacs->cin->index, &(dacs->pn_directory) );
268 
269 	if ( BAD(TestConnection( &(dacs->pn_directory) )) ) {	// reconnect ok?
270 		dacs->ret = -ECONNABORTED;
271 	} else if (BusIsServer(dacs->pn_directory.selected_connection)) {	/* is this a remote bus? */
272 		//printf("FS_dir_all_connections: Call ServerDir %s\n", dacs->pn_directory->path);
273 		dacs->ret = ServerDir(dacs->dirfunc, dacs->v, &(dacs->pn_directory), &(dacs->flags));
274 	} else if (IsAlarmDir( &(dacs->pn_directory) ) ) {	/* root or branch directory -- alarm state */
275 		//printf("FS_dir_all_connections: Call FS_alarmdir %s\n", dacs->pn_directory->path);
276 		dacs->ret = FS_alarmdir(dacs->dirfunc, dacs->v, &(dacs->pn_directory) );
277 	} else {
278 		dacs->ret = FS_cache_or_real(dacs->dirfunc, dacs->v, &(dacs->pn_directory), &(dacs->flags));
279 	}
280 
281 	// next channel
282 	dacs->cin = dacs->cin->next ;
283 	FS_dir_all_connections_callback_conn( dacs ) ;
284 }
285 
286 /* Callback (thread) once per port */
287 /* Will need  to probe each connection (channel) on this port */
FS_dir_all_connections_callback_port(void * v)288 static void *FS_dir_all_connections_callback_port(void *v)
289 {
290 	struct dir_all_connections_struct *dacs = v;
291 	struct dir_all_connections_struct dacs_next ;
292 	pthread_t thread;
293 	int threadbad = 0;
294 
295 	if ( dacs->pin == NULL ) {
296 		return VOID_RETURN;
297 	}
298 
299 	// set up structure
300 	dacs_next.pin = dacs->pin->next ;
301 
302 	if ( dacs_next.pin == NULL ) {
303 		threadbad = 1 ;
304 	} else {
305 		dacs_next.dirfunc = dacs->dirfunc ;
306 		memcpy( &(dacs_next.pn_directory), &(dacs->pn_directory), sizeof(struct parsedname));	// shallow copy
307 		dacs_next.v = dacs->v ;
308 		dacs_next.flags = dacs->flags ;
309 		dacs_next.ret = dacs->ret ;
310 		threadbad = pthread_create(&thread, DEFAULT_THREAD_ATTR, FS_dir_all_connections_callback_port, (void *) (&dacs_next));
311 	}
312 
313 	// First channel
314 	dacs->cin = dacs->pin->first ;
315 	FS_dir_all_connections_callback_conn( v ) ;
316 
317 	//printf("FS_dir_all_connections4 pid=%ld adapter=%d ret=%d\n",pthread_self(), dacs->pn_directory->selected_connection->index,ret);
318 	/* See if next bus was also queried */
319 	if (threadbad == 0) {		/* was a thread created? */
320 		if (pthread_join(thread, NULL)!= 0) {
321 			return VOID_RETURN ;			/* cannot join, so return only this result */
322 		}
323 		if (dacs_next.ret >= 0) {
324 			dacs->ret = dacs_next.ret;	/* is it an error return? Then return this one */
325 		} else {
326 			dacs->flags |= dacs_next.flags ;
327 		}
328 	}
329 	return VOID_RETURN;
330 }
331 
332 static ZERO_OR_ERROR
FS_dir_all_connections(void (* dirfunc)(void *,const struct parsedname *),void * v,const struct parsedname * pn_directory,uint32_t * flags)333 FS_dir_all_connections(void (*dirfunc) (void *, const struct parsedname *), void *v, const struct parsedname *pn_directory, uint32_t * flags)
334 {
335 	struct dir_all_connections_struct dacs ;
336 
337 	// set up structure
338 	dacs.pin = Inbound_Control.head_port ;
339 	dacs.dirfunc = dirfunc ;
340 	memcpy( &(dacs.pn_directory), pn_directory, sizeof(struct parsedname));	// shallow copy
341 	dacs.v = v ;
342 	dacs.flags = 0 ;
343 	dacs.ret = 0 ;
344 
345 	// Start iterating through buses
346 	FS_dir_all_connections_callback_port(  (void *) (& dacs) ) ;
347 
348 	*flags = dacs.flags ;
349 	return dacs.ret ;
350 }
351 
352 /* Device directory (i.e. show the properties) -- all from memory */
353 /* Respect the Visibility status and also show only the correct subdir level */
FS_devdir(void (* dirfunc)(void *,const struct parsedname *),void * v,const struct parsedname * pn_device_directory)354 static ZERO_OR_ERROR FS_devdir(void (*dirfunc) (void *, const struct parsedname *), void *v, const struct parsedname *pn_device_directory)
355 {
356 	struct device * dev = pn_device_directory->selected_device ;
357 	struct filetype *lastft = &(dev->filetype_array[dev->count_of_filetypes]);	/* last filetype struct */
358 	struct filetype *ft_pointer;	/* first filetype struct */
359 	char subdir_name[OW_FULLNAME_MAX + 1];
360 	size_t subdir_len;
361 	uint32_t ignoreflag = 0;
362 
363 	STAT_ADD1(dir_dev.calls);
364 
365 	// Add subdir to name (SubDirectory is within a device, but an extra layer of grouping of properties)
366 	if (pn_device_directory->subdir == NO_SUBDIR) {
367 		// not sub directory
368 		subdir_name[0] = '\0' ;
369 		subdir_len = 0;
370 		ft_pointer = dev->filetype_array;
371 	} else {
372 		// device subdirectory -- so use the sorted list to find all entries with the same prefix
373 		strncpy(subdir_name, pn_device_directory->subdir->name, OW_FULLNAME_MAX);
374 		strcat(subdir_name, "/");
375 		subdir_len = strlen(subdir_name);
376 		ft_pointer = pn_device_directory->subdir + 1; // next element (first one truly in the subdir)
377 	}
378 
379 	for (; ft_pointer < lastft; ++ft_pointer) {	/* loop through filetypes */
380 		char *namepart ;
381 
382 		/* test that start of name matches directory name */
383 		if (strncmp(ft_pointer->name, subdir_name, subdir_len) != 0) {
384 			// end of subdir
385 			break;
386 		}
387 
388 		namepart = &ft_pointer->name[subdir_len]; // point after subdir name
389 		if ( strchr( namepart, '/') != NULL) {
390 			// subdir elements (and we're not in this subdir!)
391 			continue;
392 		}
393 
394 		if (ft_pointer->ag==NON_AGGREGATE) {
395 			FS_dir_plus(dirfunc, v, &ignoreflag, pn_device_directory, namepart);
396 			STAT_ADD1(dir_dev.entries);
397 		} else if (ft_pointer->ag->combined==ag_sparse) {
398 			struct parsedname s_pn_file_entry;
399 			struct parsedname *pn_file_entry = &s_pn_file_entry;
400 
401 
402 			if (ft_pointer->ag->letters==ag_letters) {
403 				if (FS_ParsedNamePlusText(pn_device_directory->path, namepart, "xxx", pn_file_entry) == 0) {
404 					pn_file_entry->extension = EXTENSION_UNKNOWN ; // unspecified (for owhttpd)
405 					switch ( FS_visible(pn_file_entry) ) { // hide hidden properties
406 						case visible_now :
407 						case visible_always:
408 							FS_dir_entry_aliased( dirfunc, v, pn_file_entry) ;
409 							STAT_ADD1(dir_dev.entries);
410 							break ;
411 						default:
412 							break ;
413 					}
414 					FS_ParsedName_destroy(pn_file_entry);
415 				}
416 			} else {
417 				if (FS_ParsedNamePlusText(pn_device_directory->path, namepart, "000", pn_file_entry) == 0) {
418 					pn_file_entry->extension = EXTENSION_UNKNOWN ; // unspecified (for owhttpd)
419 					switch ( FS_visible(pn_file_entry) ) { // hide hidden properties
420 						case visible_now :
421 						case visible_always:
422 							FS_dir_entry_aliased( dirfunc, v, pn_file_entry) ;
423 							STAT_ADD1(dir_dev.entries);
424 							break ;
425 						default:
426 							break ;
427 					}
428 					FS_ParsedName_destroy(pn_file_entry);
429 				}
430 			}
431 		} else {
432 			int extension;
433 			int first_extension = (ft_pointer->format == ft_bitfield) ? EXTENSION_BYTE : EXTENSION_ALL;
434 			struct parsedname s_pn_file_entry;
435 			struct parsedname *pn_file_entry = &s_pn_file_entry;
436 			for (extension = first_extension; extension < ft_pointer->ag->elements; ++extension) {
437 				if (FS_ParsedNamePlusExt(pn_device_directory->path, namepart, extension, ft_pointer->ag->letters, pn_file_entry) == 0) {
438 					switch ( FS_visible(pn_file_entry) ) { // hide hidden properties
439 						case visible_now :
440 						case visible_always:
441 							FS_dir_entry_aliased( dirfunc, v, pn_file_entry) ;
442 							STAT_ADD1(dir_dev.entries);
443 							break ;
444 						default:
445 							break ;
446 					}
447 					FS_ParsedName_destroy(pn_file_entry);
448 				}
449 			}
450 		}
451 	}
452 	return 0;
453 }
454 
455 /* Device directory -- all from memory  -- for structure */
FS_structdevdir(void (* dirfunc)(void *,const struct parsedname *),void * v,const struct parsedname * pn_device_directory)456 static ZERO_OR_ERROR FS_structdevdir(void (*dirfunc) (void *, const struct parsedname *), void *v, const struct parsedname *pn_device_directory)
457 {
458 	struct filetype *lastft = &(pn_device_directory->selected_device->filetype_array[pn_device_directory->selected_device->count_of_filetypes]);	/* last filetype struct */
459 	struct filetype *ft_pointer;	/* first filetype struct */
460 	char subdir_name[OW_FULLNAME_MAX + 1];
461 	size_t subdir_len;
462 
463 	// Add subdir to name (SubDirectory is within a device, but an extra layer of grouping of properties)
464 	if (pn_device_directory->subdir == NO_SUBDIR) {
465 		// not sub directory
466 		subdir_name[0] = '\0' ;
467 		subdir_len = 0;
468 		ft_pointer = pn_device_directory->selected_device->filetype_array;
469 	} else {
470 		// device subdirectory -- so use the sorted list to find all entries with the same prefix
471 		strncpy(subdir_name, pn_device_directory->subdir->name, OW_FULLNAME_MAX);
472 		strcat(subdir_name, "/");
473 		subdir_len = strlen(subdir_name);
474 		ft_pointer = pn_device_directory->subdir + 1; // next element (first one truly in the subdir)
475 	}
476 
477 
478 	for (; ft_pointer < lastft; ++ft_pointer) {	/* loop through filetypes */
479 		char *namepart ;
480 
481  		if ( ft_pointer->visible == INVISIBLE ) { // hide always invisible
482 			// done without actually calling the visibility function since
483 			// the device is generic for structure listings and may not
484 			// be an actual device.
485 			continue ;
486 		}
487 
488 		/* test that start of name matches directory name */
489 		if (strncmp(ft_pointer->name, subdir_name, subdir_len) != 0) {
490 			// end of subdir
491 			break;
492 		}
493 
494 		namepart = &ft_pointer->name[subdir_len]; // point after subdir name
495 		if ( strchr( namepart, '/') != NULL) {
496 			// subdir elements (and we're not in this subdir!)
497 			continue;
498 		}
499 
500 		if (ft_pointer->ag==NON_AGGREGATE) {
501 			struct parsedname s_pn_file_entry;
502 			struct parsedname *pn_file_entry = &s_pn_file_entry;
503 			if (FS_ParsedNamePlus(pn_device_directory->path, namepart, pn_file_entry) == 0) {
504 				FS_dir_entry_aliased( dirfunc, v, pn_file_entry) ;
505 				FS_ParsedName_destroy(pn_file_entry);
506 			}
507 		} else if (ft_pointer->ag->combined==ag_sparse) {
508 			// Aggregate property
509 			struct parsedname s_pn_file_entry;
510 			struct parsedname *pn_file_entry = &s_pn_file_entry;
511 
512 			if ( ft_pointer->ag->letters == ag_letters ) {
513 				if (FS_ParsedNamePlusText(pn_device_directory->path, namepart, "xxx", pn_file_entry) == 0) {
514 					FS_dir_entry_aliased( dirfunc, v, pn_file_entry) ;
515 					FS_ParsedName_destroy(pn_file_entry);
516 				}
517 			} else {
518 				if (FS_ParsedNamePlusText(pn_device_directory->path, namepart, "000", pn_file_entry) == 0) {
519 					FS_dir_entry_aliased( dirfunc, v, pn_file_entry) ;
520 					FS_ParsedName_destroy(pn_file_entry);
521 				}
522 			}
523 		} else {
524 			// Aggregate property
525 			struct parsedname s_pn_file_entry;
526 			struct parsedname *pn_file_entry = &s_pn_file_entry;
527 
528 			if ( ft_pointer->format == ft_bitfield ) {
529 				if (FS_ParsedNamePlusExt(pn_device_directory->path, namepart, EXTENSION_BYTE, ft_pointer->ag->letters, pn_file_entry) == 0) {
530 					FS_dir_entry_aliased( dirfunc, v, pn_file_entry) ;
531 					FS_ParsedName_destroy(pn_file_entry);
532 				}
533 			}
534 			if (FS_ParsedNamePlusExt(pn_device_directory->path, namepart, EXTENSION_ALL, ft_pointer->ag->letters, pn_file_entry) == 0) {
535 				FS_dir_entry_aliased( dirfunc, v, pn_file_entry) ;
536 				FS_ParsedName_destroy(pn_file_entry);
537 			}
538 			// unlike real directory, only show the first array element since the data is redundant
539 			if (FS_ParsedNamePlusExt(pn_device_directory->path, namepart, 0, ft_pointer->ag->letters, pn_file_entry) == 0) {
540 				FS_dir_entry_aliased( dirfunc, v, pn_file_entry) ;
541 				FS_ParsedName_destroy(pn_file_entry);
542 			}
543 		}
544 	}
545 	return 0;
546 }
547 
548 /* Note -- alarm directory is smaller, no adapters or stats or uncached */
FS_alarmdir(void (* dirfunc)(void *,const struct parsedname *),void * v,const struct parsedname * pn_alarm_directory)549 static ZERO_OR_ERROR FS_alarmdir(void (*dirfunc) (void *, const struct parsedname *), void *v, const struct parsedname *pn_alarm_directory)
550 {
551 	enum search_status ret;
552 	struct device_search ds;	// holds search state
553 	uint32_t ignoreflag = 0;
554 
555 	/* Special handling for External directory -- no alarm */
556 	if ( get_busmode(pn_alarm_directory->selected_connection) == bus_external ) {
557 		return 0 ;
558 	}
559 
560 	/* cache from Server if this is a remote bus */
561 	if (BusIsServer(pn_alarm_directory->selected_connection)) {
562 		return ServerDir(dirfunc, v, pn_alarm_directory, &ignoreflag);
563 	}
564 
565 	/* Certain buses are not real bus masters (like usb-monitor and w1-monitor) */
566 	if ( pn_alarm_directory->selected_connection->iroutines.flags & ADAP_FLAG_sham ) {
567 		return 0 ;
568 	}
569 
570 	/* STATISCTICS */
571 	STAT_ADD1(dir_main.calls);
572 
573 	ret = PossiblyLockedBusCall( BUS_first_alarm, &ds, pn_alarm_directory) ;
574 
575 	while ( ret == search_good ) {
576 		char dev[PROPERTY_LENGTH_ALIAS + 1];
577 		STAT_ADD1(dir_main.entries);
578 		FS_devicename(dev, PROPERTY_LENGTH_ALIAS, ds.sn, pn_alarm_directory);
579 		FS_dir_plus(dirfunc, v, &ignoreflag, pn_alarm_directory, dev);
580 
581 		ret = PossiblyLockedBusCall( BUS_next, &ds, pn_alarm_directory) ;
582 	}
583 
584 	switch ( ret ) {
585 		case search_good:
586 			// include fo completeness -- can't actually be a value.
587 		case search_done:
588 			return 0 ;
589 		case search_error:
590 		default:
591 			return -EIO;
592 	}
593 }
594 
PossiblyLockedBusCall(enum search_status (* first_next)(struct device_search *,const struct parsedname *),struct device_search * ds,const struct parsedname * pn)595 static enum search_status PossiblyLockedBusCall( enum search_status (* first_next)(struct device_search *, const struct parsedname *), struct device_search * ds, const struct parsedname * pn )
596 {
597 	enum search_status ret;
598 
599 	// This is also called from the reconnection routine -- we use a flag to avoid mutex doubling and deadlock
600 	if ( NotReconnect(pn) ) {
601 		BUSLOCK(pn);
602 		ret = first_next(ds, pn) ;
603 		BUSUNLOCK(pn);
604 	} else {
605 		ret = first_next(ds, pn) ;
606 	}
607 	return ret ;
608 }
609 
610 /* A directory of devices -- either main or branch */
611 /* not within a device, nor alarm state */
612 /* Also, adapters and stats handled elsewhere */
613 /* Scan the directory from the BUS and add to cache */
FS_realdir(void (* dirfunc)(void *,const struct parsedname *),void * v,const struct parsedname * pn_whole_directory,uint32_t * flags)614 static ZERO_OR_ERROR FS_realdir(void (*dirfunc) (void *, const struct parsedname *), void *v, const struct parsedname *pn_whole_directory, uint32_t * flags)
615 {
616 	struct device_search ds;
617 	size_t devices = 0;
618 	struct dirblob db;
619 	enum search_status ret;
620 
621 	/* cache from Server if this is a remote bus */
622 	if (BusIsServer(pn_whole_directory->selected_connection)) {
623 		return ServerDir(dirfunc, v, pn_whole_directory, flags);
624 	}
625 
626 	/* Certain buses are not real bus masters (like usb-monitor and w1-monitor) */
627 	if ( pn_whole_directory->selected_connection->iroutines.flags & ADAP_FLAG_sham ) {
628 		return 0 ;
629 	}
630 
631 	/* STATISTICS */
632 	STAT_ADD1(dir_main.calls);
633 
634 	DirblobInit(&db);			// set up a fresh dirblob
635 
636 	ret = PossiblyLockedBusCall( BUS_first, &ds, pn_whole_directory) ;
637 
638 	if (RootNotBranch(pn_whole_directory)) {
639 		db.allocated = pn_whole_directory->selected_connection->last_root_devs;	// root dir estimated length
640 	}
641 	while ( ret == search_good ) {
642 		char dev[PROPERTY_LENGTH_ALIAS + 1];
643 
644 		/* Add to device cache */
645 		Cache_Add_Device(pn_whole_directory->selected_connection->index,ds.sn) ;
646 
647 		/* Get proper device name (including alias subst) */
648 		FS_devicename(dev, PROPERTY_LENGTH_ALIAS, ds.sn, pn_whole_directory);
649 
650 		/* Execute callback function */
651 		if ( FS_dir_plus(dirfunc, v, flags, pn_whole_directory, dev) != 0 ) {
652 			DirblobPoison(&db);
653 			break ;
654 		}
655 		DirblobAdd(ds.sn, &db);
656 		++devices;
657 
658 		ret = PossiblyLockedBusCall( BUS_next, &ds, pn_whole_directory) ;
659 	}
660 
661 	STATLOCK;
662 	dir_main.entries += devices;
663 	STATUNLOCK;
664 
665 	switch ( ret ) {
666 		case search_done:
667 			if ( RootNotBranch(pn_whole_directory) ) {
668 				pn_whole_directory->selected_connection->last_root_devs = devices;	// root dir estimated length
669 			}
670 			/* Add to the cache (full list as a single element */
671 			if (DirblobPure(&db) && (ret == search_done) ) {
672 				Cache_Add_Dir(&db, pn_whole_directory);
673 			}
674 			DirblobClear(&db);
675 			return 0 ;
676 		case search_good:
677 		case search_error:
678 		default:
679 			DirblobClear(&db);
680 			return -EIO ;
681 	}
682 }
683 
684 /* points "serial number" to directory
685    -- 0 for root
686    -- DS2409/main|aux for branch
687    -- DS2409 needs only the last element since each DS2409 is unique
688    */
FS_LoadDirectoryOnly(struct parsedname * pn_directory,const struct parsedname * pn_original)689 void FS_LoadDirectoryOnly(struct parsedname *pn_directory, const struct parsedname *pn_original)
690 {
691 	memmove( pn_directory, pn_original, sizeof(struct parsedname)) ; // shallow copy
692 	if (RootNotBranch(pn_directory)) {
693 		memset(pn_directory->sn, 0, SERIAL_NUMBER_SIZE);
694 	} else {
695 		// Stuff the branch into the checksum slot
696 		// Makes the branch address unique (DS2409 id + branch)
697 		--pn_directory->ds2409_depth;
698 		memcpy(pn_directory->sn, pn_directory->bp[pn_directory->ds2409_depth].sn, SERIAL_NUMBER_SIZE-1);
699 		pn_directory->sn[SERIAL_NUMBER_SIZE-1] = pn_directory->bp[pn_directory->ds2409_depth].branch;
700 	}
701 	pn_directory->selected_device = NO_DEVICE;
702 }
703 
704 /* A directory of devices -- either main or branch */
705 /* not within a device, nor alarm state */
706 /* Also, adapters and stats handled elsewhere */
707 /* Cache2Real try the cache first, else get directory from bus (and add to cache) */
FS_cache_or_real(void (* dirfunc)(void *,const struct parsedname *),void * v,const struct parsedname * pn_real_directory,uint32_t * flags)708 static ZERO_OR_ERROR FS_cache_or_real(void (*dirfunc) (void *, const struct parsedname *), void *v, const struct parsedname *pn_real_directory, uint32_t * flags)
709 {
710 	size_t dindex;
711 	struct dirblob db;
712 	BYTE sn[SERIAL_NUMBER_SIZE];
713 
714 	/* Special handling for External directory -- just walk tree */
715 	if ( get_busmode(pn_real_directory->selected_connection) == bus_external ) {
716 		return FS_externaldir(dirfunc, v, pn_real_directory) ;
717 	}
718 
719 	/* Test to see whether we should get the directory "directly" */
720 	if (
721 		SpecifiedBus(pn_real_directory)
722 		|| IsUncachedDir(pn_real_directory) // asking for uncached
723 		|| BAD( Cache_Get_Dir(&db, pn_real_directory)) // cache'd version isn't available (or old)
724 		) {
725 		// directly
726 		return FS_realdir(dirfunc, v, pn_real_directory, flags);
727 	}
728 
729 	// Use cached version of directory
730 
731 	/* STATISTICS */
732 	STAT_ADD1(dir_main.calls);
733 
734 	/* Get directory from the cache */
735 	for (dindex = 0; DirblobGet(dindex, sn, &db) == 0; ++dindex) {
736 		char dev[PROPERTY_LENGTH_ALIAS + 1];
737 
738 		FS_devicename(dev, PROPERTY_LENGTH_ALIAS, sn, pn_real_directory);
739 		FS_dir_plus(dirfunc, v, flags, pn_real_directory, dev);
740 	}
741 	DirblobClear(&db);			/* allocated in Cache_Get_Dir */
742 
743 	STATLOCK;
744 
745 	dir_main.entries += dindex;
746 
747 	STATUNLOCK;
748 	return 0;
749 }
750 
751 // must lock a global struct for walking through tree -- limitation of "twalk"
752 // struct for walking through tree -- cannot send data except globally
753 struct {
754 	void (*dirfunc) (void *, const struct parsedname *);
755 	void *v;
756 	struct parsedname *pn_directory;
757 } typedir_action_struct;
758 
Typediraction(const void * t,const VISIT which,const int depth)759 static void Typediraction(const void *t, const VISIT which, const int depth)
760 {
761 	uint32_t ignoreflag = 0;
762 	(void) depth;
763 	switch (which) {
764 	case leaf:
765 	case postorder:
766 		FS_dir_plus(typedir_action_struct.dirfunc, typedir_action_struct.v, &ignoreflag, typedir_action_struct.pn_directory,
767 					((const struct device_opaque *) t)->key->family_code);
768 	default:
769 		break;
770 	}
771 }
772 
773 /* Show the pn_directory->type (statistics, system, ...) entries */
774 /* Only the top levels, the rest will be shown by FS_devdir */
FS_typedir(void (* dirfunc)(void *,const struct parsedname *),void * v,const struct parsedname * pn_type_directory)775 static ZERO_OR_ERROR FS_typedir(void (*dirfunc) (void *, const struct parsedname *), void *v, const struct parsedname *pn_type_directory)
776 {
777 	struct parsedname s_pn_type_device;
778 	struct parsedname *pn_type_device = &s_pn_type_device;
779 
780 
781 	memcpy(pn_type_device, pn_type_directory, sizeof(struct parsedname));	// shallow copy
782 
783 	LEVEL_DEBUG("called on %s", pn_type_directory->path);
784 
785 	TYPEDIRLOCK;
786 
787 	typedir_action_struct.dirfunc = dirfunc;
788 	typedir_action_struct.v = v;
789 	typedir_action_struct.pn_directory = pn_type_device;
790 	twalk(Tree[pn_type_directory->type], Typediraction);
791 
792 	// Ignore dangling pointer warning of s_pn_type_device; typedir_action_struct not used outside of this fn
793 	TYPEDIRUNLOCK;
794 
795 	return 0;
796 }
797 
798 // must lock a global struct for walking through tree -- limitation of "twalk"
799 // struct for walking through tree -- cannot send data except globally
800 struct {
801 	void (*dirfunc) (void *, const struct parsedname *);
802 	void *v;
803 	struct parsedname *pn_directory;
804 } externaldir_action_struct;
805 
Externaldiraction(const void * nodep,const VISIT which,const int depth)806 static void Externaldiraction(const void *nodep, const VISIT which, const int depth)
807 {
808 	const struct sensor_node *p = *(struct sensor_node * const *) nodep;
809 	uint32_t ignoreflag = 0;
810 	(void) depth;
811 
812 	switch (which) {
813 	case leaf:
814 	case postorder:
815 		FS_dir_plus(externaldir_action_struct.dirfunc, externaldir_action_struct.v, &ignoreflag, externaldir_action_struct.pn_directory,p->name);
816 	default:
817 		break;
818 	}
819 }
820 
821 /* Show the external entries */
822 /* Only the sensors */
FS_externaldir(void (* dirfunc)(void *,const struct parsedname *),void * v,const struct parsedname * pn_external_directory)823 static ZERO_OR_ERROR FS_externaldir(void (*dirfunc) (void *, const struct parsedname *), void *v, const struct parsedname *pn_external_directory)
824 {
825 	struct parsedname s_pn_external_device;
826 	struct parsedname *pn_external_device = &s_pn_external_device;
827 
828 
829 	memcpy(pn_external_device, pn_external_directory, sizeof(struct parsedname));	// shallow copy
830 
831 	LEVEL_DEBUG("called on %s", pn_external_directory->path);
832 
833 	EXTERNALDIRLOCK;
834 
835 	externaldir_action_struct.dirfunc = dirfunc;
836 	externaldir_action_struct.v = v;
837 	externaldir_action_struct.pn_directory = pn_external_device;
838 	twalk( sensor_tree, Externaldiraction);
839 	// Ignore dangling pointer warning of s_pn_external_device; externaldir_action_struct not used outside of this fn
840 
841 	EXTERNALDIRUNLOCK;
842 
843 	return 0;
844 }
845 
846 /* Show the bus entries */
FS_busdir(void (* dirfunc)(void *,const struct parsedname *),void * v,const struct parsedname * pn_directory)847 static ZERO_OR_ERROR FS_busdir(void (*dirfunc) (void *, const struct parsedname *), void *v, const struct parsedname *pn_directory)
848 {
849 	char bus[OW_FULLNAME_MAX];
850 	struct port_in * pin ;
851 	uint32_t ignoreflag = 0 ;
852 
853 	if (!RootNotBranch(pn_directory)) {
854 		return 0;
855 	}
856 
857 	for ( pin = Inbound_Control.head_port ; pin != NULL ; pin = pin->next ) {
858 		struct connection_in * cin ;
859 		for ( cin = pin->first ; cin != NO_CONNECTION ; cin = cin->next ) {
860 			UCLIBCLOCK;
861 			snprintf(bus, OW_FULLNAME_MAX, "bus.%d", cin->index);
862 			UCLIBCUNLOCK;
863 			FS_dir_plus(dirfunc, v, &ignoreflag, pn_directory, bus);
864 		}
865 	}
866 
867 	return 0;
868 }
869 
870 /* Parse and show */
FS_dir_plus(void (* dirfunc)(void *,const struct parsedname *),void * v,uint32_t * flags,const struct parsedname * pn_directory,const char * file)871 static ZERO_OR_ERROR FS_dir_plus(void (*dirfunc) (void *, const struct parsedname *), void *v, uint32_t * flags, const struct parsedname *pn_directory, const char *file)
872 {
873 	struct parsedname s_pn_plus_directory;
874 	struct parsedname *pn_plus_directory = &s_pn_plus_directory;
875 
876 	if (FS_ParsedNamePlus(pn_directory->path, file, pn_plus_directory) == 0) {
877  		switch ( FS_visible(pn_plus_directory) ) { // hide hidden properties
878 			case visible_now :
879 			case visible_always:
880 				FS_dir_entry_aliased( dirfunc, v, pn_plus_directory) ;
881 				if ( pn_plus_directory->selected_device ){
882 					flags[0] |= pn_plus_directory->selected_device->flags;
883 				}
884 				break ;
885 			default:
886 				break ;
887 		}
888 		FS_ParsedName_destroy(pn_plus_directory) ;
889 		return 0;
890 	}
891 	return -ENOENT;
892 }
893