#define _GNU_SOURCE #include <stdlib.h> #include <string.h> #include <errno.h> #include <assert.h> #include <stdio.h> #include <libgen.h> #include <unistd.h> #include <ctype.h> #include <sys/types.h> #include <sys/stat.h> #include <sys/vfs.h> #include <fcntl.h> #include "mx_log.h" #include "mx_util.h" static inline int _mx_strbeginswith(char *str, const char *start, char **endptr, short ignore_case) { size_t len; int res; assert(str); assert(start); len = strlen(start); if (ignore_case) res = strncasecmp(str, start, len); else res = strncmp(str, start, len); if (res != 0 || !endptr) return !res; *endptr = str + len; return 1; } inline int mx_strbeginswith(char *str, const char *start, char **endptr) { return _mx_strbeginswith(str, start, endptr, 0); } inline int mx_stribeginswith(char *str, const char *start, char **endptr) { return _mx_strbeginswith(str, start, endptr, 1); } static inline int _mx_strbeginswithany(char *str, char **starts, char **endptr, short ignore_case) { char **s; char *end; char *longestmatch = NULL; int res; for (s = starts; *s; s++) { res = _mx_strbeginswith(str, *s, &end, ignore_case); if (res && (!longestmatch || end > longestmatch)) longestmatch = end; } if (longestmatch) { *endptr = longestmatch; return 1; } return 0; } inline int mx_strbeginswithany(char *str, char **starts, char **endptr) { return _mx_strbeginswithany(str, starts, endptr, 0); } int mx_stribeginswithany(char *str, char **starts, char **endptr) { return _mx_strbeginswithany(str, starts, endptr, 1); } inline int mx_strtobytes(char *str, unsigned long long int *bytes) { unsigned long long int s = 0; unsigned long long int t; char *end; if (!str || !*str) return -(errno=EINVAL); if (strchr(str, '-')) return -(errno=ERANGE); do { errno = 0; t = strtoull(str, &end, 10); if (errno) return -errno; if (str == end) return -(errno=EINVAL); for (;*end && *end == ' '; end++) /* empty */; switch (*end) { case 'T': /* tebi */ t *= 1024; case 'G': /* gibi */ t *= 1024; case 'M': /* mebi */ t *= 1024; case 'k': /* kibi */ case 'K': t *= 1024; case 'B': /* bytes */ end++; break; default: return -(errno=EINVAL); } if (s+t < s) return -(errno=ERANGE); s += t; for (;*end && *end == ' '; end++) /* empty */; str = end; } while (*str); *bytes = s; return 0; } inline int mx_strtoseconds(char *str, unsigned long long int *seconds) { unsigned long long int s = 0; unsigned long long int t; char *end; if (!str || !*str) return -(errno=EINVAL); if (strchr(str, '-')) return -(errno=ERANGE); do { errno = 0; t = strtoull(str, &end, 10); if (errno) return -errno; if (str == end) return -(errno=EINVAL); for (;*end && *end == ' '; end++) /* empty */; //if (mx_strtounit(end, &end)); switch (*end) { case 'y': /* years */ t *= 52; case 'w': /* weeks */ t *= 7; case 'd': /* days */ t *= 24; case 'h': /* hours */ t *= 60; case 'm': /* minutes */ t *= 60; case 's': /* seconds */ end++; break; default: return -(errno=EINVAL); } if (s+t < s) return -(errno=ERANGE); s += t; for (;*end && *end == ' '; end++) /* empty */; str = end; } while (*str); *seconds = s; return 0; } inline int mx_strtominutes(char *str, unsigned long long int *minutes) { int res; res = mx_strtoseconds(str, minutes); if (res >= 0) *minutes /= 60; return res; } inline char *mx_strskipwhitespaces(char *str) { char *s; assert(str); for (s = str; *s && *s == ' '; s++) /* empty */; return s; } /* wrapper unsigned */ inline int mx_strtoul(char *str, unsigned long int *to) { unsigned long int ul; char *end; assert(str); assert(to); errno = 0; ul = strtoul(str, &end, 0); if (errno) return -errno; end = mx_strskipwhitespaces(end); if (!end || str == end || *end) return -(errno=EINVAL); if (strchr(str, '-')) return -(errno=ERANGE); *to = ul; return 0; } inline int mx_strtoull(char *str, unsigned long long int *to) { unsigned long long int ull; char *end; assert(str); assert(to); errno = 0; ull = strtoull(str, &end, 0); if (errno) return -errno; end = mx_strskipwhitespaces(end); if (!end || str == end || *end) return -(errno=EINVAL); if (strchr(str, '-')) return -(errno=ERANGE); *to = ull; return 0; } /* wrapper signed */ inline int mx_strtol(char *str, signed long int *to) { long int l; char *end; assert(str); assert(to); errno = 0; l = strtoul(str, &end, 0); if (errno) return -errno; end = mx_strskipwhitespaces(end); if (!end || str == end || *end) return -(errno=EINVAL); *to = l; return 0; } inline int mx_strtoll(char *str, signed long long int *to) { long long int ll; char *end; assert(str); assert(to); errno = 0; ll = strtoll(str, &end, 0); if (errno) return -errno; end = mx_strskipwhitespaces(end); if (!end || str == end || *end) return -(errno=EINVAL); *to = ll; return 0; } /* unsigned */ int mx_strtoui(char *str, unsigned int *to) { unsigned long int ul; int res; assert(str); assert(to); res = mx_strtoul(str, &ul); if (res < 0) return res; if ((unsigned long int)(unsigned int)ul != ul) return -(errno=ERANGE); *to = (unsigned int)ul; return 0; } int mx_strtou8(char *str, uint8_t *to) { unsigned long int ul; int res; assert(str); assert(to); res = mx_strtoul(str, &ul); if (res < 0) return res; if ((unsigned long int)(uint8_t)ul != ul) return -(errno=ERANGE); *to = (uint8_t)ul; return 0; } int mx_strtou16(char *str, uint16_t *to) { unsigned long int ul; int res; assert(str); assert(to); res = mx_strtoul(str, &ul); if (res < 0) return res; if ((unsigned long int)(uint16_t)ul != ul) return -(errno=ERANGE); *to = (uint16_t)ul; return 0; } int mx_strtou32(char *str, uint32_t *to) { unsigned long int ul; int res; assert(str); assert(to); res = mx_strtoul(str, &ul); if (res < 0) return res; if ((unsigned long int)(uint32_t)ul != ul) return -(errno=ERANGE); *to = (uint32_t)ul; return 0; } int mx_strtou64(char *str, uint64_t *to) { unsigned long long int ull; int res; assert(str); assert(to); res = mx_strtoull(str, &ull); if (res < 0) return res; if ((unsigned long long int)(uint64_t)ull != ull) return -(errno=ERANGE); *to = (uint64_t)ull; return 0; } /* signed */ int mx_strtoi(char *str, signed int *to) { signed long int l; int res; assert(str); assert(to); res = mx_strtol(str, &l); if (res < 0) return res; if ((signed long int)(signed int)l != l) return -(errno=ERANGE); *to = (signed int)l; return 0; } int mx_strtoi8(char *str, int8_t *to) { signed long int l; int res; assert(str); assert(to); res = mx_strtol(str, &l); if (res < 0) return res; if ((signed long int)(int8_t)l != l) return -(errno=ERANGE); *to = (uint8_t)l; return 0; } int mx_strtoi16(char *str, int16_t *to) { signed long int l; int res; assert(str); assert(to); res = mx_strtol(str, &l); if (res < 0) return res; if ((signed long int)(int16_t)l != l) return -(errno=ERANGE); *to = (uint16_t)l; return 0; } int mx_strtoi32(char *str, int32_t *to) { signed long int l; int res; assert(str); assert(to); res = mx_strtol(str, &l); if (res < 0) return res; if ((signed long int)(int32_t)l != l) return -(errno=ERANGE); *to = (int32_t)l; return 0; } int mx_strtoi64(char *str, int64_t *to) { signed long long int ll; int res; assert(str); assert(to); res = mx_strtoll(str, &ll); if (res < 0) return res; if ((signed long long int)(int64_t)ll != ll) return -(errno=ERANGE); *to = (int64_t)ll; return 0; } void *mx_malloc_forever(size_t size) { void *ret; do { ret = malloc(size); assert(ret || (!ret && errno == ENOMEM)); } while (!ret); return ret ; } char *mx_strdup_forever(char *str) { char *dup; do { dup = strdup(str); assert(dup || (!dup && errno == ENOMEM)); } while(!dup); return dup; } int mx_vasprintf_forever(char **strp, const char *fmt, va_list ap) { int len; do { len = vasprintf(strp, fmt, ap); } while (len < 0); return len; } int mx_asprintf_forever(char **strp, const char *fmt, ...) { va_list ap; int len; va_start(ap, fmt); len = mx_vasprintf_forever(strp, fmt, ap); va_end(ap); return len; } char *mx_hostname(void) { static char hostname[1024] = ""; int res; if (*hostname) return hostname; res = gethostname(hostname, 1024); if (res == -1) { if (errno != ENAMETOOLONG) assert_perror(errno); hostname[1024-1] = 0; } return hostname; } char *mx_dirname(char *path) { char *tmp; char *dname; char *result; assert(path); tmp = strdup(path); if (!tmp) return NULL; dname = dirname(tmp); assert(dname); result = strdup(dname); free(tmp); return result; } char *mx_dirname_forever(char *path) { char *dname; assert(path); do { dname = mx_dirname(path); } while (!dname); return dname; } int mx_dup2_close_new(int oldfd, int newfd) { int res; if (oldfd == newfd) return 0; res = close(newfd); if (res == -1 && errno == EBADF) return -errno; res = dup2(oldfd, newfd); if(res == -1) return -errno; return res; } int mx_dup2_close_both(int oldfd, int newfd) { int res; if (oldfd == newfd) return 0; res = mx_dup2_close_new(oldfd, newfd); if (res < 0) return res; assert(res == newfd); res = close(oldfd); if (res == -1 && errno == EBADF) return -errno; return newfd; } int mx_setenv_forever(const char *name, const char *value) { assert(name); assert(*name); int res; do { res = setenv(name, value, 1); if (!res) return 0; assert(errno != EINVAL); } while (errno == ENOMEM); assert(errno == ENOMEM); return -errno; } int mx_setenvf_forever(const char *name, char *fmt, ...) { assert(name); assert(*name); assert(fmt); va_list ap; char *value = NULL; int res; va_start(ap, fmt); mx_vasprintf_forever(&value, fmt, ap); va_end(ap); res = mx_setenv_forever(name, value); free(value); return res; } int mx_open_newfile(char *fname) { int fh; int res; int flags = 0; mode_t mode = 0; flags |= O_CREAT|O_WRONLY|O_TRUNC; flags |= O_NOFOLLOW; mode |= S_IRUSR|S_IWUSR; mode |= S_IRGRP|S_IWGRP; mode |= S_IROTH|S_IWOTH; if (!fname) { fname = "/dev/null"; } else if (strcmp(fname, "/dev/null") != 0) { res = unlink(fname); if (res == -1 && errno != ENOENT) return -errno; flags |= O_EXCL; } fh = open(fname, flags, mode); if (fh == -1) return -errno; return fh; } int mx_read_first_line_from_file(char *fname, char **line) { _mx_cleanup_fclose_ FILE *fp; char *buf = NULL; size_t n = 0; ssize_t res; fp = fopen(fname, "r"); if (!fp) return -errno; res = getline(&buf, &n, fp); if (res == -1) return -errno; *line = buf; if (!res) return res; res--; if (buf[res] == '\n') buf[res] = 0; return res; } int mx_strscan_ull(char **str, unsigned long long int *to) { unsigned long long int l; char *s; char *p; char o = 0; int res; s = *str; p = strchr(s, ' '); if (p) { o = *p; *p = 0; p++; } else { p = s + strlen(s); } res = mx_strtoull(s, &l); if (o) *(p-1) = o; if (res == 0) { *to = l; *str = p; } return res; } int mx_strscan_ll(char **str, long long int *to) { long long int l; char *s; char *p; char o = 0; int res; s = *str; p = strchr(s, ' '); if (p) { o = *p; *p = 0; p++; } else { p = s + strlen(s); } res = mx_strtoll(s, &l); if (o) *(p-1) = o; if (res == 0) { *to = l; *str = p; } return res; } char *_mx_strconcat_do(char *first, ...) { va_list ap; char *join = NULL; char *str; char *ptr; size_t len; if (!first) { join = strdup(""); return join; } len = strlen(first); va_start(ap, first); do { str = va_arg(ap, char *); if (!str) break; len += strlen(str); } while(1); va_end(ap); join = malloc(len+1); if (!join) return NULL; ptr = stpcpy(join, first); va_start(ap, first); do { str = va_arg(ap, char *); if (!str) break; ptr = stpcpy(ptr, str); } while(1); va_end(ap); return join; } int mx_sleep(unsigned int seconds) { if (seconds) return sleep(seconds); return 0; } int mx_sleep_nofail(unsigned int seconds) { mx_sleep(seconds); return 1; } void *mx_calloc_forever_sec(size_t nmemb, size_t size, unsigned int time) { void *ptr; while (1) { ptr = calloc(nmemb, size); if (ptr) break; mx_log_debug("calloc() failed: %m - retrying (forever) in %d second(s).", time); if (time) mx_sleep(time); } return ptr; } char **mx_strvec_new(void) { char **strvec; strvec = calloc(sizeof(*strvec), 1); if (!strvec) return NULL; return strvec; } size_t mx_strvec_length(char ** strvec) { char ** sv; size_t len; assert(strvec); sv = strvec; for (; *sv; sv++); len = sv-strvec; return len; } int mx_strvec_push_str(char *** strvecp, char * str) { char ** sv; size_t len; assert(strvecp); assert(*strvecp); assert(str); len = mx_strvec_length(*strvecp); sv = realloc(*strvecp, sizeof(**strvecp) * (len + 2)); if (!sv) { return 0; } sv[len++] = str; sv[len] = NULL; *strvecp = sv; return 1; } int mx_strvec_push_strvec(char ***strvecp, char **strvec) { char **sv; size_t len1; size_t len2; assert(strvecp); assert(*strvecp); assert(strvec); len1 = mx_strvec_length(*strvecp); len2 = mx_strvec_length(strvec); sv = realloc(*strvecp, sizeof(**strvecp) * (len1 + len2 + 1)); if (!sv) { return 0; } memcpy(sv+len1, strvec, sizeof(*strvec) * (len2 + 1)); *strvecp = sv; return 1; } char *mx_strvec_to_str(char **strvec) { char **sv; char* buf; char* s; size_t totallen; char* str; assert(strvec); totallen = 0; for (sv = strvec; *sv; sv++) { totallen += strlen(*sv); } buf = malloc(sizeof(*buf) * (totallen * 2 + 2) + 1); if (!buf) return NULL; str = buf; *str = 0; for (sv = strvec; *sv; sv++) { for (s=*sv; *s; s++) { switch (*s) { case '\\': *(str++) = '\\'; *(str++) = '\\'; break; default: *(str++) = *s; } } *(str++) = '\\'; *(str++) = '0'; } *str = '\0'; return buf; } void mx_strvec_free(char **strvec) { char **sv; if (!strvec) return; for (sv = strvec; *sv; sv++) { free(*sv); } free(strvec); } char **mx_strvec_from_str(char *str) { int res; char* s; char* p; char** strvec; strvec = mx_strvec_new(); if (!strvec) return NULL; for (s=str; *s; s=p+2) { p = strstr(s, "\\0"); if (!p) { free(strvec); errno = EINVAL; return NULL; } *p = 0; res = mx_strvec_push_str(&strvec, s); if (!res) { free(strvec); return NULL; } } return strvec; } int mx_str_to_cpuset(cpu_set_t* cpuset_ptr, char *str) { char c; int cpu_low; int cpu_high; char *next; int i; CPU_ZERO(cpuset_ptr); while (1) { c = *str; if (c == '\0') break; if (!isdigit(c)) return -(errno=EINVAL); cpu_low = strtol(str, &next, 10); str = next; if (cpu_low < 0 || cpu_low >= CPU_SETSIZE) return -(errno=ERANGE); c = *str; CPU_SET(cpu_low, cpuset_ptr); if (c == '\0') { break; } else if (c == ',') { str++; continue; } else if (c != '-') { return -(errno=EINVAL); } str++; c = *str; if (!isdigit(c)) return -(errno=EINVAL); cpu_high = strtol(str, &next, 10); str = next; if (cpu_high < 0 || cpu_high >= CPU_SETSIZE || cpu_high < cpu_low) return -(errno=ERANGE); for (i = cpu_low+1; i <= cpu_high; i++) CPU_SET(i, cpuset_ptr); c = *str; if (c == '\0') { break; } else if (c != ',') { return -(errno=EINVAL); } str++; } return 0; } char *mx_strvec_join(char *sep,char **strvec) { int elements=0; int len=0; char *out; char *in; char *p; int i; assert(sep); assert(strvec); for (i=0;(in=strvec[i]);i++) { elements++; len += strlen(in); } if (elements == 0) return mx_strdup_forever(""); len += strlen(sep)*(elements-1); out = mx_malloc_forever(len+1); p = out; for (i=0;i<elements-1;i++) { p = stpcpy(p, strvec[i]); p = stpcpy(p, sep); } p = stpcpy(p, strvec[i]); return out; } char *mx_cpuset_to_str(cpu_set_t* cpuset_ptr) { char **strvec; int cpu; int cpu_low; int cpu_high; char *str; int res; char *out; strvec=mx_strvec_new(); if (!strvec) return NULL; cpu=0; while(1) { if (cpu>=CPU_SETSIZE) break; if (CPU_ISSET(cpu,cpuset_ptr)) { cpu_low=cpu; while (1) { cpu++; if (cpu>=CPU_SETSIZE || !CPU_ISSET(cpu,cpuset_ptr)) break; } cpu_high=cpu-1; if (cpu_low==cpu_high) { mx_asprintf_forever(&str,"%d",cpu_low); } else { mx_asprintf_forever(&str,"%d-%d",cpu_low,cpu_high); } res=mx_strvec_push_str(&strvec,str); if (!res) { mx_strvec_free(strvec); return NULL; } } else { cpu++; } } out=mx_strvec_join(",",strvec); mx_strvec_free(strvec); return out; } int mx_mkdir_p(char *path, mode_t mode) { struct stat st; int err; char *d; _mx_cleanup_free_ char *copy = NULL; if (stat(path, &st) == 0) return 0; copy=mx_strdup_forever(path); d=copy; while (*++d == '/'); while ((d = strchr(d, '/'))) { *d = '\0'; err = stat(copy, &st) && mkdir(copy, mode); *d++ = '/'; if (err) return -errno; while (*d == '/') ++d; } return (stat(copy, &st) && mkdir(copy, mode)) ? -errno : 0; } int mx_daemon(int nochdir, int noclose) { return daemon(nochdir, noclose); } void _mx_sort_linked_list (void **list, int (*cmp)(void *o1,void *o2), void ** getnextptr(void *o)) { void *unsorted=*list; void *sorted=NULL; while (unsorted) { void *o; void **s_ptr; void *s; o=unsorted; unsorted=*(getnextptr(o)); s_ptr=&sorted; while(1) { s=*s_ptr; if (s==NULL || cmp(o,s)<0) { break; } s_ptr=getnextptr(s); } *(getnextptr(o))=s; *s_ptr=o; } *list=sorted; } unsigned long mx_df(const char *path) { int res; struct statfs s; res=statfs(path, &s); if (res<0) { return 0; } return s.f_bavail*s.f_frsize; }