Initial import of the CDE 2.1.30 sources from the Open Group.
This commit is contained in:
464
cde/lib/tt/bin/tttrace/tttrace.C
Normal file
464
cde/lib/tt/bin/tttrace/tttrace.C
Normal file
@@ -0,0 +1,464 @@
|
||||
//%% (c) Copyright 1993, 1994 Hewlett-Packard Company
|
||||
//%% (c) Copyright 1993, 1994 International Business Machines Corp.
|
||||
//%% (c) Copyright 1993, 1994 Sun Microsystems, Inc.
|
||||
//%% (c) Copyright 1993, 1994 Novell, Inc.
|
||||
//%% $TOG: tttrace.C /main/9 1999/10/14 18:38:47 mgreess $
|
||||
/*
|
||||
* @(#)tttrace.C 1.29 95/05/02
|
||||
*
|
||||
* Copyright (c) 1993 by Sun Microsystems, Inc.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
#include <locale.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <signal.h>
|
||||
#if defined(linux)
|
||||
# include <sys/poll.h>
|
||||
#else
|
||||
# include <poll.h>
|
||||
#endif
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include "api/c/tt_c.h"
|
||||
#include "util/tt_string.h"
|
||||
#include "util/tt_port.h"
|
||||
#include "util/tt_gettext.h"
|
||||
#include "tttrace_objs.h"
|
||||
#include "tt_options.h"
|
||||
|
||||
#define SESSION_TRACE_OP "Session_Trace"
|
||||
|
||||
static _Tt_string progname = "tttrace";
|
||||
pid_t forked_pid = (pid_t) -1;
|
||||
int makequit = 0;
|
||||
int poll_timeout = -1;
|
||||
int exit_status = 0;
|
||||
int ttfd = 0;
|
||||
|
||||
static void install_signal_handler();
|
||||
static void sig_handler(int);
|
||||
static pid_t do_fork(_Tt_trace_optobj&);
|
||||
static void send_session_trace(_Tt_trace_optobj&);
|
||||
static int tail_pipe(int, _Tt_string&, pid_t, int);
|
||||
static int open_pipe(_Tt_string&, int*);
|
||||
static void send_on_exit();
|
||||
|
||||
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
void print_usage_and_exit();
|
||||
int status = 0;
|
||||
int pipe_fd = -1;
|
||||
_Tt_string temp_name; // name of FIFO file
|
||||
_Tt_trace_optobj myopts; // processes command-line options
|
||||
setlocale( LC_ALL, "" );
|
||||
|
||||
install_signal_handler();
|
||||
|
||||
//
|
||||
// parse command-line options
|
||||
//
|
||||
|
||||
switch(myopts.getopts(argc, argv)) {
|
||||
case 0:
|
||||
break;
|
||||
case 1:
|
||||
print_usage_and_exit();
|
||||
break;
|
||||
case 2:
|
||||
exit(2);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// Open the pipe for reading, if requested
|
||||
|
||||
if (myopts.pipe_name().len()) {
|
||||
status = open_pipe(myopts.pipe_name(), &pipe_fd);
|
||||
}
|
||||
|
||||
if (status == 0) {
|
||||
|
||||
// Do fork, or send session_trace requests
|
||||
|
||||
pid_t child_pid = 0;
|
||||
int sink;
|
||||
switch(myopts.operation_mode()) {
|
||||
case FORK_COMMAND:
|
||||
(void) putenv(myopts.envstr());
|
||||
child_pid = do_fork(myopts);
|
||||
sink = STDERR_FILENO;
|
||||
break;
|
||||
case SESSION_TRACE:
|
||||
send_session_trace(myopts);
|
||||
sink = STDOUT_FILENO;
|
||||
break;
|
||||
default:
|
||||
exit(2);
|
||||
break;
|
||||
}
|
||||
|
||||
_Tt_string outfile;
|
||||
if (myopts.outfile( outfile ) && (outfile == "-")) {
|
||||
sink = STDOUT_FILENO;
|
||||
}
|
||||
|
||||
status = tail_pipe(pipe_fd, myopts.pipe_name(), child_pid, sink);
|
||||
|
||||
if (pipe_fd > 0) {
|
||||
|
||||
if (unlink( myopts.pipe_name()) != 0) {
|
||||
_Tt_string msg = progname.cat( ": " );
|
||||
msg = msg.cat( myopts.pipe_name() );
|
||||
perror( msg );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
exit( status );
|
||||
}
|
||||
|
||||
//
|
||||
// Install sig_handler as the handler for all the signals it handles.
|
||||
//
|
||||
static void
|
||||
install_signal_handler()
|
||||
{
|
||||
_Tt_string err;
|
||||
err = err.cat( ": _tt_sigset(SIG" );
|
||||
if (_tt_sigset(SIGHUP, &sig_handler) == 0) {
|
||||
perror( (char *) err.cat("HUP)") );
|
||||
}
|
||||
if (_tt_sigset(SIGTERM, &sig_handler) == 0) {
|
||||
perror( (char *) err.cat("TERM)") );
|
||||
}
|
||||
if (_tt_sigset(SIGINT, &sig_handler) == 0) {
|
||||
perror( (char *) err.cat("INT)") );
|
||||
}
|
||||
if (_tt_sigset(SIGUSR1, &sig_handler) == 0) {
|
||||
perror( (char *) err.cat("USR1)") );
|
||||
}
|
||||
if (_tt_sigset(SIGUSR2, &sig_handler) == 0) {
|
||||
perror( (char *) err.cat("USR2)") );
|
||||
}
|
||||
if (_tt_sigset(SIGCHLD, &sig_handler) == 0) {
|
||||
perror( (char *) err.cat("CHLD)") );
|
||||
}
|
||||
if (_tt_sigset(SIGPIPE, &sig_handler) == 0) {
|
||||
perror( (char *) err.cat("PIPE)") );
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Prints out a usage string and exits.
|
||||
//
|
||||
void
|
||||
print_usage_and_exit()
|
||||
{
|
||||
fprintf(stderr,
|
||||
catgets(_ttcatd, 9, 2,
|
||||
"Usage: %s [-0FCa][-o outfile] [-S session | command [options]]\n"
|
||||
" %s [-e script | -f scriptfile][-S session | command [options]]\n"
|
||||
" -0 Turn off message tracing in session, or run command\n"
|
||||
" without message tracing (i.e. only API tracing)\n"
|
||||
" -F Follow all children forked by command or subsequently\n"
|
||||
" started in session by ttsession(1)\n"
|
||||
" -C Do not trace ToolTalk API calls\n"
|
||||
" -a Print all attributes, arguments, and context slots of\n"
|
||||
" traced messages. Default is single-line summary.\n"
|
||||
" -e script Read tttracefile(4) settings from script\n"
|
||||
" -f scriptfile Read tttracefile(4) settings from scriptfile. \"-\": stdin.\n"
|
||||
" -o outfile Output. \"-\": stdout. default: stdout for session tracing,\n"
|
||||
" stderr (of tttrace) for command tracing\n"
|
||||
" -S session Session to trace. default: see tt_default_session()\n"
|
||||
" command ToolTalk client command to invoke and trace\n"),
|
||||
(char *) progname, (char *) progname);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
//
|
||||
// Global signal handler for tttrace. All signals are handled by this
|
||||
// function (ie. no signal handlers should be defined in any other files)
|
||||
//
|
||||
static void
|
||||
sig_handler(int sig)
|
||||
{
|
||||
int status;
|
||||
pid_t child;
|
||||
switch (sig) {
|
||||
case SIGPIPE:
|
||||
case SIGHUP:
|
||||
break;
|
||||
case SIGCHLD:
|
||||
child = waitpid( -1, &status, WNOHANG );
|
||||
if ((child > 0) && (WIFEXITED(status))) {
|
||||
exit_status = WEXITSTATUS(status);
|
||||
poll_timeout = 2;
|
||||
makequit = 1;
|
||||
}
|
||||
break;
|
||||
case SIGTERM:
|
||||
case SIGINT:
|
||||
makequit = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static pid_t do_fork(_Tt_trace_optobj& myopts)
|
||||
{
|
||||
int i;
|
||||
int _tt_getdtablesize(void);
|
||||
int _tt_restoredtablesize(void);
|
||||
int maxfds; // max TT fd's
|
||||
_Tt_string cmd;
|
||||
|
||||
switch(forked_pid = fork()) {
|
||||
case -1:
|
||||
fprintf(stderr,"%s: fork(): %s\n", (char *)progname,
|
||||
strerror(errno));
|
||||
exit(2);
|
||||
case 0: // child
|
||||
maxfds = _tt_getdtablesize();
|
||||
for (i = 3; i < maxfds; i++) {
|
||||
close(i);
|
||||
}
|
||||
_tt_restoredtablesize();
|
||||
signal(SIGHUP, SIG_IGN);
|
||||
(void) myopts.command(cmd); // existence of cmd already checked
|
||||
execvp((char *) cmd, (char * const*)myopts.cargv());
|
||||
perror((char *) progname);
|
||||
exit(1);
|
||||
default: // parent -- wait on child process
|
||||
break;
|
||||
}
|
||||
return forked_pid;
|
||||
}
|
||||
|
||||
static void send_session_trace(_Tt_trace_optobj& myopts)
|
||||
{
|
||||
int timeout = 180; // 3-minute timeout
|
||||
int script_stat;
|
||||
size_t num_fd = 1;
|
||||
struct pollfd fds[1];
|
||||
_Tt_string tmp;
|
||||
Tt_callback_action tttrace_callback(Tt_message, Tt_pattern);
|
||||
|
||||
Tt_message msg;
|
||||
|
||||
int mark = tt_mark();
|
||||
|
||||
// We must send the message to the specified session. This routine
|
||||
// is only called when there is an explicit session in the
|
||||
// options list.
|
||||
|
||||
myopts.session(tmp);
|
||||
|
||||
ttfd = tt_fd();
|
||||
|
||||
tt_session_join((char *) tmp);
|
||||
msg = tt_prequest_create(TT_SESSION, SESSION_TRACE_OP);
|
||||
|
||||
tt_message_arg_add(msg, TT_IN, "string", (char *) 0);
|
||||
script_stat = myopts.script(tmp);
|
||||
if (script_stat == 1) { // inline script
|
||||
tt_message_arg_val_set(msg, 0, (char *) tmp);
|
||||
}
|
||||
else if (script_stat == 2) { // script is in filename
|
||||
tt_message_file_set(msg, (char *) tmp);
|
||||
}
|
||||
|
||||
tt_message_callback_add(msg, tttrace_callback);
|
||||
Tt_status mstat = tt_message_send(msg);
|
||||
|
||||
if (mstat != TT_OK) {
|
||||
fprintf(stderr, "%s: tt_message_send(): %s\n",
|
||||
(char *) progname, tt_status_message(mstat));
|
||||
exit(2);
|
||||
}
|
||||
|
||||
fds[0].fd = ttfd;
|
||||
fds[0].events = POLLIN | POLLPRI;
|
||||
fds[0].revents = 0;
|
||||
int rcode = poll(fds, num_fd, timeout);
|
||||
if (rcode == -1) {
|
||||
fprintf(stderr, "%s: Session_Trace: %s\n",
|
||||
(char *) progname, strerror(ETIMEDOUT));
|
||||
exit(2);
|
||||
}
|
||||
|
||||
Tt_message inmsg = tt_message_receive();
|
||||
|
||||
// Make sure the stop-tracing message is sent upon exit
|
||||
|
||||
send_on_exit();
|
||||
|
||||
tt_release(mark);
|
||||
}
|
||||
|
||||
Tt_callback_action tttrace_callback(Tt_message msg, Tt_pattern)
|
||||
{
|
||||
Tt_status mstat = (Tt_status) tt_message_status(msg);
|
||||
if (mstat == TT_ERR_NO_MATCH) {
|
||||
|
||||
// ttsession does not recognize this message, which
|
||||
// means an incompatible version of ttsession is being
|
||||
// used
|
||||
|
||||
fprintf(stderr, catgets(_ttcatd, 9, 3,
|
||||
"%s: session <%s> does not support "
|
||||
"Session_Trace. Use kill -USR1 instead. "
|
||||
"See ttsession(1).\n"),
|
||||
(char *) progname, tt_message_session(msg) );
|
||||
exit(5);
|
||||
}
|
||||
else if (mstat != TT_OK) {
|
||||
fprintf(stderr, "%s: Session_Trace: %s\n",
|
||||
(char *) progname, tt_status_message(mstat));
|
||||
exit(2);
|
||||
}
|
||||
|
||||
return TT_CALLBACK_PROCESSED;
|
||||
}
|
||||
|
||||
static void send_on_exit()
|
||||
{
|
||||
Tt_message msg = tt_prequest_create(TT_SESSION, SESSION_TRACE_OP);
|
||||
tt_message_arg_add(msg, TT_IN, "string",
|
||||
"version 1; states none; functions none" );
|
||||
tt_message_send_on_exit(msg);
|
||||
}
|
||||
|
||||
#define MAXLINE 255
|
||||
|
||||
static int open_pipe(_Tt_string& pipe_name, int* fd)
|
||||
{
|
||||
int quit = 0;
|
||||
int exit_status = 0;
|
||||
|
||||
while ((! quit) && (*fd <= 0)) {
|
||||
*fd = open((char *) pipe_name, O_RDONLY | O_NDELAY);
|
||||
if ((*fd < 0) && (errno != EINTR)) {
|
||||
_Tt_string msg = progname.cat( ": " ).cat(pipe_name);
|
||||
perror(msg);
|
||||
exit_status = 2;
|
||||
}
|
||||
quit = 1;
|
||||
}
|
||||
|
||||
return exit_status;
|
||||
}
|
||||
|
||||
|
||||
// No silly cracks about this routine's name, please!
|
||||
|
||||
static int tail_pipe(int fd, _Tt_string& pipenm, pid_t child_pid, int sink)
|
||||
{
|
||||
int bufsize = MAXLINE;
|
||||
int nbytes;
|
||||
int quit = 0;
|
||||
struct pollfd fds[2];
|
||||
char buf[MAXLINE];
|
||||
|
||||
if (pipenm.len() == 0) {
|
||||
int done = 0;
|
||||
while (!done) {
|
||||
pid_t child_waited_for = waitpid( child_pid, 0, 0 );
|
||||
if (child_waited_for < 0 && errno == EINTR) continue;
|
||||
if (child_waited_for < 0 && errno == ECHILD) break;
|
||||
if (child_waited_for < 0) {
|
||||
fprintf(stderr, "%s: waitpid(): ",
|
||||
(char *) progname);
|
||||
perror(0);
|
||||
if(errno == ECHILD){
|
||||
exit(2);
|
||||
}
|
||||
}
|
||||
done = 1;
|
||||
}
|
||||
} else {
|
||||
|
||||
while (!quit) {
|
||||
fds[0].fd = fd;
|
||||
fds[0].events = POLLIN;
|
||||
fds[0].revents = 0;
|
||||
fds[1].fd = ttfd;
|
||||
fds[1].events = POLLIN;
|
||||
fds[1].revents = 0;
|
||||
|
||||
int numfds = poll(fds, ttfd ? 2 : 1, poll_timeout);
|
||||
if (numfds < 0) {
|
||||
if (errno != EINTR) {
|
||||
_Tt_string msg =
|
||||
progname.cat( ": poll()" );
|
||||
perror( msg );
|
||||
exit_status = 2;
|
||||
quit = 1;
|
||||
}
|
||||
else if (makequit) {
|
||||
quit = 1;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
else if (numfds == 0) {
|
||||
quit = 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (fds[ 0 ].revents & POLLHUP) {
|
||||
if (makequit) {
|
||||
|
||||
// Child has exited -- do a last read
|
||||
while ((nbytes = read(fd,
|
||||
buf,
|
||||
bufsize)) > 0) {
|
||||
write(sink, buf, nbytes);
|
||||
}
|
||||
quit = 1;
|
||||
}
|
||||
else {
|
||||
pid_t child_waited_for = waitpid(child_pid,
|
||||
0, 0 );
|
||||
if (child_waited_for == child_pid) {
|
||||
|
||||
quit = 1;
|
||||
}
|
||||
else {
|
||||
|
||||
// Tracing is temporarily stopped.
|
||||
// Periodically wake up to see if it
|
||||
// has started again.
|
||||
|
||||
sleep( 1 );
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (fds[ 0 ].revents & POLLIN) {
|
||||
if (makequit) {
|
||||
quit = 1;
|
||||
}
|
||||
else {
|
||||
nbytes = read(fd, buf, bufsize);
|
||||
if (nbytes > 0) {
|
||||
write(sink, buf, nbytes);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (fds[ 1 ].revents & POLLIN) {
|
||||
Tt_message msg = tt_message_receive();
|
||||
if (tt_ptr_error(msg) == TT_ERR_NOMP) {
|
||||
quit = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (fd > 0) {
|
||||
close(fd);
|
||||
}
|
||||
}
|
||||
return exit_status;
|
||||
}
|
||||
Reference in New Issue
Block a user