Created
June 18, 2012 21:32
-
-
Save mwgamera/2950857 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* gcc -O3 -ansi -Wall -Wextra -pedantic -static termwait.c -lrt -o termwait */ | |
#define _POSIX_C_SOURCE 200809L | |
#include <errno.h> | |
#include <fcntl.h> | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <string.h> | |
#include <sys/mman.h> | |
#include <sys/stat.h> | |
#include <sys/wait.h> | |
#include <termios.h> | |
#include <time.h> | |
#include <unistd.h> | |
/* Structure of shared object */ | |
struct msg { | |
int error; /* error */ | |
struct timespec ts_main; /* executed */ | |
struct timespec ts_term; /* terminal prepared */ | |
struct timespec ts_ident; /* reply received */ | |
}; | |
static struct timespec TS_INF = { -1, -1 }; | |
/* Shared object name */ | |
#define SHM_PATH "/termwait.shm." | |
#define SHM_PATH_SLEN 64 | |
#define SHM_ENV "TERMWAIT_SHM" | |
/* Prepare randomized name for shared object */ | |
static char *randpath(void *seed, size_t len) { | |
static char hex[16] = "0123456789abcdef"; | |
static char path[SHM_PATH_SLEN] = SHM_PATH; | |
unsigned k, i = sizeof(SHM_PATH)-1, h = getpid(); | |
#define MIX(h) { (h) ^= (h) << 13; (h) ^= (h) >> 17; (h) ^= (h) << 5; } | |
for (k = 0; h >> k; k+=4) | |
path[i++] = hex[(h>>k) & 0xf]; | |
path[i++] = '.'; | |
h ^= 2463534242; | |
for (k = 0; k < len; k++) { | |
MIX(h); h ^= ((char*)seed)[k]; | |
} | |
for (; i < sizeof(path)-1; i++) { | |
MIX(h); path[i] = hex[h & 0xf]; | |
} | |
#undef MIX | |
path[i] = '\0'; | |
return path; | |
} | |
/* Prepare shared memory object and expose its name in environment */ | |
static struct msg *msg_create(char *path) { | |
struct msg *shm; | |
int md; | |
if (setenv(SHM_ENV, path, 1)) { | |
return NULL; | |
} | |
md = shm_open(path, O_RDWR|O_CREAT|O_EXCL, S_IRUSR|S_IWUSR); | |
if (md < 0) | |
return NULL; | |
(void) ftruncate(md, sizeof *shm); | |
shm = (struct msg*) mmap(NULL, sizeof *shm, | |
PROT_READ|PROT_WRITE, MAP_SHARED, md, 0); | |
(void) close(md); | |
if (shm == MAP_FAILED) { | |
(void) shm_unlink(SHM_PATH); | |
return NULL; | |
} | |
return shm; | |
} | |
/* Remove shared memory object */ | |
static int msg_remove(struct msg *shm) { | |
(void) munmap(shm, sizeof *shm); | |
return shm_unlink(getenv(SHM_ENV)); | |
} | |
/* Push data to shared object */ | |
static int msg_send(struct msg dat) { | |
struct msg *shm; | |
char *path = getenv(SHM_ENV); | |
int md; | |
if (!path) | |
return -1; | |
if ((md = shm_open(path, O_RDWR, 0)) < 0) | |
return errno; | |
shm = (struct msg*) mmap(NULL, sizeof *shm, | |
PROT_READ|PROT_WRITE, MAP_SHARED, md, 0); | |
(void) close(md); | |
if (shm == MAP_FAILED) | |
return errno; | |
*shm = dat; | |
(void) munmap(shm, sizeof *shm); | |
return 0; | |
} | |
/* Interrogate terminal: VT100 ident, ASCII ENQ, VT52 ident */ | |
#define TERM_ENQ "\033[c\005\033Z" | |
/* Wait for terminal's reply and record timing */ | |
static int termwait(struct msg *dat) { | |
struct termios tp, rp; | |
int td; | |
char buf; | |
if ((td = open("/dev/tty", O_RDWR | O_SYNC)) < 0) | |
return dat->error = errno; | |
if (tcgetattr(td, &rp)) | |
return dat->error = errno; | |
(void) memset(&tp, 0, sizeof(tp)); | |
tp.c_cc[VTIME] = 30; | |
tp.c_cc[VMIN] = 1; | |
if (tcsetattr(td, TCSAFLUSH, &tp)) | |
return dat->error = errno; | |
dat->ts_term = TS_INF; | |
clock_gettime(CLOCK_REALTIME, &dat->ts_term); | |
if (write(td, TERM_ENQ, sizeof(TERM_ENQ)) < 0) | |
dat->error = errno; | |
else | |
if ((buf = read(td, &buf, 1)) < 0) | |
dat->error = errno; | |
clock_gettime(CLOCK_REALTIME, &dat->ts_ident); | |
if (!buf) | |
dat->ts_ident = TS_INF; | |
(void) tcflush(td, TCIOFLUSH); | |
(void) tcsetattr(td, TCSANOW, &rp); | |
(void) tcdrain(td); | |
(void) close(td); | |
return 0; | |
} | |
/* Subtract time specs */ | |
static struct timespec ts_sub(struct timespec a, struct timespec b) { | |
if (a.tv_nsec == -1) | |
return a; /* infty */ | |
if (b.tv_nsec > a.tv_nsec) { | |
a.tv_nsec += 1000000000L; | |
a.tv_sec--; | |
} | |
a.tv_nsec -= b.tv_nsec; | |
a.tv_sec -= b.tv_sec; | |
return a; | |
} | |
/* Execute given command and read results */ | |
static int termexec(struct msg *shm, char *path, char **argv) { | |
struct timespec base; | |
pid_t child; | |
int stat; | |
shm->error = 0; | |
shm->ts_main = TS_INF; | |
clock_gettime(CLOCK_REALTIME, &base); | |
if (!(child = fork())) { | |
execv(path, argv); | |
perror("execv"); | |
exit(errno); | |
} | |
(void) waitpid(child, &stat, 0); | |
if (stat) | |
shm->error = WEXITSTATUS(stat); | |
shm->ts_main = ts_sub(shm->ts_main, base); | |
shm->ts_term = ts_sub(shm->ts_term, base); | |
shm->ts_ident = ts_sub(shm->ts_ident, base); | |
return 0; | |
} | |
/* Print results */ | |
static void ts_print(struct timespec ts) { | |
if (ts.tv_nsec == -1) | |
printf("inf\n"); | |
else | |
printf("%lu.%09ld\n", ts.tv_sec, ts.tv_nsec); | |
} | |
static void msg_print(struct msg dat) { | |
if (dat.error) | |
fprintf(stderr, "Error: %s\n", | |
strerror(dat.error)); | |
ts_print(dat.ts_main); | |
/* ts_print(dat.ts_term); *//* term-main is negligible */ | |
ts_print(dat.ts_ident); | |
} | |
int main(int argc, char **argv) { | |
struct msg dat; | |
clock_gettime(CLOCK_REALTIME, &dat.ts_main); | |
if (argc < 2) { | |
dat.error = 0; | |
termwait(&dat); | |
msg_send(dat); | |
} | |
else { | |
struct msg *shm = msg_create(randpath(&dat, sizeof(dat))); | |
if (!shm) { | |
perror("Creating shared memory object"); | |
exit(1); | |
} | |
argv[argc] = NULL; /* sic */ | |
(void) termexec(shm, argv[1], argv+1); | |
msg_print(*shm); | |
(void) msg_remove(shm); | |
} | |
exit(0); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment