Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to obtain (almost) unique system identifier in a cross platform way?

I'm looking for a way to get a number which will almost surely change when running the code on different machines and almost surely stay the same between two runs on the same machine.

If I were doing this as a shell script in Linux, I would use something like this:

{ uname -n ; cat /proc/meminfo | head -n1 ; cat /proc/cpuinfo ; } | md5sum 

But I need this in C++ (with boost) and at least on Windows, Linux and Mac.

like image 864
cube Avatar asked May 31 '13 13:05

cube


People also ask

What is unique ID in Linux?

UUID is a unique identifier used in partitions to uniquely identify partitions in Linux operating systems. UUID is a property of the disk partition itself. So, if you install the hard drive containing the partitions on another Linux computer, the partitions will have the same UUID as before. So, that's a good thing.


1 Answers

To generate a mostly unique machine id, you can get a few serial numbers from various pieces of hardware on the system. Most processors will have a CPU serial number, the hard disks each have a number, and each network card will have a unique MAC address.

You can get these and build a fingerprint for the machine. You might want to allow some of these numbers to change before declaring it a new machine. ( e.g. if the 2 out of three are the same, then the machine is the same ). So you can deal somewhat gracefully from having a component upgraded.

I've clipped some code from one of my projects that gets these numbers.

Windows:

#include "machine_id.h"     #define WIN32_LEAN_AND_MEAN         #include <windows.h>       #include <intrin.h>        #include <iphlpapi.h>      #ifndef _MSC_VER #include <cpuid.h> #else #include <intrin.h> #endif  // we just need this for purposes of unique machine id. So any one or two mac's is        // fine.  u16 hashMacAddress( PIP_ADAPTER_INFO info )           {            u16 hash = 0;              for ( u32 i = 0; i < info->AddressLength; i++ )       {            hash += ( info->Address[i] << (( i & 1 ) * 8 ));            }         return hash;            }          void getMacHash( u16& mac1, u16& mac2 )               {            IP_ADAPTER_INFO AdapterInfo[32];                      DWORD dwBufLen = sizeof( AdapterInfo );                DWORD dwStatus = GetAdaptersInfo( AdapterInfo, &dwBufLen );                      if ( dwStatus != ERROR_SUCCESS )                         return; // no adapters.           PIP_ADAPTER_INFO pAdapterInfo = AdapterInfo;          mac1 = hashMacAddress( pAdapterInfo );                if ( pAdapterInfo->Next )              mac2 = hashMacAddress( pAdapterInfo->Next );        // sort the mac addresses. We don't want to invalidate         // both macs if they just change order.               if ( mac1 > mac2 )         {            u16 tmp = mac2;            mac2 = mac1;               mac1 = tmp;             }      }          u16 getVolumeHash()        {            DWORD serialNum = 0;        // Determine if this volume uses an NTFS file system.          GetVolumeInformation( "c:\\", NULL, 0, &serialNum, NULL, NULL, NULL, 0 );        u16 hash = (u16)(( serialNum + ( serialNum >> 16 )) & 0xFFFF );                   return hash;            }          u16 getCpuHash()           {            int cpuinfo[4] = { 0, 0, 0, 0 };                      __cpuid( cpuinfo, 0 );              u16 hash = 0;              u16* ptr = (u16*)(&cpuinfo[0]);     for ( u32 i = 0; i < 8; i++ )          hash += ptr[i];          return hash;            }          const char* getMachineName()        {            static char computerName[1024];     DWORD size = 1024;         GetComputerName( computerName, &size );               return &(computerName[0]);       } 

Linux and OsX:

