OSDN Git Service

Update to v30-pre9
[android-x86/external-wireless-tools.git] / wireless_tools / ifrename.c
index 7d4ab5b..9c6c806 100644 (file)
@@ -1,14 +1,14 @@
 /*
  *     Wireless Tools
  *
- *             Jean II - HPL 04
+ *             Jean II - HPL 04 -> 07
  *
  * Main code for "ifrename". This is tool allows to rename network
  * interfaces based on various criteria (not only wireless).
  * You need to link this code against "iwlib.c" and "-lm".
  *
  * This file is released under the GPL license.
- *     Copyright (c) 2004 Jean Tourrilhes <jt@hpl.hp.com>
+ *     Copyright (c) 2007 Jean Tourrilhes <jt@hpl.hp.com>
  */
 
 /* 
  * Subject to the Gnu Public License, version 2.  
  * TODO: make it support token ring etc.
  * $Id: nameif.c,v 1.3 2003/03/06 23:26:52 ecki Exp $
- * Add hotplug compatibility : ifname -i eth0. Jean II - 03.12.03
- * Add MAC address wildcard : 01:23:45:*. Jean II - 03.12.03
- * Add interface name wildcard : wlan*. Jean II - 03.12.03
- * Add interface name probing for modular systems. Jean II - 18.02.03
  * -------------------------------------------------------
  *
- *     The last 4 patches never made it into the regular version of
- * 'nameif', and had some 'issues', which is the reason of this rewrite.
+ *     It started with a series of patches to nameif which never made
+ * into the regular version, and had some architecural 'issues' with
+ * those patches, which is the reason of this rewrite.
  *     Difference with standard 'nameif' :
  *     o 'nameif' has only a single selector, the interface MAC address.
  *     o Modular selector architecture, easily add new selectors.
+ *     o Wide range of selector, including sysfs and sysfs symlinks...
  *     o hotplug invocation support.
  *     o module loading support.
  *     o MAC address wildcard.
  *     o Interface name wildcard ('eth*' or 'wlan*').
+ *     o Non-Ethernet MAC addresses (any size, not just 48 bits)
  */
 
 /***************************** INCLUDES *****************************/
@@ -81,11 +80,14 @@ const int SELECT_BASEADDR   = 7;    /* Select by HW Base Address */
 const int SELECT_IRQ           = 8;    /* Select by HW Irq line */
 const int SELECT_INTERRUPT     = 9;    /* Select by HW Irq line */
 const int SELECT_IWPROTO       = 10;   /* Select by Wireless Protocol */
-const int SELECT_PCMCIASLOT    = 11;   /* Select by Wireless Protocol */
-#define SELECT_NUM             12
+const int SELECT_PCMCIASLOT    = 11;   /* Select by Pcmcia Slot */
+const int SELECT_SYSFS         = 12;   /* Select by sysfs file */
+const int SELECT_PREVNAME      = 13;   /* Select by previous interface name */
+#define SELECT_NUM             14
 
 #define HAS_MAC_EXACT  1
 #define HAS_MAC_FILTER 2
+#define MAX_MAC_LEN    16      /* Maximum lenght of MAC address */
 
 const struct ether_addr        zero_mac = {{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
 
@@ -98,6 +100,7 @@ const struct option long_opt[] =
   {"interface", 1, NULL, 'i' },
   {"newname", 1, NULL, 'n' },
   {"takeover", 0, NULL, 't' },
+  {"udev", 0, NULL, 'u' },
   {"version", 0, NULL, 'v' },
   {"verbose", 0, NULL, 'V' },
   {NULL, 0, NULL, '\0' },
@@ -107,6 +110,29 @@ const struct option long_opt[] =
 #define PCMCIA_STAB1   "/var/lib/pcmcia/stab"
 #define PCMCIA_STAB2   "/var/run/stab"
 
+/* Max number of sysfs file types we support */
+#define SYSFS_MAX_FILE 8
+
+/* Userspace headers lag, fix that... */
+#ifndef ARPHRD_IEEE1394
+#define ARPHRD_IEEE1394 24
+#endif
+#ifndef ARPHRD_EUI64
+#define ARPHRD_EUI64 27
+#endif
+#ifndef ARPHRD_IRDA
+#define ARPHRD_IRDA 783
+#endif
+
+/* Length of various non-standard MAC addresses */
+const int      weird_mac_len[][2] =
+{
+  { ARPHRD_IEEE1394, 8 },
+  { ARPHRD_EUI64, 8 },
+  { ARPHRD_IRDA, 4 },
+};
+const int weird_mac_len_num = sizeof(weird_mac_len) / sizeof(weird_mac_len[0]);
+
 /****************************** TYPES ******************************/
 
 /* Cut'n'paste from ethtool.h */
@@ -136,13 +162,16 @@ typedef struct if_mapping
 
   /* Name of this interface */
   char                 ifname[IFNAMSIZ+1];
+  char *               sysfs_devpath;
+  int                  sysfs_devplen;
 
   /* Selectors for this interface */
   int                  active[SELECT_NUM];     /* Selectors active */
 
   /* Selector data */
-  struct ether_addr    mac;                    /* Exact MAC address, hex */
-  char                 mac_filter[6*3 + 1];    /* WildCard, ascii */
+  unsigned char                mac[MAX_MAC_LEN];       /* Exact MAC address, hex */
+  int                  mac_len;                /* Length (usually 6) */
+  char                 mac_filter[16*3 + 1];   /* WildCard, ascii */
   unsigned short       hw_type;                /* Link/ARP type */
   char                 driver[32];             /* driver short name */
   char         bus_info[ETHTOOL_BUSINFO_LEN];  /* Bus info for this IF. */
@@ -151,13 +180,23 @@ typedef struct if_mapping
   unsigned char                irq;                    /* HW irq line */
   char                 iwproto[IFNAMSIZ + 1];  /* Wireless/protocol name */
   int                  pcmcia_slot;            /* Pcmcia slot */
-} if_mapping; 
+  char *               sysfs[SYSFS_MAX_FILE];  /* sysfs selectors */
+  char                 prevname[IFNAMSIZ+1];   /* previous interface name */
+} if_mapping;
+
+/* Extra parsing information when adding a mapping */
+typedef struct add_extra
+{ 
+  char *               modif_pos;              /* Descriptor modifier */
+  size_t               modif_len;
+} parsing_extra;
 
 /* Prototype for adding a selector to a mapping. Return -1 if invalid value. */
 typedef int (*mapping_add)(struct if_mapping * ifnode,
                           int *                active,
                           char *               pos,
                           size_t               len,
+                          struct add_extra *   extra,
                           int                  linenum);
 
 /* Prototype for comparing the selector of two mapping. Return 0 if matches. */
@@ -178,6 +217,15 @@ typedef struct mapping_selector
   mapping_get  get_fn;
 } mapping_selector;
 
