/* All ow library closeup */ void LibClose(void) { LEVEL_CALL("Starting Library cleanup"); LibStop(); PIDstop(); DeviceDestroy(); Detail_Close() ; _MUTEX_ATTR_DESTROY(Mutex.mattr); #if OW_ZERO // Used by browse and announce OW_Free_dnssd_library(); OW_Free_avahi_library(); #endif LEVEL_CALL("Finished Library cleanup"); if (log_available) { closelog(); log_available = 0; } SAFEFREE(Globals.announce_name) ; SAFEFREE(Globals.progname) ; SAFEFREE(Globals.fatal_debug_file) ; LEVEL_DEBUG("Libraries closed"); }
/* Top level detect routine */ GOOD_OR_BAD DS2482_detect(struct port_in *pin) { struct address_pair ap ; GOOD_OR_BAD gbResult ; enum ds2482_address chip_num ; Parse_Address( pin->init_data, &ap ) ; switch ( ap.second.type ) { case address_numeric: if ( ap.second.number < ds2482_18 || ap.second.number >= ds2482_too_far ) { LEVEL_CALL("DS2482 bus address <%s> invalid. Will search.", ap.second.alpha) ; chip_num = ds2482_any ; } else { chip_num = ap.second.number ; } break ; case address_all: case address_asterix: chip_num = ds2482_all ; break ; case address_none: chip_num = ds2482_any ; break ; default: LEVEL_CALL("DS2482 bus address <%s> invalid. Will scan.", ap.second.alpha) ; chip_num = ds2482_any ; break ; } switch ( ap.first.type ) { case address_all: case address_asterix: // All adapters gbResult = DS2482_detect_sys( 0, chip_num, pin ) ; break ; case address_none: // Any adapter -- first one found gbResult = DS2482_detect_sys( 1, chip_num, pin ) ; break ; default: // traditional, actual bus specified gbResult = DS2482_detect_bus( chip_num, ap.first.alpha, pin ) ; break ; } Free_Address( &ap ) ; return gbResult ; }
/* Just close in/out devices and clear cache. Just enough to make it possible to call LibStart() again. This is called from swig/ow.i to when script wants to initialize a new server-connection. */ void LibStop(void) { char *argv[1] = { NULL }; LEVEL_CALL("Clear Cache"); Cache_Clear(); LEVEL_CALL("Closing input devices"); FreeInAll(); LEVEL_CALL("Closing outout devices"); FreeOutAll(); /* Have to reset more internal variables, and this should be fixed * by setting optind = 0 and call getopt() * (first_nonopt = last_nonopt = 1;) */ optind = 0; (void) getopt_long(1, argv, " ", NULL, NULL); optarg = NULL; optind = 1; opterr = 1; optopt = '?'; }
ZERO_OR_ERROR FS_fstat(const char *path, struct stat *stbuf) { struct parsedname pn; ZERO_OR_ERROR ret = 0; LEVEL_CALL("path=%s", SAFESTRING(path)); /* Bad path */ if (FS_ParsedName(path, &pn) != 0 ) { return -ENOENT; } ret = FS_fstat_postparse(stbuf, &pn); FS_ParsedName_destroy(&pn); return ret; }
/* return size if ok, else negative */ SIZE_OR_ERROR FS_write(const char *path, const char *buf, const size_t size, const off_t offset) { ZERO_OR_ERROR write_return; OWQ_allocate_struct_and_pointer(owq); LEVEL_CALL("path=%s size=%d offset=%d", SAFESTRING(path), (int) size, (int) offset); // parsable path? if ( OWQ_create(path, owq) != 0 ) { // for write return -ENOENT; } OWQ_assign_write_buffer(buf, size, offset, owq) ; write_return = FS_write_postparse(owq); OWQ_destroy(owq); return write_return; /* here's where the size is used! */ }
SIZE_OR_ERROR FS_read(const char *path, char *buf, const size_t size, const off_t offset) { SIZE_OR_ERROR read_or_error; OWQ_allocate_struct_and_pointer(owq); LEVEL_CALL("path=%s size=%d offset=%d", SAFESTRING(path), (int) size, (int) offset); // Parseable path? if ( BAD( OWQ_create(path, owq) ) ) { // for read return -ENOENT; } OWQ_assign_read_buffer( buf, size, offset, owq) ; read_or_error = FS_read_postparse(owq); OWQ_destroy(owq); return read_or_error; }
/* Fstat with parsedname already done */ ZERO_OR_ERROR FS_fstat_postparse(struct stat *stbuf, const struct parsedname *pn) { memset(stbuf, 0, sizeof(struct stat)); LEVEL_CALL("ATTRIBUTES path=%s", SAFESTRING(pn->path)); if (KnownBus(pn) && pn->known_bus == NULL) { /* check for presence of first in-device at least since FS_ParsedName * doesn't do it yet. */ return -ENOENT; } else if (pn->selected_device == NO_DEVICE) { /* root directory */ int nr = 0; //printf("FS_fstat root\n"); stbuf->st_mode = S_IFDIR | 0755; stbuf->st_nlink = 2; // plus number of sub-directories nr = -1; // make it 1 /* If calculating NSUB is hard, the filesystem can set st_nlink of directories to 1, and find will still work. This is not documented behavior of find, and it's not clear whether this is intended or just by accident. But for example the NTFS filesysem relies on this, so it's unlikely that this "feature" will go away. */ stbuf->st_nlink += nr; FSTATLOCK; stbuf->st_atime = stbuf->st_ctime = stbuf->st_mtime = StateInfo.start_time; FSTATUNLOCK; stbuf->st_size = 4096 ; // Common directory size return 0; } else if (pn->selected_filetype == NO_FILETYPE) { int nr = 0; //printf("FS_fstat pn.selected_filetype == NULL (1-wire device)\n"); stbuf->st_mode = S_IFDIR | 0777; stbuf->st_nlink = 2; // plus number of sub-directories nr = -1; // make it 1 //printf("FS_fstat seem to be %d entries (%d dirs) in device\n", pn.selected_device->nft, nr); stbuf->st_nlink += nr; FSTATLOCK; stbuf->st_atime = stbuf->st_ctime = stbuf->st_mtime = StateInfo.dir_time; FSTATUNLOCK; stbuf->st_size = 4096 ; // Common directory size return 0; } else if (pn->selected_filetype->format == ft_directory || pn->selected_filetype->format == ft_subdir) { /* other directory */ int nr = 0; stbuf->st_mode = S_IFDIR | 0777; stbuf->st_nlink = 2; // plus number of sub-directories nr = -1; // make it 1 stbuf->st_nlink += nr; FSTATLOCK; stbuf->st_atime = stbuf->st_ctime = stbuf->st_mtime = StateInfo.dir_time; FSTATUNLOCK; stbuf->st_size = 4096 ; // Common directory size return 0; } else { /* known 1-wire filetype */ stbuf->st_mode = S_IFREG; if (pn->selected_filetype->read != NO_READ_FUNCTION) { stbuf->st_mode |= 0444; } if (!Globals.readonly && (pn->selected_filetype->write != NO_WRITE_FUNCTION)) { stbuf->st_mode |= 0222; } stbuf->st_nlink = 1; switch (pn->selected_filetype->change) { case fc_volatile: case fc_second: case fc_statistic: stbuf->st_atime = stbuf->st_ctime = stbuf->st_mtime = NOW_TIME; break; case fc_stable: FSTATLOCK; stbuf->st_atime = stbuf->st_ctime = stbuf->st_mtime = StateInfo.dir_time; FSTATUNLOCK; break; default: stbuf->st_atime = stbuf->st_ctime = stbuf->st_mtime = StateInfo.start_time; break; } stbuf->st_size = FullFileLength(pn); return 0; } }
/* Parse off starting "mode" directory (uncached, alarm...) */ static ZERO_OR_ERROR FS_ParsedName_anywhere(const char *path, enum parse_pass remote_status, struct parsedname *pn) { struct parsedname_pointers s_pp; struct parsedname_pointers *pp = &s_pp; ZERO_OR_ERROR parse_error_status = 0; enum parse_enum pe = parse_first; // To make the debug output useful it's cleared here. // Even on normal glibc, errno isn't cleared on good system calls errno = 0; LEVEL_CALL("path=[%s]", SAFESTRING(path)); RETURN_CODE_ERROR_RETURN( FS_ParsedName_setup(pp, path, pn) ); if (path == NO_PATH) { RETURN_CODE_RETURN( 0 ) ; // success (by default) } while (1) { // Check for extreme conditions (done, error) switch (pe) { case parse_done: // the only exit! //LEVEL_DEBUG("PARSENAME parse_done") ; //printf("PARSENAME end parse_error_status=%d\n",parse_error_status) ; if (parse_error_status) { FS_ParsedName_destroy(pn); return parse_error_status ; } if ( pp->pathnext != NULL ) { // extra text -- make this an error RETURN_CODE_SET_SCALAR( parse_error_status, 77 ) ; // extra text in path pe = parse_done; continue; } //printf("%s: Parse %s before corrections: %.4X -- state = %d\n",(back_from_remote)?"BACK":"FORE",pn->path,pn->state,pn->type) ; // Play with remote levels switch ( pn->type ) { case ePN_interface: // /interface is interesting -- it's actually a property of the calling server if ( SpecifiedVeryRemoteBus(pn) ) { // veryremote -> remote pn->state &= ~ePS_busveryremote ; } else if ( SpecifiedRemoteBus(pn) ) { // remote -> local pn->state &= ~ePS_busremote ; pn->state |= ePS_buslocal ; } break ; case ePN_root: // root buses are considered "real" pn->type = ePN_real; // default state break ; default: // everything else gets promoted so directories aren't added on if ( SpecifiedRemoteBus(pn) ) { // very remote pn->state |= ePS_busveryremote; } break ; } //printf("%s: Parse %s after corrections: %.4X -- state = %d\n\n",(back_from_remote)?"BACK":"FORE",pn->path,pn->state,pn->type) ; // set up detail debugging Detail_Test( pn ) ; // turns on debug mode only during this device's query return 0; case parse_error: RETURN_CODE_SET_SCALAR( parse_error_status, 27 ) ; // bad path syntax pe = parse_done; continue; default: break; } // break out next name in path if ( pp->pathnext == NULL ) { // make sure pp->pathnext isn't NULL. (SIGSEGV in uClibc) pp->pathnow = NULL ; } else { pp->pathnow = strsep(&(pp->pathnext), "/") ; } //LEVEL_DEBUG("PARSENAME pathnow=[%s] rest=[%s]",pp->pathnow,pp->pathnext) ; // test next path segment for existence if (pp->pathnow == NULL || pp->pathnow[0] == '\0') { // done parsing pe = parse_done; } // rest of state machine on parsename switch (pe) { case parse_done: // nothing left to process -- will be handled in next loop pass break ; case parse_first: //LEVEL_DEBUG("PARSENAME parse_first") ; pe = Parse_Unspecified(pp->pathnow, remote_status, pn); break; case parse_real: //LEVEL_DEBUG("PARSENAME parse_real") ; pe = Parse_Real(pp->pathnow, remote_status, pn); break; case parse_branch: //LEVEL_DEBUG("PARSENAME parse_branch") ; pe = Parse_Branch(pp->pathnow, remote_status, pn); break; case parse_nonreal: //LEVEL_DEBUG("PARSENAME parse_nonreal\n") ; pe = Parse_NonReal(pp->pathnow, pn); break; case parse_prop: //LEVEL_DEBUG("PARSENAME parse_prop") ; pn->dirlength = pp->pathnow - pp->pathcpy + 1 ; //LEVEL_DEBUG("Dirlength=%d <%*s>",pn->dirlength,pn->dirlength,pn->path) ; //printf("dirlength = %d which makes the path <%s> <%.*s>\n",pn->dirlength,pn->path,pn->dirlength,pn->path); pp->pathlast = pp->pathnow; /* Save for concatination if subdirectory later wanted */ pe = Parse_Property(pp->pathnow, pn); break; case parse_subprop: //LEVEL_DEBUG("PARSENAME parse_subprop") ; pp->pathnow[-1] = '/'; pe = Parse_Property(pp->pathlast, pn); break; default: pe = parse_error; // unknown state break; } //printf("PARSENAME pe=%d\n",pe) ; } }
/* This is a test to implement daemon() */ static int my_daemon(int nochdir, int noclose) { struct sigaction act; int pid; FILE_DESCRIPTOR_OR_ERROR file_descriptor; signal(SIGCHLD, SIG_DFL); #if defined(__UCLIBC__) pid = vfork(); #else /* __UCLIBC__ */ pid = fork(); #endif /* __UCLIBC__ */ switch (pid) { case -1: memset(&act, 0, sizeof(act)); act.sa_handler = catchchild; act.sa_flags = SA_RESTART; sigaction(SIGCHLD, &act, NULL); //printf("owlib: my_daemon: pid=%d fork error\n", getpid()); LEVEL_CALL("Libsetup ok"); return (-1); case 0: break; default: //signal(SIGCHLD, SIG_DFL); //printf("owlib: my_daemon: pid=%d exit parent\n", getpid()); _exit(0); } if (setsid() < 0) { perror("setsid:"); return -1; } /* Make certain we are not a session leader, or else we * might reacquire a controlling terminal */ #ifdef __UCLIBC__ pid = vfork(); #else /* __UCLIBC__ */ pid = fork(); #endif /* __UCLIBC__ */ if (pid) { //printf("owlib: my_daemon: _exit() pid=%d\n", getpid()); _exit(0); } memset(&act, 0, sizeof(act)); act.sa_handler = catchchild; act.sa_flags = SA_RESTART; sigaction(SIGCHLD, &act, NULL); if (!nochdir) { chdir("/"); } if (!noclose) { close(STDIN_FILENO); close(STDOUT_FILENO); close(STDERR_FILENO); if (dup(dup(open("/dev/null", O_APPEND))) == -1) { perror("dup:"); return -1; } } return 0; }
/* * lower level routine for actually handling a request * deals with data (ping is handled higher) */ void *DataHandler(void *v) { struct handlerdata *hd = v; char *retbuffer = NULL; struct client_msg cm; // the return message #if OW_CYGWIN /* Random generator seem to need initialization for each new thread * If not, seed will be reset and rand() will return 0 the first call. */ { struct timeval tv; gettimeofday(&tv, NULL); srand((unsigned int) tv.tv_usec); } #endif memset(&cm, 0, sizeof(struct client_msg)); cm.version = MakeServerprotocol(OWSERVER_PROTOCOL_VERSION); cm.control_flags = hd->sm.control_flags; // default flag return -- includes persistence state /* Pre-handling for special testing mode to exclude certain messages */ switch ((enum msg_classification) hd->sm.type) { case msg_dirall: case msg_dirallslash: if (Globals.no_dirall) { LEVEL_DEBUG("DIRALL message rejected.") ; hd->sm.type = msg_error; } break; case msg_get: case msg_getslash: if (Globals.no_get) { LEVEL_DEBUG("GET message rejected.") ; hd->sm.type = msg_error; } break; default: break; } /* Now Check message types and only parse valid messages */ switch ((enum msg_classification) hd->sm.type) { // outer switch case msg_read: // good message case msg_write: // good message case msg_dir: // good message case msg_presence: // good message case msg_dirall: // good message case msg_dirallslash: // good message case msg_get: // good message case msg_getslash: // good message if (hd->sm.payload == 0) { /* Bad query -- no data after header */ LEVEL_DEBUG("No payload -- ignore.") ; cm.ret = -EBADMSG; } else { struct parsedname *pn; OWQ_allocate_struct_and_pointer(owq); pn = PN(owq); /* Parse the path string and crete query object */ LEVEL_CALL("DataHandler: parse path=%s", hd->sp.path); cm.ret = BAD( OWQ_create(hd->sp.path, owq) ) ? -1 : 0 ; if ( cm.ret != 0 ) { LEVEL_DEBUG("DataHandler: OWQ_create failed cm.ret=%d", cm.ret); break; } /* Use client persistent settings (temp scale, display mode ...) */ pn->control_flags = hd->sm.control_flags; /* Override some settings from control flags */ if ( (pn->control_flags & UNCACHED) != 0 ) { // client wants uncached pn->state |= ePS_uncached; } if ( (pn->control_flags & ALIAS_REQUEST) == 0 ) { // client wants unaliased pn->state |= ePS_unaliased; } /* Antilooping tags */ pn->tokens = hd->sp.tokens; pn->tokenstring = hd->sp.tokenstring; //printf("Handler: sm.sg=%X pn.state=%X\n", sm.sg, pn.state); //printf("Scale=%s\n", TemperatureScaleName(SGTemperatureScale(sm.sg))); switch ((enum msg_classification) hd->sm.type) { case msg_presence: LEVEL_CALL("Presence message for %s", SAFESTRING(pn->path)); // Basically, if we were able to ParsedName it's here! cm.size = 0; retbuffer = owmalloc( SERIAL_NUMBER_SIZE ) ; if ( retbuffer ) { memcpy( retbuffer, pn->sn, SERIAL_NUMBER_SIZE ) ; cm.payload = SERIAL_NUMBER_SIZE ; } else { cm.payload = 0 ; } cm.ret = 0; // good answer break; case msg_read: LEVEL_CALL("Read message"); retbuffer = ReadHandler(hd, &cm, owq); LEVEL_DEBUG("Read message done value=%p", retbuffer); break; case msg_write: LEVEL_CALL("Write message"); if ( (int) hd->sp.datasize < hd->sm.size ) { cm.ret = -EMSGSIZE; } else { /* set buffer (size already set) */ OWQ_assign_write_buffer( (ASCII *) hd->sp.data, hd->sm.size, hd->sm.offset, owq) ; WriteHandler(hd, &cm, owq); } break; case msg_dir: LEVEL_CALL("Directory message (one at a time)"); DirHandler(hd, &cm, pn); break; case msg_dirall: LEVEL_CALL("Directory message (all at once)"); retbuffer = DirallHandler(hd, &cm, pn); break; case msg_dirallslash: LEVEL_CALL("Directory message (all at once, with directory /)"); retbuffer = DirallslashHandler(hd, &cm, pn); break; case msg_get: if (IsDir(pn)) { LEVEL_CALL("Get -> Directory message (all at once)"); retbuffer = DirallHandler(hd, &cm, pn); } else { LEVEL_CALL("Get -> Read message"); retbuffer = ReadHandler(hd, &cm, owq); } break; case msg_getslash: if (IsDir(pn)) { LEVEL_CALL("Get -> Directory message (all at once)"); retbuffer = DirallslashHandler(hd, &cm, pn); } else { LEVEL_CALL("Get -> Read message"); retbuffer = ReadHandler(hd, &cm, owq); } break; default: // never reached LEVEL_CALL("Error: unknown message %d", (int) hd->sm.type); break; } OWQ_destroy(owq); LEVEL_DEBUG("DataHandler: FS_ParsedName_destroy done"); } break; case msg_nop: // "bad" message LEVEL_CALL("NOP message"); cm.ret = 0; break; case msg_size: // no longer used case msg_error: default: // "bad" message cm.ret = -ENOMSG; LEVEL_CALL("No message"); break; } LEVEL_DEBUG("DataHandler: cm.ret=%d", cm.ret); TOCLIENTLOCK(hd); if (cm.ret != -EIO) { ToClient(hd->file_descriptor, &cm, retbuffer); } else { ErrorToClient(hd, &cm) ; } // Signal to PingLoop that we're done. hd->toclient = toclient_complete ; if ( hd->ping_pipe[fd_pipe_write] != FILE_DESCRIPTOR_BAD ) { ignore_result = write( hd->ping_pipe[fd_pipe_write],"X",1) ; //dummy payload } TOCLIENTUNLOCK(hd); if (retbuffer) { owfree(retbuffer); } LEVEL_DEBUG("Finished with client request"); return VOID_RETURN; }