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(®ex_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(®ex_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(®ex_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(®ex_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(®ex_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(®ex_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