1/* 2 * Access helpers for sysfs. 3 * Copyright (c) Mark Lord 2008 4 * 5 * You may use/distribute this freely, under the terms of either 6 * (your choice) the GNU General Public License version 2, 7 * or a BSD style license. 8 */ 9#include <stdlib.h> 10#include <unistd.h> 11#include <string.h> 12#include <stdio.h> 13#include <fcntl.h> 14#include <errno.h> 15#include <dirent.h> 16#include <sys/stat.h> 17#include <linux/types.h> 18 19#include "hdparm.h" 20 21static char *path_append (char *path, const char *new) 22{ 23 char *pathtail = path + strlen(path); 24 25 *pathtail = '/'; 26 strcpy(pathtail+1, new); 27 return pathtail; 28} 29 30static int sysfs_write_attr (char *path, const char *attr, const char *fmt, void *val, int verbose) 31{ 32 FILE *fp; 33 int count = -1, err = 0; 34 char *pathtail = path_append(path, attr); 35 36 fp = fopen(path, "w"); 37 if (!fp) { 38 err = errno; 39 } else if (fmt[0] != '%') { 40 err = EINVAL; 41 } else { 42 switch (fmt[1]) { 43 case 's': 44 count = fprintf(fp, fmt, val); 45 break; 46 case 'd': 47 case 'u': 48 count = fprintf(fp, fmt, *(unsigned int *)val); 49 break; 50 case 'l': 51 if (fmt[2] == 'l') 52 count = fprintf(fp, fmt, *(unsigned long long *)val); 53 else 54 count = fprintf(fp, fmt, *(unsigned long *)val); 55 break; 56 default: 57 errno = EINVAL; 58 } 59 if (count < 0) 60 err = errno; 61 fclose(fp); 62 } 63 if (err && verbose) perror(path); 64 *pathtail = '\0'; 65 return err; 66} 67 68static int sysfs_read_attr (char *path, const char *attr, const char *fmt, void *val1, void *val2, int verbose) 69{ 70 FILE *fp; 71 int count, err = 0; 72 char *pathtail = path_append(path, attr); 73 74 fp = fopen(path, "r"); 75 if (!fp) { 76 err = errno; 77 } else { 78 count = fscanf(fp, fmt, val1, val2); 79 if (count != (val2 ? 2 : 1)) 80 err = (count == EOF) ? errno : EINVAL; 81 fclose(fp); 82 } 83 if (err && verbose) perror(path); 84 *pathtail = '\0'; 85 return err; 86} 87 88static int sysfs_find_dev2 (char *path, dev_t dev, int recurse, int verbose) 89{ 90 DIR *dp; 91 struct dirent *entry; 92 char *pathtail; 93 94 if (!(dp = opendir(path))) { 95 int err = errno; 96 if (verbose) perror(path); 97 return err; 98 } 99 pathtail = path + strlen(path); 100 while ((entry = readdir(dp)) != NULL) { 101 if ((entry->d_type == DT_DIR || entry->d_type == DT_LNK) && entry->d_name[0] != '.') { 102 unsigned int maj, min; 103 sprintf(pathtail, "/%s", entry->d_name); 104 if (sysfs_read_attr(path, "/dev", "%u:%u", &maj, &min, verbose)) 105 min = ~minor(dev); 106 else if (maj != (unsigned)major(dev)) 107 continue; 108 if (min == (unsigned)minor(dev) 109 || (recurse && sysfs_find_dev2(path, dev, recurse - 1, verbose) == 0)) { 110 closedir(dp); 111 return 0; 112 } 113 } 114 } 115 closedir(dp); 116 *pathtail = '\0'; 117 if (verbose) 118 fprintf(stderr, "%u,%u: device not found in /sys\n", major(dev), minor(dev)); 119 return ENOENT; 120} 121 122static int sysfs_find_dev (dev_t dev, char *path, int verbose) 123{ 124 int err, recurse = 1; 125 126 strcpy(path, "/sys/block"); 127 err = sysfs_find_dev2(path, dev, recurse, 0); 128 if (err && verbose) 129 fprintf(stderr, "%s(%u:%u): %s\n", __func__, 130 major(dev), minor(dev), strerror(err)); 131 return err; 132} 133 134static int get_dev_from_fd (int fd, dev_t *dev, int verbose) 135{ 136 struct stat st; 137 138 if (0 != fstat(fd, &st)) { 139 int err = errno; 140 if (verbose) perror(" fstat() failed"); 141 return err; 142 } 143 if (S_ISBLK(st.st_mode) || S_ISCHR(st.st_mode)) 144 *dev = st.st_rdev; 145 else 146 *dev = st.st_dev; 147 return 0; 148} 149 150static int sysfs_find_fd (int fd, char **path_p, int verbose) 151{ 152 static int have_prev = 0; 153 static dev_t prev; 154 static char path[PATH_MAX]; 155 dev_t dev; 156 int err; 157 158 memset(&dev, 0, sizeof(dev)); 159 err = get_dev_from_fd(fd, &dev, verbose); 160 if (!err) { 161 if (have_prev && 0 == memcmp(&dev, &prev, sizeof(dev))) { 162 /*re-use previous path, since dev was unchanged from before */ 163 } else { 164 prev = dev; 165 have_prev = 1; 166 err = sysfs_find_dev(dev, path, verbose); 167 } 168 } 169 if (err) 170 have_prev = 0; 171 else 172 *path_p = path; 173 return err; 174} 175 176int sysfs_get_attr (int fd, const char *attr, const char *fmt, void *val1, void *val2, int verbose) 177{ 178 char *path; 179 int err; 180 181 err = sysfs_find_fd(fd, &path, verbose); 182 if (!err) 183 err = sysfs_read_attr(path, attr, fmt, val1, val2, verbose); 184 return err; 185} 186 187int sysfs_set_attr (int fd, const char *attr, const char *fmt, void *val_p, int verbose) 188{ 189 char *path; 190 int err; 191 192 err = sysfs_find_fd(fd, &path, verbose); 193 if (!err) 194 err = sysfs_write_attr(path, attr, fmt, val_p, verbose); 195 return err; 196} 197