Skip to content

Instantly share code, notes, and snippets.

@mwgamera
Created June 18, 2012 21:32
Show Gist options
  • Save mwgamera/2950857 to your computer and use it in GitHub Desktop.
Save mwgamera/2950857 to your computer and use it in GitHub Desktop.
/* 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