/*
Copyright (C) 2010 ABRT team
Copyright (C) 2010 RedHat Inc
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "internal_libreport.h"
/* Turn on nonblocking I/O on a fd */
int ndelay_on(int fd)
{
int flags = fcntl(fd, F_GETFL);
if (flags & O_NONBLOCK)
return 0;
return fcntl(fd, F_SETFL, flags | O_NONBLOCK);
}
int ndelay_off(int fd)
{
int flags = fcntl(fd, F_GETFL);
if (!(flags & O_NONBLOCK))
return 0;
return fcntl(fd, F_SETFL, flags & ~O_NONBLOCK);
}
int close_on_exec_on(int fd)
{
return fcntl(fd, F_SETFD, FD_CLOEXEC);
}
// Die if we can't allocate size bytes of memory.
void* xmalloc(size_t size)
{
void *ptr = malloc(size);
if (ptr == NULL && size != 0)
die_out_of_memory();
return ptr;
}
// Die if we can't resize previously allocated memory. (This returns a pointer
// to the new memory, which may or may not be the same as the old memory.
// It'll copy the contents to a new chunk and free the old one if necessary.)
void* xrealloc(void *ptr, size_t size)
{
ptr = realloc(ptr, size);
if (ptr == NULL && size != 0)
die_out_of_memory();
return ptr;
}
// Die if we can't allocate and zero size bytes of memory.
void* xzalloc(size_t size)
{
void *ptr = xmalloc(size);
memset(ptr, 0, size);
return ptr;
}
// Die if we can't copy a string to freshly allocated memory.
char* xstrdup(const char *s)
{
char *t;
if (s == NULL)
return NULL;
t = strdup(s);
if (t == NULL)
die_out_of_memory();
return t;
}
// Die if we can't allocate n+1 bytes (space for the null terminator) and copy
// the (possibly truncated to length n) string into it.
char* xstrndup(const char *s, int n)
{
int m;
char *t;
/* We can just xmalloc(n+1) and strncpy into it, */
/* but think about xstrndup("abc", 10000) wastage! */
m = n;
t = (char*) s;
while (m)
{
if (!*t) break;
m--;
t++;
}
n -= m;
t = (char*) xmalloc(n + 1);
t[n] = '\0';
return (char*) memcpy(t, s, n);
}
char *xstrdup_between(const char *src, const char *open, const char *close)
{
const char *start = strstr(src, open);
if (start == NULL)
{
log_debug("Open tag not found: '%s'", open);
return NULL;
}
start += strlen(open);
const char *stop = strstr(start, close);
if (stop == NULL)
{
log_debug("Close tag not found: '%s'", close);
return NULL;
}
return xstrndup(start, stop - start);
}
void xpipe(int filedes[2])
{
if (pipe(filedes))
perror_msg_and_die("Can't create pipe");
}
int xdup(int from)
{
int fd = dup(from);
if (fd < 0)
perror_msg_and_die("Can't duplicate file descriptor");
return fd;
}
void xdup2(int from, int to)
{
if (dup2(from, to) != to)
perror_msg_and_die("Can't duplicate file descriptor");
}
// "Renumber" opened fd
void xmove_fd(int from, int to)
{
if (from == to)
return;
xdup2(from, to);
close(from);
}
// Die with an error message if we can't write the entire buffer.
void xwrite(int fd, const void *buf, size_t count)
{
if (count == 0)
return;
ssize_t size = full_write(fd, buf, count);
if ((size_t)size != count)
error_msg_and_die("short write");
}
void xwrite_str(int fd, const char *str)
{
xwrite(fd, str, strlen(str));
}
// Die with an error message if we can't lseek to the right spot.
off_t xlseek(int fd, off_t offset, int whence)
{
off_t off = lseek(fd, offset, whence);
if (off == (off_t)-1) {
if (whence == SEEK_SET)
perror_msg_and_die("lseek(%llu)", (long long)offset);
perror_msg_and_die("lseek");
}
return off;
}
void xchdir(const char *path)
{
if (chdir(path))
perror_msg_and_die("chdir(%s)", path);
}
char* xvasprintf(const char *format, va_list p)
{
int r;
char *string_ptr;
#if 1
// GNU extension
r = vasprintf(&string_ptr, format, p);
#else
// Bloat for systems that haven't got the GNU extension.
va_list p2;
va_copy(p2, p);
r = vsnprintf(NULL, 0, format, p);
string_ptr = xmalloc(r+1);
r = vsnprintf(string_ptr, r+1, format, p2);
va_end(p2);
#endif
if (r < 0)
die_out_of_memory();
return string_ptr;
}
// Die with an error message if we can't malloc() enough space and do an
// sprintf() into that space., sizeof() into that space.)
char* xasprintf(const char *format, ...)
{
va_list p;
char *string_ptr;
va_start(p, format);
string_ptr = xvasprintf(format, p);
va_end(p);
return string_ptr;
}
void xsetenv(const char *key, const char *value)
{
if (setenv(key, value, 1))
die_out_of_memory();
}
void safe_unsetenv(const char *var_val)
{
//char *name = xstrndup(var_val, strchrnul(var_val, '=') - var_val);
//unsetenv(name);
//free(name);
/* Avoid malloc/free (name is usually very short) */
unsigned len = strchrnul(var_val, '=') - var_val;
char name[len + 1];
memcpy(name, var_val, len);
name[len] = '\0';
unsetenv(name);
}
// Die with an error message if we can't open a new socket.
int xsocket(int domain, int type, int protocol)
{
int r = socket(domain, type, protocol);
if (r < 0)
{
const char *s = "INET";
if (domain == AF_PACKET) s = "PACKET";
if (domain == AF_NETLINK) s = "NETLINK";
if (domain == AF_INET6) s = "INET6";
perror_msg_and_die("socket(AF_%s)", s);
}
return r;
}
// Die with an error message if we can't bind a socket to an address.
void xbind(int sockfd, struct sockaddr *my_addr, socklen_t addrlen)
{
if (bind(sockfd, my_addr, addrlen))
perror_msg_and_die("bind");
}
// Die with an error message if we can't listen for connections on a socket.
void xlisten(int s, int backlog)
{
if (listen(s, backlog))
perror_msg_and_die("listen");
}
// Die with an error message if sendto failed.
// Return bytes sent otherwise
ssize_t xsendto(int s, const void *buf, size_t len,
const struct sockaddr *to,
socklen_t tolen)
{
ssize_t ret = sendto(s, buf, len, 0, to, tolen);
if (ret < 0)
{
close(s);
perror_msg_and_die("sendto");
}
return ret;
}
// xstat() - a stat() which dies on failure with meaningful error message
void xstat(const char *name, struct stat *stat_buf)
{
if (stat(name, stat_buf))
perror_msg_and_die("Can't stat '%s'", name);
}
off_t fstat_st_size_or_die(int fd)
{
struct stat statbuf;
if (fstat(fd, &statbuf))
perror_msg_and_die("Can't stat");
return statbuf.st_size;
}
off_t stat_st_size_or_die(const char *filename)
{
struct stat statbuf;
if (stat(filename, &statbuf))
perror_msg_and_die("Can't stat '%s'", filename);
return statbuf.st_size;
}
// Die if we can't open a file and return a fd
int xopen3(const char *pathname, int flags, int mode)
{
int ret;
ret = open(pathname, flags, mode);
if (ret < 0)
perror_msg_and_die("Can't open '%s'", pathname);
return ret;
}
// Die if we can't open an existing file and return a fd
int xopen(const char *pathname, int flags)
{
return xopen3(pathname, flags, 0666);
}
void xunlinkat(int dir_fd, const char *pathname, int flags)
{
if (unlinkat(dir_fd, pathname, flags))
perror_msg_and_die("Can't remove file '%s'", pathname);
}
void xunlink(const char *pathname)
{
if (unlink(pathname))
perror_msg_and_die("Can't remove file '%s'", pathname);
}
#if 0 //UNUSED
// Warn if we can't open a file and return a fd.
int open3_or_warn(const char *pathname, int flags, int mode)
{
int ret;
ret = open(pathname, flags, mode);
if (ret < 0)
perror_msg("Can't open '%s'", pathname);
return ret;
}
// Warn if we can't open a file and return a fd.
int open_or_warn(const char *pathname, int flags)
{
return open3_or_warn(pathname, flags, 0666);
}
#endif
/* Just testing dent->d_type == DT_REG is wrong: some filesystems
* do not report the type, they report DT_UNKNOWN for every dirent
* (and this is not a bug in filesystem, this is allowed by standards).
*/
int is_regular_file_at(struct dirent *dent, int dir_fd)
{
if (dent->d_type == DT_REG)
return 1;
if (dent->d_type != DT_UNKNOWN)
return 0;
struct stat statbuf;
int r = fstatat(dir_fd, dent->d_name, &statbuf, AT_SYMLINK_NOFOLLOW);
return r == 0 && S_ISREG(statbuf.st_mode);
}
int is_regular_file(struct dirent *dent, const char *dirname)
{
int dir_fd = open(dirname, O_DIRECTORY);
if (dir_fd < 0)
return 0;
int r = is_regular_file_at(dent, dir_fd);
close(dir_fd);
return r;
}
/* Is it "." or ".."? */
/* abrtlib candidate */
bool dot_or_dotdot(const char *filename)
{
if (filename[0] != '.') return false;
if (filename[1] == '\0') return true;
if (filename[1] != '.') return false;
if (filename[2] == '\0') return true;
return false;
}
/* Find out if the last character of a string matches the one given.
* Don't underrun the buffer if the string length is 0.
*/
char *last_char_is(const char *s, int c)
{
if (s && *s)
{
s += strlen(s) - 1;
if ((unsigned char)*s == c)
return (char*)s;
}
return NULL;
}
bool string_to_bool(const char *s)
{
if (s[0] == '1' && s[1] == '\0')
return true;
if (strcasecmp(s, "on") == 0)
return true;
if (strcasecmp(s, "yes") == 0)
return true;
if (strcasecmp(s, "true") == 0)
return true;
return false;
}
void xseteuid(uid_t euid)
{
if (seteuid(euid) != 0)
perror_msg_and_die("Can't set %cid %lu", 'u', (long)euid);
}
void xsetegid(gid_t egid)
{
if (setegid(egid) != 0)
perror_msg_and_die("Can't set %cid %lu", 'g', (long)egid);
}
void xsetreuid(uid_t ruid, uid_t euid)
{
if (setreuid(ruid, euid) != 0)
perror_msg_and_die("Can't set %cid %lu", 'u', (long)ruid);
}
void xsetregid(gid_t rgid, gid_t egid)
{
if (setregid(rgid, egid) != 0)
perror_msg_and_die("Can't set %cid %lu", 'g', (long)rgid);
}
FILE *xfdopen(int fd, const char *mode)
{
FILE *const r = fdopen(fd, mode);
if (NULL == r)
perror_msg_and_die("Can't open file descriptor %d as FILE", fd);
return r;
}