/* * rename_device.c: udev helper to rename ethernet devices. * * Copyright (C) 2006-2009 Red Hat, Inc. All rights reserved. * * This copyrighted material is made available to anyone wishing to use, * modify, copy, or redistribute it subject to the terms and conditions of the * GNU General Public License v.2. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define LOCKFILE "/dev/.rename_device.lock" void sighandler(int dummy) { unlink(LOCKFILE); _exit(1); } struct netdev { char *hwaddr; char *dev; struct netdev *next; }; struct tmp { char *src; char *target; struct tmp *next; }; struct netdev *configs = NULL; struct tmp *tmplist = NULL; #if defined(__s390__) || defined(__s390x__) static int is_cdev(const struct dirent *dent) { char *end = NULL; if (strncmp(dent->d_name,"cdev",4)) return 0; strtoul(dent->d_name+4,&end, 10); if (*end) return 0; return 1; } static inline char *getdev(char *path, char *ent) { char *a, *b, *ret; asprintf(&a,"%s/%s",path,ent); b = canonicalize_file_name(a); ret = strdup(basename(b)); free(b); free(a); return ret; } char *read_subchannels(char *path) { char *tmp, *ret; int n, x; struct dirent **cdevs; if ((n = scandir(path, &cdevs, is_cdev, alphasort)) <= 0) return NULL; ret = getdev(path,cdevs[0]->d_name); for (x = 1 ; x < n ; x++ ) { if (asprintf(&tmp, "%s,%s", ret, getdev(path, cdevs[x]->d_name)) == -1) return NULL; free(ret); ret = tmp; } return ret; } #endif /* * Taken from systemd: * https://github.com/systemd/systemd/blob/master/src/basic/string-util.c#L49 */ char* endswith(const char *s, const char *postfix) { size_t sl, pl; sl = strlen(s); pl = strlen(postfix); if (pl == 0) return (char*) s + sl; if (sl < pl) return NULL; if (memcmp(s + sl - pl, postfix, pl) != 0) return NULL; return (char*) s + sl - pl; } int isCfg(const struct dirent *dent) { if (strncmp(dent->d_name, "ifcfg-",6) || endswith(dent->d_name, ".rpmnew") != NULL || endswith(dent->d_name, ".rpmsave") != NULL || endswith(dent->d_name, ".rpmorig") != NULL || endswith(dent->d_name, ".orig") != NULL || endswith(dent->d_name, ".old") != NULL || endswith(dent->d_name, ".bak") != NULL || endswith(dent->d_name, "~") != NULL) return 0; else return 1; } static inline char *dequote(char *start, char *end) { char *c; //remove comments and trailing whitespace for (c = start; c && *c; c++) { c = strchr(c, '#'); if (!c) break; if (c > start && isblank(*(c-1))) { *c = '\0'; break; } } g_strchomp(start); if (end==NULL) { end=start; while(*end) end++; } if (end > start) end--; if ((*start == '\'' || *start == '\"') && ( *start == *end ) ) { *end='\0'; if (startd_name) == -1) continue; g_file_get_contents(path, &contents, NULL, NULL); if (!contents) continue; lines = g_strsplit(contents,"\n", 0); for (i = 0; lines[i]; i++) { if (g_str_has_prefix(lines[i],"DEVICE=")) { devname = dequote(lines[i] + 7, NULL); /* ignore alias devices */ if (strchr(devname,':')) devname = NULL; } #if defined(__s390__) || defined(__s390x__) if (g_str_has_prefix(lines[i],"SUBCHANNELS=")) { hwaddr = dequote(lines[i] + 12, NULL); } else if (g_str_has_prefix(lines[i],"HWADDR=")) { hwaddr = dequote(lines[i] + 7, NULL); } #else if (g_str_has_prefix(lines[i],"HWADDR=")) { hwaddr = dequote(lines[i] + 7, NULL); } #endif if (g_str_has_prefix(lines[i],"VLAN=yes")) { vlan=1; } } if (!devname || !hwaddr || vlan) { g_free(contents); g_strfreev(lines); continue; } tmpdev = calloc(1, sizeof(struct netdev)); tmpdev->dev = g_strstrip(g_strdup(devname)); tmpdev->hwaddr = g_strstrip(g_strdup(hwaddr)); if (ret) tmpdev->next = ret; ret = tmpdev; g_free(contents); g_strfreev(lines); } free(cfgs); return ret; } char *get_hwaddr(char *device) { char *path = NULL; char *contents = NULL; #if defined(__s390__) || defined(__s390x__) if (asprintf(&path, "/sys/class/net/%s/device/.", device) == -1) return NULL; contents = read_subchannels(path); if (contents == NULL) { if (asprintf(&path, "/sys/class/net/%s/address", device) == -1) return NULL; g_file_get_contents(path, &contents, NULL, NULL); } #else if (asprintf(&path, "/sys/class/net/%s/address", device) == -1) return NULL; g_file_get_contents(path, &contents, NULL, NULL); #endif free(path); return g_strstrip(contents); } char *get_config_by_hwaddr(char *hwaddr, char *current) { struct netdev *config; char *first = NULL; if (!hwaddr) return NULL; for (config = configs; config; config = config->next) { if (strcasecmp(config->hwaddr, hwaddr) != 0) continue; if (!current || !strcasecmp(config->dev, current)) return config->dev; if (!first) first = config->dev; } return first; } /* Let's check kernel cmdline and also process ifname= entries * as they are documented in dracut.cmdline(7) * Example: ifname=test:aa:bb:cc:dd:ee:ff */ char *get_cmdline_by_hwaddr(char *hwaddr) { gchar *contents; gchar **entries; int i; char *name = NULL; g_file_get_contents("/proc/cmdline", &contents, NULL, NULL); entries = g_strsplit(contents," ", 0); for (i = 0; entries[i]; i++) { char *c = (char *) entries[i]; char *n; if (!g_str_has_prefix((gchar *)c,"ifname=")) continue; c = strstr(c, "="); if (c == NULL) continue; c++; n=c; c = strstr(c, ":"); if (c == NULL) continue; *c = '\0'; c++; if (!strcasecmp(c, hwaddr)) { name = strdup(n); break; } } g_free(contents); g_strfreev(entries); return name; } void take_lock() { int count = 0; int lockfd; ssize_t ignored_retval __attribute__((unused)); while (1) { lockfd = open(LOCKFILE, O_RDWR|O_CREAT|O_EXCL, 0644); if (lockfd != -1) { char buf[32]; snprintf(buf,32,"%d\n",getpid()); ignored_retval = write(lockfd,buf,strlen(buf)); close(lockfd); break; } else if (errno == EACCES) break; count++; /* If we've slept for 20 seconds, break the lock. */ if (count >= 200) { int fd; char buf[32]; int pid; fd = open(LOCKFILE, O_RDONLY); if (fd == -1) break; ignored_retval = read(fd,buf,32); close(fd); pid = atoi(buf); if (pid && pid != 1) { kill(pid,SIGKILL); } } usleep(100000); continue; } return; } int main(int argc, char **argv) { char *src, *target, *hw; struct timeval tv; gettimeofday(&tv, NULL); srand(tv.tv_usec); take_lock(); signal(SIGSEGV,sighandler); signal(SIGKILL,sighandler); signal(SIGTERM,sighandler); signal(SIGSEGV,sighandler); signal(SIGALRM,sighandler); alarm(10); src = getenv("INTERFACE"); if (!src) goto out_unlock; configs = get_configs(); hw = get_hwaddr(src); if (!hw) goto out_unlock; target = get_cmdline_by_hwaddr(hw); if (!target) target = get_config_by_hwaddr(hw, src); if (!target) goto out_unlock; printf("%s", target); out_unlock: unlink(LOCKFILE); exit(0); }