/* Editor Settings: expandtabs and use 4 spaces for indentation * ex: set softtabstop=4 tabstop=8 expandtab shiftwidth=4: * * -*- mode: c, c-basic-offset: 4 -*- */ /* * Copyright Likewise Software 2004-2008 * All rights reserved. * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the license, or (at * your option) any later version. * * This library 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 Lesser * General Public License for more details. You should have received a copy * of the GNU Lesser General Public License along with this program. If * not, see . * * LIKEWISE SOFTWARE MAKES THIS SOFTWARE AVAILABLE UNDER OTHER LICENSING * TERMS AS WELL. IF YOU HAVE ENTERED INTO A SEPARATE LICENSE AGREEMENT * WITH LIKEWISE SOFTWARE, THEN YOU MAY ELECT TO USE THE SOFTWARE UNDER THE * TERMS OF THAT SOFTWARE LICENSE AGREEMENT INSTEAD OF THE TERMS OF THE GNU * LESSER GENERAL PUBLIC LICENSE, NOTWITHSTANDING THE ABOVE NOTICE. IF YOU * HAVE QUESTIONS, OR WISH TO REQUEST A COPY OF THE ALTERNATE LICENSING * TERMS OFFERED BY LIKEWISE SOFTWARE, PLEASE CONTACT LIKEWISE SOFTWARE AT * license@likewisesoftware.com */ #include "domainjoin.h" #include "djdistroinfo.h" #include #include #include #ifdef HAVE_SYS_SYSTEMINFO_H #include #endif #ifdef HAVE_UNISTD_H #include #endif #define GCE(x) GOTO_CLEANUP_ON_CENTERROR((x)) CENTERROR DJGetDistroInfo(const char *testPrefix, DistroInfo *info) { BOOLEAN exists; CENTERROR ceError = CENTERROR_SUCCESS; struct utsname unameStruct; char *path = NULL; PSTR fileContents = NULL; PSTR distroString = NULL; BOOLEAN rxAlloced = FALSE; regex_t rx; memset(info, 0, sizeof(*info)); #if defined(HAVE_SYSINFO) && defined(SI_ARCHITECTURE) char archBuffer[100]; #endif //According to the Solaris man page, any non-negative return value //indicates success. In fact, Solaris 8 returns 1, while Linux returns //0. if(uname(&unameStruct) < 0) return CTMapSystemError(errno); //Check for os override file if(testPrefix == NULL) testPrefix = ""; GCE(ceError = CTAllocateStringPrintf( &path, "%s/ostype", testPrefix)); GCE(ceError = CTCheckFileOrLinkExists( path, &exists)); if(exists) { GCE(ceError = CTReadFile( path, &fileContents, NULL)); } if(fileContents != NULL) { CTStripWhitespace(fileContents); info->os = DJGetOSFromString(fileContents); } else info->os = DJGetOSFromString(unameStruct.sysname); CT_SAFE_FREE_STRING(fileContents); CT_SAFE_FREE_STRING(path); //Check for distro override file GCE(ceError = CTAllocateStringPrintf( &path, "%s/osdistro", testPrefix)); GCE(ceError = CTCheckFileOrLinkExists( path, &exists)); if(exists) { GCE(ceError = CTReadFile( path, &distroString, NULL)); } CT_SAFE_FREE_STRING(path); if(distroString != NULL) { CTStripWhitespace(distroString); info->distro = DJGetDistroFromString(distroString); CT_SAFE_FREE_STRING(distroString); GCE(ceError = CTStrdup("unknown", &info->version)); } else if(info->os == OS_LINUX) { struct { DistroType matchDistro; const char *searchFile; const char *matchRegex; int versionMatchNum; BOOLEAN compareCase; } const distroSearch[] = { { DISTRO_RHEL, "/etc/redhat-release", /* # The format of the line is something like: # Red Hat Enterprise Linux ES release 4 (Nahant Update 1) # Red Hat Linux Advanced Server release 2.1AS (Pensacola) # Red Hat Enterprise Linux Client release 5 (Tikanga) # Red Hat Enterprise Linux Server release 5.3 (Tikanga) # In addition, Oracle Linux reports itself as: # Enterprise Linux Enterprise Linux AS release 4 (October Update 5) */ //Find a matching distro name "^[[:space:]]*((Red Hat)|(Enterprise Linux)) ((Enterprise Linux)|(Linux (Advanced|Enterprise) Server))[[:space:]]+(AS |ES |Client |Server )?" "release ([[:digit:]]+(\\.[[:digit:]]+)?(AS|ES)?)", 9, 1 }, { DISTRO_REDHAT, "/etc/redhat-release", /* # The format of the line is something like: # Red Hat Linux release 7.3 (Valhala) */ "^[[:space:]]*Red Hat Linux release ([[:digit:]]+(\\.[[:digit:]]+)?)", 1, 1 }, { DISTRO_FEDORA, "/etc/redhat-release", /* # The format of the line is something like: # Fedora Core release 4 (Stentz) */ "^[[:space:]]*Fedora (Core )?release (\\S+)", 2, 1 }, { DISTRO_CENTOS, "/etc/redhat-release", /* # The format of the line is something like: # CentOS release 4.x (Final) */ "^[[:space:]]*CentOS release ([[:digit:]]+(\\.[[:digit:]]+)?)", 1, 1 }, { DISTRO_SUSE, "/etc/SuSE-release", "^[[:space:]]*SUSE LINUX ([[:digit:]]+\\.[[:digit:]]+)[[:space:]]+", 1, 0 }, { DISTRO_OPENSUSE, "/etc/SuSE-release", "^[[:space:]]*openSUSE ([[:digit:]]+\\.[[:digit:]]+)[[:space:]]+", 1, 0 }, { DISTRO_SLES, "/etc/SuSE-release", "^[[:space:]]*SUSE LINUX Enterprise Server ([[:digit:]]+( SP[[:digit:]]+)?)", 1, 0 }, { DISTRO_SLED, "/etc/SuSE-release", "^[[:space:]]*SUSE LINUX Enterprise Desktop ([[:digit:]]+( SP[[:digit:]]+)?)", 1, 0 }, { DISTRO_UBUNTU, "/etc/lsb-release", /* # The file will have lines that include: # DISTRIB_ID=Ubuntu # DISTRIB_RELEASE=6.06 */ "^[[:space:]]*DISTRIB_ID[[:space:]]*=[[:space:]]*Ubuntu[[:space:]]*\n" "(.*\n)?DISTRIB_RELEASE[[:space:]]*=[[:space:]]*(\\S+)[[:space:]]*(\n.*)?$", 2, 1 }, { DISTRO_DEBIAN, "/etc/debian_version", /* # The format of the entire file is a single line like: # 3.1 # and nothing else, so that is the version */ "^[[:space:]]*(\\S+)[[:space:]]*$", 1, 1 }, }; int i; regmatch_t matches[10]; info->distro = DISTRO_UNKNOWN; for(i = 0; info->distro == DISTRO_UNKNOWN; i++) { if(i == sizeof(distroSearch)/sizeof(distroSearch[0])) { //We past the last item in DistroSearch break; } GCE(ceError = CTAllocateStringPrintf( &path, "%s%s", testPrefix, distroSearch[i].searchFile)); GCE(ceError = CTCheckFileOrLinkExists(path, &exists)); if(exists) { int flags = REG_EXTENDED; if(!distroSearch[i].compareCase) flags |= REG_ICASE; GCE(ceError = CTReadFile(path, &fileContents, NULL)); if(regcomp(&rx, distroSearch[i].matchRegex, flags) != 0) { GCE(ceError = CENTERROR_REGEX_COMPILE_FAILED); } rxAlloced = TRUE; if(regexec(&rx, fileContents, sizeof(matches)/sizeof(matches[0]), matches, 0) == 0 && matches[distroSearch[i].versionMatchNum].rm_so != -1) { //This is the correct distro regmatch_t *ver = &matches[distroSearch[i].versionMatchNum]; info->distro = distroSearch[i].matchDistro; GCE(ceError = CTStrndup(fileContents + ver->rm_so, ver->rm_eo - ver->rm_so, &info->version)); } regfree(&rx); rxAlloced = FALSE; CT_SAFE_FREE_STRING(fileContents); } CT_SAFE_FREE_STRING(path); } if(info->distro == DISTRO_DEBIAN) { /* # # Debian and Ubuntu both have /etc/debian_version, # but only Ubuntu has an /etc/lsb-release # */ GCE(ceError = CTAllocateStringPrintf( &path, "%s/etc/lsb-release", testPrefix)); GCE(ceError = CTCheckFileOrLinkExists(path, &exists)); if(exists) { DJ_LOG_ERROR("Unexpected file: %s", path); info->distro = DISTRO_UNKNOWN; } CT_SAFE_FREE_STRING(path); } } else { //It's a UNIX system switch(info->os) { case OS_AIX: info->distro = DISTRO_AIX; /*Uname output from AIX 5.3: $ uname -v 5 $ uname -r 3 */ GCE(ceError = CTAllocateStringPrintf(&info->version, "%s.%s", unameStruct.version, unameStruct.release)); break; case OS_SUNOS: info->distro = DISTRO_SUNOS; /*Uname output from Solaris 8: $ uname -r 5.8 */ GCE(ceError = CTAllocateStringPrintf(&info->version, "%s", unameStruct.release)); break; case OS_DARWIN: info->distro = DISTRO_DARWIN; GCE(ceError = CTCaptureOutput("sw_vers -productVersion", &info->version)); CTStripWhitespace(info->version); break; case OS_HPUX: info->distro = DISTRO_HPUX; { const char *temp = unameStruct.release; while(!isdigit((int)*temp)) temp++; GCE(ceError = CTStrdup(temp, &info->version)); } break; case OS_FREEBSD: info->distro = DISTRO_FREEBSD; GCE(ceError = CTAllocateStringPrintf(&info->version, "%s", unameStruct.release)); break; default: info->distro = DISTRO_UNKNOWN; } } if(info->distro == DISTRO_UNKNOWN) { CT_SAFE_FREE_STRING(info->version); GCE(ceError = CTStrdup("unknown", &info->version)); } //Check for version override file GCE(ceError = CTAllocateStringPrintf( &path, "%s/osver", testPrefix)); GCE(ceError = CTCheckFileOrLinkExists( path, &exists)); if(exists) { GCE(ceError = CTReadFile( path, &fileContents, NULL)); } if(fileContents != NULL) { CTStripWhitespace(fileContents); CT_SAFE_FREE_STRING(info->version); info->version = fileContents; fileContents = NULL; } CT_SAFE_FREE_STRING(path); /* uname -m output: Linux: x86_64 Linux: i386 Linux: i686 AIX: 00CBE1DD4C00 Solaris: sun4u Solaris: i86pc Darwin: i386 Darwin: Power Macintosh HPUX: 9000/785 uname -i output: Linux: x86_64 Linux: i386 RHEL21: not recogn AIX: not recogn Darwin: not recogn Solaris: SUNW,Ultra-4 Solaris: i86pc HPUX: 2000365584 uname -p output: Linux reads /proc/cpuinfo Linux: x86_64 Linux: i686 Linux: athlon Darwin: i386 Darwin: powerpc AIX has the value hard coded in uname AIX: powerpc Solaris uses sysinfo(SI_ARCHITECTURE, buff, sizeof(buff) Solaris: sparc Solaris: i386 HPUX: not recogn */ info->arch = ARCH_UNKNOWN; #if defined(HAVE_SYSINFO) && defined(SI_ARCHITECTURE) //Solaris has this if(info->arch == ARCH_UNKNOWN && sysinfo(SI_ARCHITECTURE, archBuffer, sizeof(archBuffer)) != -1) { info->arch = DJGetArchFromString(archBuffer); } #endif #if defined(HAVE_SYSCONF) && defined(_SC_CPU_VERSION) //HPUX uses this if(info->arch == ARCH_UNKNOWN) { switch(sysconf(_SC_CPU_VERSION)) { case CPU_PA_RISC1_0: case CPU_PA_RISC1_1: case CPU_PA_RISC1_2: case CPU_PA_RISC2_0: case CPU_PA_RISC_MAX: info->arch = ARCH_HPPA; break; #ifdef CPU_HP_INTEL_EM_1_0 case CPU_HP_INTEL_EM_1_0: #endif #ifdef CPU_IA64_ARCHREV_0 case CPU_IA64_ARCHREV_0: #endif info->arch = ARCH_IA64; break; //If it's not any of the previous values, let another test figure //it out. } } #endif if(info->arch == ARCH_UNKNOWN) { //Linux uses this, and sometimes Darwin does too. If 'uname -m' doesn't //return something meaningful on this platform, then the arch will stay //as unknown. info->arch = DJGetArchFromString(unameStruct.machine); } if(info->arch == ARCH_UNKNOWN) { //AIX and sometimes Darwin use this GCE(ceError = CTCaptureOutput("uname -p", &distroString)); CTStripWhitespace(distroString); info->arch = DJGetArchFromString(distroString); CT_SAFE_FREE_STRING(distroString); } //Check for arch override file GCE(ceError = CTAllocateStringPrintf( &path, "%s/osarch", testPrefix)); GCE(ceError = CTCheckFileOrLinkExists( path, &exists)); if(exists) { GCE(ceError = CTReadFile( path, &fileContents, NULL)); info->arch = DJGetArchFromString(fileContents); CT_SAFE_FREE_STRING(fileContents); } CT_SAFE_FREE_STRING(path); cleanup: CT_SAFE_FREE_STRING(path); CT_SAFE_FREE_STRING(fileContents); CT_SAFE_FREE_STRING(distroString); if(rxAlloced) regfree(&rx); if(!CENTERROR_IS_OK(ceError)) { DJFreeDistroInfo(info); } return ceError; } struct { OSType value; const char *name; } static const osList[] = { { OS_AIX, "AIX" }, { OS_SUNOS, "SunOS" }, { OS_SUNOS, "Solaris" }, { OS_DARWIN, "Darwin"}, { OS_DARWIN, "OsX" }, { OS_HPUX, "HP-UX"}, { OS_LINUX, "Linux" }, { OS_FREEBSD, "FreeBSD" }, }; OSType DJGetOSFromString(const char *str) { int i; for(i = 0; i < sizeof(osList)/sizeof(osList[0]); i++) { if(!strcasecmp(str, osList[i].name)) return osList[i].value; } return OS_UNKNOWN; } CENTERROR DJGetOSString(OSType type, char **result) { int i; for(i = 0; i < sizeof(osList)/sizeof(osList[0]); i++) { if(type == osList[i].value) return CTStrdup(osList[i].name, result); } return CTStrdup("unknown", result); } struct { DistroType value; const char *name; } static const distroList[] = { { DISTRO_AIX, "AIX" }, { DISTRO_SUNOS, "Solaris" }, { DISTRO_SUNOS, "SunOS" }, { DISTRO_DARWIN, "Mac OS X"}, { DISTRO_HPUX, "HP-UX"}, { DISTRO_RHEL, "RHEL" }, { DISTRO_REDHAT, "Redhat" }, { DISTRO_FEDORA, "Fedora" }, { DISTRO_CENTOS, "CentOS" }, { DISTRO_SUSE, "SuSE" }, { DISTRO_OPENSUSE, "OpenSuSE" }, { DISTRO_SLES, "SLES" }, { DISTRO_SLED, "SLED" }, { DISTRO_UBUNTU, "Ubuntu" }, { DISTRO_DEBIAN, "Debian" }, { DISTRO_FREEBSD, "FreeBSD" }, }; DistroType DJGetDistroFromString(const char *str) { int i; for(i = 0; i < sizeof(distroList)/sizeof(distroList[0]); i++) { if(!strcasecmp(str, distroList[i].name)) return distroList[i].value; } return DISTRO_UNKNOWN; } CENTERROR DJGetDistroString(DistroType type, char **result) { int i; for(i = 0; i < sizeof(distroList)/sizeof(distroList[0]); i++) { if(type == distroList[i].value) return CTStrdup(distroList[i].name, result); } return CTStrdup("unknown", result); } struct { ArchType value; const char *name; } static const archList[] = { { ARCH_X86_32, "x86_32" }, { ARCH_X86_32, "i386" }, { ARCH_X86_32, "i486" }, { ARCH_X86_32, "i586" }, { ARCH_X86_32, "i686" }, { ARCH_X86_64, "x86_64" }, { ARCH_HPPA, "hppa" }, { ARCH_IA64, "ia64" }, { ARCH_IA64, "itanium" }, { ARCH_SPARC, "sparc" }, { ARCH_POWERPC, "powerpc" }, { ARCH_POWERPC, "ppc" }, }; ArchType DJGetArchFromString(const char * str) { int i; for(i = 0; i < sizeof(archList)/sizeof(archList[0]); i++) { if(!strcasecmp(str, archList[i].name)) return archList[i].value; } return ARCH_UNKNOWN; } CENTERROR DJGetArchString(ArchType type, char **result) { int i; for(i = 0; i < sizeof(archList)/sizeof(archList[0]); i++) { if(type == archList[i].value) return CTStrdup(archList[i].name, result); } return CTStrdup("unknown", result); } void DJFreeDistroInfo(DistroInfo *info) { if(info != NULL) CT_SAFE_FREE_STRING(info->version); } CENTERROR DJGetLikewiseVersion(PSTR *version, PSTR *build, PSTR *revision) { FILE *versionFile = NULL; PSTR line = NULL; BOOLEAN isEndOfFile = FALSE; CENTERROR ceError = CENTERROR_SUCCESS; PSTR _version = NULL; PSTR _build = NULL; PSTR _revision = NULL; *version = NULL; *build = NULL; *revision = NULL; #ifdef MINIMAL_JOIN GCE(ceError = CTOpenFile(LOCALSTATEDIR "/VERSION", "r", &versionFile)); #else GCE(ceError = CTOpenFile(PREFIXDIR "/data/VERSION", "r", &versionFile)); #endif while (TRUE) { GCE(ceError = CTReadNextLine(versionFile, &line, &isEndOfFile)); if (isEndOfFile) break; CTStripWhitespace(line); if (!strncmp(line, "VERSION=", sizeof("VERSION=") - 1)) { GCE(ceError = CTStrdup(line + sizeof("VERSION=") - 1, &_version)); } else if (!strncmp(line, "BUILD=", sizeof("BUILD=") - 1)) { GCE(ceError = CTStrdup(line + sizeof("BUILD=") - 1, &_build)); } else if (!strncmp(line, "REVISION=", sizeof("REVISION=") - 1)) { GCE(ceError = CTStrdup(line + sizeof("REVISION=") - 1, &_revision)); } } if (_version == NULL) { GCE(ceError = CTStrdup("unknown", &_version)); } if (_build == NULL) { GCE(ceError = CTStrdup("unknown", &_build)); } if (_revision == NULL) { GCE(ceError = CTStrdup("unknown", &_revision)); } GCE(ceError = CTSafeCloseFile(&versionFile)); *version = _version; *build = _build; *revision = _revision; _version = NULL; _build = NULL; _revision = NULL; cleanup: CTSafeCloseFile(&versionFile); CT_SAFE_FREE_STRING(line); CT_SAFE_FREE_STRING(_version); CT_SAFE_FREE_STRING(_build); CT_SAFE_FREE_STRING(_revision); return ceError; }