// find a field in the uevent buffer, using vdev_read_file and vdev_sysfs_uevent_get_key // return 0 on success, and set *value and *value_len // return negative on error int vdev_sysfs_uevent_read_key( char const* sysfs_device_path, char const* uevent_key, char** uevent_value, size_t* uevent_value_len ) { int rc = 0; char* uevent_buf = NULL; size_t uevent_len = 0; char* uevent_path = (char*)calloc( strlen(sysfs_device_path) + strlen("/uevent") + 1, 1 ); if( uevent_path == NULL ) { return -ENOMEM; } sprintf( uevent_path, "%s/uevent", sysfs_device_path ); // get uevent rc = vdev_read_file( uevent_path, &uevent_buf, &uevent_len ); if( rc < 0 ) { if( DEBUG ) { fprintf(stderr, "[WARN]: vdev_read_file('%s') rc = %d\n", uevent_path, rc ); } return rc; } // get devtype rc = vdev_sysfs_uevent_get_key( uevent_buf, uevent_len, uevent_key, uevent_value, uevent_value_len ); free( uevent_buf ); free( uevent_path ); return rc; }
// walk up a sysfs device path to find the ancestor device with the given subsystem and devtype // if devtype_name is NULL, match any devtype (only match the subsystem) // return 0 on success // return -ENOMEM on OOM // return -ENOENT if a subsystem or uevent was not found, when one was expected int vdev_sysfs_get_parent_with_subsystem_devtype( char const* sysfs_device_path, char const* subsystem_name, char const* devtype_name, char** devpath, size_t* devpath_len ) { char cur_dir[4097]; char subsystem_path[4097]; char subsystem_link[4097]; char uevent_path[4097]; memset( subsystem_path, 0, 4097 ); memset( subsystem_link, 0, 4097 ); char* tmp = NULL; int rc = 0; char* uevent_buf = NULL; size_t uevent_len = 0; char* devtype = NULL; size_t devtype_len = 0; char* parent_device = NULL; size_t parent_device_len = 0; strcpy( cur_dir, sysfs_device_path ); while( 1 ) { // get parent device rc = vdev_sysfs_get_parent_device( cur_dir, &parent_device, &parent_device_len ); if( rc != 0 ) { break; } // subsystem? sprintf( subsystem_path, "%s/subsystem", parent_device ); memset( subsystem_link, 0, 4096 ); rc = readlink( subsystem_path, subsystem_link, 4096 ); if( rc < 0 ) { rc = -errno; if( rc != -ENOENT ) { fprintf(stderr, "[WARN]: readlink('%s') errno = %d\n", subsystem_path, rc ); } free( parent_device ); parent_device = NULL; return rc; } // get subsystem name... tmp = rindex( subsystem_link, '/' ); if( tmp != NULL && strcmp( tmp + 1, subsystem_name ) != 0 ) { // subsystem does not match // crawl up to the parent strcpy( cur_dir, parent_device ); free( parent_device ); parent_device = NULL; continue; } // subsystem matches... *tmp = 0; if( devtype_name != NULL ) { // get uevent // get DEVTYPE from uevent // make uevent path sprintf( uevent_path, "%s/uevent", parent_device ); // get uevent rc = vdev_read_file( uevent_path, &uevent_buf, &uevent_len ); if( rc < 0 ) { fprintf(stderr, "[WARN]: vdev_read_file('%s') rc = %d\n", uevent_path, rc ); free( parent_device ); parent_device = NULL; return rc; } // get devtype rc = vdev_sysfs_uevent_get_key( uevent_buf, uevent_len, "DEVTYPE", &devtype, &devtype_len ); free( uevent_buf ); if( rc != 0 ) { if( rc == -ENOENT ) { // not found // next parent strcpy( cur_dir, parent_device ); free( parent_device ); parent_device = NULL; continue; } else { free( parent_device ); parent_device = NULL; return rc; } } // matches? if( strcmp( devtype, devtype_name ) == 0 ) { free( devtype ); // this is the path to the device *devpath = parent_device; *devpath_len = strlen( parent_device ); // found! rc = 0; break; } else { // no match. // next device free( devtype ); strcpy( cur_dir, parent_device ); free( parent_device ); parent_device = NULL; continue; } } else { // match only on subsystem *devpath = parent_device; *devpath_len = strlen(parent_device); rc = 0; break; } } return rc; }
// get a uevent from a uevent file // replace newlines with '\0', making the uevent look like it came from the netlink socket // (i.e. so it can be parsed by vdev_linux_parse_request) // return 0 on success // return -ENOMEM on OOM // return -errno on failure to stat or read static int vdev_linux_sysfs_read_uevent( char const* fp_uevent, char** ret_uevent_buf, size_t* ret_uevent_len ) { int rc = 0; struct stat sb; char* uevent_buf = NULL; size_t uevent_buf_len = 0; size_t uevent_len = 0; // get uevent size rc = stat( fp_uevent, &sb ); if( rc != 0 ) { rc = -errno; vdev_error("stat('%s') rc = %d\n", fp_uevent, rc ); return rc; } else { uevent_buf_len = sb.st_size; } // read the uevent if( fp_uevent != NULL ) { uevent_buf = VDEV_CALLOC( char, uevent_buf_len ); if( uevent_buf == NULL ) { return -ENOMEM; } rc = vdev_read_file( fp_uevent, uevent_buf, uevent_buf_len ); if( rc != 0 ) { // failed in this vdev_error("vdev_read_file('%s') rc = %d\n", fp_uevent, rc ); free( uevent_buf ); } else { for( unsigned int i = 0; i < uevent_buf_len; i++ ) { if( uevent_buf[i] == '\n' ) { uevent_buf[i] = '\0'; } } // NOTE: the stat size is an upper-bound. Find the exact number of bytes. for( uevent_len = 0; uevent_len < uevent_buf_len; ) { if( *(uevent_buf + uevent_len) == '\0' ) { break; } uevent_len += strlen( uevent_buf + uevent_len ) + 1; } *ret_uevent_buf = uevent_buf; *ret_uevent_len = uevent_len; } }