+/* sysfs global data */
+typedef struct sysfs_metadata
+{
+  char *               root;                   /* Root of the sysfs */
+  int                  rlen;                   /* Size of it */
+  int                  filenum;                /* Number of files */
+  char *               filename[SYSFS_MAX_FILE];       /* Name of files */
+} sysfs_metadata;
+
 /**************************** PROTOTYPES ****************************/
 
 static int
@@ -185,6 +233,7 @@ static int
                       int *                    active,
                       char *                   pos,
                       size_t                   len,
+                      struct add_extra *       extra,
                       int                      linenum);
 static int
        mapping_cmpmac(struct if_mapping *      ifnode,
@@ -199,6 +248,7 @@ static int
                       int *                    active,
                       char *                   pos,
                       size_t                   len,
+                      struct add_extra *       extra,
                       int                      linenum);
 static int
        mapping_cmparp(struct if_mapping *      ifnode,
@@ -213,6 +263,7 @@ static int
                          int *                 active,
                          char *                pos,
                          size_t                len,
+                         struct add_extra *    extra,
                          int                   linenum);
 static int
        mapping_cmpdriver(struct if_mapping *   ifnode,
@@ -222,6 +273,7 @@ static int
                           int *                active,
                           char *               pos,
                           size_t               len,
+                          struct add_extra *   extra,
                           int                  linenum);
 static int
        mapping_cmpbusinfo(struct if_mapping *  ifnode,
@@ -231,6 +283,7 @@ static int
                            int *               active,
                            char *              pos,
                            size_t              len,
+                           struct add_extra *  extra,
                            int                 linenum);
 static int
        mapping_cmpfirmware(struct if_mapping * ifnode,
@@ -245,6 +298,7 @@ static int
                            int *               active,
                            char *              pos,
                            size_t              len,
+                           struct add_extra *  extra,
                            int                 linenum);
 static int
        mapping_cmpbaseaddr(struct if_mapping * ifnode,
@@ -254,6 +308,7 @@ static int
                       int *                    active,
                       char *                   pos,
                       size_t                   len,
+                      struct add_extra *       extra,
                       int                      linenum);
 static int
        mapping_cmpirq(struct if_mapping *      ifnode,
@@ -268,6 +323,7 @@ static int
                           int *                active,
                           char *               pos,
                           size_t               len,
+                          struct add_extra *   extra,
                           int                  linenum);
 static int
        mapping_cmpiwproto(struct if_mapping *  ifnode,
@@ -282,6 +338,7 @@ static int
                              int *                     active,
                              char *                    pos,
                              size_t                    len,
+                             struct add_extra *        extra,
                              int                       linenum);
 static int
        mapping_cmppcmciaslot(struct if_mapping *       ifnode,
@@ -291,6 +348,36 @@ static int
                              const char *              ifname,
                              struct if_mapping *       target,
                              int                       flag);
+static int
+       mapping_addsysfs(struct if_mapping *    ifnode,
+                        int *                  active,
+                        char *                 pos,
+                        size_t                 len,
+                        struct add_extra *     extra,
+                        int                    linenum);
+static int
+       mapping_cmpsysfs(struct if_mapping *    ifnode,
+                        struct if_mapping *    target);
+static int
+       mapping_getsysfs(int                    skfd,
+                        const char *           ifname,
+                        struct if_mapping *    target,
+                        int                    flag);
+static int
+       mapping_addprevname(struct if_mapping * ifnode,
+                          int *                active,
+                          char *               pos,
+                          size_t               len,
+                          struct add_extra *   extra,
+                          int                  linenum);
+static int
+       mapping_cmpprevname(struct if_mapping * ifnode,
+                          struct if_mapping *  target);
+static int
+       mapping_getprevname(int                 skfd,
+                          const char *         ifname,
+                          struct if_mapping *  target,
+                          int                  flag);
 
 /**************************** VARIABLES ****************************/
 
@@ -321,6 +408,10 @@ const struct mapping_selector      selector_list[] =
   { "iwproto", &mapping_addiwproto, &mapping_cmpiwproto, &mapping_getiwproto },
   /* Pcmcia slot from cardmgr */
   { "pcmciaslot", &mapping_addpcmciaslot, &mapping_cmppcmciaslot, &mapping_getpcmciaslot },
+  /* sysfs file (udev emulation) */
+  { "sysfs", &mapping_addsysfs, &mapping_cmpsysfs, &mapping_getsysfs },
+  /* previous interface name */
+  { "prevname", &mapping_addprevname, &mapping_cmpprevname, &mapping_getprevname },
   /* The Terminator */
   { NULL, NULL, NULL, NULL },
 };
@@ -329,16 +420,40 @@ const int selector_num = sizeof(selector_list)/sizeof(selector_list[0]);
 /* List of active selectors */
 int    selector_active[SELECT_NUM];    /* Selectors active */
 
+/*
+ * All the following flags are controlled by the command line switches...
+ * It's a bit hackish to have them all as global, so maybe we should pass
+ * them in a big struct as function arguments... More complex and
+ * probably not worth it ?
+ */
+
+/* Invocation type */
+int    print_newname = 0;
+char * new_name = NULL;
+
 /* Takeover support */
-int    force_takeover = 0;     /* Takeover name from other interface */
+int    force_takeover = 0;     /* Takeover name from other interfaces */
 int    num_takeover = 0;       /* Number of takeover done */
 
+/* Number of mapping matched */
+int    num_mapping_match = 0;
+
 /* Dry-run support */
 int    dry_run = 0;            /* Just print new name, don't rename */
 
 /* Verbose support (i.e. debugging) */
 int    verbose = 0;
 
+/* udev output support (print new DEVPATH) */
+int    udev_output = 0;
+
+/* sysfs global data */
+struct sysfs_metadata  sysfs_global =
+{
+  NULL, 0,
+  0, { NULL, NULL, NULL, NULL, NULL },
+};
+
 /******************** INTERFACE NAME MANAGEMENT ********************/
 /*
  * Bunch of low level function for managing interface names.
@@ -362,8 +477,8 @@ if_match_ifname(const char *        pattern,
   int          n;
   int          ret;
 
-  /* Check for a wildcard (converted from '*' to '%d' in mapping_create()) */
-  p = strstr(pattern, "%d");
+  /* Check for a wildcard */
+  p = strchr(pattern, '*');
 
   /* No wildcard, simple comparison */
   if(p == NULL)
