#include #include #include #include #include #include #include #include #include #include #include #include "trace.h" #include "versinfo.h" typedef struct _CompilerInfo { char *path; char *versionInfo; struct timespec modTime; struct _CompilerInfo *next; } CompilerInfo; /* Invokes the command line given by commandLine (using popen) and returns the text of the command's output. Returns NULL if any error is encountered. */ static char *dcc_run_simple_command(const char *commandLine) { FILE *output; char *versionInfo = NULL;; int buffSize = 1024, len = 0; output = popen(commandLine, "r"); if (output) { versionInfo = (char *)malloc(buffSize * sizeof(char)); while (versionInfo && !feof(output)) { if (len == buffSize-1) { char *newBuff = (char *)realloc(versionInfo, buffSize*2); if (newBuff) { versionInfo = newBuff; buffSize *= 2; } else { free(versionInfo); return NULL; } } len += fread(&versionInfo[len], 1, buffSize-len-1, output); } pclose(output); } versionInfo[len]=0; return versionInfo; } static CompilerInfo *dcc_parse_etc_compilers() { static CompilerInfo *compilers = NULL; struct stat sb; if (compilers == NULL && stat("/etc/compilers", &sb) == 0) { int compilersFD = open("/etc/compilers", O_RDONLY, 0); if (compilersFD != 1) { char *compilersBuff = (char *)malloc(sb.st_size+1); if (compilersBuff) { if (read(compilersFD, compilersBuff, sb.st_size) == sb.st_size) { int i, lineCount; compilersBuff[sb.st_size] = 0; // null terminate // change all the newlines to terminators for (i=0; i 0 && compilersBuff[lineStart] != '#' && stat(&compilersBuff[lineStart], &cc_sb) == 0) { // we have not seen this compiler before so allocate a new node // note that we do not fill in the version info here CompilerInfo *ci = (CompilerInfo *)calloc(1, sizeof(CompilerInfo)); ci->path = strdup(&compilersBuff[lineStart]); ci->next = compilers; compilers = ci; } } } free(compilersBuff); } close(compilersFD); } } return compilers; } static CompilerInfo *dcc_compiler_info_for_path(char *compiler) { CompilerInfo *compilers = dcc_parse_etc_compilers(); while (compilers && strcmp(compilers->path, compiler) != 0) compilers = compilers->next; return compilers; } static char *_dcc_get_compiler_version(CompilerInfo *compiler) { char *result = NULL; struct stat sb; if (compiler) { if (stat(compiler->path, &sb) == 0 && compiler->versionInfo != NULL) { // we found the compiler, check that the timestamp is unchanged if (memcmp(&sb.st_ctimespec, &compiler->modTime, sizeof(struct timespec)) != 0) { // didn't match, so throw away version number rs_log_warning("compiler version changed: %s", compiler->path); //free(compiler->versionInfo); // leak; should be very uncommon compiler->versionInfo = NULL; } } if (!compiler->versionInfo) { // have to fetch the version number int lineLen = strlen(compiler->path); char *versionArgs = " -v 2>&1"; char commandBuff[lineLen+strlen(versionArgs)+1]; char *versionOutput; strcpy(commandBuff, compiler->path); strcat(commandBuff, versionArgs); versionOutput = dcc_run_simple_command(commandBuff); if (versionOutput) { char *version = strstr(versionOutput, "gcc version"); if (version) { int newline = 0; while (version[newline] != '\n' && version[newline] != 0) newline++; compiler->versionInfo = (char *)malloc(newline+1); strncpy(compiler->versionInfo, version, newline); compiler->versionInfo[newline]=0; compiler->modTime = sb.st_ctimespec; } } } result = compiler->versionInfo; } return result; } char *dcc_get_compiler_version(char *compilerPath) { return _dcc_get_compiler_version(dcc_compiler_info_for_path(compilerPath)); } int dcc_is_allowed_compiler(char *path) { return dcc_compiler_info_for_path(path) != NULL; } /* This function returns a list of compiler version strings to use in the TXT record. It returns a NULL terminated array of version strings, or NULL if an error is encountered. */ char **dcc_get_all_compiler_versions(void) { CompilerInfo *compilers = dcc_parse_etc_compilers(); char **result = NULL; if (result == NULL) { struct stat sb; CompilerInfo *ci; int i, j, count = 0; for (ci = compilers; ci != NULL; ci=ci->next) count++; result = (char **)calloc(count+1, sizeof(char *)); for (i=0, ci = compilers; ci != NULL; ci = ci->next) { char *version = _dcc_get_compiler_version(ci); if (version) { for (j=0; jcputype) { case CPU_TYPE_POWERPC: archName = "ppc"; break; case CPU_TYPE_I386: archName = "i386"; break; default: // don't know the name, just the number archName = archbuf; sprintf(archbuf, "%d", myArch->cputype); rs_log_warning("unknown cputype: %d", myArch->cputype); break; } } else { rs_log_warning("failed to get arch info"); archName = "unknown"; } // construct a string of the form 10.x.y (ppc) ret = malloc(strlen(prodVers) + strlen(archName) + strlen(" ()") + 1); sprintf(ret, "%s (%s)", prodVers, archName); free(sw_vers); } } return ret; }