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_external.h"
18 
19 static ZERO_OR_ERROR BranchAdd(struct parsedname *pn);
20 
21 enum parse_pass {
22 	parse_pass_pre_remote,
23 	parse_pass_post_remote,
24 };
25 
26 struct parsedname_pointers {
27 	char pathcpy[PATH_MAX+1];
28 	char *pathnow;
29 	char *pathnext;
30 	char *pathlast;
31 };
32 
33 static enum parse_enum set_type( enum ePN_type epntype, struct parsedname * pn ) ;
34 static enum parse_enum Parse_Unspecified(char *pathnow, enum parse_pass remote_status, struct parsedname *pn);
35 static enum parse_enum Parse_Branch(char *pathnow, enum parse_pass remote_status, struct parsedname *pn);
36 static enum parse_enum Parse_Real(char *pathnow, enum parse_pass remote_status, struct parsedname *pn);
37 static enum parse_enum Parse_NonReal(char *pathnow, struct parsedname *pn);
38 static enum parse_enum Parse_RealDevice(char *filename, enum parse_pass remote_status, struct parsedname *pn);
39 static enum parse_enum Parse_Property(char *filename, struct parsedname *pn);
40 
41 static enum parse_enum Parse_RealDeviceSN(enum parse_pass remote_status, struct parsedname *pn);
42 static enum parse_enum Parse_NonRealDevice(char *filename, struct parsedname *pn);
43 static enum parse_enum Parse_External_Device( char *filename, struct parsedname *pn) ;
44 static enum parse_enum Parse_Bus( INDEX_OR_ERROR bus_number, struct parsedname *pn);
45 static enum parse_enum Parse_Alias(char *filename, enum parse_pass remote_status, struct parsedname *pn);
46 static enum parse_enum Parse_Alias_Known( char *filename, enum parse_pass remote_status, struct parsedname *pn);
47 static void ReplaceAliasInPath( char * filename, struct parsedname * pn);
48 
49 static ZERO_OR_ERROR FS_ParsedName_anywhere(const char *path, enum parse_pass remote_status, struct parsedname *pn);
50 static ZERO_OR_ERROR FS_ParsedName_setup(struct parsedname_pointers *pp, const char *path, struct parsedname *pn);
51 static char * find_segment_in_path( char * segment, char * path ) ;
52 
53 #define BRANCH_INCR (9)
54 
55 static regex_t rx_bus;
56 static regex_t rx_set;
57 static regex_t rx_sta;
58 static regex_t rx_str;
59 static regex_t rx_sys;
60 static regex_t rx_int;
61 static regex_t rx_tex;
62 static regex_t rx_jso;
63 static regex_t rx_unc;
64 static regex_t rx_una;
65 static regex_t rx_ala;
66 static regex_t rx_sim;
67 static regex_t rx_the;
68 static regex_t rx_p_bus;
69 static regex_t rx_extension;
70 static regex_t rx_all;
71 static regex_t rx_byte;
72 static regex_t rx_number;
73 static regex_t rx_letter;
74 
regex_fini(void)75 static void regex_fini(void)
76 {
77 	regfree(&rx_bus);
78 	regfree(&rx_set);
79 	regfree(&rx_sta);
80 	regfree(&rx_str);
81 	regfree(&rx_sys);
82 	regfree(&rx_int);
83 	regfree(&rx_tex);
84 	regfree(&rx_jso);
85 	regfree(&rx_unc);
86 	regfree(&rx_una);
87 	regfree(&rx_ala);
88 	regfree(&rx_sim);
89 	regfree(&rx_the);
90 	regfree(&rx_p_bus);
91 	regfree(&rx_extension);
92 	regfree(&rx_all);
93 	regfree(&rx_byte);
94 	regfree(&rx_number);
95 	regfree(&rx_letter);
96 }
97 
98 static pthread_once_t regex_init_once = PTHREAD_ONCE_INIT;
99 
regex_init(void)100 static void regex_init(void)
101 {
102 	ow_regcomp(&rx_bus, "^bus\\.([[:digit:]]+)/?", REG_ICASE);
103 	ow_regcomp(&rx_set, "^settings/?", REG_ICASE | REG_NOSUB);
104 	ow_regcomp(&rx_sta, "^statistics/?", REG_ICASE | REG_NOSUB);
105 	ow_regcomp(&rx_str, "^structure/?", REG_ICASE | REG_NOSUB);
106 	ow_regcomp(&rx_sys, "^system/?", REG_ICASE | REG_NOSUB);
107 	ow_regcomp(&rx_int, "^interface/?", REG_ICASE | REG_NOSUB);
108 	ow_regcomp(&rx_tex, "^text/?", REG_ICASE | REG_NOSUB);
109 	ow_regcomp(&rx_jso, "^json/?", REG_ICASE | REG_NOSUB);
110 	ow_regcomp(&rx_unc, "^uncached/?", REG_ICASE | REG_NOSUB);
111 	ow_regcomp(&rx_una, "^unaliased/?", REG_ICASE | REG_NOSUB);
112 	ow_regcomp(&rx_ala, "^alarm\?", REG_ICASE | REG_NOSUB);
113 	ow_regcomp(&rx_sim, "^simultaneous/?", REG_ICASE | REG_NOSUB);
114 	ow_regcomp(&rx_the, "^thermostat/?", REG_ICASE | REG_NOSUB);
115 	ow_regcomp(&rx_p_bus, "^/bus\\.[[:digit:]]+/?", REG_ICASE);
116 	ow_regcomp(&rx_extension, "\\.", 0);
117 	ow_regcomp(&rx_all, "\\.all$", REG_ICASE);
118 	ow_regcomp(&rx_byte, "\\.byte$", REG_ICASE);
119 	ow_regcomp(&rx_number, "\\.[[:digit:]]+$", 0);
120 	ow_regcomp(&rx_letter, "\\.[[:alpha:]]$", REG_ICASE);
121 
122 	atexit(regex_fini);
123 }
124 
125 /* ---------------------------------------------- */
126 /* Filename (path) parsing functions              */
127 /* ---------------------------------------------- */
FS_ParsedName_destroy(struct parsedname * pn)128 void FS_ParsedName_destroy(struct parsedname *pn)
129 {
130 	if (!pn) {
131 		return;
132 	}
133 	LEVEL_DEBUG("%s", SAFESTRING(pn->path));
134 	CONNIN_RUNLOCK ;
135 	Detail_Free( pn ) ;
136 	SAFEFREE(pn->sparse_name);
137 	SAFEFREE(pn->bp) ;
138 }
139 
140 /*
141  * Path is either NULL (in which case a minimal structure is created that doesn't need Destroy -- used for Bus Master setups)
142  * or Path is a full "filename" type string of form: 10.1243Ab000 or uncached/bus.0/statistics etc.
143  *
144  * The Path passed in isn't altered, but 2 copies are made -- one with the full path, the other (to_server) has the first bus.n removed.
145  * An initial / is added to the path, and the full length has to be less than MAX_PATH (2048)
146  *
147  * For efficiency, the two path copies are allocated in the same memory allocation call, and so can be removed together.
148  * */
149 
150 /* Parse a path to check it's validity and attach to the propery data structures */
FS_ParsedName(const char * path,struct parsedname * pn)151 ZERO_OR_ERROR FS_ParsedName(const char *path, struct parsedname *pn)
152 {
153 	return FS_ParsedName_anywhere(path, parse_pass_pre_remote, pn);
154 }
155 
156 /* Parse a path from a remote source back -- so don't check presence */
FS_ParsedName_BackFromRemote(const char * path,struct parsedname * pn)157 ZERO_OR_ERROR FS_ParsedName_BackFromRemote(const char *path, struct parsedname *pn)
158 {
159 	return FS_ParsedName_anywhere(path, parse_pass_post_remote, pn);
160 }
161 
162 /* Parse off starting "mode" directory (uncached, alarm...) */
FS_ParsedName_anywhere(const char * path,enum parse_pass remote_status,struct parsedname * pn)163 static ZERO_OR_ERROR FS_ParsedName_anywhere(const char *path, enum parse_pass remote_status, struct parsedname *pn)
164 {
165 	struct parsedname_pointers s_pp;
166 	struct parsedname_pointers *pp = &s_pp;
167 	ZERO_OR_ERROR parse_error_status = 0;
168 	enum parse_enum pe = parse_first;
169 
170 	// To make the debug output useful it's cleared here.
171 	// Even on normal glibc, errno isn't cleared on good system calls
172 	errno = 0;
173 
174 	LEVEL_CALL("path=[%s]", SAFESTRING(path));
175 
176 	RETURN_CODE_ERROR_RETURN( FS_ParsedName_setup(pp, path, pn) );
177 
178 	if (path == NO_PATH) {
179 		RETURN_CODE_RETURN( 0 ) ; // success (by default)
180 	}
181 
182 	while (1) {
183 		// Check for extreme conditions (done, error)
184 		switch (pe) {
185 
186 		case parse_done:		// the only exit!
187 			//LEVEL_DEBUG("PARSENAME parse_done") ;
188 			//printf("PARSENAME end parse_error_status=%d\n",parse_error_status) ;
189 			if (parse_error_status) {
190 				FS_ParsedName_destroy(pn);
191 				return parse_error_status ;
192 			}
193 
194 			if ( pp->pathnext != NULL ) {
195 				// extra text -- make this an error
196 				RETURN_CODE_SET_SCALAR( parse_error_status, 77 ) ; // extra text in path
197 				pe = parse_done;
198 				continue;
199 			}
200 
201 			//printf("%s: Parse %s before corrections: %.4X -- state = %d\n",(back_from_remote)?"BACK":"FORE",pn->path,pn->state,pn->type) ;
202 			// Play with remote levels
203 			switch ( pn->type ) {
204 				case ePN_interface:
205 					// /interface is interesting -- it's actually a property of the calling server
206 					if ( SpecifiedVeryRemoteBus(pn) ) {
207 						// veryremote -> remote
208 						pn->state &= ~ePS_busveryremote ;
209 					} else if ( SpecifiedRemoteBus(pn) ) {
210 						// remote -> local
211 						pn->state &= ~ePS_busremote ;
212 						pn->state |= ePS_buslocal ;
213 					}
214 					break ;
215 
216 				case ePN_root:
217 					// root buses are considered "real"
218 					pn->type = ePN_real;	// default state
219 					break ;
220 				default:
221 					// everything else gets promoted so directories aren't added on
222 					if ( SpecifiedRemoteBus(pn) ) {
223 						// very remote
224 						pn->state |= ePS_busveryremote;
225 					}
226 					break ;
227 			}
228 			//printf("%s: Parse %s after  corrections: %.4X -- state = %d\n\n",(back_from_remote)?"BACK":"FORE",pn->path,pn->state,pn->type) ;
229 			// set up detail debugging
230 			Detail_Test( pn ) ; // turns on debug mode only during this device's query
231 			return 0;
232 
233 		case parse_error:
234 			RETURN_CODE_SET_SCALAR( parse_error_status, 27 ) ; // bad path syntax
235 			pe = parse_done;
236 			continue;
237 
238 		default:
239 			break;
240 		}
241 
242 		// break out next name in path
243 		if ( pp->pathnext == NULL ) {
244 			// make sure pp->pathnext isn't NULL. (SIGSEGV in uClibc)
245 			pp->pathnow = NULL ;
246 		} else {
247 			pp->pathnow = strsep(&(pp->pathnext), "/") ;
248 		}
249 		//LEVEL_DEBUG("PARSENAME pathnow=[%s] rest=[%s]",pp->pathnow,pp->pathnext) ;
250 
251 		// test next path segment for existence
252 		if (pp->pathnow == NULL || pp->pathnow[0] == '\0') {
253 			// done parsing
254 			pe = parse_done;
255 		}
256 
257 		// rest of state machine on parsename
258 		switch (pe) {
259 
260 		case parse_done:
261 			// nothing left to process -- will be handled in next loop pass
262 			break ;
263 
264 		case parse_first:
265 			//LEVEL_DEBUG("PARSENAME parse_first") ;
266 			pe = Parse_Unspecified(pp->pathnow, remote_status, pn);
267 			break;
268 
269 		case parse_real:
270 			//LEVEL_DEBUG("PARSENAME parse_real") ;
271 			pe = Parse_Real(pp->pathnow, remote_status, pn);
272 			break;
273 
274 		case parse_branch:
275 			//LEVEL_DEBUG("PARSENAME parse_branch") ;
276 			pe = Parse_Branch(pp->pathnow, remote_status, pn);
277 			break;
278 
279 		case parse_nonreal:
280 			//LEVEL_DEBUG("PARSENAME parse_nonreal\n") ;
281 			pe = Parse_NonReal(pp->pathnow, pn);
282 			break;
283 
284 		case parse_prop:
285 			//LEVEL_DEBUG("PARSENAME parse_prop") ;
286 			pn->dirlength = pp->pathnow - pp->pathcpy + 1 ;
287 			//LEVEL_DEBUG("Dirlength=%d <%*s>",pn->dirlength,pn->dirlength,pn->path) ;
288 			//printf("dirlength = %d which makes the path <%s> <%.*s>\n",pn->dirlength,pn->path,pn->dirlength,pn->path);
289 			pp->pathlast = pp->pathnow;	/* Save for concatination if subdirectory later wanted */
290 			pe = Parse_Property(pp->pathnow, pn);
291 			break;
292 
293 		case parse_subprop:
294 			//LEVEL_DEBUG("PARSENAME parse_subprop") ;
295 			pp->pathnow[-1] = '/';
296 			pe = Parse_Property(pp->pathlast, pn);
297 			break;
298 
299 		default:
300 			pe = parse_error;	// unknown state
301 			break;
302 
303 		}
304 		//printf("PARSENAME pe=%d\n",pe) ;
305 	}
306 }
307 
308 /* Initial memory allocation and pn setup */
FS_ParsedName_setup(struct parsedname_pointers * pp,const char * path,struct parsedname * pn)309 static ZERO_OR_ERROR FS_ParsedName_setup(struct parsedname_pointers *pp, const char *path, struct parsedname *pn)
310 {
311 	if (pn == NO_PARSEDNAME) {
312 		RETURN_CODE_RETURN( 78 ); // unexpected null pointer
313 	}
314 
315 	memset(pn, 0, sizeof(struct parsedname));
316 	pn->known_bus = NULL;		/* all buses */
317 	pn->sparse_name = NULL ;
318 	RETURN_CODE_INIT(pn);
319 
320 	/* Set the persistent state info (temp scale, ...) -- will be overwritten by client settings in the server */
321 	CONTROLFLAGSLOCK;
322 	pn->control_flags = LocalControlFlags | SHOULD_RETURN_BUS_LIST;	// initial flag as the bus-returning level, will change if a bus is specified
323 	CONTROLFLAGSUNLOCK;
324 
325 	// initialization
326 	pp->pathnow = NO_PATH;
327 	pp->pathlast = NO_PATH;
328 	pp->pathnext = NO_PATH;
329 
330 	/* Default attributes */
331 	pn->state = ePS_normal;
332 	pn->type = ePN_root;
333 
334 	/* uncached attribute */
335 	if ( Globals.uncached ) {
336 		// local settings (--uncached) can set
337 		pn->state |= ePS_uncached;
338 	}
339 	// Also can be set by path ("/uncached")
340 	// Also in owserver, can be set by client flags
341 	// sibling inherits parent
342 
343 	/* unaliased attribute */
344 	if ( Globals.unaliased ) {
345 		// local settings (--unalaised) can set
346 		pn->state |= ePS_unaliased;
347 	}
348 	// Also can be set by path ("/unaliased")
349 	// Also in owserver, can be set by client flags
350 	// sibling inherits parent
351 
352 	/* No device lock yet assigned */
353 	pn->lock = NULL ;
354 
355 	/* minimal structure for initial bus "detect" use -- really has connection and LocalControlFlags only */
356 	pn->dirlength = -1 ;
357 	if (path == NO_PATH) {
358 		return 0; // success
359 	}
360 
361 	if (strlen(path) > PATH_MAX) {
362 		RETURN_CODE_RETURN( 26 ) ; // path too long
363 	}
364 
365 	/* Have to save pn->path at once */
366 	strcpy(pn->path, "/"); // initial slash
367 	strcpy(pn->path+1, path[0]=='/'?path+1:path);
368 	strcpy(pn->path_to_server, pn->path);
369 
370 	/* make a copy for destructive parsing  without initial '/'*/
371 	strcpy(pp->pathcpy,&pn->path[1]);
372 	/* pointer to rest of path after current token peeled off */
373 	pp->pathnext = pp->pathcpy;
374 	pn->dirlength = strlen(pn->path) ;
375 
376 	/* device name */
377 	pn->device_name = NULL ;
378 
379 	/* connection_in list and start */
380 	/* ---------------------------- */
381 	/* -- This is important:     -- */
382 	/* --     Buses can --          */
383 	/* -- be added by Browse so  -- */
384 	/* -- a reader/writer lock is - */
385 	/* -- held until ParsedNameDestroy */
386 	/* ---------------------------- */
387 
388 	CONNIN_RLOCK;
389 	pn->selected_connection = NO_CONNECTION ; // Default bus assignment
390 
391 	return 0 ; // success
392 }
393 
394 /* Used for virtual directories like settings and statistics
395  * If local, applies to all local (this program) and not a
396  * specific local bus.
397  * If remote, pass it on for the remote to handle
398  * */
set_type(enum ePN_type epntype,struct parsedname * pn)399 static enum parse_enum set_type( enum ePN_type epntype, struct parsedname * pn )
400 {
401 	if (SpecifiedLocalBus(pn)) {
402 		return parse_error;
403 	} else if ( ! SpecifiedRemoteBus(pn) ) {
404 		pn->type |= ePS_busanylocal;
405 	}
406 	pn->type = epntype;
407 	return parse_nonreal;
408 }
409 
410 
411 // Early parsing -- only bus entries, uncached and text may have preceeded
Parse_Unspecified(char * pathnow,enum parse_pass remote_status,struct parsedname * pn)412 static enum parse_enum Parse_Unspecified(char *pathnow, enum parse_pass remote_status, struct parsedname *pn)
413 {
414 	pthread_once(&regex_init_once, regex_init);
415 
416 	struct ow_regmatch orm ;
417 	orm.number = 1 ; // for bus
418 
419 	if ( ow_regexec( &rx_bus, pathnow, &orm ) == 0) {
420 		INDEX_OR_ERROR bus_number = (INDEX_OR_ERROR) atoi(orm.match[1]) ;
421 		ow_regexec_free( &orm ) ;
422 		return Parse_Bus( bus_number, pn);
423 
424 	} else if (ow_regexec( &rx_set, pathnow, NULL ) == 0) {
425 		return set_type( ePN_settings, pn ) ;
426 
427 	} else if (ow_regexec( &rx_sta, pathnow, NULL ) == 0) {
428 		return set_type( ePN_statistics, pn ) ;
429 
430 	} else if (ow_regexec( &rx_str, pathnow, NULL ) == 0) {
431 		return set_type( ePN_structure, pn ) ;
432 
433 	} else if (ow_regexec( &rx_sys, pathnow, NULL ) == 0) {
434 		return set_type( ePN_system, pn ) ;
435 
436 	} else if (ow_regexec( &rx_int, pathnow, NULL ) == 0) {
437 		if (!SpecifiedBus(pn)) {
438 			return parse_error;
439 		}
440 		pn->type = ePN_interface;
441 		return parse_nonreal;
442 
443 	} else if (ow_regexec( &rx_tex, pathnow, NULL ) == 0) {
444 		pn->state |= ePS_text;
445 		return parse_first;
446 
447 	} else if (ow_regexec( &rx_jso, pathnow, NULL ) == 0) {
448 		pn->state |= ePS_json;
449 		return parse_first;
450 
451 	} else if (ow_regexec( &rx_unc, pathnow, NULL ) == 0) {
452 		pn->state |= ePS_uncached;
453 		return parse_first;
454 
455 	} else if (ow_regexec( &rx_una, pathnow, NULL ) == 0) {
456 		pn->state |= ePS_unaliased;
457 		return parse_first;
458 
459 	}
460 
461 	pn->type = ePN_real;
462 	return Parse_Branch(pathnow, remote_status, pn);
463 }
464 
Parse_Branch(char * pathnow,enum parse_pass remote_status,struct parsedname * pn)465 static enum parse_enum Parse_Branch(char *pathnow, enum parse_pass remote_status, struct parsedname *pn)
466 {
467 	pthread_once(&regex_init_once, regex_init);
468 
469 	if (ow_regexec( &rx_ala, pathnow, NULL ) == 0) {
470 		pn->state |= ePS_alarm;
471 		pn->type = ePN_real;
472 		return parse_real;
473 	}
474 	return Parse_Real(pathnow, remote_status, pn);
475 }
476 
Parse_Real(char * pathnow,enum parse_pass remote_status,struct parsedname * pn)477 static enum parse_enum Parse_Real(char *pathnow, enum parse_pass remote_status, struct parsedname *pn)
478 {
479 	pthread_once(&regex_init_once, regex_init);
480 
481 	if (ow_regexec( &rx_sim, pathnow, NULL ) == 0) {
482 		pn->selected_device = DeviceSimultaneous;
483 		return parse_prop;
484 
485 	} else if (ow_regexec( &rx_tex, pathnow, NULL ) == 0) {
486 		pn->state |= ePS_text;
487 		return parse_real;
488 
489 	} else if (ow_regexec( &rx_jso, pathnow, NULL ) == 0) {
490 		pn->state |= ePS_json;
491 		return parse_real;
492 
493 	} else if (ow_regexec( &rx_the, pathnow, NULL ) == 0) {
494 		pn->selected_device = DeviceThermostat;
495 		return parse_prop;
496 
497 	} else if (ow_regexec( &rx_unc, pathnow, NULL ) == 0) {
498 		pn->state |= ePS_uncached;
499 		return parse_real;
500 
501 	} else if (ow_regexec( &rx_una, pathnow, NULL ) == 0) {
502 		pn->state |= ePS_unaliased;
503 		return parse_real;
504 
505 	} else {
506 		return Parse_RealDevice(pathnow, remote_status, pn);
507 	}
508 }
509 
Parse_NonReal(char * pathnow,struct parsedname * pn)510 static enum parse_enum Parse_NonReal(char *pathnow, struct parsedname *pn)
511 {
512 	pthread_once(&regex_init_once, regex_init);
513 
514 	if (ow_regexec( &rx_tex, pathnow, NULL )  == 0) {
515 		pn->state |= ePS_text;
516 		return parse_nonreal;
517 
518 	} else if (ow_regexec( &rx_jso, pathnow, NULL )  == 0) {
519 		pn->state |= ePS_json;
520 		return parse_nonreal;
521 
522 	} else if (ow_regexec( &rx_unc, pathnow, NULL )  == 0) {
523 		pn->state |= ePS_uncached;
524 		return parse_nonreal;
525 
526 	} else if (ow_regexec( &rx_una, pathnow, NULL )  == 0) {
527 		pn->state |= ePS_unaliased;
528 		return parse_nonreal;
529 
530 	} else {
531 		return Parse_NonRealDevice(pathnow, pn);
532 	}
533 
534 	return parse_error;
535 }
536 
537 /* We've reached a /bus.n entry */
Parse_Bus(INDEX_OR_ERROR bus_number,struct parsedname * pn)538 static enum parse_enum Parse_Bus( INDEX_OR_ERROR bus_number, struct parsedname *pn)
539 {
540 	pthread_once(&regex_init_once, regex_init);
541 
542 	struct ow_regmatch orm ;
543 	orm.number = 0 ;
544 
545 	/* Processing for bus.X directories -- eventually will make this more generic */
546 	if ( INDEX_NOT_VALID(bus_number) ) {
547 		return parse_error;
548 	}
549 
550 	/* Should make a presence check on remote buses here, but
551 	 * it's not a major problem if people use bad paths since
552 	 * they will just end up with empty directory listings. */
553 	if (SpecifiedLocalBus(pn)) {	/* already specified a "bus." */
554 		/* too many levels of bus for a non-remote adapter */
555 		return parse_error;
556 	} else if (SpecifiedRemoteBus(pn)) {	/* already specified a "bus." */
557 		/* Let the remote bus do the heavy lifting */
558 		pn->state |= ePS_busveryremote;
559 		return parse_first;
560 	}
561 
562 	/* Since we are going to use a specific in-device now, set
563 	 * pn->selected_connection to point at that device at once. */
564 	if ( SetKnownBus(bus_number, pn) ) {
565 		return parse_error ; // bus doesn't exist
566 	}
567 	pn->state |= BusIsServer((pn)->selected_connection) ? ePS_busremote : ePS_buslocal ;
568 
569 	if (SpecifiedLocalBus(pn)) {
570 		/* don't return bus-list for local paths. */
571 		pn->control_flags &= (~SHOULD_RETURN_BUS_LIST);
572 	}
573 
574 	/* Create the path without the "bus.x" part in pn->path_to_server */
575 	if ( ow_regexec( &rx_p_bus, pn->path, &orm ) == 0 ) {
576 		strcpy( pn->path_to_server, orm.pre[0] ) ;
577 		strcat( pn->path_to_server, "/" ) ;
578 		strcat( pn->path_to_server, orm.post[0] ) ;
579 		ow_regexec_free( &orm ) ;
580 	}
581 	return parse_first;
582 }
583 
584 // search path for this exact matching path segment
find_segment_in_path(char * segment,char * path)585 static char * find_segment_in_path( char * segment, char * path )
586 {
587 	int segment_length = strlen(segment) ;
588 	char augmented_segment[ segment_length + 2 ] ;
589 
590 	char * path_pointer = path ;
591 
592 	augmented_segment[0] = '/' ;
593 	strcpy( &augmented_segment[1], segment ) ;
594 
595 	while ( (path_pointer = strstr( path_pointer , augmented_segment )) != NULL ) {
596 		++ path_pointer ; // point after '/'
597 		switch( path_pointer[segment_length] ) {
598 			case '\0':
599 			case '/':
600 				return path_pointer ;
601 			default:
602 				// not a full match -- try again
603 				break ;
604 		}
605 	}
606 
607 	return NULL ;
608 }
609 
610 /* replace alias with sn */
ReplaceAliasInPath(char * filename,struct parsedname * pn)611 static void ReplaceAliasInPath( char * filename, struct parsedname * pn)
612 {
613 	int alias_len = strlen(filename) ;
614 
615 	// check total length
616 	if ( strlen(pn->path_to_server) + 14 - alias_len <= PATH_MAX ) {
617 		// find the alias
618 		char * alias_loc = find_segment_in_path( filename, pn->path_to_server ) ;
619 
620 		if ( alias_loc != NULL ) {
621 			char * post_alias_loc ;
622 
623 			post_alias_loc = alias_loc + alias_len ;
624 
625 			// move rest of path
626 			memmove( &alias_loc[14], post_alias_loc, strlen(post_alias_loc)+1 ) ;
627 			//write in serial number for alias
628 			bytes2string( alias_loc, pn->sn, 7 ) ;
629 		}
630 	}
631 }
632 
633 /* This is when the alias name in mapped to a known serial number
634  * behaves much more like the standard handling -- bus from sn */
Parse_Alias_Known(char * filename,enum parse_pass remote_status,struct parsedname * pn)635 static enum parse_enum Parse_Alias_Known( char *filename, enum parse_pass remote_status, struct parsedname *pn)
636 {
637 	if (remote_status == parse_pass_pre_remote) {
638 		ReplaceAliasInPath( filename, pn ) ;
639 	}
640 
641 	return Parse_RealDeviceSN( remote_status, pn ) ;
642 }
643 
644 /* Get a device that isn't a serial number -- see if it's an alias */
Parse_Alias(char * filename,enum parse_pass remote_status,struct parsedname * pn)645 static enum parse_enum Parse_Alias(char *filename, enum parse_pass remote_status, struct parsedname *pn)
646 {
647 	INDEX_OR_ERROR bus ;
648 
649 	// See if the alias is known in the permanent list. We get the serial number
650 	if ( GOOD( Cache_Get_Alias_SN(filename,pn->sn)) ) {
651 		// Success! The alias is already registered and the serial
652 		//  number just now loaded in pn->sn
653 		return Parse_Alias_Known( filename, remote_status, pn ) ;
654 	}
655 
656 	// By definition this is a remote device, or non-existent.
657 	pn->selected_device = &RemoteDevice ;
658 
659 	// is alias name cached from previous query?
660 	bus = Cache_Get_Alias_Bus( filename ) ;
661 	if ( bus != INDEX_BAD ) {
662 		// This alias is cached in temporary list
663 		SetKnownBus(bus, pn);
664 		return parse_prop ;
665 	}
666 
667 	// Look for alias in remote buses
668 	bus = RemoteAlias(pn) ;
669 	if ( bus == INDEX_BAD ) {
670 		return parse_error ;
671 	}
672 
673 	// Found the alias (remotely)
674 	SetKnownBus(bus, pn);
675 
676 	if ( pn->sn[0] == 0 && pn->sn[7]==0 ) { // no serial number owserver (older)
677 		Cache_Add_Alias_Bus(filename,bus) ;
678 	} else {
679 		Cache_Add_Alias( filename, pn->sn ) ;
680 		Cache_Add_Device( bus, pn->sn ) ;
681 		pn->selected_device = FS_devicefindhex(pn->sn[0], pn);
682 	}
683 
684 	return parse_prop ;
685 }
686 
687 /* Parse Name (only device name) part of string */
688 /* Return -ENOENT if not a valid name
689    return 0 if good
690    *next points to next segment, or NULL if not filetype
691  */
Parse_RealDevice(char * filename,enum parse_pass remote_status,struct parsedname * pn)692 static enum parse_enum Parse_RealDevice(char *filename, enum parse_pass remote_status, struct parsedname *pn)
693 {
694 	pn->device_name = find_segment_in_path( filename, pn->path ) ;
695 	switch ( Parse_SerialNumber(filename,pn->sn) ) {
696 		case sn_not_sn:
697 			if ( Find_External_Sensor( filename ) ) {
698 				return Parse_External_Device( filename, pn ) ;
699 			} else {
700 				return Parse_Alias( filename, remote_status, pn) ;
701 			}
702 		case sn_valid:
703 			return Parse_RealDeviceSN( remote_status, pn ) ;
704 		case sn_invalid:
705 		default:
706 			return parse_error ;
707 	}
708 }
709 
710 /* Device is known with serial number */
Parse_RealDeviceSN(enum parse_pass remote_status,struct parsedname * pn)711 static enum parse_enum Parse_RealDeviceSN(enum parse_pass remote_status, struct parsedname *pn)
712 {
713 	/* Search for known 1-wire device -- keyed to device name (family code in HEX) */
714 	pn->selected_device = FS_devicefindhex(pn->sn[0], pn);
715 
716 	// returning from owserver -- don't need to check presence (it's implied)
717 	if (remote_status == parse_pass_post_remote) {
718 		return parse_prop;
719 	}
720 
721 	if (Globals.one_device) {
722 		// Single slave device -- use faster routines
723 		SetKnownBus(INDEX_DEFAULT, pn);
724 	} else {
725 		/* Check the presence, and cache the proper bus number for better performance */
726 		INDEX_OR_ERROR bus_nr = CheckPresence(pn);
727 		if ( INDEX_NOT_VALID(bus_nr) ) {
728 			return parse_error;	/* CheckPresence failed */
729 		}
730 	}
731 
732 	return parse_prop;
733 }
734 
735 /* Device is known with serial number */
Parse_External_Device(char * filename,struct parsedname * pn)736 static enum parse_enum Parse_External_Device( char *filename, struct parsedname *pn)
737 {
738 	struct sensor_node * sensor_n = Find_External_Sensor( filename ) ;
739 	struct family_node * family_n = Find_External_Family( sensor_n->family ) ;
740 
741 	pn->selected_device = &(family_n->dev) ;
742 	SetKnownBus(Inbound_Control.external->index, pn);
743 	return parse_prop;
744 }
745 
746 
747 /* Parse Name (non-device name) part of string */
Parse_NonRealDevice(char * filename,struct parsedname * pn)748 static enum parse_enum Parse_NonRealDevice(char *filename, struct parsedname *pn)
749 {
750 	//printf("Parse_NonRealDevice: [%s] [%s]\n", filename, pn->path);
751 	pn->device_name = find_segment_in_path( filename, pn->path ) ;
752 	FS_devicefind(filename, pn);
753 	return (pn->selected_device == &UnknownDevice) ? parse_error : parse_prop;
754 }
755 
Parse_Property(char * filename,struct parsedname * pn)756 static enum parse_enum Parse_Property(char *filename, struct parsedname *pn)
757 {
758 	pthread_once(&regex_init_once, regex_init);
759 
760 	struct device * pdev = pn->selected_device ;
761 	struct filetype * ft ;
762 
763 	int extension_given ;
764 
765 	struct ow_regmatch orm ;
766 	orm.number = 0 ;
767 
768 	//printf("FilePart: %s %s\n", filename, pn->path);
769 
770 	// Special case for remote device. Use distant data
771 	if ( pdev == &RemoteDevice ) {
772 		// remote device, no known sn, handle property in server.
773 		return parse_done ;
774 	}
775 
776 	// separate filename.dot
777 //	filename = strsep(&dot, ".");
778 	if ( ow_regexec( &rx_extension, filename, &orm ) == 0 ) {
779 		// extension given
780 		extension_given = 1 ;
781 		ft =
782 			 bsearch(orm.pre[0], pdev->filetype_array,
783 					 (size_t) pdev->count_of_filetypes, sizeof(struct filetype), filetype_cmp) ;
784 		ow_regexec_free( &orm ) ;
785 	} else {
786 		// no extension given
787 		extension_given = 0 ;
788 		ft =
789 			 bsearch(filename, pdev->filetype_array,
790 					 (size_t) pdev->count_of_filetypes, sizeof(struct filetype), filetype_cmp) ;
791 	}
792 
793 	pn->selected_filetype = ft ;
794 	if (ft == NO_FILETYPE ) {
795 		LEVEL_DEBUG("Unknown property for this device %s",SAFESTRING(filename) ) ;
796 		return parse_error;			/* filetype not found */
797 	}
798 
799 	//printf("FP known filetype %s\n",pn->selected_filetype->name) ;
800 	/* Filetype found, now process extension */
801 	if (extension_given==0) {	/* no extension */
802 		if (ft->ag != NON_AGGREGATE) {
803 			return parse_error;	/* aggregate filetypes need an extension */
804 		}
805 		pn->extension = 0;	/* default when no aggregate */
806 
807 	// Non-aggregate cannot have an extension
808 	} else if (ft->ag == NON_AGGREGATE) {
809 		return parse_error;	/* An extension not allowed when non-aggregate */
810 
811 	// Sparse uses the extension verbatim (text or number)
812 	} else if (ft->ag->combined==ag_sparse)  { /* Sparse */
813 		if (ft->ag->letters == ag_letters) {	/* text string */
814 			pn->extension = 0;	/* text extension, not number */
815 			ow_regexec( &rx_extension, filename, &orm ) ; // don't need to test -- already succesful
816 			pn->sparse_name = owstrdup(orm.post[0]) ;
817 			ow_regexec_free( &orm ) ;
818 			LEVEL_DEBUG("Sparse alpha extension found: <%s>",pn->sparse_name);
819 		} else {			/* Numbers */
820 			if ( ow_regexec( &rx_number, filename, &orm ) == 0 ) {
821 				pn->extension = atoi( &orm.match[0][1] );	/* Number conversion */
822 				ow_regexec_free( &orm ) ;
823 				LEVEL_DEBUG("Sparse numeric extension found: <%ld>",(long int) pn->extension);
824 			} else {
825 				LEVEL_DEBUG("Non numeric extension for %s",filename ) ;
826 				return parse_error ;
827 			}
828 		}
829 
830 	// Non-sparse "ALL"
831 	} else if (ow_regexec( &rx_all, filename, NULL ) == 0) {
832 		//printf("FP ALL\n");
833 		pn->extension = EXTENSION_ALL;	/* ALL */
834 
835 	// Non-sparse "BYTE"
836 	} else if (ft->format == ft_bitfield && ow_regexec( &rx_byte, filename, NULL) == 0) {
837 		pn->extension = EXTENSION_BYTE;	/* BYTE */
838 		//printf("FP BYTE\n") ;
839 
840 	// Non-sparse extension -- interpret and check bounds
841 	} else {				/* specific extension */
842 		if (ft->ag->letters == ag_letters) {	/* Letters */
843 			//printf("FP letters\n") ;
844 			if ( ow_regexec( &rx_letter, filename, &orm ) == 0 ) {
845 				pn->extension = toupper(orm.match[0][1]) - 'A';	/* Letter extension */
846 				ow_regexec_free( &orm ) ;
847 			} else {
848 				return parse_error;
849 			}
850 		} else {			/* Numbers */
851 			if ( ow_regexec( &rx_number, filename, &orm ) == 0 ) {
852 				pn->extension = atoi( &orm.match[0][1] );	/* Number conversion */
853 				ow_regexec_free( &orm ) ;
854 			} else {
855 				return parse_error;
856 			}
857 		}
858 		//printf("FP ext=%d nr_elements=%d\n", pn->extension, pn->selected_filetype->ag->elements) ;
859 		/* Now check range */
860 		if ((pn->extension < 0)
861 			|| (pn->extension >= ft->ag->elements)) {
862 			//printf("FP Extension out of range %d %d %s\n", pn->extension, pn->selected_filetype->ag->elements, pn->path);
863 			LEVEL_DEBUG("Extension %d out of range",pn->extension ) ;
864 			return parse_error;	/* Extension out of range */
865 		}
866 		//printf("FP in range\n") ;
867 	}
868 
869 	//printf("FP Good\n") ;
870 	switch (ft->format) {
871 	case ft_directory:		// aux or main
872 		if ( pn->type == ePN_structure ) {
873 			// special case, structure for aux and main
874 			return parse_done;
875 		}
876 		if (BranchAdd(pn) != 0) {
877 			//printf("PN BranchAdd failed for %s\n", filename);
878 			return parse_error;
879 		}
880 		/* STATISTICS */
881 		STATLOCK;
882 		if (pn->ds2409_depth > dir_depth) {
883 			dir_depth = pn->ds2409_depth;
884 		}
885 		STATUNLOCK;
886 		return parse_branch;
887 	case ft_subdir:
888 		//printf("PN %s is a subdirectory\n", filename);
889 		pn->subdir = ft;
890 		pn->selected_filetype = NO_FILETYPE;
891 		return parse_subprop;
892 	default:
893 		return parse_done;
894 	}
895 }
896 
BranchAdd(struct parsedname * pn)897 static ZERO_OR_ERROR BranchAdd(struct parsedname *pn)
898 {
899 	//printf("BRANCHADD\n");
900 	if ((pn->ds2409_depth % BRANCH_INCR) == 0) {
901 		void *temp = pn->bp;
902 		if ((pn->bp = owrealloc(temp, (BRANCH_INCR + pn->ds2409_depth) * sizeof(struct ds2409_hubs))) == NULL) {
903 			SAFEFREE(temp) ;
904 			RETURN_CODE_RETURN( 79 ) ; // unable to allocate memory
905 		}
906 	}
907 	memcpy(pn->bp[pn->ds2409_depth].sn, pn->sn, SERIAL_NUMBER_SIZE);	/* copy over DS2409 name */
908 	pn->bp[pn->ds2409_depth].branch = pn->selected_filetype->data.i;
909 	++pn->ds2409_depth;
910 	pn->selected_filetype = NO_FILETYPE;
911 	pn->selected_device = NO_DEVICE;
912 	return 0;
913 }
914 
filetype_cmp(const void * name,const void * ex)915 int filetype_cmp(const void *name, const void *ex)
916 {
917 	return strcmp((const char *) name, ((const struct filetype *) ex)->name);
918 }
919 
920 /* Parse a path/file combination */
FS_ParsedNamePlus(const char * path,const char * file,struct parsedname * pn)921 ZERO_OR_ERROR FS_ParsedNamePlus(const char *path, const char *file, struct parsedname *pn)
922 {
923 	ZERO_OR_ERROR ret = 0;
924 	char *fullpath;
925 
926 	if (path == NO_PATH) {
927 		path = "" ;
928 	}
929 	if (file == NULL) {
930 		file = "" ;
931 	}
932 
933 	fullpath = owmalloc(strlen(file) + strlen(path) + 2);
934 	if (fullpath == NO_PATH) {
935 		RETURN_CODE_RETURN( 79 ) ; // unable to allocate memory
936 	}
937 	strcpy(fullpath, path);
938 	if (fullpath[strlen(fullpath) - 1] != '/') {
939 		strcat(fullpath, "/");
940 	}
941 	strcat(fullpath, file);
942 	//printf("PARSENAMEPLUS path=%s pre\n",fullpath) ;
943 	ret = FS_ParsedName(fullpath, pn);
944 	//printf("PARSENAMEPLUS path=%s post\n",fullpath) ;
945 	owfree(fullpath);
946 	//printf("PARSENAMEPLUS free\n") ;
947 	return ret;
948 }
949 
950 
951 
952 /* Parse a path/file combination */
FS_ParsedNamePlusExt(const char * path,const char * file,int extension,enum ag_index alphanumeric,struct parsedname * pn)953 ZERO_OR_ERROR FS_ParsedNamePlusExt(const char *path, const char *file, int extension, enum ag_index alphanumeric, struct parsedname *pn)
954 {
955 	if (extension == EXTENSION_BYTE ) {
956 		return FS_ParsedNamePlusText(path, file, "BYTE", pn);
957 	} else if (extension == EXTENSION_ALL ) {
958 		return FS_ParsedNamePlusText(path, file, "ALL", pn);
959 	} else if (alphanumeric == ag_letters) {
960 		char name[2] = { 'A'+extension, 0x00, } ;
961 		return FS_ParsedNamePlusText(path, file, name, pn);
962 	} else {
963 		char name[OW_FULLNAME_MAX];
964 		UCLIBCLOCK;
965 		snprintf(name, OW_FULLNAME_MAX, "%d", extension);
966 		UCLIBCUNLOCK;
967 		return FS_ParsedNamePlusText(path, file, name, pn);
968 	}
969 }
970 
971 /* Parse a path/file combination */
FS_ParsedNamePlusText(const char * path,const char * file,const char * extension,struct parsedname * pn)972 ZERO_OR_ERROR FS_ParsedNamePlusText(const char *path, const char *file, const char *extension, struct parsedname *pn)
973 {
974 	char name[OW_FULLNAME_MAX];
975 	UCLIBCLOCK;
976 		snprintf(name, OW_FULLNAME_MAX, "%s.%s", file, extension );
977 	UCLIBCUNLOCK;
978 	return FS_ParsedNamePlus(path, name, pn);
979 }
980 
FS_ParsedName_Placeholder(struct parsedname * pn)981 void FS_ParsedName_Placeholder( struct parsedname * pn )
982 {
983 	FS_ParsedName( NULL, pn ) ; // minimal parsename -- no destroy needed
984 }
985