Skip to content
Permalink
0663c4e920
Switch branches/tags

Name already in use

A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
Go to file
 
 
Cannot retrieve contributors at this time
1404 lines (1058 sloc) 23.4 KB
#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 <time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/vfs.h>
#include <fcntl.h>
#include <sys/wait.h>
#include "mx_log.h"
#include "mx_util.h"
static 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;
}
int mx_strbeginswith(char *str, const char *start, char **endptr)
{
return _mx_strbeginswith(str, start, endptr, 0);
}
int mx_stribeginswith(char *str, const char *start, char **endptr)
{
return _mx_strbeginswith(str, start, endptr, 1);
}
static 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;
}
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);
}
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;
// fall through
case 'G': /* gibi */
t *= 1024;
// fall through
case 'M': /* mebi */
t *= 1024;
// fall through
case 'k': /* kibi */
case 'K':
t *= 1024;
// fall through
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;
}
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;
// fall through
case 'w': /* weeks */
t *= 7;
// fall through
case 'd': /* days */
t *= 24;
// fall through
case 'h': /* hours */
t *= 60;
// fall through
case 'm': /* minutes */
t *= 60;
// fall through
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;
}
int mx_strtominutes(char *str, unsigned long long int *minutes)
{
int res;
res = mx_strtoseconds(str, minutes);
if (res >= 0)
*minutes /= 60;
return res;
}
char *mx_strskipwhitespaces(char *str)
{
char *s;
assert(str);
for (s = str; *s && *s == ' '; s++)
/* empty */;
return s;
}
/* wrapper unsigned */
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 > 0)
return -errno;
end = mx_strskipwhitespaces(end);
if (!end || str == end || *end)
return -(errno=EINVAL);
if (strchr(str, '-'))
return -(errno=ERANGE);
*to = ul;
return 0;
}
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 > 0)
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 */
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 > 0)
return -errno;
end = mx_strskipwhitespaces(end);
if (!end || str == end || *end)
return -(errno=EINVAL);
*to = l;
return 0;
}
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 > 0)
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;
}
char *mx_vasprintf_forever(const char *fmt, va_list ap)
{
int len;
char *strp;
do {
len = vasprintf(&strp, fmt, ap);
} while (len < 0);
return strp;
}
char *mx_asprintf_forever(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
char *strp = mx_vasprintf_forever(fmt, ap);
va_end(ap);
return strp;
}
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;
int res;
va_start(ap, fmt);
char *value = mx_vasprintf_forever(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);
}
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) {
str = mx_asprintf_forever("%d", cpu_low);
} else {
str = mx_asprintf_forever("%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);
}
/* guarantee stable sort */
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;
}
time_t mx_clock_boottime(void) {
struct timespec ts;
int res = clock_gettime(CLOCK_BOOTTIME, &ts);
if (res != 0) {
mx_log_err("clock_gettime: %m");
}
return (ts.tv_sec);
}
int mx_call_external(char *helper, char **argv) {
pid_t pid = fork();
if (pid == 0) {
execv(helper, argv);
mx_log_err("%s: %m", helper);
_exit(1);
}
if (pid == -1)
return -1;
int wstatus;
waitpid(pid, &wstatus, 0);
if (wstatus != 0) {
errno = EPROTO;
return -1;
}
return 0;
}
static ssize_t readall(int fd, char *buf, size_t buflen) {
ssize_t len = 0;
while (buflen) {
ssize_t l = read(fd, buf, buflen);
if (l==0)
return len;
if (l<0)
return l;
len += l;
buf += l;
buflen -= l;
}
return len;
}
char *mx_pipe_external(char *helper, char **argv) {
int pipefd[2];
int err;
char buf[2048];
if (pipe(pipefd) < 0) {
return NULL;
}
pid_t pid = fork();
if (pid == 0) {
close(pipefd[0]);
dup2(pipefd[1], 1);
execv(helper, argv);
mx_log_err("%s: %m", helper);
_exit(1);
}
if (pid == -1) {
err = errno;
close(pipefd[0]);
close(pipefd[1]);
errno = err;
return NULL;
}
close(pipefd[1]);
ssize_t len = readall(pipefd[0], buf, sizeof(buf));
if (len<0) {
err = errno;
goto err_close;
}
if (len == sizeof(buf)) {
err = EPROTO;
goto err_close;
}
close(pipefd[0]);
int wstatus;
waitpid(pid, &wstatus, 0);
if (wstatus != 0) {
err = EPROTO;
goto err_err;
}
buf[len] = '\0';
if (len && buf[len-1] == '\n')
buf[len-1] = 0;
return mx_strdup_forever(buf);
err_close:
close(pipefd[0]);
waitpid(pid, NULL, 0);
err_err:
errno = err;
return NULL;
}