#include <stdio.h> #include <string.h> #include <unistd.h>           #include <errno.h>            #include <sys/types.h>        #include <sys/socket.h>       #include <sys/ioctl.h>   #include <sys/resource.h>     #include <sys/utsname.h>        #include <netdb.h>            #include <netinet/in.h>       #include <netinet/in_systm.h>                  #include <netinet/ip.h>       #include <netinet/ip_icmp.h>  #include <assert.h>  #ifdef DARWIN                     #include <net/if_dl.h>        #include <ifaddrs.h>          #include <net/if_types.h>     #else //!DARWIN               // #include <linux/if.h>         // #include <linux/sockios.h>    #endif //!DARWIN                 const char* getMachineName()  {     static struct utsname u;       if ( uname( &u ) < 0 )        {              assert(0);                    return "unknown";          }            return u.nodename;         }      //---------------------------------get MAC addresses ------------------------------------unsigned short-unsigned short----------         // we just need this for purposes of unique machine id. So any one or two mac's is fine.             unsigned short hashMacAddress( unsigned char* mac )                  {     unsigned short hash = 0;                  for ( unsigned int i = 0; i < 6; i++ )                  {              hash += ( mac[i] << (( i & 1 ) * 8 ));               }           return hash;               }   void getMacHash( unsigned short& mac1, unsigned short& mac2 )        {     mac1 = 0;                     mac2 = 0;                   #ifdef DARWIN                     struct ifaddrs* ifaphead;     if ( getifaddrs( &ifaphead ) != 0 )               return;                     // iterate over the net interfaces             bool foundMac1 = false;       struct ifaddrs* ifap;         for ( ifap = ifaphead; ifap; ifap = ifap->ifa_next )                      {              struct sockaddr_dl* sdl = (struct sockaddr_dl*)ifap->ifa_addr;            if ( sdl && ( sdl->sdl_family == AF_LINK ) && ( sdl->sdl_type == IFT_ETHER ))                        {               if ( !foundMac1 )             {                                foundMac1 = true;                              mac1 = hashMacAddress( (unsigned char*)(LLADDR(sdl))); //sdl->sdl_data) + sdl->sdl_nlen) );                  } else {                         mac2 = hashMacAddress( (unsigned char*)(LLADDR(sdl))); //sdl->sdl_data) + sdl->sdl_nlen) );                     break;                     }                         }        }            freeifaddrs( ifaphead );    #else // !DARWIN                  int sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP );                      if ( sock < 0 ) return;        // enumerate all IP addresses of the system             struct ifconf conf;           char ifconfbuf[ 128 * sizeof(struct ifreq)  ];          memset( ifconfbuf, 0, sizeof( ifconfbuf ));             conf.ifc_buf = ifconfbuf;     conf.ifc_len = sizeof( ifconfbuf );            if ( ioctl( sock, SIOCGIFCONF, &conf ))        {              assert(0);                    return;                    }            // get MAC address            bool foundMac1 = false;       struct ifreq* ifr;            for ( ifr = conf.ifc_req; (char*)ifr < (char*)conf.ifc_req + conf.ifc_len; ifr++ )     {              if ( ifr->ifr_addr.sa_data == (ifr+1)->ifr_addr.sa_data )                    continue;  // duplicate, skip it             if ( ioctl( sock, SIOCGIFFLAGS, ifr ))                     continue;  // failed to get flags, skip it           if ( ioctl( sock, SIOCGIFHWADDR, ifr ) == 0 )           {              if ( !foundMac1 )             {              foundMac1 = true;                              mac1 = hashMacAddress( (unsigned char*)&(ifr->ifr_addr.sa_data));                 } else {                         mac2 = hashMacAddress( (unsigned char*)&(ifr->ifr_addr.sa_data));                    break;                     }        }        }            close( sock );              #endif // !DARWIN                 // sort the mac addresses. We don't want to invalidate                    // both macs if they just change order.        if ( mac1 > mac2 )            {              unsigned short tmp = mac2;               mac2 = mac1;                  mac1 = tmp;                }        }   unsigned short getVolumeHash()           {     // we don't have a 'volume serial number' like on windows. Lets hash the system name instead.        unsigned char* sysname = (unsigned char*)getMachineName();           unsigned short hash = 0;                  for ( unsigned int i = 0; sysname[i]; i++ )                hash += ( sysname[i] << (( i & 1 ) * 8 ));            return hash;               }   #ifdef DARWIN                  #include <mach-o/arch.h>      unsigned short getCpuHash()              {               const NXArchInfo* info = NXGetLocalArchInfo();          unsigned short val = 0;                  val += (unsigned short)info->cputype;                     val += (unsigned short)info->cpusubtype;                  return val;               }           #else // !DARWIN                static void getCpuid( unsigned int* p, unsigned int ax )         {              __asm __volatile              (   "movl %%ebx, %%esi\n\t"                        "cpuid\n\t"                   "xchgl %%ebx, %%esi"          : "=a" (p[0]), "=S" (p[1]),                      "=c" (p[2]), "=d" (p[3])                     : "0" (ax)                );       }            unsigned short getCpuHash()              {              unsigned int cpuinfo[4] = { 0, 0, 0, 0 };               getCpuid( cpuinfo, 0 );       unsigned short hash = 0;                 unsigned int* ptr = (&cpuinfo[0]);                      for ( unsigned int i = 0; i < 4; i++ )                     hash += (ptr[i] & 0xFFFF) + ( ptr[i] >> 16 );         return hash;               }          #endif // !DARWIN              int main() {    printf("Machine: %s\n", getMachineName());   printf("CPU: %d\n", getCpuHash());   printf("Volume: %d\n", getVolumeHash());   return 0; }     
like image 109
Rafael Baptista Avatar answered Sep 25 '22 12:09

Rafael Baptista