/*
* 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 <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <ctype.h>
#include <sys/ioctl.h>
#include <sys/time.h>
#include <sys/types.h>
#include <linux/sockios.h>
#include <net/if.h>
#include <glib.h>
#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 (start<end) start++;
}
return start;
}
struct netdev *get_configs() {
int ncfgs = 0;
struct netdev *ret, *tmpdev;
struct dirent **cfgs;
int x;
ret = NULL;
if ((ncfgs = scandir("/etc/sysconfig/network-scripts",&cfgs,
isCfg, alphasort)) == -1) {
return ret;
}
for (x = 0; x < ncfgs; x++ ) {
char *path;
char *devname, *hwaddr;
int vlan;
gchar *contents, **lines;
int i;
devname = hwaddr = contents = NULL;
lines = NULL;
vlan = 0;
if (asprintf(&path,"/etc/sysconfig/network-scripts/%s",
cfgs[x]->d_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;
}
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_config_by_hwaddr(hw, src);
if (!target)
goto out_unlock;
printf("%s", target);
out_unlock:
unlink(LOCKFILE);
exit(0);
}