1/* 2 * Copyright (C) 2004-2006 Kay Sievers <kay@vrfy.org> 3 * Copyright (C) 2006 Hannes Reinecke <hare@suse.de> 4 * 5 * This program is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License as published by the 7 * Free Software Foundation version 2 of the License. 8 * 9 * This program is distributed in the hope that it will be useful, but 10 * WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 * General Public License for more details. 13 * 14 * You should have received a copy of the GNU General Public License along 15 * with this program; if not, write to the Free Software Foundation, Inc., 16 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 17 * 18 */ 19 20#include <stdlib.h> 21#include <stddef.h> 22#include <stdarg.h> 23#include <string.h> 24#include <stdio.h> 25#include <unistd.h> 26#include <getopt.h> 27#include <errno.h> 28#include <dirent.h> 29#include <fcntl.h> 30#include <syslog.h> 31#include <sys/stat.h> 32#include <sys/types.h> 33 34#define PATH_SIZE 512 35 36static int verbose; 37static int dry_run; 38 39void log_message(int priority, const char *format, ...) 40{ 41 va_list args; 42 43 va_start(args, format); 44 vsyslog(priority, format, args); 45 va_end(args); 46} 47 48#undef err 49#define err(format, arg...) \ 50 do { \ 51 log_message(LOG_ERR ,"%s: " format ,__FUNCTION__ ,## arg); \ 52 } while (0) 53 54#undef info 55#define info(format, arg...) \ 56 do { \ 57 log_message(LOG_INFO ,"%s: " format ,__FUNCTION__ ,## arg); \ 58 } while (0) 59 60#ifdef DEBUG 61#undef dbg 62#define dbg(format, arg...) \ 63 do { \ 64 log_message(LOG_DEBUG ,"%s: " format ,__FUNCTION__ ,## arg); \ 65 } while (0) 66#else 67#define dbg(...) do {} while(0) 68#endif 69 70 71static void trigger_uevent(const char *devpath) 72{ 73 char filename[PATH_SIZE]; 74 int fd; 75 76 strlcpy(filename, "/sys", sizeof(filename)); 77 strlcat(filename, devpath, sizeof(filename)); 78 strlcat(filename, "/uevent", sizeof(filename)); 79 80 if (verbose) 81 printf("%s\n", devpath); 82 83 if (dry_run) 84 return; 85 86 fd = open(filename, O_WRONLY); 87 if (fd < 0) { 88 dbg("error on opening %s: %s\n", filename, strerror(errno)); 89 return; 90 } 91 92 if (write(fd, "add", 3) < 0) 93 info("error on triggering %s: %s\n", filename, strerror(errno)); 94 95 close(fd); 96} 97 98static int sysfs_resolve_link(char *devpath, size_t size) 99{ 100 char link_path[PATH_SIZE]; 101 char link_target[PATH_SIZE]; 102 int len; 103 int i; 104 int back; 105 106 strlcpy(link_path, "/sys", sizeof(link_path)); 107 strlcat(link_path, devpath, sizeof(link_path)); 108 len = readlink(link_path, link_target, sizeof(link_target)); 109 if (len <= 0) 110 return -1; 111 link_target[len] = '\0'; 112 dbg("path link '%s' points to '%s'", devpath, link_target); 113 114 for (back = 0; strncmp(&link_target[back * 3], "../", 3) == 0; back++) 115 ; 116 dbg("base '%s', tail '%s', back %i", devpath, &link_target[back * 3], back); 117 for (i = 0; i <= back; i++) { 118 char *pos = strrchr(devpath, '/'); 119 120 if (pos == NULL) 121 return -1; 122 pos[0] = '\0'; 123 } 124 dbg("after moving back '%s'", devpath); 125 strlcat(devpath, "/", size); 126 strlcat(devpath, &link_target[back * 3], size); 127 return 0; 128} 129 130 131static int device_list_insert(const char *path) 132{ 133 char filename[PATH_SIZE]; 134 char devpath[PATH_SIZE]; 135 struct stat statbuf; 136 137 dbg("add '%s'" , path); 138 139 /* we only have a device, if we have an uevent file */ 140 strlcpy(filename, path, sizeof(filename)); 141 strlcat(filename, "/uevent", sizeof(filename)); 142 if (stat(filename, &statbuf) < 0) 143 return -1; 144 if (!(statbuf.st_mode & S_IWUSR)) 145 return -1; 146 147 strlcpy(devpath, &path[4], sizeof(devpath)); 148 149 /* resolve possible link to real target */ 150 if (lstat(path, &statbuf) < 0) 151 return -1; 152 if (S_ISLNK(statbuf.st_mode)) 153 if (sysfs_resolve_link(devpath, sizeof(devpath)) != 0) 154 return -1; 155 156 trigger_uevent(devpath); 157 return 0; 158} 159 160 161static void scan_subsystem(const char *subsys) 162{ 163 char base[PATH_SIZE]; 164 DIR *dir; 165 struct dirent *dent; 166 167 strlcpy(base, "/sys/", sizeof(base)); 168 strlcat(base, subsys, sizeof(base)); 169 170 dir = opendir(base); 171 if (dir != NULL) { 172 for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) { 173 char dirname[PATH_SIZE]; 174 DIR *dir2; 175 struct dirent *dent2; 176 177 if (dent->d_name[0] == '.') 178 continue; 179 180 strlcpy(dirname, base, sizeof(dirname)); 181 strlcat(dirname, "/", sizeof(dirname)); 182 strlcat(dirname, dent->d_name, sizeof(dirname)); 183 strlcat(dirname, "/devices", sizeof(dirname)); 184 185 /* look for devices */ 186 dir2 = opendir(dirname); 187 if (dir2 != NULL) { 188 for (dent2 = readdir(dir2); dent2 != NULL; dent2 = readdir(dir2)) { 189 char dirname2[PATH_SIZE]; 190 191 if (dent2->d_name[0] == '.') 192 continue; 193 194 strlcpy(dirname2, dirname, sizeof(dirname2)); 195 strlcat(dirname2, "/", sizeof(dirname2)); 196 strlcat(dirname2, dent2->d_name, sizeof(dirname2)); 197 device_list_insert(dirname2); 198 } 199 closedir(dir2); 200 } 201 } 202 closedir(dir); 203 } 204} 205 206static void scan_block(void) 207{ 208 char base[PATH_SIZE]; 209 DIR *dir; 210 struct dirent *dent; 211 212 strlcpy(base, "/sys/block", sizeof(base)); 213 214 dir = opendir(base); 215 if (dir != NULL) { 216 for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) { 217 char dirname[PATH_SIZE]; 218 DIR *dir2; 219 struct dirent *dent2; 220 221 if (dent->d_name[0] == '.') 222 continue; 223 224 strlcpy(dirname, base, sizeof(dirname)); 225 strlcat(dirname, "/", sizeof(dirname)); 226 strlcat(dirname, dent->d_name, sizeof(dirname)); 227 if (device_list_insert(dirname) != 0) 228 continue; 229 230 /* look for partitions */ 231 dir2 = opendir(dirname); 232 if (dir2 != NULL) { 233 for (dent2 = readdir(dir2); dent2 != NULL; dent2 = readdir(dir2)) { 234 char dirname2[PATH_SIZE]; 235 236 if (dent2->d_name[0] == '.') 237 continue; 238 239 if (!strcmp(dent2->d_name,"device")) 240 continue; 241 242 strlcpy(dirname2, dirname, sizeof(dirname2)); 243 strlcat(dirname2, "/", sizeof(dirname2)); 244 strlcat(dirname2, dent2->d_name, sizeof(dirname2)); 245 device_list_insert(dirname2); 246 } 247 closedir(dir2); 248 } 249 } 250 closedir(dir); 251 } 252} 253 254static void scan_class(void) 255{ 256 char base[PATH_SIZE]; 257 DIR *dir; 258 struct dirent *dent; 259 260 strlcpy(base, "/sys/class", sizeof(base)); 261 262 dir = opendir(base); 263 if (dir != NULL) { 264 for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) { 265 char dirname[PATH_SIZE]; 266 DIR *dir2; 267 struct dirent *dent2; 268 269 if (dent->d_name[0] == '.') 270 continue; 271 272 strlcpy(dirname, base, sizeof(dirname)); 273 strlcat(dirname, "/", sizeof(dirname)); 274 strlcat(dirname, dent->d_name, sizeof(dirname)); 275 dir2 = opendir(dirname); 276 if (dir2 != NULL) { 277 for (dent2 = readdir(dir2); dent2 != NULL; dent2 = readdir(dir2)) { 278 char dirname2[PATH_SIZE]; 279 280 if (dent2->d_name[0] == '.') 281 continue; 282 283 if (!strcmp(dent2->d_name, "device")) 284 continue; 285 286 strlcpy(dirname2, dirname, sizeof(dirname2)); 287 strlcat(dirname2, "/", sizeof(dirname2)); 288 strlcat(dirname2, dent2->d_name, sizeof(dirname2)); 289 device_list_insert(dirname2); 290 } 291 closedir(dir2); 292 } 293 } 294 closedir(dir); 295 } 296} 297 298int main(int argc, char *argv[], char *envp[]) 299{ 300 char base[PATH_SIZE]; 301 struct stat statbuf; 302 int failed = 0; 303 int option; 304 305 openlog("udevtrigger", LOG_PID | LOG_CONS, LOG_DAEMON); 306 307 while (1) { 308 option = getopt(argc, argv, "vnh"); 309 if (option == -1) 310 break; 311 312 switch (option) { 313 case 'v': 314 verbose = 1; 315 break; 316 case 'n': 317 dry_run = 1; 318 break; 319 case 'h': 320 printf("Usage: udevtrigger OPTIONS\n" 321 " -v print the list of devices while running\n" 322 " -n do not actually trigger the events\n" 323 " -h print this text\n" 324 "\n"); 325 goto exit; 326 default: 327 goto exit; 328 } 329 } 330 331 332 /* if we have /sys/subsystem, forget all the old stuff */ 333 scan_subsystem("bus"); 334 scan_class(); 335 336 /* scan "block" if it isn't a "class" */ 337 strlcpy(base, "/sys/class/block", sizeof(base)); 338 if (stat(base, &statbuf) != 0) 339 scan_block(); 340 341exit: 342 343 closelog(); 344 return 0; 345} 346