#include #include #include #include #include #include #include extern const char *__progname; #include "nf.h" #include "cli.h" #include "node.h" #include "link.h" #include "ctype.h" #include "forker.h" #include "pollloop.h" #include "scm-rights.h" #define NNODES 3 typedef struct node NODE; typedef struct link LINK; struct link { LINK *flink; LINK *blink; NODE *a; NODE *b; int cfd; int cid; int dfd; int did; } ; struct node { char *name; int namelen; int inx; int cfd; int cid; int dfd; int did; } ; static NODE nodes[NNODES]; static LINK *links; static int forker_cfd; static int forker_dfd; static int cli_cfd; static int cli_dfd; static unsigned char *msgbuf; static int msglen; #define dequal(x) ((void *)((((const volatile char *)(x))-(const volatile char *)0)+(char *)0)) static void rd_forker_dfd(void *arg __attribute__((__unused__))) { fprintf(stderr,"%s: parent: forker death pipe readable\n",__progname); exit(0); } static void init_forker(void) { int cp[2]; int dp[2]; nf_socketpair(AF_LOCAL,SOCK_DGRAM,0,&cp[0]); nf_socketpair(AF_LOCAL,SOCK_STREAM,0,&dp[0]); if (nf_fork()) { close(cp[0]); close(dp[0]); forker_cfd = cp[1]; forker_dfd = dp[1]; add_poll_fd(forker_dfd,&rwtest_always,&rwtest_never,&rd_forker_dfd,0,0); return; } close(cp[1]); close(dp[1]); forker_main(cp[0],dp[0]); _exit(0); } static void send_data_with_fds(int fd, const void *data, int datalen, int *fdv, int nfds) { static unsigned char *ctlbuf = 0; static int ctlspace = 0; int ctlwant; struct msghdr mh; struct cmsghdr cmh; struct iovec iov; ctlwant = CMSPACE(nfds*sizeof(int)); if (ctlwant > ctlspace) { free(ctlbuf); ctlspace = ctlwant; ctlbuf = malloc(ctlspace); } iov.iov_base = dequal(data); iov.iov_len = datalen; cmh.cmsg_len = CMLEN(nfds*sizeof(int)); cmh.cmsg_level = SOL_SOCKET; cmh.cmsg_type = SCM_RIGHTS; bcopy(&cmh,&ctlbuf[0],sizeof(struct cmsghdr)); bcopy(fdv,&ctlbuf[CMSKIP(&cmh)],nfds*sizeof(int)); mh.msg_name = 0; mh.msg_namelen = 0; mh.msg_iov = &iov; mh.msg_iovlen = 1; mh.msg_control = (void *)&ctlbuf[0]; mh.msg_controllen = CMLEN(nfds*sizeof(int)); mh.msg_flags = 0; nf_sendmsg(fd,&mh,0); } static void fork_child(void (*fn)(int, int), int *fdp) { int cp[2]; int dp[2]; int fdv[2]; nf_socketpair(AF_LOCAL,SOCK_DGRAM,0,&cp[0]); nf_socketpair(AF_LOCAL,SOCK_STREAM,0,&dp[0]); fdv[0] = cp[0]; fdv[1] = dp[0]; send_data_with_fds(forker_cfd,&fn,sizeof(fn),&fdv[0],2); close(cp[0]); close(dp[0]); fdp[0] = cp[1]; fdp[1] = dp[1]; } static void cli_bufsize(int l) { unsigned char msg[1+sizeof(int)]; msg[0] = CLIMSG_BUFSIZE; bcopy(&l,&msg[1],sizeof(int)); nf_send(cli_cfd,&msg[0],1+sizeof(int),0); } static void cli_output(const void *s, int len) { unsigned char pref; struct iovec iov[2]; struct msghdr mh; cli_bufsize(len+1); pref = CLIMSG_TEXT; iov[0].iov_base = &pref; iov[0].iov_len = 1; iov[1].iov_base = dequal(s); iov[1].iov_len = len; mh.msg_name = 0; mh.msg_namelen = 0; mh.msg_iov = &iov[0]; mh.msg_iovlen = 2; mh.msg_control = 0; mh.msg_controllen = 0; mh.msg_flags = 0; nf_sendmsg(cli_cfd,&mh,0); } static void cli_printf(const char *, ...) __attribute__((__format__(__printf__,1,2))); static void cli_printf(const char *fmt, ...) { va_list ap; char *s; int len; va_start(ap,fmt); len = vasprintf(&s,fmt,ap); va_end(ap); if (len < 1) { free(s); return; } cli_output(s,len); free(s); } static void msgbuf_room(int nb) { if (msglen < nb) { free(msgbuf); msglen = nb; msgbuf = malloc(msglen); } } static void node_rctl(void *nv) { NODE *n; int r; int v; n = nv; r = recv(n->cfd,msgbuf,msglen,0); if (r < 0) { fprintf(stderr,"%s: parent: node %s recv: %s\n",__progname,n->name,strerror(errno)); exit(1); } if (r == 0) { fprintf(stderr,"%s: parent: node %s empty recv\n",__progname,n->name); exit(1); } switch (msgbuf[0]) { case NODEMSG_BUFSIZE: if (r != 1+sizeof(int)) { fprintf(stderr,"%s: parent: node %s BUFSIZE msg size (%d) wrong (not %d)\n",__progname,n->name,r,1+(int)sizeof(int)); exit(1); } bcopy(&msgbuf[1],&v,sizeof(int)); msgbuf_room(v); break; case NODEMSG_OUTPUT: cli_output(msgbuf+1,r-1); break; default: fprintf(stderr,"%s: parent: node %s msg %02x\n",__progname,n->name,msgbuf[0]); exit(1); break; } } static void node_death(void *nv) { NODE *n; n = nv; fprintf(stderr,"%s: death of node %s\n",__progname,n->name); exit(1); } static void start_node(int inx) { NODE *n; int fds[2]; int i; fork_child(&node_main,&fds[0]); n = &nodes[inx]; n->name = malloc(2); n->name[0] = 'A' + inx; n->name[1] = '\0'; n->namelen = 1; n->inx = inx; n->cfd = fds[0]; n->cid = add_poll_fd(fds[0],&rwtest_always,&rwtest_never,&node_rctl,0,n); n->dfd = fds[1]; n->did = add_poll_fd(fds[1],&rwtest_always,&rwtest_never,&node_death,0,n); i = strlen(n->name); nf_send(n->cfd,&i,sizeof(int),0); nf_send(n->cfd,n->name,i,0); nf_send(n->cfd,&inx,sizeof(int),0); } static void init_msg(void) { msglen = 0; msgbuf = 0; msgbuf_room(1+sizeof(int)); } static void init_nodes(void) { int i; for (i=NNODES-1;i>=0;i--) start_node(i); } static void init_links(void) { links = 0; } static void tell_cli_prompt(void) { unsigned char msg; msg = CLIMSG_PROMPT; nf_send(cli_cfd,&msg,1,0); } static void cli_cmd_show_nodes(void) { int i; for (i=0;i=0;i--) { n = &nodes[i]; if ((n->namelen == namelen) && !bcmp(name,n->name,namelen)) return(n); } return(0); } static void link_destroy(LINK *l) { if (l->blink) l->blink->flink = l->flink; else links = l->flink; if (l->flink) l->flink->blink = l->blink; close(l->cfd); close(l->dfd); remove_poll_id(l->cid); remove_poll_id(l->did); free(l); } static void rd_parent_link_ctl(void *lv) { LINK *l; int r; int v; l = lv; r = recv(l->cfd,msgbuf,msglen,0); if (r < 0) { fprintf(stderr,"%s: link %s <-> %s control recv: %s\n",__progname,l->a->name,l->b->name,strerror(errno)); exit(1); } if (r == 0) { fprintf(stderr,"%s: link %s <-> %s empty control recv\n",__progname,l->a->name,l->b->name); exit(1); } switch (msgbuf[0]) { case LINKMSG_DYING: cli_printf("Link %s <-> %s shutting down\n",l->a->name,l->b->name); link_destroy(l); break; case LINKMSG_BUFSIZE: if (r != 1+sizeof(int)) { fprintf(stderr,"%s: parent: link %s <-> %s BUFSIZE msg size (%d) wrong (not %d)\n",__progname,l->a->name,l->b->name,r,1+(int)sizeof(int)); exit(1); } bcopy(&msgbuf[1],&v,sizeof(int)); msgbuf_room(v); break; case LINKMSG_OUTPUT: cli_output(msgbuf+1,r-1); break; default: fprintf(stderr,"%s: link %s <-> %s control msg %02x\n",__progname,l->a->name,l->b->name,msgbuf[0]); break; } } static void rd_parent_link_death(void *lv) { LINK *l; l = lv; fprintf(stderr,"%s: link %s <-> %s death pipe readable\n",__progname,l->a->name,l->b->name); exit(1); } static void link_bringup(NODE *a, NODE *b, const char *params, int paramslen) { LINK *l; int fds[2]; int pa[2]; int pb[2]; int fdv[2]; unsigned char msg[1+sizeof(int)]; int lens[3]; for (l=links;l;l=l->flink) { if ( ((l->a == a) && (l->b == b)) || ((l->b == a) && (l->a == b)) ) { cli_printf("Already linked\n"); return; } } nf_socketpair(AF_LOCAL,SOCK_STREAM,0,&pa[0]); nf_socketpair(AF_LOCAL,SOCK_STREAM,0,&pb[0]); fork_child(&link_main,&fds[0]); l = malloc(sizeof(LINK)); l->a = a; l->b = b; l->cfd = fds[0]; l->cid = add_poll_fd(l->cfd,&rwtest_always,&rwtest_never,&rd_parent_link_ctl,0,l); l->dfd = fds[1]; l->did = add_poll_fd(l->dfd,&rwtest_always,&rwtest_never,&rd_parent_link_death,0,l); l->flink = links; l->blink = 0; links = l; if (l->flink) l->flink->blink = l; lens[0] = strlen(a->name); lens[1] = strlen(b->name); lens[2] = paramslen; fdv[0] = pa[0]; fdv[1] = pb[0]; send_data_with_fds(l->cfd,&lens[0],3*sizeof(int),&fdv[0],2); nf_send(l->cfd,a->name,lens[0],0); nf_send(l->cfd,b->name,lens[1],0); nf_send(l->cfd,params,paramslen,0); close(pa[0]); close(pb[0]); msg[0] = NODEMSG_LINKUP; bcopy(&b->inx,&msg[1],sizeof(int)); send_data_with_fds(a->cfd,&msg[0],1+sizeof(int),&pa[1],1); close(pa[1]); msg[0] = NODEMSG_LINKUP; bcopy(&a->inx,&msg[1],sizeof(int)); send_data_with_fds(b->cfd,&msg[0],1+sizeof(int),&pb[1],1); close(pb[1]); } static void link_takedown(NODE *a, NODE *b) { LINK *l; unsigned char msg; for (l=links;l;l=l->flink) { if ( ((l->a == a) && (l->b == b)) || ((l->b == a) && (l->a == b)) ) { msg = LINKMSG_DIE; nf_send(l->cfd,&msg,1,0); link_destroy(l); return; } } cli_printf("Not linked\n"); } static void cli_cmd_link(const char *args, int len) { int i; int a0; int al; int b0; int bl; int c0; NODE *a; NODE *b; for (i=0;(i buflen) { free(buf); buflen = nb; buf = malloc(buflen); } } buf_room(sizeof(int)+1); n = recv(cli_cfd,buf,buflen,0); if (n < 0) { fprintf(stderr,"%s: parent: cli recv: %s\n",__progname,strerror(errno)); exit(0); } if (n == 0) { fprintf(stderr,"%s: parent: empty cli recv\n",__progname); exit(0); } switch (buf[0]) { case CLIMSG_BUFSIZE: if (n != sizeof(int)+1) { fprintf(stderr,"%s: parent: cli BUFSIZE msg size (%d) wrong (not %d)\n",__progname,n,(int)sizeof(int)+1); exit(0); } bcopy(buf+1,&n,sizeof(int)); buf_room(n); break; case CLIMSG_TEXT: cli_message(buf+1,n-1); break; default: fprintf(stderr,"%s: parent: bad cli msg %02x\n",__progname,buf[0]); exit(0); break; } } static void rd_cli_dfd(void *arg __attribute__((__unused__))) { fprintf(stderr,"%s: parent: cli death pipe readable\n",__progname); exit(0); } static void init_cmd(void) { int fds[2]; fork_child(&cli_main,&fds[0]); cli_cfd = fds[0]; cli_dfd = fds[1]; add_poll_fd(cli_cfd,&rwtest_always,&rwtest_never,&rd_cli_cfd,0,0); add_poll_fd(cli_dfd,&rwtest_always,&rwtest_never,&rd_cli_dfd,0,0); tell_cli_prompt(); } int main(void); int main(void) { init_forker(); init_msg(); init_polling(); init_nodes(); init_links(); init_cmd(); setproctitle("parent"); poll_run(); exit(0); }