util.c
222 lines
| 4.3 KiB
| text/x-c
|
CLexer
Yuya Nishihara
|
r28060 | /* | ||
* Utility functions | ||||
* | ||||
* Copyright (c) 2011 Yuya Nishihara <yuya@tcha.org> | ||||
* | ||||
* This software may be used and distributed according to the terms of the | ||||
* GNU General Public License version 2 or any later version. | ||||
*/ | ||||
Jun Wu
|
r28788 | #include <errno.h> | ||
Jun Wu
|
r28855 | #include <fcntl.h> | ||
Yuya Nishihara
|
r28060 | #include <signal.h> | ||
#include <stdarg.h> | ||||
#include <stdio.h> | ||||
#include <stdlib.h> | ||||
Yuya Nishihara
|
r28084 | #include <string.h> | ||
Jun Wu
|
r34310 | #include <sys/time.h> | ||
Yuya Nishihara
|
r28060 | #include <sys/types.h> | ||
#include <sys/wait.h> | ||||
#include <unistd.h> | ||||
#include "util.h" | ||||
Jun Wu
|
r28787 | static int colorenabled = 0; | ||
static inline void fsetcolor(FILE *fp, const char *code) | ||||
{ | ||||
Augie Fackler
|
r41367 | if (!colorenabled) { | ||
Jun Wu
|
r28787 | return; | ||
Augie Fackler
|
r41367 | } | ||
Jun Wu
|
r28787 | fprintf(fp, "\033[%sm", code); | ||
} | ||||
Jun Wu
|
r28788 | static void vabortmsgerrno(int no, const char *fmt, va_list args) | ||
{ | ||||
fsetcolor(stderr, "1;31"); | ||||
fputs("chg: abort: ", stderr); | ||||
vfprintf(stderr, fmt, args); | ||||
Augie Fackler
|
r41367 | if (no != 0) { | ||
Jun Wu
|
r28788 | fprintf(stderr, " (errno = %d, %s)", no, strerror(no)); | ||
Augie Fackler
|
r41367 | } | ||
Jun Wu
|
r28788 | fsetcolor(stderr, ""); | ||
fputc('\n', stderr); | ||||
exit(255); | ||||
} | ||||
Yuya Nishihara
|
r28060 | void abortmsg(const char *fmt, ...) | ||
{ | ||||
va_list args; | ||||
va_start(args, fmt); | ||||
Jun Wu
|
r28788 | vabortmsgerrno(0, fmt, args); | ||
Yuya Nishihara
|
r28060 | va_end(args); | ||
Jun Wu
|
r28788 | } | ||
Yuya Nishihara
|
r28060 | |||
Jun Wu
|
r28788 | void abortmsgerrno(const char *fmt, ...) | ||
{ | ||||
int no = errno; | ||||
va_list args; | ||||
va_start(args, fmt); | ||||
vabortmsgerrno(no, fmt, args); | ||||
va_end(args); | ||||
Yuya Nishihara
|
r28060 | } | ||
static int debugmsgenabled = 0; | ||||
Jun Wu
|
r34310 | static double debugstart = 0; | ||
Augie Fackler
|
r35977 | static double now() | ||
{ | ||||
Jun Wu
|
r34310 | struct timeval t; | ||
gettimeofday(&t, NULL); | ||||
return t.tv_usec / 1e6 + t.tv_sec; | ||||
} | ||||
Yuya Nishihara
|
r28060 | |||
Jun Wu
|
r28787 | void enablecolor(void) | ||
{ | ||||
colorenabled = 1; | ||||
} | ||||
Yuya Nishihara
|
r28060 | void enabledebugmsg(void) | ||
{ | ||||
debugmsgenabled = 1; | ||||
Jun Wu
|
r34310 | debugstart = now(); | ||
Yuya Nishihara
|
r28060 | } | ||
void debugmsg(const char *fmt, ...) | ||||
{ | ||||
Augie Fackler
|
r41367 | if (!debugmsgenabled) { | ||
Yuya Nishihara
|
r28060 | return; | ||
Augie Fackler
|
r41367 | } | ||
Yuya Nishihara
|
r28060 | |||
va_list args; | ||||
va_start(args, fmt); | ||||
Jun Wu
|
r28787 | fsetcolor(stderr, "1;30"); | ||
Jun Wu
|
r34310 | fprintf(stderr, "chg: debug: %4.6f ", now() - debugstart); | ||
Yuya Nishihara
|
r28060 | vfprintf(stderr, fmt, args); | ||
Jun Wu
|
r28787 | fsetcolor(stderr, ""); | ||
fputc('\n', stderr); | ||||
Yuya Nishihara
|
r28060 | va_end(args); | ||
} | ||||
Jun Wu
|
r28854 | void fchdirx(int dirfd) | ||
{ | ||||
int r = fchdir(dirfd); | ||||
Augie Fackler
|
r41367 | if (r == -1) { | ||
Jun Wu
|
r28854 | abortmsgerrno("failed to fchdir"); | ||
Augie Fackler
|
r41367 | } | ||
Jun Wu
|
r28854 | } | ||
Jun Wu
|
r28855 | void fsetcloexec(int fd) | ||
{ | ||||
int flags = fcntl(fd, F_GETFD); | ||||
Augie Fackler
|
r41367 | if (flags < 0) { | ||
Jun Wu
|
r28855 | abortmsgerrno("cannot get flags of fd %d", fd); | ||
Augie Fackler
|
r41367 | } | ||
if (fcntl(fd, F_SETFD, flags | FD_CLOEXEC) < 0) { | ||||
Jun Wu
|
r28855 | abortmsgerrno("cannot set flags of fd %d", fd); | ||
Augie Fackler
|
r41367 | } | ||
Jun Wu
|
r28855 | } | ||
Jun Wu
|
r28165 | void *mallocx(size_t size) | ||
{ | ||||
void *result = malloc(size); | ||||
Augie Fackler
|
r41367 | if (!result) { | ||
Jun Wu
|
r28165 | abortmsg("failed to malloc"); | ||
Augie Fackler
|
r41367 | } | ||
Jun Wu
|
r28165 | return result; | ||
} | ||||
void *reallocx(void *ptr, size_t size) | ||||
{ | ||||
void *result = realloc(ptr, size); | ||||
Augie Fackler
|
r41367 | if (!result) { | ||
Jun Wu
|
r28165 | abortmsg("failed to realloc"); | ||
Augie Fackler
|
r41367 | } | ||
Jun Wu
|
r28165 | return result; | ||
} | ||||
Yuya Nishihara
|
r28060 | /* | ||
* Execute a shell command in mostly the same manner as system(), with the | ||||
* give environment variables, after chdir to the given cwd. Returns a status | ||||
* code compatible with the Python subprocess module. | ||||
*/ | ||||
int runshellcmd(const char *cmd, const char *envp[], const char *cwd) | ||||
{ | ||||
enum { F_SIGINT = 1, F_SIGQUIT = 2, F_SIGMASK = 4, F_WAITPID = 8 }; | ||||
unsigned int doneflags = 0; | ||||
int status = 0; | ||||
struct sigaction newsa, oldsaint, oldsaquit; | ||||
sigset_t oldmask; | ||||
/* block or mask signals just as system() does */ | ||||
Yuya Nishihara
|
r28084 | memset(&newsa, 0, sizeof(newsa)); | ||
Yuya Nishihara
|
r28060 | newsa.sa_handler = SIG_IGN; | ||
newsa.sa_flags = 0; | ||||
Augie Fackler
|
r41367 | if (sigemptyset(&newsa.sa_mask) < 0) { | ||
Yuya Nishihara
|
r28060 | goto done; | ||
Augie Fackler
|
r41367 | } | ||
if (sigaction(SIGINT, &newsa, &oldsaint) < 0) { | ||||
Yuya Nishihara
|
r28060 | goto done; | ||
Augie Fackler
|
r41367 | } | ||
Yuya Nishihara
|
r28060 | doneflags |= F_SIGINT; | ||
Augie Fackler
|
r41367 | if (sigaction(SIGQUIT, &newsa, &oldsaquit) < 0) { | ||
Yuya Nishihara
|
r28060 | goto done; | ||
Augie Fackler
|
r41367 | } | ||
Yuya Nishihara
|
r28060 | doneflags |= F_SIGQUIT; | ||
Augie Fackler
|
r41367 | if (sigaddset(&newsa.sa_mask, SIGCHLD) < 0) { | ||
Yuya Nishihara
|
r28060 | goto done; | ||
Augie Fackler
|
r41367 | } | ||
if (sigprocmask(SIG_BLOCK, &newsa.sa_mask, &oldmask) < 0) { | ||||
Yuya Nishihara
|
r28060 | goto done; | ||
Augie Fackler
|
r41367 | } | ||
Yuya Nishihara
|
r28060 | doneflags |= F_SIGMASK; | ||
pid_t pid = fork(); | ||||
Augie Fackler
|
r41367 | if (pid < 0) { | ||
Yuya Nishihara
|
r28060 | goto done; | ||
Augie Fackler
|
r41367 | } | ||
Yuya Nishihara
|
r28060 | if (pid == 0) { | ||
sigaction(SIGINT, &oldsaint, NULL); | ||||
sigaction(SIGQUIT, &oldsaquit, NULL); | ||||
sigprocmask(SIG_SETMASK, &oldmask, NULL); | ||||
Augie Fackler
|
r41367 | if (cwd && chdir(cwd) < 0) { | ||
Yuya Nishihara
|
r28060 | _exit(127); | ||
Augie Fackler
|
r41367 | } | ||
Yuya Nishihara
|
r28060 | const char *argv[] = {"sh", "-c", cmd, NULL}; | ||
if (envp) { | ||||
execve("/bin/sh", (char **)argv, (char **)envp); | ||||
} else { | ||||
execv("/bin/sh", (char **)argv); | ||||
} | ||||
_exit(127); | ||||
} else { | ||||
Augie Fackler
|
r41367 | if (waitpid(pid, &status, 0) < 0) { | ||
Yuya Nishihara
|
r28060 | goto done; | ||
Augie Fackler
|
r41367 | } | ||
Yuya Nishihara
|
r28060 | doneflags |= F_WAITPID; | ||
} | ||||
done: | ||||
Augie Fackler
|
r41367 | if (doneflags & F_SIGINT) { | ||
Yuya Nishihara
|
r28060 | sigaction(SIGINT, &oldsaint, NULL); | ||
Augie Fackler
|
r41367 | } | ||
if (doneflags & F_SIGQUIT) { | ||||
Yuya Nishihara
|
r28060 | sigaction(SIGQUIT, &oldsaquit, NULL); | ||
Augie Fackler
|
r41367 | } | ||
if (doneflags & F_SIGMASK) { | ||||
Yuya Nishihara
|
r28060 | sigprocmask(SIG_SETMASK, &oldmask, NULL); | ||
Augie Fackler
|
r41367 | } | ||
Yuya Nishihara
|
r28060 | |||
/* no way to report other errors, use 127 (= shell termination) */ | ||||
Augie Fackler
|
r41367 | if (!(doneflags & F_WAITPID)) { | ||
Yuya Nishihara
|
r28060 | return 127; | ||
Augie Fackler
|
r41367 | } | ||
if (WIFEXITED(status)) { | ||||
Yuya Nishihara
|
r28060 | return WEXITSTATUS(status); | ||
Augie Fackler
|
r41367 | } | ||
if (WIFSIGNALED(status)) { | ||||
Yuya Nishihara
|
r28060 | return -WTERMSIG(status); | ||
Augie Fackler
|
r41367 | } | ||
Yuya Nishihara
|
r28060 | return 127; | ||
} | ||||