#include #include #include #include #include #include #if defined(__GLIBC__) && (__GLIBC__ == 2 && __GLIBC_MINOR__ < 28) #include #else #include #endif #include #include #if defined(__linux__) #include #endif #include #include #include "config.h" #include "controller.capnp.h" #include #include "utils.h" #include "xce/errnox.h" #include "xce/types.h" #define ARGV_INCR_DELTA (16) #define FILL_ARGV(rc, argv, argc, maxargc, delta, s) \ do { \ (rc) = 0; \ char* s1; \ if (((argc) >= (maxargc))) { \ char** p; \ int nc, i; \ nc = (maxargc) + (delta); \ p = (char**)realloc((argv), nc * sizeof(char*)); \ if (p == NULL) { \ (rc) = -ENOMEM; \ break; \ } \ for (i = (maxargc); i < nc; i++) { \ p[i] = NULL; \ } \ (argv) = p; \ (maxargc) = nc; \ } \ s1 = strdup(s); \ if (s1 == NULL) { \ (rc) = -ENOMEM; \ break; \ } \ (argv)[argc] = s1; \ (argc)++; \ } while (0) #define FREE_ARGV(argv, argc) \ do { \ int i; \ if ((argv) == NULL) { \ break; \ } \ for (i = 0; i < (argc); i++) { \ if ((argv)[i] != NULL) { \ free((argv)[i]); \ (argv)[i] = NULL; \ } \ } \ free(argv); \ } while (0) int parse_argv(char* input, char*** argv) { int max_argc = 0; char* arg = NULL; int i = 0; int in_quotes = 0; char quote_char = 0; int argc = 0; int rc = 0; rc = 0; arg = (char*)malloc(strlen(input) + 1); if (arg == NULL) { return -ENOMEM; } *argv = NULL; while (*input) { if (in_quotes) { if (*input == quote_char) { in_quotes = 0; } else if (*input == '\\') { input++; if (*input) { arg[i++] = *input; } } else { arg[i++] = *input; } } else { if (isspace(*input)) { if (i > 0) { arg[i] = '\0'; FILL_ARGV(rc, *argv, argc, max_argc, ARGV_INCR_DELTA, arg); if (rc < 0) { FREE_ARGV(*argv, argc); free(arg); return rc; } i = 0; } } else if ((*input == '"') || (*input == '\'')) { in_quotes = 1; quote_char = *input; } else if (*input == '\\') { input++; if (*input) { arg[i++] = *input; } } else { arg[i++] = *input; } } input++; } if (i > 0) { arg[i] = '\0'; FILL_ARGV(rc, *argv, argc, max_argc, ARGV_INCR_DELTA, arg); if (rc < 0) { FREE_ARGV(*argv, argc); free(arg); return rc; } } free(arg); return argc; } void free_argv(char** argv, int argc) { FREE_ARGV(argv, argc); } int get_myname(char* name, size_t size) { return gethostname(name, size); } char* frame_symbol(int k) { #if defined(__linux__) void* array[k + 1]; size_t size = backtrace(array, k + 1); char** symbols = backtrace_symbols(array, size); char* s = NULL; if (symbols == NULL) { return NULL; } s = strdup(symbols[MIN(size - 1, k)]); free(symbols); return s; #else return NULL; #endif } int xce_vprintf(const char* fmt, va_list ap) { int rc; if (tlog_get_root() == NULL) { rc = vfprintf(stdout, fmt, ap); } else { rc = tlog_vext(TLOG_INFO, __FILE__, __LINE__, __func__, NULL, fmt, ap); } return rc; } int name_to_level(char* lvlname) { static struct { char* lvlstr; int lvl; } levels[] = { { "debug", TLOG_DEBUG }, { "debug2", TLOG_DEBUG2 }, { "debug3", TLOG_DEBUG3 }, { "info", TLOG_INFO }, { "notice", TLOG_NOTICE }, { "warn", TLOG_WARN }, { "error", TLOG_ERROR }, { "fatal", TLOG_FATAL }, { NULL, -1 } }; int i; for (i = 0; levels[i].lvlstr != NULL; i++) { if (strcasecmp(levels[i].lvlstr, lvlname) == 0) { return levels[i].lvl; } } return TLOG_INFO; } static int my_tlog_format(char* buff, int maxlen, struct tlog_loginfo* info, void* userptr, const char* fmt, va_list ap) { int len = 0; int total_len = 0; struct tlog_time* tm = &info->time; xce_use(userptr); /* format prefix */ if (info->level < TLOG_INFO) { len = snprintf( buff, maxlen, "[%.4d-%.2d-%.2d %.2d:%.2d:%.2d,%.3d] [%5d] [%6s] [%17s:%-4d] ", tm->year, tm->mon, tm->mday, tm->hour, tm->min, tm->sec, tm->usec / 1000, getpid(), tlog_get_level_string(info->level), info->file, info->line); } else { len = snprintf( buff, maxlen, "[%.4d-%.2d-%.2d %.2d:%.2d:%.2d,%.3d] [%5d] [%6s] [%22s] ", tm->year, tm->mon, tm->mday, tm->hour, tm->min, tm->sec, tm->usec / 1000, getpid(), tlog_get_level_string(info->level), info->func); } if (len < 0 || len >= maxlen) { return -1; } buff += len; total_len += len; maxlen -= len; /* format log message */ len = vsnprintf(buff, maxlen, fmt, ap); if (len < 0 || len == maxlen) { return -1; } buff += len; total_len += len; /* return total length */ return total_len; } int mylog_init(char* logfile, char* loglvl, int max_log_size, int max_log_count) { int flag = 0; int rc = 0; if (isatty(STDERR_FILENO)) { flag = TLOG_SCREEN_COLOR; } rc = tlog_init(logfile, max_log_size, max_log_count, 0, flag); if (rc >= 0) { rc = tlog_setlevel(name_to_level(loglvl)); } tlog_reg_format_func(my_tlog_format); return rc; } char* verbose_to_loglevel(int verbose) { char* loglevel = "info"; switch (verbose) { case 1: loglevel = "debug"; break; case 2: loglevel = "debug2"; break; case 3: loglevel = "debug3"; break; default: if (verbose > 3) { loglevel = "debug3"; } break; } return loglevel; } int mylog_setlevel(char* level) { return tlog_setlevel(name_to_level(level)); } void mylog_fini() { tlog_exit(); } static _Thread_local ectx_t ectx; void set_error(char* file, char* func, int line, int code) { ectx.file = file; ectx.func = func; ectx.line = line; ectx.code = code; errno = code; } ectx_t* get_ectx() { return &ectx; } static char* xce_err_str[] = { "invalid job", "invalid worker", "duplicated jobs", "duplicated workers", "bad opcode", "unknown role", "unknown resource", "unknown queue", "unknown group", "internal error", NULL }; int xce_strerror_r(int errnum, char* buf, size_t bufsize) { if (errnum < XCE_ERRNO_BASE) { return strerror_r(errnum, buf, bufsize); } if (errnum >= E_XCE_MAX) { strncpy(buf, "unknown error", bufsize); return -1; } strncpy(buf, xce_err_str[errnum - XCE_ERRNO_BASE], bufsize); return 0; } int copy_int_list(int** dst, int* src, int count) { int i; FREEUP(*dst); if (count == 0) { return 0; } *dst = (int*)calloc(count, sizeof(int)); if ((*dst) == NULL) { return -ENOMEM; } for (i = 0; i < count; i++) { (*dst)[i] = (int)src[i]; } return count; } int copy_strlist(xce_strlist_t* dst, xce_strlist_t src) { if (!dst) { return -1; } for (int i = 0; i < dst->count; i++) { FREEUP(dst->data[i]); } FREEUP(dst->data); dst->count = src.count; if (dst->count == 0) { return 0; } dst->data = (char**)calloc(dst->count, sizeof(char*)); if (dst->data == NULL) { return -ENOMEM; } for (int i = 0; i < src.count; i++) { if (src.data[i]) { dst->data[i] = strndup(src.data[i], strlen(src.data[i])); if (dst->data[i] == NULL) { return -ENOMEM; } } } return 0; } void free_name_val_list(xce_name_val_t*** vals, int *count_p) { xce_name_val_t** resvals = *vals; int count = *count_p; int i; if (resvals == NULL || count <= 0) { *count_p = 0; return; } for (i = 0; i < count; i++) { xce_name_val_t* nv = resvals[i]; if (nv == NULL) { continue; } FREEUP(nv->name); if (nv->val != NULL) { if (nv->val->which == Value_u_svalue) { FREEUP(nv->val->u.svalue); } FREEUP(nv->val); } FREEUP(nv); } FREEUP(resvals); *vals = NULL; *count_p = 0; } void free_strlist(xce_strlist_t* l) { int i; if (l == NULL) { return; } if ((l->count <= 0) || (l->data == NULL)) { return; } for (i = 0; i < l->count; i++) { FREEUP(l->data[i]); } FREEUP(l->data); } int fmt_jobid_ex(char* buf, size_t sz, uint64_t jobid, char* lc, char* rc) { uint32_t tag, id_cluster, id_apiserver, id_timestamp; char data[128]; int len = 0; tag = EXTRACT_ID_TAG(jobid); id_cluster = EXTRACT_ID_CLUSTER(jobid); id_apiserver = EXTRACT_ID_APISERVER(jobid); id_timestamp = EXTRACT_ID_TIMESTAMP(jobid); memset(data, 0x0, sizeof(data)); if ((tag & TAG_JOB_ARRAY) != 0) { uint32_t id_job, id_idx; id_job = EXTRACT_ARRAY_JOB(jobid); id_idx = EXTRACT_ARRAY_IDX(jobid); snprintf(data, sizeof(data) - 1, "%d-%d-%d-%d%s%d%s", id_cluster, id_apiserver, id_timestamp, id_job, lc, id_idx, rc); } else { uint32_t id_seq; id_seq = EXTRACT_ID_SEQUENCE(jobid); snprintf(data, sizeof(data) - 1, "%d-%d-%d-%d", id_cluster, id_apiserver, id_timestamp, id_seq); } len = strlen(data); if (len > sz - 1) { return -1; } strncpy(buf, data, sz); return len; } int fmt_jobid(char* buf, size_t sz, uint64_t jobid) { return fmt_jobid_ex(buf, sz, jobid, "[", "]"); } int parse_jobid(uint64_t* jobid, char* buf) { regex_t regex; regmatch_t pmatch[N_MAX_FIELDS_JOBID + 1]; int rc; int len = strlen(buf); if ((jobid == NULL) || (buf == NULL)) { return -EINVAL; } rc = regcomp(®ex, REGEX_JOBARRAY_ID, REG_EXTENDED); if (rc != 0) { return -errno; } *jobid = 0; rc = regexec(®ex, buf, N_EL_ARRAY(pmatch), pmatch, 0); if (rc == 0) { regfree(®ex); if ((pmatch[0].rm_so == 0) && (pmatch[0].rm_eo == len)) { uint32_t tag, id_cluster, id_apiserver, id_timestamp, id_job, id_idx; tag = 0x01; /* job array */ id_cluster = atoi(&buf[pmatch[1].rm_so]); id_apiserver = atoi(&buf[pmatch[2].rm_so]); id_timestamp = atoi(&buf[pmatch[3].rm_so]); id_job = atoi(&buf[pmatch[4].rm_so]); id_idx = atoi(&buf[pmatch[5].rm_so]); *jobid = MAKE_JOBID(id_cluster, id_apiserver, id_timestamp, tag, id_job + id_idx); return 1; } } regfree(®ex); rc = regcomp(®ex, REGEX_JOBID, REG_EXTENDED); if (rc != 0) { return -errno; } rc = regexec(®ex, buf, N_EL_ARRAY(pmatch), pmatch, 0); if (rc == 0) { uint32_t id_cluster, id_apiserver, id_timestamp, id_job; if ((pmatch[0].rm_so != 0) || (pmatch[0].rm_eo != len)) { regfree(®ex); return -EINVAL; } id_cluster = atoi(&buf[pmatch[1].rm_so]); id_apiserver = atoi(&buf[pmatch[2].rm_so]); id_timestamp = atoi(&buf[pmatch[3].rm_so]); id_job = atoi(&buf[pmatch[4].rm_so]); *jobid = MAKE_JOBID(id_cluster, id_apiserver, id_timestamp, 0x0, id_job); regfree(®ex); return 1; } regfree(®ex); return -EINVAL; } uint64_t array_jobid(uint64_t jobid) { uint32_t tag = EXTRACT_ID_TAG(jobid); if (tag == 0x00) { return jobid; } return EXTRACT_ARRAY_BASEID(jobid); } uint64_t array_idx(uint64_t jobid) { uint32_t tag = EXTRACT_ID_TAG(jobid); if (tag == 0x00) { return 0; } return EXTRACT_ARRAY_IDX(jobid); } void unblock_sig(int signo) { sigset_t mask; sigemptyset(&mask); sigaddset(&mask, signo); sigprocmask(SIG_UNBLOCK, &mask, NULL); } void block_sig(int signo) { sigset_t mask; sigemptyset(&mask); sigaddset(&mask, signo); sigprocmask(SIG_BLOCK, &mask, NULL); } #define rot(x, k) (((x) << (k)) | ((x) >> (32 - (k)))) #define mix(a, b, c) \ { \ a -= c; \ a ^= rot(c, 4); \ c += b; \ b -= a; \ b ^= rot(a, 6); \ a += c; \ c -= b; \ c ^= rot(b, 8); \ b += a; \ a -= c; \ a ^= rot(c, 16); \ c += b; \ b -= a; \ b ^= rot(a, 19); \ a += c; \ c -= b; \ c ^= rot(b, 4); \ b += a; \ } #define final(a, b, c) \ { \ c ^= b; \ c -= rot(b, 14); \ a ^= c; \ a -= rot(c, 11); \ b ^= a; \ b -= rot(a, 25); \ c ^= b; \ c -= rot(b, 16); \ a ^= c; \ a -= rot(c, 4); \ b ^= a; \ b -= rot(a, 14); \ c ^= b; \ c -= rot(b, 24); \ } uint32_t hash4(uint32_t a, uint32_t b, uint32_t c, uint32_t d) { uint32_t i1 = a, i2 = b, i3 = c, i4 = d; mix(i1, i2, i3); mix(i2, i3, i4); mix(i3, i4, i1); final(i4, i1, i2); return i2; } uint32_t hash64(uint64_t x) { uint32_t a = (uint32_t)(x >> 32); uint32_t b = (uint32_t)(x & UMAX_BITS(32)); uint32_t c = ((a & UMAX_BITS(16)) << 16) | (b >> 16); uint32_t d = ((b & UMAX_BITS(16)) << 16) | (a >> 16); return hash4(a, b, c, d); } #define RUN_HASH(hc, c) (((hc) << 5) + (hc) + (c)) uint32_t hash_block(char* buf, size_t len) { uint32_t hc = 5381; int i; for (i = 0; i < len; i++) { int c = (int)buf[i]; hc = RUN_HASH(hc, c); } return hc; } uint32_t hash_string(const char* key) { uint32_t hc = 5381; int c; while ((c = *key++)) { hc = RUN_HASH(hc, c); } return hc; }