Skip to content

Instantly share code, notes, and snippets.

@tuxillo
Created February 26, 2013 22:40
Show Gist options
  • Select an option

  • Save tuxillo/5043012 to your computer and use it in GitHub Desktop.

Select an option

Save tuxillo/5043012 to your computer and use it in GitHub Desktop.
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <err.h>
#include <dirent.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/limits.h>
#include <sys/stat.h>
#include <sys/param.h>
#include <sys/un.h>
#include <sys/event.h>
#define VCMUX_SOCKDEAD 0
#define VCMUX_SOCKALIVE 1
#define VCMUX_SOCKUNKNOWN 2
#define VCMUX_MAXCLIENTS 128
#define VCMUX_MAXSOCKETS 1024
#define VCMUX_PATHPREFIX "/tmp/vkernel"
struct kevent changes[VCMUX_MAXCLIENTS];
struct kevent events[VCMUX_MAXCLIENTS];
struct vcmux {
char *sockpath; /* Socket absolute path */
char *sockdir; /* Socket directory */
int sockfd; /* Socket file descriptor */
struct sockaddr_un sockaddr;
/* Client connections */
int clients[VCMUX_MAXCLIENTS];
};
typedef struct vcmux * vcmux_t;
static int
vcmux_issocket(char *path)
{
struct stat st;
if (stat(path, &st) < 0)
return -1;
return S_ISSOCK(st.st_mode);
}
static int
vcmux_socket_access(char *path)
{
struct stat st;
return ((stat(path, &st) == 0));
}
static int
vcmux_socket_isalive(char *path)
{
struct sockaddr_un sa;
int ret = VCMUX_SOCKUNKNOWN;
int fd;
if (!vcmux_socket_access(path))
return VCMUX_SOCKUNKNOWN;
fd = socket(PF_LOCAL, SOCK_STREAM, 0);
if (fd == -1)
return VCMUX_SOCKUNKNOWN;
sa.sun_family = PF_LOCAL;
strlcpy(sa.sun_path, path, 104);
if (connect(fd, (struct sockaddr *)&sa, sizeof(sa)) == 0)
ret = VCMUX_SOCKALIVE;
else
if (errno == ECONNREFUSED)
ret = VCMUX_SOCKDEAD;
printf("%s: closing %d\n", __func__, fd);
close(fd);
return ret;
}
/*
* Can be used at startup to clean up all the not-connected
* sockets in specified basepath.
*
* XXX - Not very efficient if we have hundreds of vkernels.
*
*/
static int
vcmux_cleandead(char *basepath)
{
char path[MAXPATHLEN];
struct dirent *dp;
DIR *dirp;
dirp = opendir(basepath);
if (dirp == NULL)
return -1;
while ((dp = readdir(dirp)) != NULL) {
snprintf(path, MAXPATHLEN, "%s/%s", basepath, dp->d_name);
if (vcmux_socket_access(path))
if (vcmux_socket_isalive(path) == VCMUX_SOCKDEAD)
unlink(path);
}
closedir(dirp);
return 0;
}
/*
* Returns socket directory based on user ID
*/
void
vcmux_sockdir(char *buf)
{
if (buf)
snprintf(buf, MAXPATHLEN, VCMUX_PATHPREFIX "-%d", getuid());
}
/*
* Create a new vcmux object
*/
vcmux_t
vcmux_open(void)
{
char bpath[MAXPATHLEN];
char fpath[MAXPATHLEN];
struct stat st;
vcmux_t vcm;
int gotfree = 0;
int num;
/* Too big for a path prefix */
if (strlen(VCMUX_PATHPREFIX) >= MAXPATHLEN)
return NULL;
vcm = malloc(sizeof(*vcm));
/*
* Attempt to create the base directory where the vcmux
* sockets will be stored.
*/
vcmux_sockdir(bpath);
if ((stat(bpath, &st)) < 0) {
if (errno == ENOENT)
if ((mkdir(bpath, S_IRWXU | S_IRWXG)) < 0)
return NULL;
}
/*
* Find next free vcmux socket name available as
* a vcmux per vkernel(7) is needed. No visibility
* among vkernel should be available.
*/
for (num = 0; num < VCMUX_MAXSOCKETS; num++) {
snprintf(fpath, MAXPATHLEN, "%s/vkernel-%d", bpath, num);
if (vcmux_issocket(fpath))
if (!vcmux_socket_access(fpath) && errno == ENOENT) {
gotfree = 1;
break;
}
}
if (!gotfree)
return NULL;
/* Fill in what we got so far. */
vcm->sockdir = strdup(bpath);
vcm->sockpath = strdup(fpath);
return vcm;
}
/*
* Destroy a new vcmux object
*/
void
vcmux_close(vcmux_t vcm)
{
if (vcm->sockdir)
free(vcm->sockdir);
if (vcm->sockpath)
free(vcm->sockpath);
free(vcm);
}
static int
vcmux_newconn(vcmux_t vcm, int *nchanges)
{
int fd;
socklen_t rsize;
int chg = *nchanges;
fd = accept(vcm->sockfd, NULL, &rsize);
/*
* XXX - As we limit the number of clients to
* VCMUX_MAXCLIENTS, we should really keep track
* of the fds in a more reliable way. i.e. if
* fd > VCMUX_MAXCLIENTS is opened we're in trouble!
*/
if (fd != -1) {
vcm->clients[fd] = fd;
EV_SET(&changes[chg++], fd, EVFILT_READ,
EV_ADD | EV_ENABLE, 0, 0, NULL);
}
*nchanges = chg;
return fd;
}
static int
vcmux_server(vcmux_t vcm)
{
int nchanges, nevents;
int error, ret, ev;
int kq, newfd, evfd;
size_t nbytes;
char buf[512];
error = ret = nchanges = nevents = 0;
ev = newfd = evfd = 0;
if (vcm == NULL)
return -1;
vcm->sockfd = socket(PF_LOCAL, SOCK_STREAM, 0);
if (vcm->sockfd == -1)
return -1;
bzero(&vcm->sockaddr, sizeof(vcm->sockaddr));
vcm->sockaddr.sun_family = AF_LOCAL;
/*
* XXX sun_path is only 104 chars in DragonFly BSD!
* MAXPATHLEN can't be used with vcm->sockpath, ffs.
*/
snprintf(vcm->sockaddr.sun_path, sizeof(vcm->sockaddr.sun_path),
"%s",vcm->sockpath);
unlink(vcm->sockaddr.sun_path);
error = bind(vcm->sockfd, (struct sockaddr *)&vcm->sockaddr,
sizeof(vcm->sockaddr));
if (error == -1)
return -1;
error = listen(vcm->sockfd, 0);
if (error == -1)
return -1;
if ((kq = kqueue()) == -1)
goto endconn;
memset(&changes, 0, sizeof(changes));
memset(&events, 0, sizeof(events));
EV_SET(&changes[0], vcm->sockfd, EVFILT_READ, EV_ADD,
0, 0, 0);
nchanges = 1;
printf("Starting up server on %s\n", vcm->sockaddr.sun_path);
for (;;) {
nevents = kevent(kq, changes, nchanges, events,
VCMUX_MAXCLIENTS, NULL);
nchanges = 0;
if (nevents == -1)
errx(1, "kevent failed");
for (ev = 0; ev < nevents; ev++) {
evfd = events[ev].ident;
/* New incoming connection handling */
if (evfd == vcm->sockfd) {
newfd = vcmux_newconn(vcm, &nchanges);
printf("New connection on fd=%d\n", newfd);
} else {
if (events[ev].filter & EVFILT_READ) {
printf("Existing connection on fd=%d flags=%d\n", evfd, events[ev].flags);
if (events[ev].flags & EV_EOF) {
printf("Bye bye %d\n", evfd);
close(evfd);
} else {
bzero(buf, 512);
nbytes = read(evfd, buf, 512);
if (nbytes > 0)
printf("%s", buf);
}
} else
printf("Unknown filter handler\n");
}
}
}
endconn:
if (vcm->sockfd > 0)
close(vcm->sockfd);
return ret;
}
int
main(int argc, char *argv[])
{
char buf[MAXPATHLEN];
vcmux_t vcm;
vcmux_sockdir(buf);
printf("Cleaning dead sockets on %s\n", buf);
vcmux_cleandead(buf);
vcm = vcmux_open();
printf("%s %s \n", vcm->sockdir, vcm->sockpath);
vcmux_server(vcm);
vcmux_close(vcm);
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment