Created
February 26, 2013 22:40
-
-
Save tuxillo/5043012 to your computer and use it in GitHub Desktop.
This file contains hidden or 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
| #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