Files
s390-tools/dump2tar/src/misc.c
Michael Holzheu b627b8d8e1 Initial s390-tools-2.0.0 import
This commit is based on the s390-tools-1.39.0 version.

Changes on top of s390-tools-1.39.0:

 - Add MIT license to all source files
 - Add LICENSE file
 - Transform REAMDE to README.md (markdown)
 - Add AUTHORS.md file
 - Add CONTRIBUTING.md file
 - Move changelog from README to CHANGELOG.md file

Reviewed-by: Stefan Haberland <sth@linux.vnet.ibm.com>
Signed-off-by: Michael Holzheu <holzheu@linux.vnet.ibm.com>
2017-08-21 10:55:40 +02:00

493 lines
10 KiB
C

/*
* dump2tar - tool to dump files and command output into a tar archive
*
* Helper functions
*
* Copyright IBM Corp. 2016, 2017
*
* s390-tools is free software; you can redistribute it and/or modify
* it under the terms of the MIT license. See LICENSE for details.
*/
#include <errno.h>
#include <fcntl.h>
#include <pthread.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <unistd.h>
#include "dref.h"
#include "global.h"
#include "misc.h"
struct timespec main_start_ts;
static pthread_key_t thread_name_key;
static bool stdout_data;
/* Write @len bytes at @addr to @fd. Return %EXIT_OK on success, %EXIT_RUNTIME
* otherwise. */
int misc_write_data(int fd, char *addr, size_t len)
{
ssize_t w;
while (len > 0) {
w = write(fd, addr, len);
if (w < 0)
return EXIT_RUNTIME;
len -= w;
addr += w;
}
return EXIT_OK;
}
/* Read at most @len bytes from @fd to @addr. Return the number of bytes read
* or %-1 on error. */
ssize_t misc_read_data(int fd, char *addr, size_t len)
{
size_t done = 0;
ssize_t r;
while (len > 0) {
r = read(fd, addr, len);
if (r < 0)
return -1;
if (r == 0)
break;
len -= r;
addr += r;
done += r;
}
return done;
}
/* Advance timespec @ts by @sec seconds and @nsec nanoseconds */
void inc_timespec(struct timespec *ts, time_t sec, long nsec)
{
ts->tv_nsec += nsec;
ts->tv_sec += sec;
if (ts->tv_nsec > NSEC_PER_SEC) {
ts->tv_nsec -= NSEC_PER_SEC;
ts->tv_sec++;
}
}
/* Set timespec @ts to point to @sec seconds and @nsec nanoseconds in the
* future */
void set_timespec(struct timespec *ts, time_t sec, long nsec)
{
clock_gettime(CLOCK_MONOTONIC, ts);
inc_timespec(ts, sec, nsec);
}
/* Return true if timespec @a refers to a point in time before @b */
bool ts_before(struct timespec *a, struct timespec *b)
{
if (a->tv_sec < b->tv_sec ||
(a->tv_sec == b->tv_sec && a->tv_nsec < b->tv_nsec))
return true;
return false;
}
/* Store a string representing the time duration between @start and @end in
* at most @len bytes of @buff. */
int snprintf_duration(char *buff, size_t len, struct timespec *start,
struct timespec *end)
{
time_t sec;
long nsec, msec, s, m, h;
sec = end->tv_sec - start->tv_sec;
nsec = end->tv_nsec - start->tv_nsec;
if (nsec < 0) {
nsec += NSEC_PER_SEC;
sec--;
}
msec = nsec / NSEC_PER_MSEC;
s = sec % 60;
sec /= 60;
m = sec % 60;
sec /= 60;
h = sec;
if (h > 0)
return snprintf(buff, len, "%luh%lum%lu.%03lus", h, m, s, msec);
else if (m > 0)
return snprintf(buff, len, "%lum%lu.%03lus", m, s, msec);
else
return snprintf(buff, len, "%lu.%03lus", s, msec);
}
/* Return the name of the current thread */
char *get_threadname(void)
{
return pthread_getspecific(thread_name_key);
}
static int snprintf_timestamp(char *str, size_t size)
{
struct timespec now_ts;
set_timespec(&now_ts, 0, 0);
now_ts.tv_sec -= main_start_ts.tv_sec;
now_ts.tv_nsec -= main_start_ts.tv_nsec;
if (now_ts.tv_nsec < 0) {
now_ts.tv_nsec += NSEC_PER_SEC;
now_ts.tv_sec--;
}
return snprintf(str, size, "[%3lu.%06lu] ", now_ts.tv_sec,
now_ts.tv_nsec / NSEC_PER_USEC);
}
/* When DUMP2TAR_DEBUG is set to non-zero, print debugging information */
void debug(const char *file, unsigned long line, const char *format, ...)
{
char msg[MSG_LEN];
size_t off = 0;
int rc;
va_list args;
/* Debug marker */
rc = snprintf(&msg[off], MSG_LEN - off, "DEBUG: ");
HANDLE_RC(rc, MSG_LEN, off, out);
/* Timestamp */
rc = snprintf_timestamp(&msg[off], MSG_LEN - off);
HANDLE_RC(rc, MSG_LEN, off, out);
/* Thread name */
rc = snprintf(&msg[off], MSG_LEN - off, "%s: ", get_threadname());
HANDLE_RC(rc, MSG_LEN, off, out);
/* Message */
va_start(args, format);
rc = vsnprintf(&msg[off], MSG_LEN - off, format, args);
va_end(args);
HANDLE_RC(rc, MSG_LEN, off, out);
/* Call site */
rc = snprintf(&msg[off], MSG_LEN - off, " (%s:%lu)", file, line);
out:
fprintf(stderr, "%s\n", msg);
}
/* Print a warning message consisting of @format and variable arguments.
* If @print_errno is true, also print the text corresponding to errno.
* We're not using err.h's warn since we want timestamps and synchronized
* output. */
void _mwarn(bool print_errno, const char *format, ...)
{
char msg[MSG_LEN];
size_t off = 0;
int rc;
va_list args;
if (global_timestamps) {
rc = snprintf_timestamp(&msg[off], MSG_LEN - off);
HANDLE_RC(rc, MSG_LEN, off, out);
}
rc = snprintf(&msg[off], MSG_LEN - off, "%s: ",
program_invocation_short_name);
HANDLE_RC(rc, MSG_LEN, off, out);
va_start(args, format);
rc = vsnprintf(&msg[off], MSG_LEN - off, format, args);
va_end(args);
HANDLE_RC(rc, MSG_LEN, off, out);
if (print_errno)
snprintf(&msg[off], MSG_LEN - off, ": %s", strerror(errno));
out:
fprintf(stderr, "%s\n", msg);
}
/* Provide informational output if --verbose was specified */
void verb(const char *format, ...)
{
char msg[MSG_LEN];
size_t off = 0;
int rc;
va_list args;
FILE *fd;
if (!global_verbose)
return;
if (stdout_data)
fd = stderr;
else
fd = stdout;
if (global_timestamps) {
rc = snprintf_timestamp(&msg[off], MSG_LEN - off);
HANDLE_RC(rc, MSG_LEN, off, out);
}
va_start(args, format);
rc = vsnprintf(&msg[off], MSG_LEN - off, format, args);
va_end(args);
out:
fprintf(fd, "%s", msg);
}
/* Provide informational output. */
void info(const char *format, ...)
{
char msg[MSG_LEN];
size_t off = 0;
int rc;
va_list args;
FILE *fd;
if (global_quiet)
return;
if (stdout_data)
fd = stderr;
else
fd = stdout;
if (global_timestamps) {
rc = snprintf_timestamp(&msg[off], MSG_LEN - off);
HANDLE_RC(rc, MSG_LEN, off, out);
}
va_start(args, format);
rc = vsnprintf(&msg[off], MSG_LEN - off, format, args);
va_end(args);
out:
fprintf(fd, "%s", msg);
}
/* Return a newly allocated buffer containing the result of the specified
* string format arguments */
char *__masprintf(const char *func, const char *file, int line, const char *fmt,
...)
{
char *str;
va_list args;
va_start(args, fmt);
__util_vasprintf(func, file, line, &str, fmt, args);
va_end(args);
return str;
}
/* Set the internal name of the calling thread */
void __set_threadname(const char *func, const char *file, int line,
const char *fmt, ...)
{
char *str;
va_list args;
va_start(args, fmt);
__util_vasprintf(func, file, line, &str, fmt, args);
va_end(args);
pthread_setspecific(thread_name_key, str);
}
/* Clear any previously set thread name */
void clear_threadname(void)
{
void *addr = pthread_getspecific(thread_name_key);
if (addr) {
pthread_setspecific(thread_name_key, NULL);
free(addr);
}
}
/* Remove any number of trailing characters @c in @str */
void chomp(char *str, char *c)
{
ssize_t i;
for (i = strlen(str) - 1; i >= 0 && strchr(c, str[i]); i--)
str[i] = 0;
}
/* Remove any number of leading characters @c in @str */
void lchomp(char *str, char *c)
{
char *from;
for (from = str; *from && strchr(c, *from); from++)
;
if (str != from)
memmove(str, from, strlen(from) + 1);
}
/* Perform a stat on file referenced by either @abs or @rel and @dref. Store
* results in @stat and return stat()'s return code. */
int stat_file(bool dereference, const char *abs, const char *rel,
struct dref *dref, struct stat *st)
{
int rc;
if (dref) {
if (dereference)
rc = fstatat(dref->dirfd, rel, st, 0);
else
rc = fstatat(dref->dirfd, rel, st, AT_SYMLINK_NOFOLLOW);
} else {
if (dereference)
rc = stat(abs, st);
else
rc = lstat(abs, st);
}
return rc;
}
/* Fill stat buffer @st with dummy values. */
void set_dummy_stat(struct stat *st)
{
/* Fake stat */
memset(st, 0, sizeof(struct stat));
st->st_mode = S_IRUSR | S_IWUSR | S_IFREG;
st->st_uid = geteuid();
st->st_gid = getegid();
st->st_mtime = time(NULL);
}
/* Redirect all output streams to @fd and execute command @CMD */
int cmd_child(int fd, char *cmd)
{
char *argv[] = { "/bin/sh", "-c", NULL, NULL };
char *env[] = { NULL };
argv[2] = cmd;
if (dup2(fd, STDOUT_FILENO) == -1 || dup2(fd, STDERR_FILENO) == -1) {
mwarn("Could not redirect command output");
return EXIT_RUNTIME;
}
execve("/bin/sh", argv, env);
return EXIT_RUNTIME;
}
#define PIPE_READ 0
#define PIPE_WRITE 1
/* Run command @cmd as a child process and store its PID in @pid_ptr. On
* success, return a file descriptor that is an output pipe to the standard
* output and standard error streams of the child process. Return %-1 on
* error. */
int cmd_open(char *cmd, pid_t *pid_ptr)
{
int pfd[2];
pid_t pid;
if (pipe(pfd) < 0)
return -1;
pid = fork();
if (pid < 0) {
/* Fork error */
close(pfd[PIPE_READ]);
close(pfd[PIPE_WRITE]);
return -1;
} else if (pid == 0) {
/* Child process */
close(pfd[PIPE_READ]);
exit(cmd_child(pfd[PIPE_WRITE], cmd));
}
/* Parent process */
close(pfd[PIPE_WRITE]);
*pid_ptr = pid;
return pfd[PIPE_READ];
}
/* Close the file descriptor @fd and end the process with PID @pid. When
* not %NULL, use @status_ptr to store the resulting process status. */
int cmd_close(int fd, pid_t pid, int *status_ptr)
{
int status, rc = EXIT_OK;
close(fd);
kill(pid, SIGQUIT);
if (waitpid(pid, &status, 0) == -1) {
status = -errno;
rc = EXIT_RUNTIME;
}
if (status_ptr)
*status_ptr = status;
return rc;
}
void misc_init(void)
{
set_timespec(&main_start_ts, 0, 0);
pthread_key_create(&thread_name_key, free);
set_threadname("main");
}
void misc_cleanup(void)
{
clear_threadname();
pthread_key_delete(thread_name_key);
}
void set_stdout_data(void)
{
stdout_data = true;
}
bool starts_with(const char *str, const char *prefix)
{
size_t len;
len = strlen(prefix);
if (strncmp(str, prefix, len) == 0)
return true;
return false;
}
bool ends_with(const char *str, const char *suffix)
{
size_t str_len, s_len;
str_len = strlen(str);
s_len = strlen(suffix);
if (str_len < s_len)
return false;
if (strcmp(str + str_len - s_len, suffix) != 0)
return false;
return true;
}
/* Remove subsequent slashes in @str */
void remove_double_slashes(char *str)
{
size_t i, to;
char last;
last = 0;
for (i = 0, to = 0; str[i]; i++) {
if (last != '/' || str[i] != '/')
last = str[to++] = str[i];
}
str[to] = 0;
}