/* * Tracing manager. * * This exists to keep different processes from interfering with one * another when generating trace output. * * Trace output is generated to a pipe (actually an AF_LOCAL * SOCK_STREAM socketpair) between the generating process and the * manager. The manager breaks it into lines, prepends process ID and * sequence numbers, and parcels it out to where it's supposed to go. * * Turning tracing on or off is handled by the generating process * enabling or disabling the production of the messages. Changing * where output is to go is handled by telling the tracing manager * about it. * * When a process forks, the child drops its copy of the parent's * tracing manager connection and establishes its own. This is done * by everyone having a shared socketpair to the manager; when a * tracing generator wants a new data connection, it creates an * AF_LOCAL SOCK_STREAM socketpair and uses SCM_RIGHTS to attach one * end of it to a send on the shared connection. The manager then * uses that descriptor as the data descriptor for that client. (The * first thing the client sends over the data descriptor is a message * telling the manager what PID to attach to messages.) * * The protocols in question are fairly simple, as sketched above. * Here are the details. * * For a new client: * * - Client generates an AF_LOCAL SOCK_STREAM socketpair. * - Client sends one half of the socketpair, attached with * SCM_RIGHTS to one octet of data, on the shared connection * (which is a SOCK_DGRAM connection). * - Client generates a "set my PID" message on the new data * connection. * * Over an existing data connection, the client sends * newline-terminated lines. Some of the possible classes of lines * prompt the manager to send something back. The first character of * each line indicates what kind of line it is: * * P Rest of line consists the client PID, in decimal. * C Rest of line consists of another client's PID, in * decimal. This copies that client's settings to this * client. * M Rest of line is a message class, in decimal, then a * single :, then the body of the message. * O Rest of line is message class, in decimal, then a * separator, then a string destination for that class. * If the separator is :, the string names something * open()able; if the separator is #, the string is a * decimal file descriptor number; if the separator is * -, the string is empty and output is discarded. * R Rest of line is rotation parameters: either a single -, * which means no rotation, or two numbers in decimal * with one space in between, specifying, in order, a * file byte size and a file count. This works only for * : style destination. (Rotation applies to all such * destinations.) * * C lines draw a response. The response consists of a single newline * character. * * O lines draw a response. The response consists of a number, in * decimal, with a newline terminator. If the response is zero, the * command worked; otherwise, it failed, and the response is the * relevant errno value. * * This scheme needs some care with vfork. fork is easy to handle, but * vfork is more trouble. When a process vforks and the child * establishes a new connection, everything is fine, except that once * the parent runs again, its original connection is still * established, but the variable giving the descriptor number of it * holds the value from the vforked child. Fortunately, the main line * has other related problems and has a facility for dealing with them * (see the vforkbackout stuff in user.c). */ #include #include #include #include #include #include #include #include #include #include extern const char *__progname; #include "scm-rights.h" #include "tracemgr.h" /* * The kind of a DEST: either discard, file descriptor, or file. */ typedef enum { DK_DROP = 1, DK_FD, DK_FILE, } DESTKIND; typedef struct tclient TCLIENT; typedef struct dest DEST; typedef struct tclops TCLOPS; typedef struct tclline_one_priv TCLLINE_ONE_PRIV; typedef struct tclline_two_priv TCLLINE_TWO_PRIV; /* * A client of the tracing manager. */ struct tclient { TCLIENT *flink; TCLIENT *blink; int datafd; DEST **dests; AIO_OQ oq; int id; char *pl; int pla; int pln; int pid; unsigned long long int lno; } ; /* * A destination tracing output can, potentially, go to. */ struct dest { DESTKIND kind; int refcnt; int inx; union { // nothing for DK_DROP int fd; // for DK_FD struct { char *path; int fd; int rotnum; unsigned long long int size; #define FOBSIZE 1048576 char *obuf; int obfill; } file; // for DK_FILE } ; } ; struct tclops { int (*ch)(void *, int); void (*throw)(void *); int (*iov)(void *, struct iovec *, int); void *arg; int x; void *priv; } ; struct tclline_one_priv { const unsigned char *data; int len; } ; struct tclline_two_priv { const unsigned char *data1; int len1; const unsigned char *data2; int len2; } ; // Used by manager static DEST **dests; static int adests; static int ndests; static TCLIENT *clients; static int nkinds; static int drainid; // Used by manager and client both static int commonfd; // Used by client static int clientfd; static int clientpid; static unsigned long long int rotsize; static int rotkeep; static void *dequal(const volatile void *arg) { return((((const volatile char *)arg)-(const volatile char *)0)+(char *)0); } static DEST *ref_dest(DEST *d) { if (d) d->refcnt ++; return(d); } static void deref_dest(DEST *d) { if (! d) return; d->refcnt --; if (d->refcnt > 0) return; if (d->refcnt < 0) abort(); if ((d->inx < 0) || (d->inx >= ndests)) abort(); if (d != dests[d->inx]) abort(); if (d->inx != ndests-1) { dests[d->inx] = dests[ndests-1]; dests[d->inx]->inx = d->inx; } ndests --; switch (d->kind) { case DK_DROP: break; case DK_FD: close(d->fd); break; case DK_FILE: free(d->file.path); close(d->file.fd); free(d->file.obuf); break; default: abort(); break; } free(d); } static void kill_tclient(TCLIENT *c) { int i; if (c->flink) c->flink->blink = c->blink; if (c->blink) c->blink->flink = c->flink; else clients = c->flink; close(c->datafd); aio_remove_poll(c->id); aio_oq_flush(&c->oq); for (i=nkinds-1;i>=0;i--) deref_dest(c->dests[i]); free(c->dests); free(c); } static int rtest_tclient(void *cv) { (void)cv; return(1); } static int wtest_tclient(void *cv) { return(aio_oq_nonempty(&((TCLIENT *)cv)->oq)); } static DEST *new_dest(void) { DEST *d; if (ndests >= adests) dests = realloc(dests,(adests=ndests+8)*sizeof(DEST *)); d = malloc(sizeof(DEST)); d->refcnt = 1; d->inx = ndests; dests[ndests++] = d; return(d); } static DEST *find_dest_drop(void) { int i; DEST *d; for (i=ndests-1;i>=0;i--) if (dests[i]->kind == DK_DROP) return(ref_dest(dests[i])); d = new_dest(); d->kind = DK_DROP; return(d); } static DEST *find_dest_fd(int fd) { int i; DEST *d; for (i=ndests-1;i>=0;i--) if ((dests[i]->kind == DK_FD) && (dests[i]->fd == fd)) return(ref_dest(dests[i])); if (fcntl(fd,F_GETFD,0) == -1) return(0); d = new_dest(); d->kind = DK_FD; d->fd = fd; return(d); } static DEST *find_dest_path(const char *path) { int i; DEST *d; int fd; for (i=ndests-1;i>=0;i--) if ((dests[i]->kind == DK_FILE) && !strcmp(dests[i]->file.path,path)) return(ref_dest(dests[i])); fd = open(path,O_WRONLY|O_CREAT|O_TRUNC,0666); if (fd < 0) return(0); d = new_dest(); d->kind = DK_FILE; d->file.path = strdup(path); d->file.fd = fd; d->file.rotnum = 0; d->file.size = 0; d->file.obuf = malloc(FOBSIZE); d->file.obfill = 0; return(d); } static void flush_file_dest(DEST *d) { if (d->kind != DK_FILE) abort(); if (d->file.obfill < 1) return; write(d->file.fd,d->file.obuf,d->file.obfill); d->file.obfill = 0; } static void maybe_rotate_file(DEST *d) { char *pt; int fd; if (d->kind != DK_FILE) abort(); if (d->file.size < rotsize) return; flush_file_dest(d); d->file.rotnum ++; asprintf(&pt,"%s.%d",d->file.path,d->file.rotnum); fd = open(pt,O_WRONLY|O_CREAT|O_TRUNC,0666); if (fd < 0) { fprintf(stderr,"file rotation: can't create %s: %s\n",pt,strerror(errno)); exit(1); } free(pt); close(d->file.fd); d->file.fd = fd; d->file.size = 0; if (d->file.rotnum > rotkeep) { asprintf(&pt,"%s.%d",d->file.path,d->file.rotnum-rotkeep); unlink(pt); free(pt); } else if (d->file.rotnum < rotkeep) { return; } else if (d->file.rotnum == rotkeep) { unlink(d->file.path); } else { abort(); } } static void send_to_file_dest(DEST *d, struct iovec *iov, int niov) { int msize; int i; char *p; msize = 0; for (i=niov-1;i>=0;i--) msize += iov[i].iov_len; if (msize > FOBSIZE) abort(); if (d->file.obfill+msize > FOBSIZE) flush_file_dest(d); p = d->file.obuf + d->file.obfill; for (i=0;ifile.obfill += msize; d->file.size += msize; } static long long int parse_line_int(TCLOPS *ops, void (*nondigit)(const TCLOPS *, int)) { int c; long long int v; int dv; v = 0; while <"digits"> (1) { c = (*ops->ch)(ops->arg,ops->x++); if (c < 0) { (*nondigit)(ops,c); break <"digits">; } switch (c) { case '0': dv = 0; break; case '1': dv = 1; break; case '2': dv = 2; break; case '3': dv = 3; break; case '4': dv = 4; break; case '5': dv = 5; break; case '6': dv = 6; break; case '7': dv = 7; break; case '8': dv = 8; break; case '9': dv = 9; break; default: (*nondigit)(ops,c); break <"digits">; } v = (v * 10) + dv; } return(v); } static void nondigit_P(const TCLOPS *ops, int c) { if (c < 0) return; fprintf(stderr,"%s: %s: bad digit %02x in P line\n",__progname,__func__,c); (*ops->throw)(ops->arg); abort(); } static void tclient_line_P(TCLIENT *tc, TCLOPS *ops) { int v; v = parse_line_int(ops,&nondigit_P); tc->pid = v; } static void nondigit_C(const TCLOPS *ops, int c) { if (c < 0) return; fprintf(stderr,"%s: %s: bad digit %02x in C line\n",__progname,__func__,c); (*ops->throw)(ops->arg); abort(); } static void copy_dests(TCLIENT *fc, TCLIENT *tc) { int i; DEST *d; for (i=nkinds-1;i>=0;i--) { d = ref_dest(fc->dests[i]); deref_dest(tc->dests[i]); tc->dests[i] = d; } } static void tclient_line_C(TCLIENT *tc, TCLOPS *ops) { int v; TCLIENT *fc; v = parse_line_int(ops,&nondigit_C); for (fc=clients;fc;fc=fc->flink) { if (fc->pid == v) { copy_dests(fc,tc); break; } } aio_oq_queue_point(&tc->oq,"\n",1); } static void nondigit_M(const TCLOPS *ops, int c) { if (c < 0) { fprintf(stderr,"%s: %s: no class terminator\n",__progname,__func__); (*ops->throw)(ops->arg); } if (c == ':') return; fprintf(stderr,"%s: %s: bad char %02x in class in M line\n",__progname,__func__,c); (*ops->throw)(ops->arg); abort(); } static void tclient_line_M(TCLIENT *tc, TCLOPS *ops) { int v; DEST *d; struct iovec iov[4]; int niov; char *pref; int plen; int fd; v = parse_line_int(ops,&nondigit_M); if ((v < 0) || (v >= nkinds)) { fprintf(stderr,"%s: %s: bad class %d in M line\n",__progname,__func__,v); (*ops->throw)(ops->arg); } d = tc->dests[v]; plen = asprintf(&pref,"%llu: %d: ",tc->lno++,tc->pid); iov[0].iov_base = pref; iov[0].iov_len = plen; niov = 1 + (*ops->iov)(ops->arg,&iov[1],ops->x); iov[niov].iov_base = dequal("\n"); iov[niov].iov_len = 1; niov ++; if (d) { switch (d->kind) { case DK_DROP: fd = -1; break; case DK_FD: fd = d->fd; break; case DK_FILE: if (rotkeep > 0) maybe_rotate_file(d); send_to_file_dest(d,&iov[0],niov); free(pref); return; break; default: abort(); break; } } else { fd = 1; } if (fd >= 0) writev(fd,&iov[0],niov); free(pref); } static void nondigit_O_class(const TCLOPS *ops, int c) { if (c < 0) { fprintf(stderr,"%s: %s: no class terminator\n",__progname,__func__); (*ops->throw)(ops->arg); } switch (c) { case ':': case '#': case '-': *(char *)ops->priv = c; return; } fprintf(stderr,"%s: %s: bad char %02x in class in O line\n",__progname,__func__,c); (*ops->throw)(ops->arg); abort(); } static void nondigit_O_fd(const TCLOPS *ops, int c) { if (c < 0) return; fprintf(stderr,"%s: %s: bad char %02x in fd in O line\n",__progname,__func__,c); (*ops->throw)(ops->arg); abort(); } static void tclient_line_O(TCLIENT *tc, TCLOPS *ops) { int kind; int c; char sep; DEST *d; int fd; struct iovec iov[2]; int niov; char *path; int e; char *estr; int l; ops->priv = &sep; kind = parse_line_int(ops,&nondigit_O_class); if ((kind < 0) || (kind >= nkinds)) { fprintf(stderr,"%s: %s: bad class %d in O line\n",__progname,__func__,kind); (*ops->throw)(ops->arg); } switch (sep) { case '-': c = (*ops->ch)(ops->arg,ops->x); if (c >= 0) { fprintf(stderr,"%s: %s: bad O line: sep is - but rest of line isn't empty\n",__progname,__func__); (*ops->throw)(ops->arg); } d = find_dest_drop(); e = 0; break; case '#': fd = parse_line_int(ops,&nondigit_O_fd); d = find_dest_fd(fd); if (! d) e = errno; break; case ':': niov = (*ops->iov)(ops->arg,&iov[0],ops->x); switch (niov) { case 0: fprintf(stderr,"%s: %s: bad O line: empty path\n",__progname,__func__); (*ops->throw)(ops->arg); break; case 1: path = malloc(iov[0].iov_len+1); bcopy(iov[0].iov_base,path,iov[0].iov_len); path[iov[0].iov_len] = '\0'; break; case 2: path = malloc(iov[0].iov_len+iov[1].iov_len+1); bcopy(iov[0].iov_base,path,iov[0].iov_len); bcopy(iov[1].iov_base,path+iov[0].iov_len,iov[1].iov_len); path[iov[0].iov_len+iov[1].iov_len] = '\0'; break; default: abort(); break; } d = find_dest_path(path); if (! d) e = errno; free(path); break; default: abort(); break; } if (d) { deref_dest(tc->dests[kind]); tc->dests[kind] = d; aio_oq_queue_point(&tc->oq,"0\n",2); } else { l = asprintf(&estr,"%d\n",e); aio_oq_queue_free(&tc->oq,estr,l); } } static void nondigit_R_bcount(const TCLOPS *ops, int c) { if (c < 0) { fprintf(stderr,"%s: %s: no flag/terminator\n",__progname,__func__); (*ops->throw)(ops->arg); } switch (c) { case '-': case ' ': *(char *)ops->priv = c; return; } fprintf(stderr,"%s: %s: bad terminator %02x in R line\n",__progname,__func__,c); (*ops->throw)(ops->arg); abort(); } static void nondigit_R_fcount(const TCLOPS *ops, int c) { if (c < 0) return; fprintf(stderr,"%s: %s: bad second terminator %02x in R line\n",__progname,__func__,c); (*ops->throw)(ops->arg); abort(); } static void tclient_line_R(TCLIENT *tc __attribute__((__unused__)), TCLOPS *ops) { char sep; long long int bcount; int fcount; int c; ops->priv = &sep; bcount = parse_line_int(ops,&nondigit_R_bcount); if (bcount < 1024) { fprintf(stderr,"%s: %s: bad byte count %lld in R line\n",__progname,__func__,bcount); (*ops->throw)(ops->arg); } switch (sep) { case '-': c = (*ops->ch)(ops->arg,ops->x); if (c >= 0) { fprintf(stderr,"%s: %s: bad R line: sep is - but rest of line isn't empty\n",__progname,__func__); (*ops->throw)(ops->arg); } rotsize = 0; rotkeep = 0; break; case ' ': fcount = parse_line_int(ops,&nondigit_R_fcount); if (fcount < 1) { fprintf(stderr,"%s: %s: bad file count %d in R line\n",__progname,__func__,fcount); (*ops->throw)(ops->arg); } rotsize = bcount; rotkeep = fcount; break; } } static void tclient_line(TCLIENT *tc, TCLOPS *ops) { int c; c = (*ops->ch)(ops->arg,0); if (c < 0) { fprintf(stderr,"%s: %s: zero-length line\n",__progname,__func__); (*ops->throw)(ops->arg); } ops->x = 1; switch (c) { case 'P': tclient_line_P(tc,ops); break; case 'C': tclient_line_C(tc,ops); break; case 'M': tclient_line_M(tc,ops); break; case 'O': tclient_line_O(tc,ops); break; case 'R': tclient_line_R(tc,ops); break; default: fprintf(stderr,"%s: %s: bad line op char %02x\n",__progname,__func__,c); (*ops->throw)(ops->arg); break; } } static int tclch_one(void *arg, int x) { TCLLINE_ONE_PRIV *p; p = arg; if (x < 0) return(-1); if (x < p->len) return(p->data[x]); return(-1); } static int iov_one(void *arg, struct iovec *iov, int x) { TCLLINE_ONE_PRIV *p; p = arg; if ((x < 0) || (x > p->len)) abort(); if (x == p->len) return(0); iov[0].iov_base = dequal(p->data+x); iov[0].iov_len = p->len - x; return(1); } static int tclient_line_onepart(TCLIENT *c, const char *data, int len) { __label__ throwto; TCLLINE_ONE_PRIV p; TCLOPS ops; void throw(void *arg __attribute__((__unused__))) { goto throwto; } p.data = (const void *) data; p.len = len; ops.ch = &tclch_one; ops.throw = &throw; ops.iov = &iov_one; ops.arg = &p; tclient_line(c,&ops); return(0); throwto:; return(1); } static int tclch_two(void *arg, int x) { TCLLINE_TWO_PRIV *p; p = arg; if (x < 0) return(-1); if (x < p->len1) return(p->data1[x]); if (x < p->len1+p->len2) return(p->data2[x-p->len1]); return(-1); } static int iov_two(void *arg, struct iovec *iov, int x) { TCLLINE_TWO_PRIV *p; p = arg; if (x < 0) abort(); if (x < p->len1) { iov[0].iov_base = dequal(p->data1+x); iov[0].iov_len = p->len1 - x; iov[1].iov_base = dequal(p->data2); iov[1].iov_len = p->len2; return(2); } if (x < p->len1+p->len2) { x -= p->len1; iov[0].iov_base = dequal(p->data2+x); iov[0].iov_len = p->len2 - x; return(1); } if (x == p->len1+p->len2) return(0); abort(); } static int tclient_line_twopart(TCLIENT *c, const char *d1, int l1, const char *d2, int l2) { __label__ throwto; TCLLINE_TWO_PRIV p; TCLOPS ops; void throw(void *arg __attribute__((__unused__))) { goto throwto; } p.data1 = (const void *) d1; p.len1 = l1; p.data2 = (const void *) d2; p.len2 = l2; ops.ch = &tclch_two; ops.throw = &throw; ops.iov = &iov_two; ops.arg = &p; tclient_line(c,&ops); return(0); throwto:; return(1); } static void rd_tclient(void *cv) { TCLIENT *c; char rbuf[8192]; int nr; char *nl; int o; int n; int e; int pln; c = cv; nr = read(c->datafd,&rbuf[0],sizeof(rbuf)); if (nr < 0) { switch (errno) { case EINTR: case EWOULDBLOCK: return; } fprintf(stderr,"%s: client read error: %s\n",__progname,strerror(errno)); kill_tclient(c); return; } if (nr == 0) { kill_tclient(c); return; } o = 0; while (o < nr) { n = nr - o; nl = memchr(&rbuf[o],'\n',n); if (nl) { n = nl - &rbuf[o]; pln = c->pln; if (pln) { c->pln = 0; if (n > 0) { e = tclient_line_twopart(c,c->pl,pln,&rbuf[o],n); } else { e = tclient_line_onepart(c,c->pl,pln); } } else { e = tclient_line_onepart(c,&rbuf[o],n); } if (e) { kill_tclient(c); return; } o += n + 1; } else { if (c->pln+n > c->pla) c->pl = realloc(c->pl,c->pla=c->pln+n); bcopy(&rbuf[o],c->pl+c->pln,nr-o); c->pln += n; o += n; } } } static void wr_tclient(void *cv) { TCLIENT *c; int n; c = cv; n = aio_oq_writev(&c->oq,c->datafd,-1); if (n == AIO_WRITEV_ERROR) { fprintf(stderr,"%s: client write error: %s\n",__progname,strerror(errno)); kill_tclient(c); return; } if (n < 0) { fprintf(stderr,"%s: impossible return value %d from aio_oq_writev\n",__progname,n); exit(1); } aio_oq_dropdata(&c->oq,n); } static void flush_buffers(void) { int dx; DEST *d; for (dx=ndests-1;dx>=0;dx--) { d = dests[dx]; if (d->kind == DK_FILE) flush_file_dest(d); } } static int drain_test(void *arg __attribute__((__unused__))) { flush_buffers(); if (! clients) exit(0); return(AIO_BLOCK_NIL); } static void rd_common(void *arg __attribute__((__unused__))) { struct msghdr mh; struct cmsghdr cmh; struct iovec iov; unsigned char ctlbuf[CMSPACE(sizeof(int))]; char body; int r; int fd; TCLIENT *c; int i; iov.iov_base= &body; iov.iov_len = 1; mh.msg_name = 0; mh.msg_namelen = 0; mh.msg_iov = &iov; mh.msg_iovlen = 1; mh.msg_control = &ctlbuf[0]; mh.msg_controllen = CMSPACE(sizeof(int)); mh.msg_flags = 0; r = recvmsg(commonfd,&mh,0); if (r < 0) { if (errno == ECONNRESET) { // This seems to be what we get when all clients are gone // EOF would make more sense, seems to me flush_buffers(); exit(0); } else { fprintf(stderr,"%s: %s: control recvmsg: %s\n",__progname,__func__,strerror(errno)); exit(1); } } if (r != 1) { fprintf(stderr,"%s: %s: data length %d is unexpected\n",__progname,__func__,r); return; } if (mh.msg_controllen < sizeof(struct cmsghdr)) { fprintf(stderr,"%s: %s: control length %d too small\n",__progname,__func__,(int)mh.msg_controllen); return; } bcopy(&ctlbuf[0],&cmh,sizeof(struct cmsghdr)); if (cmh.cmsg_level != SOL_SOCKET) { fprintf(stderr,"%s: %s: control level (%d) isn't SOL_SOCKET (%d)\n",__progname,__func__,(int)cmh.cmsg_level,(int)SOL_SOCKET); return; } if (cmh.cmsg_type != SCM_RIGHTS) { fprintf(stderr,"%s: %s: control type (%d) isn't SCM_RIGHTS (%d)\n",__progname,__func__,(int)cmh.cmsg_type,(int)SCM_RIGHTS); return; } if (cmh.cmsg_len < CMLEN(sizeof(int))) { fprintf(stderr,"%s: %s: cmsg length (%d) too small\n",__progname,__func__,(int)cmh.cmsg_len); return; } bcopy(&ctlbuf[CMSKIP(&cmh)],&fd,sizeof(int)); fcntl(fd,F_SETFL,fcntl(fd,F_GETFL,0)|O_NONBLOCK); c = malloc(sizeof(TCLIENT)); c->datafd = fd; c->dests = malloc(nkinds*sizeof(DEST *)); for (i=nkinds-1;i>=0;i--) c->dests[i] = 0; aio_oq_init(&c->oq); c->pl = 0; c->pla = 0; c->pln = 0; c->pid = -1; c->lno = 1; c->flink = clients; c->blink = 0; if (clients) clients->blink = c; clients = c; c->id = aio_add_poll(fd,&rtest_tclient,&wtest_tclient,&rd_tclient,&wr_tclient,c); if (drainid == AIO_NOID) drainid = aio_add_block(&drain_test,0); } static void block_tty_signals(void) { sigset_t ss; sigemptyset(&ss); sigaddset(&ss,SIGHUP); sigaddset(&ss,SIGINT); sigaddset(&ss,SIGQUIT); sigaddset(&ss,SIGTSTP); sigaddset(&ss,SIGTTIN); sigaddset(&ss,SIGTTOU); sigaddset(&ss,SIGWINCH); sigaddset(&ss,SIGINFO); sigprocmask(SIG_BLOCK,&ss,0); } static void trace_manager(int nkind_arg) { block_tty_signals(); setproctitle("trace manager"); dests = 0; adests = 0; ndests = 0; clients = 0; rotkeep = 0; nkinds = nkind_arg; fcntl(commonfd,F_SETFL,fcntl(commonfd,F_GETFL,0)|O_NONBLOCK); aio_poll_init(); aio_add_poll(commonfd,&aio_rwtest_always,&aio_rwtest_never,&rd_common,0,0); aio_event_loop(); _exit(0); } static void set_C_resp(void) { unsigned char rbuf; int n; while (1) { n = read(clientfd,&rbuf,1); if (n < 0) { fprintf(stderr,"%s: %s: response read: %s\n",__progname,__func__,strerror(errno)); exit(1); } if (n == 0) { fprintf(stderr,"%s: %s: response read EOF\n",__progname,__func__); exit(1); } if (rbuf != '\n') { fprintf(stderr,"%s: %s: invalid response to C line: %02x\n",__progname,__func__,rbuf); exit(1); } break; } } static void establish_client(int oldpid) { int dp[2]; struct msghdr mh; struct cmsghdr cmh; struct iovec iov; unsigned char ctlbuf[CMSPACE(sizeof(int))]; char body; char *msg; int msglen; if (socketpair(AF_LOCAL,SOCK_STREAM,0,&dp[0]) < 0) { fprintf(stderr,"%s: %s: socketpair: %s\n",__progname,__func__,strerror(errno)); exit(1); } clientfd = dp[0]; body = '\0'; iov.iov_base = &body; iov.iov_len = 1; cmh.cmsg_len = CMLEN(sizeof(int)); cmh.cmsg_level = SOL_SOCKET; cmh.cmsg_type = SCM_RIGHTS; bcopy(&cmh,&ctlbuf[0],sizeof(struct cmsghdr)); bcopy(&dp[1],&ctlbuf[CMSKIP(&cmh)],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(sizeof(int)); mh.msg_flags = 0; if (sendmsg(commonfd,&mh,0) < 0) { fprintf(stderr,"%s: %s: sendmsg: %s\n",__progname,__func__,strerror(errno)); exit(1); } close(dp[1]); if (oldpid < 0) { msglen = asprintf(&msg,"P%d\n",clientpid); } else { msglen = asprintf(&msg,"P%d\nC%d\n",clientpid,oldpid); } write(clientfd,msg,msglen); free(msg); if (oldpid >= 0) set_C_resp(); } static int set_O_resp(void) { unsigned char rbuf[64]; int f; int n; f = 0; while (1) { if (f >= sizeof(rbuf)) { fprintf(stderr,"%s: %s: response buffer overflow\n",__progname,__func__); exit(1); } n = read(clientfd,&rbuf[f],sizeof(rbuf)-f); if (n < 0) { fprintf(stderr,"%s: %s: response read: %s\n",__progname,__func__,strerror(errno)); exit(1); } if (n == 0) { fprintf(stderr,"%s: %s: response read EOF\n",__progname,__func__); exit(1); } f += n; if (rbuf[f-1] == '\n') break; } return(atoi(&rbuf[0])); } void trcmgr_startup(int nkind) { int cfd[2]; pid_t kid; drainid = AIO_NOID; if (socketpair(AF_LOCAL,SOCK_DGRAM,0,&cfd[0]) < 0) { fprintf(stderr,"%s: %s: socketpair: %s\n",__progname,__func__,strerror(errno)); exit(1); } fflush(0); fflush(0); kid = fork(); if (kid == 0) { close(cfd[0]); commonfd = cfd[1]; kid = fork(); if (kid) exit(0); trace_manager(nkind); _exit(0); } close(cfd[1]); commonfd = cfd[0]; clientfd = -1; clientpid = -1; trcmgr_newpid(getpid()); wait4(kid,0,0,0); } int trcmgr_set_output_fd(int kind, int fd) { char *msg; int msglen; msglen = asprintf(&msg,"O%d#%d\n",kind,fd); write(clientfd,msg,msglen); free(msg); return(set_O_resp()); } int trcmgr_set_output_file(int kind, const char *fn, int fnlen) { char *hdr; int hdrlen; struct iovec iov[3]; hdrlen = asprintf(&hdr,"O%d:",kind); iov[0].iov_base = hdr; iov[0].iov_len = hdrlen; iov[1].iov_base = dequal(fn); iov[1].iov_len = fnlen; iov[2].iov_base = dequal("\n"); iov[2].iov_len = 1; writev(clientfd,&iov[0],3); free(hdr); return(set_O_resp()); } void trcmgr_set_rotation(long long int size, int count) { char *s; int l; l = asprintf(&s,"R%lld %d\n",size,count); write(clientfd,s,l); free(s); } void trcmgr_line(const char *tag, int kind, const char *data, int datalen) { char *hdr; int hdrlen; struct iovec iov[6]; hdrlen = asprintf(&hdr,"M%d:",kind); iov[0].iov_base = hdr; iov[0].iov_len = hdrlen; iov[1].iov_base = dequal("["); iov[1].iov_len = 1; iov[2].iov_base = dequal(tag); iov[2].iov_len = strlen(tag); iov[3].iov_base = dequal("] "); iov[3].iov_len = 2; iov[4].iov_base = dequal(data); iov[4].iov_len = datalen; iov[5].iov_base = dequal("\n"); iov[5].iov_len = 1; writev(clientfd,&iov[0],6); free(hdr); } void trcmgr_line_twopart(const char *tag, int kind, const char *d1, int l1, const char *d2, int l2) { char *hdr; int hdrlen; struct iovec iov[7]; hdrlen = asprintf(&hdr,"M%d:",kind); iov[0].iov_base = hdr; iov[0].iov_len = hdrlen; iov[1].iov_base = dequal("["); iov[1].iov_len = 1; iov[2].iov_base = dequal(tag); iov[2].iov_len = strlen(tag); iov[3].iov_base = dequal("] "); iov[3].iov_len = 2; iov[4].iov_base = dequal(d1); iov[4].iov_len = l1; iov[5].iov_base = dequal(d2); iov[5].iov_len = l2; iov[6].iov_base = dequal("\n"); iov[6].iov_len = 1; writev(clientfd,&iov[0],7); free(hdr); } void trcmgr_newpid(int mypid) { int oldpid; int oldfd; if (mypid == clientpid) return; oldpid = clientpid; oldfd = clientfd; clientpid = mypid; establish_client(oldpid); if (oldfd >= 0) close(oldfd); } /* * The following functions exist for the sake of dealing with vfork. */ int trcmgr_get_fd(void) { return(clientfd); } void trcmgr_set_fd(int fd) { clientfd = fd; } void trcmgr_set_mypid(int mypid) { clientpid = mypid; }