//! //! Retrieves a given device information (assigned IPs and NMS). //! //! @param[in] dev //! @param[out] outips //! @param[out] outnms //! @param[out] len //! //! @return EUCA_OK on success and the out fields will be set properly. On failure the //! following error codes are returned: //! - EUCA_ERROR: if we fail to retrieve the interfaces addresses. //! - EUCA_INVALID_ERROR: if any parameter does not meet the preconditions //! //! @pre dev, outips, outnms and len must not be NULL. //! //! @note //! @todo replace with a better version. //! int getdevinfo(char *dev, u32 ** outips, u32 ** outnms, int *len) { int rc = 0; int count = 0; char host[NI_MAXHOST] = ""; char buf[32] = ""; void *tmpAddrPtr = NULL; struct ifaddrs *ifaddr = NULL; struct ifaddrs *ifa = NULL; struct sockaddr_in *ifs = NULL; if ((dev == NULL) || (outips == NULL) || (outnms == NULL) || (len == NULL)) return (EUCA_INVALID_ERROR); if ((rc = getifaddrs(&ifaddr)) != 0) { return (EUCA_ERROR); } *outips = *outnms = NULL; *len = 0; count = 0; for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) { if (!strcmp(dev, "all") || !strcmp(ifa->ifa_name, dev)) { if (ifa->ifa_addr && ifa->ifa_addr->sa_family == AF_INET) { if ((rc = getnameinfo(ifa->ifa_addr, sizeof(struct sockaddr_in), host, NI_MAXHOST, NULL, 0, NI_NUMERICHOST)) == 0) { count++; //! @todo handle graceful out of memory condition and report it *outips = EUCA_REALLOC(*outips, count, sizeof(u32)); *outnms = EUCA_REALLOC(*outnms, count, sizeof(u32)); (*outips)[count - 1] = dot2hex(host); ifs = ((struct sockaddr_in *)ifa->ifa_netmask); tmpAddrPtr = &ifs->sin_addr; if (inet_ntop(AF_INET, tmpAddrPtr, buf, 32)) { (*outnms)[count - 1] = dot2hex(buf); } } } } } freeifaddrs(ifaddr); *len = count; return (EUCA_OK); }
//! //! appends string 'src' to 'dst', up to 'src_len' characters (unless set //! to 0, in which case to the end of 'src'), enlarging the 'dst' as necessary //! returns the concatenated string or NULL if memory could not be allocated //! if 'src' is an empty string, 'dst' is returned. //! //! @param[in] dst the destination string to append to //! @param[in] src the source string to add to 'dst' //! @param[in] src_limit the number of character to append from 'src' //! //! @return the concatenated string or NULL if memory could not be allocated //! if 'src' is an empty string, 'dst' is returned. //! static char *c_wcappendn(char *dst, const char *src, size_t src_limit) { size_t src_len = 0; size_t dst_len = 0; // Make sure we have a valid source if (src == NULL) return (dst); // Should not be empty if ((src_len = strlen(src)) < 1) return (dst); // Estimate the proper length if ((src_len > src_limit) && (src_limit > 0)) src_len = src_limit; if (dst != NULL) { dst_len = strlen(dst); if ((dst = (char *)EUCA_REALLOC(dst, (dst_len + src_len + 1), sizeof(char))) == NULL) { return (NULL); } } else { if ((dst = (char *)EUCA_ALLOC((dst_len + src_len + 1), sizeof(char))) == NULL) { return (NULL); } *dst = '\0'; } return (strncat(dst, src, src_len)); }
//! //! //! //! @param[in] log_error //! @param[in] format //! @param[in] ... //! //! @return //! //! @pre //! //! @note //! static char *pruntf(boolean log_error, char *format, ...) { va_list ap; FILE *IF = NULL; int rc = -1; int outsize = 1025; char cmd[1024] = { 0 }; size_t bytes = 0; char *output = NULL; va_start(ap, format); vsnprintf(cmd, 1024, format, ap); strncat(cmd, " 2>&1", 1024 - 1); output = NULL; IF = popen(cmd, "r"); if (!IF) { LOGERROR("cannot popen() cmd '%s' for read\n", cmd); va_end(ap); return (NULL); } output = EUCA_ALLOC(outsize, sizeof(char)); if (output) { output[0] = '\0'; // make sure we return an empty string if there is no output } while ((output != NULL) && (bytes = fread(output + (outsize - 1025), 1, 1024, IF)) > 0) { output[(outsize - 1025) + bytes] = '\0'; outsize += 1024; output = EUCA_REALLOC(output, outsize, sizeof(char)); } if (output == NULL) { LOGERROR("failed to allocate mem for output\n"); va_end(ap); pclose(IF); return (NULL); } rc = pclose(IF); if (rc) { //! @TODO improve this hacky special case: failure to find or detach non-existing loop device is not a failure if (strstr(cmd, "losetup") && strstr(output, ": No such device or address")) { rc = 0; } else { if (log_error) { LOGERROR("bad return code from cmd '%s'\n", cmd); LOGDEBUG("%s\n", output); } EUCA_FREE(output); } } va_end(ap); return (output); }
/** * Appends pointer ptr to the end of the given pointer array arr. The array should * have been malloc'd. The allocation is adjusted as needed. * @param arr [i/o] arr pointer to an array of pointers * @param max_arr [i/o] max_arr the number of array entries. * @param ptr (in] pointer to be appended to the array. * @return 0 on success. 1 otherwise. */ void *append_ptrarr(void *arr, int *max_arr, void *ptr) { arr = EUCA_REALLOC(arr, *max_arr + 1, sizeof (void *)); if (arr == NULL) { LOGFATAL("out of memory: failed to (re)allocate array of pointers\n"); LOGFATAL("Shutting down eucanetd.\n"); get_stack_trace(); exit (1); } void **parr = arr; parr[*max_arr] = ptr; (*max_arr)++; return (arr); }
//! //! //! //! @param[in] fp //! //! @return a pointer to the file output string //! //! @pre \li The fp field MUST not be NULL. //! \li The file handles must have previously been opened. //! //! @post The file remains open regardless of the result. //! //! @note caller is responsible to free the returned memory //! char *fp2str(FILE * fp) { #define INCREMENT 512 int buf_max = INCREMENT; int buf_current = 0; void *new_buf = NULL; char *last_read = NULL; char *buf = NULL; if (fp == NULL) return (NULL); do { // create/enlarge the buffer if ((new_buf = EUCA_REALLOC(buf, buf_max, sizeof(char))) == NULL) { // free partial buffer EUCA_FREE(buf); return (NULL); } memset((new_buf + buf_current), 0, (INCREMENT * sizeof(char))); buf = new_buf; LOGEXTREME("enlarged buf to %d\n", buf_max); do { // read in until EOF or buffer is full last_read = fgets(buf + buf_current, buf_max - buf_current, fp); if (last_read != NULL) { buf_current = strlen(buf); } else if (!feof(fp)) { LOGERROR("failed while reading from file handle\n"); EUCA_FREE(buf); return (NULL); } LOGEXTREME("read %d characters so far (max=%d, last=%s)\n", buf_current, buf_max, last_read ? "no" : "yes"); } while (last_read && (buf_max > (buf_current + 1))); // +1 is needed for fgets() to put \0 // in case it is full buf_max += INCREMENT; } while (last_read); return (buf); #undef INCREMENT }
//! //! //! //! @param[in] pMeta a pointer to the node controller (NC) metadata structure //! @param[in] serviceIds a list of service info. //! @param[in] serviceIdsLen the number of service info in the serviceIds list //! @param[out] outStatuses list of service status //! @param[out] outStatusesLen number of service status in the outStatuses list //! //! @return //! //! @pre //! //! @note //! int doDescribeServices(ncMetadata * pMeta, serviceInfoType * serviceIds, int serviceIdsLen, serviceStatusType ** outStatuses, int *outStatusesLen) { int rc = 0; int i = 0; int j = 0; int port = 0; char uri[MAX_PATH] = { 0 }; char uriType[32] = { 0 }; char host[MAX_PATH] = { 0 }; char path[MAX_PATH] = { 0 }; serviceStatusType *myStatus = NULL; int do_report_cluster = 1; // always do report on the cluster, otherwise CC won't get ENABLED int do_report_nodes = 0; int do_report_all = 0; char *my_partition = NULL; rc = initialize(pMeta, TRUE); // DescribeServices is the only authoritative source of epoch if (rc) { return (1); } LOGDEBUG("invoked: userId=%s, serviceIdsLen=%d\n", SP(pMeta ? pMeta->userId : "UNKNOWN"), serviceIdsLen); //! @TODO for now, return error if list of services is passed in as parameter /* if (serviceIdsLen > 0) { LOGERROR("DescribeServices(): received non-zero number of input services, returning fail\n"); *outStatusesLen = 0; *outStatuses = NULL; return(1); } */ sem_mywait(CONFIG); { if (!strcmp(config->ccStatus.serviceId.name, "self")) { for (i = 0; i < serviceIdsLen; i++) { LOGDEBUG("received input serviceId[%d]\n", i); if (strlen(serviceIds[i].type)) { if (!strcmp(serviceIds[i].type, "cluster")) { snprintf(uri, MAX_PATH, "%s", serviceIds[i].uris[0]); rc = tokenize_uri(uri, uriType, host, &port, path); if (strlen(host)) { LOGDEBUG("setting local serviceId to input serviceId (type=%s name=%s partition=%s)\n", SP(serviceIds[i].type), SP(serviceIds[i].name), SP(serviceIds[i].partition)); memcpy(&(config->ccStatus.serviceId), &(serviceIds[i]), sizeof(serviceInfoType)); } } else if (!strcmp(serviceIds[i].type, "node")) { do_report_nodes = 1; // report on node services if requested explicitly } } } } } sem_mypost(CONFIG); if (serviceIdsLen < 1) { // if the describe request is not specific, report on everything do_report_cluster = 1; do_report_nodes = 1; do_report_all = 1; } else { // if the describe request is specific, identify which types are requested do_report_cluster = 0; do_report_nodes = 0; do_report_all = 0; for (i = 0; i < serviceIdsLen; i++) { LOGDEBUG("received input serviceId[%d]: %s %s %s %s\n", i, serviceIds[i].type, serviceIds[i].partition, serviceIds[i].name, serviceIds[i].uris[0]); if (strlen(serviceIds[i].type)) { if (!strcmp(serviceIds[i].type, "cluster")) { do_report_cluster = 1; // report on cluster services if requested explicitly } else if (!strcmp(serviceIds[i].type, "node")) { do_report_nodes++; // count number of and report on node services if requested explicitly } } } } for (i = 0; i < 16; i++) { if (strlen(config->services[i].type)) { LOGDEBUG("internal serviceInfos type=%s name=%s partition=%s urisLen=%d\n", config->services[i].type, config->services[i].name, config->services[i].partition, config->services[i].urisLen); if (!strcmp(config->services[i].type, "cluster")) { my_partition = config->services[i].partition; } for (j = 0; j < MAX_SERVICE_URIS; j++) { if (strlen(config->services[i].uris[j])) { LOGDEBUG("internal serviceInfos\t uri[%d]:%s\n", j, config->services[i].uris[j]); } } } } for (i = 0; i < 16; i++) { if (strlen(config->disabledServices[i].type)) { LOGDEBUG("internal disabled serviceInfos type=%s name=%s partition=%s urisLen=%d\n", config->disabledServices[i].type, config->disabledServices[i].name, config->disabledServices[i].partition, config->disabledServices[i].urisLen); for (j = 0; j < MAX_SERVICE_URIS; j++) { if (strlen(config->disabledServices[i].uris[j])) { LOGDEBUG("internal disabled serviceInfos\t uri[%d]:%s\n", j, config->disabledServices[i].uris[j]); } } } } for (i = 0; i < 16; i++) { if (strlen(config->notreadyServices[i].type)) { LOGDEBUG("internal not ready serviceInfos type=%s name=%s partition=%s urisLen=%d\n", config->notreadyServices[i].type, config->notreadyServices[i].name, config->notreadyServices[i].partition, config->notreadyServices[i].urisLen); for (j = 0; j < MAX_SERVICE_URIS; j++) { if (strlen(config->notreadyServices[i].uris[j])) { LOGDEBUG("internal not ready serviceInfos\t uri[%d]:%s\n", j, config->notreadyServices[i].uris[j]); } } } } *outStatusesLen = 0; *outStatuses = NULL; if (do_report_cluster) { (*outStatusesLen) += 1; *outStatuses = EUCA_ZALLOC(1, sizeof(serviceStatusType)); if (!*outStatuses) { LOGFATAL("out of memory!\n"); unlock_exit(1); } myStatus = *outStatuses; snprintf(myStatus->localState, 32, "%s", config->ccStatus.localState); // ENABLED, DISABLED, STOPPED, NOTREADY snprintf(myStatus->details, 1024, "%s", config->ccStatus.details); // string that gets printed by 'euca-describe-services -E' myStatus->localEpoch = config->ccStatus.localEpoch; memcpy(&(myStatus->serviceId), &(config->ccStatus.serviceId), sizeof(serviceInfoType)); LOGDEBUG("external services\t uri[%d]: %s %s %s %s %s\n", 0, myStatus->serviceId.type, myStatus->serviceId.partition, myStatus->serviceId.name, myStatus->localState, myStatus->serviceId.uris[0]); } if (do_report_nodes) { extern ccResourceCache *resourceCache; ccResourceCache resourceCacheLocal; sem_mywait(RESCACHE); memcpy(&resourceCacheLocal, resourceCache, sizeof(ccResourceCache)); sem_mypost(RESCACHE); if (resourceCacheLocal.numResources > 0 && my_partition != NULL) { // parition is unknown at early stages of CC initialization for (int idIdx = 0; idIdx < serviceIdsLen; idIdx++) { if (do_report_all || !strcmp(serviceIds[idIdx].type, "node")) { for (int rIdx = 0; rIdx < resourceCacheLocal.numResources; rIdx++) { ccResource *r = resourceCacheLocal.resources + rIdx; if (do_report_all || (strlen(serviceIds[idIdx].name) && strlen(r->ip) && !strcmp(serviceIds[idIdx].name, r->ip))) { // we have a node that we want to report about (*outStatusesLen) += 1; *outStatuses = EUCA_REALLOC(*outStatuses, *outStatusesLen, sizeof(serviceStatusType)); if (*outStatuses == NULL) { LOGFATAL("out of memory! (outStatusesLen=%d)\n", *outStatusesLen); unlock_exit(1); } myStatus = *outStatuses + *outStatusesLen - 1; { int resState = r->state; int resNcState = r->ncState; char *state = "BUGGY"; char *msg = ""; if (resState == RESUP) { if (resNcState == ENABLED) { state = "ENABLED"; msg = "the node is operating normally"; } else if (resNcState == STOPPED) { state = "STOPPED"; msg = "the node is not accepting new instances"; } else if (resNcState == NOTREADY) { state = "NOTREADY"; if (strnlen(r->nodeMessage, 1024)) { msg = r->nodeMessage; } else { msg = "the node is currently experiencing problems and needs attention"; } } } else if (resState == RESASLEEP || resState == RESWAKING) { state = "NOTREADY"; msg = "the node is currently in the sleep state"; } else if (resState == RESDOWN) { state = "NOTREADY"; if (strnlen(r->nodeMessage, 1024)) { msg = r->nodeMessage; } else { msg = "the node is not responding to the cluster controller"; } } snprintf(myStatus->localState, 32, "%s", state); snprintf(myStatus->details, 1024, "%s", msg); // string that gets printed by 'euca-describe-services -E' } myStatus->localEpoch = config->ccStatus.localEpoch; sprintf(myStatus->serviceId.type, "node"); sprintf(myStatus->serviceId.name, r->hostname); sprintf(myStatus->serviceId.partition, config->ccStatus.serviceId.partition); sprintf(myStatus->serviceId.uris[0], r->ncURL); myStatus->serviceId.urisLen = 1; LOGDEBUG("external services\t uri[%d]: %s %s %s %s %s\n", idIdx, myStatus->serviceId.type, myStatus->serviceId.partition, myStatus->serviceId.name, myStatus->localState, myStatus->serviceId.uris[0]); } } } } } } LOGDEBUG("done\n"); return (0); }
//! //! //! //! @param[in] log_error //! @param[in] format //! @param[in] ... //! //! @return //! //! @pre //! //! @note //! static char *execlp_output(boolean log_error, ...) { va_list ap; int ntokens = 0; char cmd[256] = ""; // for logging, OK if command gets truncated // run through arguments once to count them va_start(ap, log_error); { char *s; while ((s = va_arg(ap, char *)) != NULL) { ntokens++; } } va_end(ap); if (ntokens < 1) { LOGERROR("internal error: too few arguments to %s\n", __func__); return NULL; } // allocate an array and run through arguments again, copying them into the array char **argv = EUCA_ZALLOC(ntokens + 1, sizeof(char *)); // one extra for the terminating NULL va_start(ap, log_error); { for (int i = 0; i < ntokens; i++) { argv[i] = strdup(va_arg(ap, char *)); // append tokens to 'cmd', strictly for logging purposes int left_in_cmd = sizeof(cmd) - strlen(cmd); if (left_in_cmd > 1) // has room for at least one character and '\0' snprintf(cmd + strlen(cmd), left_in_cmd, "%s%s%s%s", (i > 0) ? (" ") : (""), // add space in front all but the first argument (i == 0 || argv[i][0] == '-') ? ("") : ("'"), // add quoates around non-flags argv[i], (i == 0 || argv[i][0] == '-') ? ("") : ("'")); } } va_end(ap); char *output = NULL; // set up a pipe for getting stdout and stderror from child process int filedes[2]; if (pipe(filedes)) { LOGERROR("failed to create a pipe\n"); goto free; } LOGTRACE("executing: %s\n", cmd); pid_t cpid = fork(); int rc = -1; if (cpid == -1) { LOGERROR("failed to fork\n"); close(filedes[0]); close(filedes[1]); goto free; } else if (cpid == 0) { // child close(filedes[0]); if (dup2(filedes[1], STDOUT_FILENO) == -1) { LOGERROR("failed to dup2\n"); exit(-1); } if (dup2(filedes[1], STDERR_FILENO) == -1) { LOGERROR("failed to dup2\n"); exit(-1); } exit(execvp(argv[0], argv)); } // parent reads stdout and stdin from child into a string close(filedes[1]); int outsize = OUTPUT_ALLOC_CHUNK; // allocate in chunks of this size int nextchar = 0; // offset of the next usable char int bytesread; output = EUCA_ALLOC(outsize, sizeof(char)); if (output) { output[0] = '\0'; // return an empty string if there is no output } while ((output != NULL) && (bytesread = read(filedes[0], output + nextchar, outsize - nextchar - 1)) > 0) { nextchar += bytesread; output[nextchar] = '\0'; if (nextchar + 1 == outsize) { outsize += OUTPUT_ALLOC_CHUNK; if (outsize > MAX_OUTPUT_BYTES) { LOGERROR("internal error: output from command is too long\n"); EUCA_FREE(output); break; } output = EUCA_REALLOC(output, outsize, sizeof(char)); } } if (output == NULL) { LOGERROR("failed to allocate mem for output\n"); } close(filedes[0]); { // wait for the child to reap status int status; rc = waitpid(cpid, &status, 0); if (rc == -1) { LOGERROR("failed to wait for child process\n"); } else if (WIFEXITED(status)) { rc = WEXITSTATUS(status); if (rc) { LOGERROR("child return non-zero status (%d)\n", rc); } } else { LOGERROR("child process did not terminate normally\n"); rc = -1; } } if (rc) { // there were problems above if ((output != NULL) && strstr(cmd, "losetup") && strstr(output, ": No such device or address")) { rc = 0; } else { if (log_error) { LOGERROR("bad return code from cmd %s\n", cmd); LOGDEBUG("%s\n", output); } EUCA_FREE(output); // will be set to NULL } } free: for (int i = 0; i < ntokens; i++) { EUCA_FREE(argv[i]); } EUCA_FREE(argv); return output; }