@@ -386,7 +501,7 @@ if_match_ifname(const char *        pattern,
   while(isdigit(*v));
 
   /* Pattern suffix */
-  p += 2;
+  p += 1;
 
   /* Compare suffixes */
   return(strcmp(p, v));
@@ -452,6 +567,7 @@ if_set_name(int                     skfd,
            char *              retname)
 {
   struct ifreq ifr;
+  char *       star;
   int          ret;
 
   /* The kernel doesn't check is the interface already has the correct
@@ -475,6 +591,22 @@ if_set_name(int                    skfd,
   strncpy(ifr.ifr_name, oldname, IFNAMSIZ); 
   strncpy(ifr.ifr_newname, newname, IFNAMSIZ); 
 
+  /* Check for wildcard interface name, such as 'eth*' or 'wlan*'...
+   * This require specific kernel support (2.6.2-rc1 and later).
+   * We externally use '*', but the kernel doesn't know about that,
+   * so convert it to something it knows about... */
+  star = strchr(newname, '*');
+  if(star != NULL)
+    {
+      int      slen = star - newname;
+      /* Replace '*' with '%d' in the new buffer */
+      star = ifr.ifr_newname + slen;
+      /* Size was checked in process_rename() and mapping_create() */
+      memmove(star + 2, star + 1, IFNAMSIZ - slen - 2);
+      star[0] = '%';
+      star[1] = 'd';
+    }
+
   /* Do it */
   ret = ioctl(skfd, SIOCSIFNAME, &ifr);
 
@@ -515,14 +647,18 @@ mapping_addmac(struct if_mapping *        ifnode,
               int *                    active,
               char *                   string,
               size_t                   len,
+              struct add_extra *       extra,
               int                      linenum)
 {
   size_t       n;
 
+  /* Avoid "Unused parameter" warning */
+  extra = extra;
+
   /* Verify validity of string */
   if(len >= sizeof(ifnode->mac_filter))
     { 
-      fprintf(stderr, "MAC address too long at line %d\n", linenum);  
+      fprintf(stderr, "Error : MAC address too long at line %d\n", linenum);  
       return(-1);
     }
   n = strspn(string, "0123456789ABCDEFabcdef:*"); 
@@ -547,7 +683,9 @@ mapping_addmac(struct if_mapping *  ifnode,
   else
     {
       /* Not a wildcard : "01:23:45:67:89:AB" */
-      if(iw_ether_aton(ifnode->mac_filter, &ifnode->mac) != 1)
+      ifnode->mac_len = iw_mac_aton(ifnode->mac_filter,
+                                   ifnode->mac, MAX_MAC_LEN);
+      if(ifnode->mac_len == 0)
        {
          fprintf(stderr, "Error: Invalid MAC address `%s' at line %d\n",
                  ifnode->mac_filter, linenum);
@@ -555,7 +693,7 @@ mapping_addmac(struct if_mapping *  ifnode,
        }
 
       /* Check that it's not NULL */
-      if(!memcmp(&ifnode->mac, &zero_mac, 6))
+      if((ifnode->mac_len == 6) && (!memcmp(&ifnode->mac, &zero_mac, 6)))
        {
          fprintf(stderr,
                  "Warning: MAC address is null at line %d, this is dangerous...\n",
@@ -590,8 +728,8 @@ mapping_cmpmac(struct if_mapping *  ifnode,
     return(fnmatch(ifnode->mac_filter, target->mac_filter, FNM_CASEFOLD));
   else
     /* Exact matching, in hex */
-    return(memcmp(&ifnode->mac.ether_addr_octet, &target->mac.ether_addr_octet,
-                 6));
+    return((ifnode->mac_len != target->mac_len) ||
+          memcmp(ifnode->mac, target->mac, ifnode->mac_len));
 }
 
 /*------------------------------------------------------------------*/
@@ -604,10 +742,14 @@ mapping_getmac(int                        skfd,
               struct if_mapping *      target,
               int                      flag)
 {
-  int  ret;
+  struct ifreq ifr;
+  int          ret;
+  int          i;
 
-  /* Extract MAC address */
-  ret = iw_get_mac_addr(skfd, ifname, &target->mac, &target->hw_type);
+  /* Get MAC address */
+  bzero(&ifr, sizeof(struct ifreq));
+  strncpy(ifr.ifr_name, ifname, IFNAMSIZ);
+  ret = ioctl(skfd, SIOCGIFHWADDR, &ifr);
   if(ret < 0)
     {
       fprintf(stderr, "Error: Can't read MAC address on interface `%s' : %s\n",
@@ -615,11 +757,25 @@ mapping_getmac(int                        skfd,
       return(-1);
     }
 
+  /* Extract ARP type */
+  target->hw_type = ifr.ifr_hwaddr.sa_family;
+  /* Calculate address length */
+  target->mac_len = 6;
+  for(i = 0; i < weird_mac_len_num; i++)
+    if(weird_mac_len[i][0] == ifr.ifr_hwaddr.sa_family)
+      {
+       target->mac_len = weird_mac_len[i][1];
+       break;
+      }
+  /* Extract MAC address bytes */
+  memcpy(target->mac, ifr.ifr_hwaddr.sa_data, target->mac_len);
+
   /* Check the type of comparison */
   if((flag == HAS_MAC_FILTER) || verbose)
     {
       /* Convert to ASCII */
-      iw_ether_ntop(&target->mac, target->mac_filter);
+      iw_mac_ntop(target->mac, target->mac_len,
+                 target->mac_filter, sizeof(target->mac_filter));
     }
 
   target->active[SELECT_MAC] = flag;
@@ -642,11 +798,15 @@ mapping_addarp(struct if_mapping *        ifnode,
               int *                    active,
               char *                   string,
               size_t                   len,
+              struct add_extra *       extra,
               int                      linenum)
 {
   size_t       n;
   unsigned int type;
 
+  /* Avoid "Unused parameter" warning */
+  extra = extra;
+
   /* Verify validity of string, convert to int */
   n = strspn(string, "0123456789"); 
   if((n < len) || (sscanf(string, "%d", &type) != 1))
@@ -705,12 +865,16 @@ mapping_adddriver(struct if_mapping *     ifnode,
                  int *                 active,
                  char *                string,
                  size_t                len,
+                 struct add_extra *    extra,
                  int                   linenum)
 {
+  /* Avoid "Unused parameter" warning */
+  extra = extra;
+
   /* Plain string, minimal verification */
   if(len >= sizeof(ifnode->driver))
     { 
-      fprintf(stderr, "Driver name too long at line %d\n", linenum);  
+      fprintf(stderr, "Error: Driver name too long at line %d\n", linenum);  
       return(-1);
     }
 
@@ -750,12 +914,16 @@ mapping_addbusinfo(struct if_mapping *    ifnode,
                   int *                active,
                   char *               string,
                   size_t               len,
+                  struct add_extra *   extra,
                   int                  linenum)
 {
 #if 0
   size_t       n;
 #endif
 
+  /* Avoid "Unused parameter" warning */
+  extra = extra;
+
   /* Verify validity of string */
   if(len >= sizeof(ifnode->bus_info))
     { 
@@ -809,8 +977,12 @@ mapping_addfirmware(struct if_mapping *    ifnode,
                    int *               active,
                    char *              string,
                    size_t              len,
+                   struct add_extra *  extra,
                    int                 linenum)
 {
+  /* Avoid "Unused parameter" warning */
+  extra = extra;
+
   /* Verify validity of string */
   if(len >= sizeof(ifnode->fw_version))
     { 
@@ -913,11 +1085,15 @@ mapping_addbaseaddr(struct if_mapping *  ifnode,
                    int *               active,
                    char *              string,
                    size_t              len,
+                   struct add_extra *  extra,
                    int                 linenum)
 {
   size_t       n;
   unsigned int address;
 
+  /* Avoid "Unused parameter" warning */
+  extra = extra;
+
   /* Verify validity of string */
   n = strspn(string, "0123456789ABCDEFabcdefx"); 
   if((n < len) || (sscanf(string, "0x%X", &address) != 1))
@@ -963,11 +1139,15 @@ mapping_addirq(struct if_mapping *       ifnode,
               int *                    active,
               char *                   string,
               size_t                   len,
+              struct add_extra *       extra,
               int                      linenum)
 {
   size_t       n;
   unsigned int irq;
 
+  /* Avoid "Unused parameter" warning */
+  extra = extra;
+
   /* Verify validity of string */
   n = strspn(string, "0123456789"); 
   if((n < len) || (sscanf(string, "%d", &irq) != 1))
@@ -1068,8 +1248,12 @@ mapping_addiwproto(struct if_mapping *   ifnode,
                   int *                active,
                   char *               string,
                   size_t               len,
+                  struct add_extra *   extra,
                   int                  linenum)
 {
+  /* Avoid "Unused parameter" warning */
+  extra = extra;
+
   /* Verify validity of string */
   if(len >= sizeof(ifnode->iwproto))
     { 
@@ -1144,13 +1328,17 @@ mapping_getiwproto(int                  skfd,
  */
 static int
 mapping_addpcmciaslot(struct if_mapping *      ifnode,
-              int *                    active,
-              char *                   string,
-              size_t                   len,
-              int                      linenum)
+                     int *                     active,
+                     char *                    string,
+                     size_t                    len,
+                     struct add_extra *        extra,
+                     int                       linenum)
 {
   size_t       n;
 
+  /* Avoid "Unused parameter" warning */
+  extra = extra;
+
   /* Verify validity of string, convert to int */
   n = strspn(string, "0123456789"); 
   if((n < len) || (sscanf(string, "%d", &ifnode->pcmcia_slot) != 1))
@@ -1176,7 +1364,7 @@ mapping_addpcmciaslot(struct if_mapping * ifnode,
  */
 static int
 mapping_cmppcmciaslot(struct if_mapping *      ifnode,
-              struct if_mapping *      target)
+                     struct if_mapping *       target)
 {
   return(!(ifnode->pcmcia_slot == target->pcmcia_slot));
 }
@@ -1290,10 +1478,450 @@ mapping_getpcmciaslot(int                      skfd,
 
   /* Cleanup */
   free(linebuf);
+  fclose(stream);
 
   return(target->active[SELECT_PCMCIASLOT] ? 0 : -1);
 }
 
+/*------------------------------------------------------------------*/
+/*
+ * Add a sysfs selector to a mapping
+ */
+static int
+mapping_addsysfs(struct if_mapping *   ifnode,
+                int *                  active,
+                char *                 string,
+                size_t                 len,
+                struct add_extra *     extra,
+                int                    linenum)
+{
+  int          findex; /* filename index */
+  char *       sdup;
+
+  /* Check if we have a modifier */
+  if((extra == NULL) || (extra->modif_pos == NULL))
+    { 
+      fprintf(stderr, "Error: No SYSFS filename at line %d\n", linenum);  
+      return(-1);
+    }
+
+  /* Search if the filename already exist */
+  for(findex = 0; findex < sysfs_global.filenum; findex++)
+    {
+      if(!strcmp(extra->modif_pos, sysfs_global.filename[findex]))
+       break;
+    }
+
+  /* If filename does not exist, creates it */
+  if(findex == sysfs_global.filenum)
+    {
+      if(findex == SYSFS_MAX_FILE)
+       {
+         fprintf(stderr, "Error: Too many SYSFS filenames at line %d\n", linenum);  
+         return(-1);
+       }
+      sdup = strndup(extra->modif_pos, extra->modif_len);
+      if(sdup == NULL)
+       {
+         fprintf(stderr, "Error: Can't allocate SYSFS file\n");  
+         return(-1);
+       }
+      sysfs_global.filename[findex] = sdup;
+      sysfs_global.filenum++;
+    }
+
+  /* Store value */
+  sdup = strndup(string, len);
+  if(sdup == NULL)
+    {
+      fprintf(stderr, "Error: Can't allocate SYSFS value\n");  
+      return(-1);
+    }
+  ifnode->sysfs[findex] = sdup;
+
+  /* Activate */
+  ifnode->active[SELECT_SYSFS] = 1;
+  active[SELECT_SYSFS] = 1;
+
+  if(verbose)
+    fprintf(stderr,
+           "Parsing : Added SYSFS filename `%s' value `%s' from line %d.\n",
+           sysfs_global.filename[findex], ifnode->sysfs[findex], linenum);
+
+  return(0);
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Compare all the sysfs values of two mappings
+ */
+static int
+mapping_cmpsysfs(struct if_mapping *   ifnode,
+                struct if_mapping *    target)
+{
+  int          findex; /* filename index */
+  int          match = 1;
+
+  /* Loop on all sysfs selector */
+  for(findex = 0; findex < sysfs_global.filenum; findex++)
+    {
+      /* If the mapping defines this sysfs selector.. */
+      if(ifnode->sysfs[findex] != NULL)
+       /* And if the sysfs values don't match */
+       if((target->sysfs[findex] == NULL) ||
+          (fnmatch(ifnode->sysfs[findex], target->sysfs[findex],
+                   FNM_CASEFOLD)))
+         /* Then the sysfs selector doesn't match */
+         match = 0;
+    }
+
+  return(!match);
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Extract all the sysfs values of an interface
+ */
+static int
+mapping_getsysfs(int                   skfd,
+                const char *           ifname,
+                struct if_mapping *    target,
+                int                    flag)
+{
+  FILE *       stream;
+  char *       fname;
+  int          fnsize;
+  char *       linebuf = NULL;
+  size_t       linelen = 0; 
+  char *       sdup;
+  int          findex; /* filename index */
+
+  /* Avoid "Unused parameter" warning */
+  skfd = skfd;
+  flag = flag;
+
+  /* Check if we know the devpath of this device */
+  if(target->sysfs_devpath == NULL)
+    {
+      /* Check if we know the root of the sysfs filesystem */
+      if(sysfs_global.root == NULL)
+       {
+         /* Open the mount file for reading */
+         stream = fopen("/proc/mounts", "r");
+         if(!stream) 
+           {
+             fprintf(stderr, "Error: Can't open /proc/mounts file: %s\n",
+                     strerror(errno)); 
+             return(-1);
+           }
+
+         /* Read each line of file
+          * getline is a GNU extension :-( The buffer is recycled and
+          * increased as needed by getline. */
+         while(getline(&linebuf, &linelen, stream) > 0)
+           {
+             int               i;
+             char *    p;
+             size_t    n;
+             char *    token[3];
+             size_t    toklen[3];
+
+             /* The format of /proc/mounts is similar to /etc/fstab (5).
+              * The first argument is the device. For sysfs, there is no
+              * associated device, so this argument is ignored.
+              * The second argument is the mount point.
+              * The third argument is the filesystem type.
+              */
+
+             /* Extract the first 3 tokens */
+             p = linebuf;
+             for(i = 0; i < 3; i++)
+               {
+                 while(isspace(*p))
+                   ++p; 
+                 token[i] = p;
+                 n = strcspn(p, " \t\n");
+                 toklen[i] = n;
+                 p += n;
+               }
+             /* Get the filesystem which type is "sysfs" */
+             if((n == 5) && (!strncasecmp(token[2], "sysfs", 5)))
+               {
+                 /* Get its mount point */
+                 n = toklen[1];
+                 sdup = strndup(token[1], n);
+                 if((n == 0) || (sdup == NULL))
+                   {
+                     fprintf(stderr,
+                             "Error: Can't parse /proc/mounts file: %s\n",
+                             strerror(errno)); 
+                     return(-1);
+                   }
+                 /* Store it */
+                 sysfs_global.root = sdup;
+                 sysfs_global.rlen = n;
+                 break;
+               }
+             /* Finished -> next line */
+           }
+
+         /* Cleanup */
+         fclose(stream);
+
+         /* Check if we found it */
+         if(sysfs_global.root == NULL)
+           {
+             fprintf(stderr,
+                     "Error: Can't find sysfs in /proc/mounts file\n");
+             free(linebuf);
+             return(-1);
+           }
+       }
+
+      /* Construct devpath for this interface.
+       * Reserve enough space to replace name without realloc. */
+      fnsize = (sysfs_global.rlen + 11 + IFNAMSIZ + 1);
+      fname = malloc(fnsize);
+      if(fname == NULL)
+       {
+         fprintf(stderr, "Error: Can't allocate SYSFS devpath\n");  
+         return(-1);
+       }
+      /* Not true devpath for 2.6.20+, but this syslink should work */
+      target->sysfs_devplen = sprintf(fname, "%s/class/net/%s",
+                                     sysfs_global.root, ifname);
+      target->sysfs_devpath = fname;
+    }
+
+  /* Loop on all sysfs selector */
+  for(findex = 0; findex < sysfs_global.filenum; findex++)
+    {
+      char *   p;
+      ssize_t  n;
+
+      /* Construct complete filename for the sysfs selector */
+      fnsize = (target->sysfs_devplen + 1 +
+               strlen(sysfs_global.filename[findex]) + 1);
+      fname = malloc(fnsize);
+      if(fname == NULL)
+       {
+         fprintf(stderr, "Error: Can't allocate SYSFS filename\n");  
+         free(linebuf);
+         return(-1);
+       }
+      sprintf(fname, "%s/%s", target->sysfs_devpath,
+             sysfs_global.filename[findex]);
+
+      /* Open the sysfs file for reading */
+      stream = fopen(fname, "r");
+      if(!stream) 
+       {
+         /* Some sysfs attribute may no exist for some interface */
+         if(verbose)
+           fprintf(stderr, "Error: Can't open file `%s': %s\n", fname,
+                   strerror(errno)); 
+         /* Next sysfs selector */
+         continue;
+       }
+
+      /* Read file. Only one line in file. */
+      n = getline(&linebuf, &linelen, stream);
+      fclose(stream);
+      if(n <= 0)
+       {
+         /* Some attributes are just symlinks to another directory.
+          * We can read the attributes in that other directory
+          * just fine, but sometimes the symlink itself gives a lot
+          * of information.
+          * Examples : SYSFS{device} and SYSFS{device/driver}
+          * In such cases, get the name of the directory pointed to...
+          */
+         /*
+          * I must note that the API for readlink() is very bad,
+          * which force us to have this ugly code. Yuck !
+          */
+         int           allocsize = 128;        /* 256 = Good start */
+         int           retry = 16;
+         char *        linkpath = NULL;
+         int           pathlen;
+
+         /* Try reading the link with increased buffer size */
+         do
+           {
+             allocsize *= 2;
+             linkpath = realloc(linkpath, allocsize);
+             pathlen = readlink(fname, linkpath, allocsize);
+             /* If we did not hit the buffer limit, success */
+             if(pathlen < allocsize)
+               break;
+           }
+         while(retry-- > 0);
+
+         /* Check for error, most likely ENOENT */
+         if(pathlen > 0)
+           /* We have a symlink ;-) Terminate the string. */
+           linkpath[pathlen] = '\0';
+         else
+           {
+             /* Error ! */
+             free(linkpath);
+
+             /* A lot of information in the sysfs is implicit, given
+              * by the position of a file in the tree. It is therefore
+              * important to be able to read the various components
+              * of a path. For this reason, we resolve '..' to the
+              * real name of the parent directory... */
+             /* We have at least 11 char, see above */
+             if(!strcmp(fname + fnsize - 4, "/.."))
+               //if(!strcmp(fname + strlen(fname) - 3, "/.."))
+               {
+                 /* This procedure to get the realpath is not very
+                  * nice, but it's the "best practice". Hmm... */
+                 int   cwd_fd = open(".", O_RDONLY);
+                 linkpath = NULL;
+                 if(cwd_fd > 0)
+                   {
+                     int       ret = chdir(fname);
+                     if(ret == 0)
+                       /* Using getcwd with NULL is a GNU extension. Nice. */
+                       linkpath = getcwd(NULL, 0);
+                     /* This may fail, but it's not fatal */
+                     fchdir(cwd_fd);
+                   }
+                 /* Check if we suceeded */
+                 if(!linkpath)
+                   {
+                     free(linkpath);
+                     if(verbose)
+                       fprintf(stderr, "Error: Can't read parent directory `%s'\n", fname);
+                     /* Next sysfs selector */
+                     continue;
+                   }
+               }
+             else
+               {
+                 /* Some sysfs attribute are void for some interface,
+                  * we may have a real directory, or we may have permission
+                  * issues... */
+                 if(verbose)
+                   fprintf(stderr, "Error: Can't read file `%s'\n", fname);
+                 /* Next sysfs selector */
+                 continue;
+               }
+           }
+
+         /* Here, we have a link name or a parent directory name */
+
+         /* Keep only the last component of path name, save it */
+         p = basename(linkpath);
+         sdup = strdup(p);
+         free(linkpath);
+       }
+      else
+       {
+         /* This is a regular file (well, pseudo file) */
+         /* Get content, remove trailing '/n', save it */
+         p = linebuf;
+         if(p[n - 1] == '\n')
+           n--;
+         sdup = strndup(p, n);
+       }
+      if(sdup == NULL)
+       {
+         fprintf(stderr, "Error: Can't allocate SYSFS value\n"); 
+         free(linebuf);
+         return(-1);
+       }
+      target->sysfs[findex] = sdup;
+
+      /* Activate */
+      target->active[SELECT_SYSFS] = 1;
+
+      if(verbose)
+       fprintf(stderr,
+               "Querying %s : Got SYSFS filename `%s' value `%s'.\n",
+               ifname, sysfs_global.filename[findex], target->sysfs[findex]);
+
+      /* Finished : Next sysfs selector */
+    }
+
+  /* Cleanup */
+  free(linebuf);
+
+  return(target->active[SELECT_SYSFS] ? 0 : -1);
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Add a Previous Interface Name selector to a mapping
+ */
+static int
+mapping_addprevname(struct if_mapping *        ifnode,
+                  int *                active,
+                  char *               string,
+                  size_t               len,
+                  struct add_extra *   extra,
+                  int                  linenum)
+{
+  /* Avoid "Unused parameter" warning */
+  extra = extra;
+
+  /* Verify validity of string */
+  if(len >= sizeof(ifnode->prevname))
+    { 
+      fprintf(stderr, "Old Interface Name too long at line %d\n", linenum);  
+      return(-1);
+    }
+
+  /* Copy */
+  memcpy(ifnode->prevname, string, len + 1); 
+
+  /* Activate */
+  ifnode->active[SELECT_PREVNAME] = 1;
+  active[SELECT_PREVNAME] = 1;
+
+  if(verbose)
+    fprintf(stderr,
+           "Parsing : Added Old Interface Name `%s' from line %d.\n",
+           ifnode->prevname, linenum);
+
+  return(0);
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Compare the Previous Interface Name of two mappings
+ * Note : this one is special.
+ */
+static int
+mapping_cmpprevname(struct if_mapping *        ifnode,
+                  struct if_mapping *  target)
+{
+  /* Do wildcard matching, case insensitive */
+  return(fnmatch(ifnode->prevname, target->ifname, FNM_CASEFOLD));
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Extract the Previous Interface Name from a live interface
+ */
+static int
+mapping_getprevname(int                        skfd,
+                  const char *         ifname,
+                  struct if_mapping *  target,
+                  int                  flag)
+{
+  /* Avoid "Unused parameter" warning */
+  skfd = skfd; ifname = ifname; flag = flag;
+
+  /* Don't do anything, it's already in target->ifname ;-) */
+
+  /* Activate */
+  target->active[SELECT_PREVNAME] = 1;
+
+  return(0);
+}
+
 
 /*********************** MAPPING MANAGEMENTS ***********************/
 /*
@@ -1314,8 +1942,10 @@ mapping_create(char *    pos,
   struct if_mapping *  ifnode;
   char *               star;
 
-  /* Check overflow. */
-  if(len > IFNAMSIZ)
+  star = memchr(pos, '*', len);
+
+  /* Check overflow, need one extra char for wildcard */
+  if((len + (star != NULL)) > IFNAMSIZ)
     {
       fprintf(stderr, "Error: Interface name `%.*s' too long at line %d\n",
              (int) len, pos, linenum);  
@@ -1334,8 +1964,11 @@ mapping_create(char *    pos,
   memcpy(ifnode->ifname, pos, len); 
   ifnode->ifname[len] = '\0'; 
 
-  /* Check the interface name and issue various pedantic warnings */
-  if((!strcmp(ifnode->ifname, "eth0")) || (!strcmp(ifnode->ifname, "wlan0")))
+  /* Check the interface name and issue various pedantic warnings.
+   * We assume people using takeover want to force interfaces to those
+   * names and know what they are doing, so don't bother them... */
+  if((!force_takeover) &&
+     ((!strcmp(ifnode->ifname, "eth0")) || (!strcmp(ifnode->ifname, "wlan0"))))
     fprintf(stderr,
            "Warning: Interface name is `%s' at line %d, can't be mapped reliably.\n",
            ifnode->ifname, linenum);
@@ -1343,29 +1976,6 @@ mapping_create(char *    pos,
     fprintf(stderr, "Warning: Alias device `%s' at line %d probably can't be mapped.\n",
            ifnode->ifname, linenum);
 
-  /* Check for wildcard interface name, such as 'eth*' or 'wlan*'...
-   * This require specific kernel support (2.6.2-rc1 and later).
-   * We externally use '*', but the kernel doesn't know about that,
-   * so convert it to something it knows about... */
-  star = strchr(ifnode->ifname, '*');
-  if(star != NULL)
-    {
-      /* We need an extra char */
-      if(len >= IFNAMSIZ)
-       {
-         fprintf(stderr,
-                 "Error: Interface wildcard `%s' too long at line %d\n",
-                 ifnode->ifname, linenum);  
-         free(ifnode);
-         return(NULL);
-       }
-
-      /* Replace '*' with '%d' */
-      memmove(star + 2, star + 1, len + 1 - (star - ifnode->ifname));
-      star[0] = '%';
-      star[1] = 'd';
-    }
-
   if(verbose)
     fprintf(stderr, "Parsing : Added Mapping `%s' from line %d.\n",
            ifnode->ifname, linenum);
@@ -1433,10 +2043,11 @@ selector_find(const char *      string,
 static int
 mapping_readfile(const char *  filename)
 {
-  FILE *       stream;
-  char *       linebuf = NULL;
-  size_t       linelen = 0; 
-  int          linenum = 0; 
+  FILE *               stream;
+  char *               linebuf = NULL;
+  size_t               linelen = 0; 
+  int                  linenum = 0; 
+  struct add_extra     extrainfo;
 
   /* Reset the list of filters */
   bzero(selector_active, sizeof(selector_active));
@@ -1497,9 +2108,10 @@ mapping_readfile(const char *    filename)
       while(*p != '\0')
        {
          const struct mapping_selector *       selector = NULL;
+         struct add_extra *                    extra = NULL;
 
-         /* Selector name length */
-         n = strcspn(p, " \t\n");
+         /* Selector name length - stop at modifier start */
+         n = strcspn(p, " \t\n{");
 
          /* Find it */
          selector = selector_find(p, n, linenum);
@@ -1508,9 +2120,33 @@ mapping_readfile(const char *    filename)
              ret = -1;
              break;
            }
+         p += n;
+
+         /* Check for modifier */
+         if(*p == '{')
+           {
+             p++;
+             /* Find end of modifier */
+             e = strchr(p, '}');
+             if(e == NULL)
+               {
+                 fprintf(stderr,
+                         "Error: unterminated selector modifier value on line %d\n",
+                         linenum);
+                 ret = -1;
+                 break;        /* Line ended */
+               }
+             /* Fill in struct and hook it */
+             extrainfo.modif_pos = p;
+             extrainfo.modif_len = e - p;
+             extra = &extrainfo;
+             /* Terminate modifier value */
+             e[0] = '\0';
+             /* Skip it */
+             p = e + 1;
+           }
 
          /* Get to selector value */
-         p += n;
          p += strspn(p, " \t\n"); 
          if(*p == '\0')
            {
@@ -1548,7 +2184,8 @@ mapping_readfile(const char *     filename)
          p[n] = '\0';
 
          /* Add it to the mapping */
-         ret = selector->add_fn(ifnode, selector_active, p, n, linenum);
+         ret = selector->add_fn(ifnode, selector_active, p, n,
+                                extra, linenum);
          if(ret < 0)
            break;
 
@@ -1679,22 +2316,21 @@ mapping_find(struct if_mapping *        target)
  * Probe interfaces based on our list of mappings.
  * This is the default, but usually not the best way to do it.
  */
-static void
+static inline void
 probe_mappings(int             skfd)
 {
   struct if_mapping *  ifnode;
-  struct ether_addr    mac;                    /* Exact MAC address, hex */
-  unsigned short       hw_type;
+  struct ifreq         ifr;
 
   /* Look over all our mappings */
   for(ifnode = mapping_list; ifnode != NULL; ifnode = ifnode->next)
     {
       /* Can't load wildcards interface name :-( */
-      if(strchr(ifnode->ifname, '%') != NULL)
+      if(strchr(ifnode->ifname, '*') != NULL)
        continue;
 
       if(verbose)
-       fprintf(stderr, "Probing : Trying to load interface [%s]\n",
+       fprintf(stderr, "Probing : Trying to load/probe interface [%s]\n",
                ifnode->ifname);
 
       /* Trick the kernel into loading the interface.
@@ -1703,7 +2339,8 @@ probe_mappings(int                skfd)
        * Obviously, we expect this command to 'fail', as
        * the interface will load with the old/wrong name.
        */
-      iw_get_mac_addr(skfd, ifnode->ifname, &mac, &hw_type);
+      strncpy(ifr.ifr_name, ifnode->ifname, IFNAMSIZ);
+      ioctl(skfd, SIOCGIFHWADDR, &ifr);
     }
 }
 
@@ -1714,14 +2351,13 @@ probe_mappings(int              skfd)
  * all built-in interfaces that should remain unconfigured won't
  * be probed (and can have mappings).
  */
-static void
+static inline void
 probe_debian(int               skfd)
 {
   FILE *               stream;
   char *               linebuf = NULL;
   size_t               linelen = 0; 
-  struct ether_addr    mac;                    /* Exact MAC address, hex */
-  unsigned short       hw_type;
+  struct ifreq         ifr;
 
   /* Open Debian config file */
   stream = fopen(DEBIAN_CONFIG_FILE, "r");
@@ -1769,8 +2405,9 @@ probe_debian(int          skfd)
                fprintf(stderr, "Probing : Trying to load interface [%s]\n",
                        p);
 
-             /* Do it ! */
-             iw_get_mac_addr(skfd, p, &mac, &hw_type);
+             /* Load interface */
+             strncpy(ifr.ifr_name, p, IFNAMSIZ);
+             ioctl(skfd, SIOCGIFHWADDR, &ifr);
 
              /* Go to next interface name */
              p = e;
@@ -1793,38 +2430,23 @@ probe_debian(int                skfd)
 static int
 process_rename(int     skfd,
               char *   ifname,
-              char *   pattern)
+              char *   newname)
 {
-  char         newname[IFNAMSIZ+1];
   char         retname[IFNAMSIZ+1];
   int          len;
   char *       star;
 
-  len = strlen(pattern);
-  star = strchr(pattern, '*');
+  len = strlen(newname);
+  star = strchr(newname, '*');
 
   /* Check newname length, need one extra char for wildcard */
   if((len + (star != NULL)) > IFNAMSIZ)
     {
       fprintf(stderr, "Error: Interface name `%s' too long.\n",
-             pattern);  
+             newname);  
       return(-1);
     }
 
-  /* Copy to local buffer */
-  memcpy(newname, pattern, len + 1);
-
-  /* Convert wildcard to the proper format */
-  if(star != NULL)
-    {
-      /* Replace '*' with '%d' in the new buffer */
-      star += newname - pattern;
-      memmove(star + 2, star + 1, len + 1 - (star - newname));
-      star[0] = '%';
-      star[1] = 'd';
-    }
-
-
   /* Change the name of the interface */
   if(if_set_name(skfd, ifname, newname, retname) < 0)
     {
@@ -1864,36 +2486,83 @@ process_ifname(int      skfd,
   if(target == NULL)
     return(-1);
 
+  /* If udev is calling us, get the real devpath. */
+  if(udev_output)
+    {
+      const char *env;
+      /* It's passed to us as an environment variable */
+      env = getenv("DEVPATH");
+      if(env)
+       {
+         int   env_len = strlen(env);
+         target->sysfs_devplen = env_len;
+         /* Make enough space for new interface name */
+         target->sysfs_devpath = malloc(env_len + IFNAMSIZ + 1);
+         if(target->sysfs_devpath != NULL)
+           memcpy(target->sysfs_devpath, env, env_len + 1);
+       }
+      /* We will get a second chance is the user has some sysfs selectors */
+    }
+
   /* Find matching mapping */
   mapping = mapping_find(target);
   if(mapping == NULL)
     return(-1);
 
+  /* If user specified a new name, keep only interfaces that would
+   * match the new name... */
+  if((new_name != NULL) && (if_match_ifname(mapping->ifname, new_name) != 0))
+    return(-1);
+
   /* Check if user want only dry-run.
    * Note that, in the case of wildcard, we don't resolve the wildcard.
    * That would be tricky to do... */
   if(dry_run)
     {
-      printf("Dry-run : Would rename %s to %s.\n",
-            target->ifname, mapping->ifname);
-      return(0);
+      strcpy(retname, mapping->ifname);
+      fprintf(stderr, "Dry-run : Would rename %s to %s.\n",
+             target->ifname, mapping->ifname);
     }
-
-  /* Change the name of the interface */
-  if(if_set_name(skfd, target->ifname, mapping->ifname, retname) < 0)
+  else
     {
-      fprintf(stderr, "Error: cannot change name of %s to %s: %s\n",
-             target->ifname, mapping->ifname, strerror(errno)); 
-      return(-1);
+      /* Change the name of the interface */
+      if(if_set_name(skfd, target->ifname, mapping->ifname, retname) < 0)
+       {
+         fprintf(stderr, "Error: cannot change name of %s to %s: %s\n",
+                 target->ifname, mapping->ifname, strerror(errno)); 
+         return(-1);
+       }
     }
 
+  /* This one matched */
+  num_mapping_match++;
+
   /* Check if called with an explicit interface name */
-  if(!count)
+  if(print_newname)
     {
-      /* Always print out the *new* interface name so that
-       * the calling script can pick it up and know where its interface
-       * has gone. */
-      printf("%s\n", retname);
+      if(!udev_output)
+       /* Always print out the *new* interface name so that
+        * the calling script can pick it up and know where its interface
+        * has gone. */
+       printf("%s\n", retname);
+      else
+       /* udev likes to call us as an IMPORT action. This means that
+        * we need to return udev the environment variables changed.
+        * Obviously, we don't want to return anything is nothing changed. */
+       if(strcmp(target->ifname, retname))
+         {
+           char *      pos;
+           /* Hack */
+           if(!target->sysfs_devpath)
+             mapping_getsysfs(skfd, ifname, target, 0);
+           /* Update devpath. Size is large enough. */
+           pos = strrchr(target->sysfs_devpath, '/');
+           if((pos != NULL) && (!strcmp(target->ifname, pos + 1)))
+             strcpy(pos + 1, retname);
+           /* Return new environment variables */
+           printf("DEVPATH=%s\nINTERFACE=%s\nINTERFACE_OLD=%s\n",
+                  target->sysfs_devpath, retname, target->ifname);
+         }
     }
 
   /* Done */
@@ -1904,28 +2573,28 @@ process_ifname(int      skfd,
 /*
  * Process all network interface present on the system.
  */
-static inline int
+static int
 process_iflist(int     skfd,
               char *   args[],
-              int      count)
+              int      count,
+              int      use_probe,
+              int      is_debian)
 {
   num_takeover = 0;
+  num_mapping_match = 0;
+
+  /* Load all the necesary modules */
+  if(use_probe)
+    {
+      if(is_debian)
+       probe_debian(skfd);
+      else
+       probe_mappings(skfd);
+    }
 
   /* Just do it */
   iw_enum_devices(skfd, &process_ifname, args, count);
 
-  /* If we do any takeover, the interface list grabbed with
-   * iw_enum_devices() may get out of sync with the real interfaces,
-   * and we may miss the victim interface. So, let's go through the
-   * list again.
-   * On the other hand, we may have ping pong between two interfaces,
-   * each claiming the same name, so let's not do it forever...
-   * Two time should be enough for most configs...
-   * Jean II */
-  if(force_takeover && num_takeover)
-    /* Play it again, Sam... */
-    iw_enum_devices(skfd, &process_ifname, args, count);
-
   /* Done */
   return(0);
 }
@@ -1953,16 +2622,16 @@ main(int        argc,
 {
   const char * conf_file = DEFAULT_CONF;
   char *       ifname = NULL;
-  char *       newname = NULL;
-  int          use_probe = 0;
-  int          is_debian = 0;
+  int          use_probe = 0;          /* Probe for modules */
+  int          is_debian = 0;          /* Debian quirks (probing) */
+  int          print_num_match = 0;    /* Print/Return num of matches */
   int          skfd;
   int          ret;
 
   /* Loop over all command line options */
   while(1)
     {
-      int c = getopt_long(argc, argv, "c:dDi:n:ptvV", long_opt, NULL);
+      int c = getopt_long(argc, argv, "c:CdDi:n:ptuvV", long_opt, NULL);
       if(c == -1)
        break;
 
@@ -1974,6 +2643,9 @@ main(int  argc,
        case 'c':
          conf_file = optarg;
          break;
+       case 'C':
+         print_num_match = 1;
+         break;
        case 'd':
          is_debian = 1;
          break;
@@ -1984,7 +2656,7 @@ main(int  argc,
          ifname = optarg;
          break;
        case 'n':
-         newname = optarg;
+         new_name = optarg;
          break;
        case 'p':
          use_probe = 1;
@@ -1992,6 +2664,9 @@ main(int  argc,
        case 't':
          force_takeover = 1;
          break;
+       case 'u':
+         udev_output = 1;
+         break;
        case 'v':
          printf("%-8.16s  Wireless-Tools version %d\n", "ifrename", WT_VERSION);
          return(0);
@@ -2001,10 +2676,6 @@ main(int argc,
        }
     }
 
-  /* Read the specified/default config file, or stdin. */
-  if(mapping_readfile(conf_file) < 0)
-    return(-1);
-
   /* Create a channel to the NET kernel. */
   if((skfd = iw_sockets_open()) < 0)
     {
@@ -2013,39 +2684,64 @@ main(int        argc,
     }
 
   /* Check if interface name was specified with -i. */
-  if(ifname)
+  if(ifname != NULL)
     {
       /* Check is target name specified */
-      if(newname != NULL)
+      if(new_name != NULL)
        {
          /* User want to simply rename an interface to a specified name */
-         ret = process_rename(skfd, ifname, newname);
+         ret = process_rename(skfd, ifname, new_name);
        }
       else
        {
+         /* Read the specified/default config file, or stdin. */
+         if(mapping_readfile(conf_file) < 0)
+           return(-1);
+
          /* Rename only this interface based on mappings
-          * Mostly used for HotPlug processing (from /etc/hotplug/net.agent).
+          * Mostly used for HotPlug processing (from /etc/hotplug/net.agent)
+          * or udev processing (from a udev IMPORT rule).
           * Process the network interface specified on the command line,
           * and return the new name on stdout.
           */
+         print_newname = 1;
          ret = process_ifname(skfd, ifname, NULL, 0);
        }
     }
   else
     {
-      /* Load all the necesary modules */
-      if(use_probe)
-       {
-         if(is_debian)
-           probe_debian(skfd);
-         else
-           probe_mappings(skfd);
-       }
+      /* Read the specified/default config file, or stdin. */
+      if(mapping_readfile(conf_file) < 0)
+       return(-1);
 
       /* Rename all system interfaces
        * Mostly used for boot time processing (from init scripts).
        */
-      ret = process_iflist(skfd, &newname, 1);
+      ret = process_iflist(skfd, NULL, 0, use_probe, is_debian);
+
+      /* If we do any takeover, the interface list grabbed with
+       * iw_enum_devices() may get out of sync with the real interfaces,
+       * and we may miss the victim interface. So, let's go through the
+       * list again.
+       * On the other hand, we may have ping pong between two interfaces,
+       * each claiming the same name, so let's not do it forever...
+       * Two time should be enough for most configs...
+       * Note also that takeover is usually done with eth0, and many time
+       * we fail to probe eth0 because an unrenamed interface was using it,
+       * so we redo everything also when probing...
+       * Jean II */
+      if(force_takeover && (num_takeover || use_probe))
+       {
+         /* Play it again, Sam... */
+         ret = process_iflist(skfd, NULL, 0, use_probe, is_debian);
+       }
+
+      /* Print number of mapping that matched */
+      if(print_num_match)
+       {
+         fprintf(stderr, "Setting : %d mapping matched.\n", num_mapping_match);
+         ret = num_mapping_match;
+       }
     }
 
   /* Cleanup */