2 * CDE - Common Desktop Environment
4 * Copyright (c) 1993-2012, The Open Group. All rights reserved.
6 * These libraries and programs are free software; you can
7 * redistribute them and/or modify them under the terms of the GNU
8 * Lesser General Public License as published by the Free Software
9 * Foundation; either version 2 of the License, or (at your option)
12 * These libraries and programs are distributed in the hope that
13 * they will be useful, but WITHOUT ANY WARRANTY; without even the
14 * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15 * PURPOSE. See the GNU Lesser General Public License for more
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with these librararies and programs; if not, write
20 * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
21 * Floor, Boston, MA 02110-1301 USA
23 //%% (c) Copyright 1993, 1994 Hewlett-Packard Company
24 //%% (c) Copyright 1993, 1994 International Business Machines Corp.
25 //%% (c) Copyright 1993, 1994 Sun Microsystems, Inc.
26 //%% (c) Copyright 1993, 1994 Novell, Inc.
27 //%% $TOG: tttrace.C /main/9 1999/10/14 18:38:47 mgreess $
29 * @(#)tttrace.C 1.29 95/05/02
31 * Copyright (c) 1993 by Sun Microsystems, Inc.
42 # include <sys/poll.h>
46 #include <sys/types.h>
49 #include "api/c/tt_c.h"
50 #include "util/tt_string.h"
51 #include "util/tt_port.h"
52 #include "util/tt_gettext.h"
53 #include "tttrace_objs.h"
54 #include "tt_options.h"
56 #define SESSION_TRACE_OP "Session_Trace"
58 static _Tt_string progname = "tttrace";
59 pid_t forked_pid = (pid_t) -1;
61 int poll_timeout = -1;
65 static void install_signal_handler();
66 static void sig_handler(int);
67 static pid_t do_fork(_Tt_trace_optobj&);
68 static void send_session_trace(_Tt_trace_optobj&);
69 static int tail_pipe(int, _Tt_string&, pid_t, int);
70 static int open_pipe(_Tt_string&, int*);
71 static void send_on_exit();
75 int main(int argc, char **argv)
77 void print_usage_and_exit();
80 _Tt_string temp_name; // name of FIFO file
81 _Tt_trace_optobj myopts; // processes command-line options
82 setlocale( LC_ALL, "" );
84 install_signal_handler();
87 // parse command-line options
90 switch(myopts.getopts(argc, argv)) {
94 print_usage_and_exit();
102 // Open the pipe for reading, if requested
104 if (myopts.pipe_name().len()) {
105 status = open_pipe(myopts.pipe_name(), &pipe_fd);
110 // Do fork, or send session_trace requests
114 switch(myopts.operation_mode()) {
116 (void) putenv(myopts.envstr());
117 child_pid = do_fork(myopts);
118 sink = STDERR_FILENO;
121 send_session_trace(myopts);
122 sink = STDOUT_FILENO;
130 if (myopts.outfile( outfile ) && (outfile == "-")) {
131 sink = STDOUT_FILENO;
134 status = tail_pipe(pipe_fd, myopts.pipe_name(), child_pid, sink);
138 if (unlink( myopts.pipe_name()) != 0) {
139 _Tt_string msg = progname.cat( ": " );
140 msg = msg.cat( myopts.pipe_name() );
150 // Install sig_handler as the handler for all the signals it handles.
153 install_signal_handler()
156 err = err.cat( ": _tt_sigset(SIG" );
157 if (_tt_sigset(SIGHUP, &sig_handler) == 0) {
158 perror( (char *) err.cat("HUP)") );
160 if (_tt_sigset(SIGTERM, &sig_handler) == 0) {
161 perror( (char *) err.cat("TERM)") );
163 if (_tt_sigset(SIGINT, &sig_handler) == 0) {
164 perror( (char *) err.cat("INT)") );
166 if (_tt_sigset(SIGUSR1, &sig_handler) == 0) {
167 perror( (char *) err.cat("USR1)") );
169 if (_tt_sigset(SIGUSR2, &sig_handler) == 0) {
170 perror( (char *) err.cat("USR2)") );
172 if (_tt_sigset(SIGCHLD, &sig_handler) == 0) {
173 perror( (char *) err.cat("CHLD)") );
175 if (_tt_sigset(SIGPIPE, &sig_handler) == 0) {
176 perror( (char *) err.cat("PIPE)") );
181 // Prints out a usage string and exits.
184 print_usage_and_exit()
187 catgets(_ttcatd, 9, 2,
188 "Usage: %s [-0FCa][-o outfile] [-S session | command [options]]\n"
189 " %s [-e script | -f scriptfile][-S session | command [options]]\n"
190 " -0 Turn off message tracing in session, or run command\n"
191 " without message tracing (i.e. only API tracing)\n"
192 " -F Follow all children forked by command or subsequently\n"
193 " started in session by ttsession(1)\n"
194 " -C Do not trace ToolTalk API calls\n"
195 " -a Print all attributes, arguments, and context slots of\n"
196 " traced messages. Default is single-line summary.\n"
197 " -e script Read tttracefile(4) settings from script\n"
198 " -f scriptfile Read tttracefile(4) settings from scriptfile. \"-\": stdin.\n"
199 " -o outfile Output. \"-\": stdout. default: stdout for session tracing,\n"
200 " stderr (of tttrace) for command tracing\n"
201 " -S session Session to trace. default: see tt_default_session()\n"
202 " command ToolTalk client command to invoke and trace\n"),
203 (char *) progname, (char *) progname);
208 // Global signal handler for tttrace. All signals are handled by this
209 // function (ie. no signal handlers should be defined in any other files)
221 child = waitpid( -1, &status, WNOHANG );
222 if ((child > 0) && (WIFEXITED(status))) {
223 exit_status = WEXITSTATUS(status);
235 static pid_t do_fork(_Tt_trace_optobj& myopts)
238 int _tt_getdtablesize(void);
239 int _tt_restoredtablesize(void);
240 int maxfds; // max TT fd's
243 switch(forked_pid = fork()) {
245 fprintf(stderr,"%s: fork(): %s\n", (char *)progname,
249 maxfds = _tt_getdtablesize();
250 for (i = 3; i < maxfds; i++) {
253 _tt_restoredtablesize();
254 signal(SIGHUP, SIG_IGN);
255 (void) myopts.command(cmd); // existence of cmd already checked
256 execvp((char *) cmd, (char * const*)myopts.cargv());
257 perror((char *) progname);
259 default: // parent -- wait on child process
265 static void send_session_trace(_Tt_trace_optobj& myopts)
267 int timeout = 180; // 3-minute timeout
270 struct pollfd fds[1];
272 Tt_callback_action tttrace_callback(Tt_message, Tt_pattern);
276 int mark = tt_mark();
278 // We must send the message to the specified session. This routine
279 // is only called when there is an explicit session in the
286 tt_session_join((char *) tmp);
287 msg = tt_prequest_create(TT_SESSION, SESSION_TRACE_OP);
289 tt_message_arg_add(msg, TT_IN, "string", (char *) 0);
290 script_stat = myopts.script(tmp);
291 if (script_stat == 1) { // inline script
292 tt_message_arg_val_set(msg, 0, (char *) tmp);
294 else if (script_stat == 2) { // script is in filename
295 tt_message_file_set(msg, (char *) tmp);
298 tt_message_callback_add(msg, tttrace_callback);
299 Tt_status mstat = tt_message_send(msg);
301 if (mstat != TT_OK) {
302 fprintf(stderr, "%s: tt_message_send(): %s\n",
303 (char *) progname, tt_status_message(mstat));
308 fds[0].events = POLLIN | POLLPRI;
310 int rcode = poll(fds, num_fd, timeout);
312 fprintf(stderr, "%s: Session_Trace: %s\n",
313 (char *) progname, strerror(ETIMEDOUT));
317 Tt_message inmsg = tt_message_receive();
319 // Make sure the stop-tracing message is sent upon exit
326 Tt_callback_action tttrace_callback(Tt_message msg, Tt_pattern)
328 Tt_status mstat = (Tt_status) tt_message_status(msg);
329 if (mstat == TT_ERR_NO_MATCH) {
331 // ttsession does not recognize this message, which
332 // means an incompatible version of ttsession is being
335 fprintf(stderr, catgets(_ttcatd, 9, 3,
336 "%s: session <%s> does not support "
337 "Session_Trace. Use kill -USR1 instead. "
338 "See ttsession(1).\n"),
339 (char *) progname, tt_message_session(msg) );
342 else if (mstat != TT_OK) {
343 fprintf(stderr, "%s: Session_Trace: %s\n",
344 (char *) progname, tt_status_message(mstat));
348 return TT_CALLBACK_PROCESSED;
351 static void send_on_exit()
353 Tt_message msg = tt_prequest_create(TT_SESSION, SESSION_TRACE_OP);
354 tt_message_arg_add(msg, TT_IN, "string",
355 "version 1; states none; functions none" );
356 tt_message_send_on_exit(msg);
361 static int open_pipe(_Tt_string& pipe_name, int* fd)
366 while ((! quit) && (*fd <= 0)) {
367 *fd = open((char *) pipe_name, O_RDONLY | O_NDELAY);
368 if ((*fd < 0) && (errno != EINTR)) {
369 _Tt_string msg = progname.cat( ": " ).cat(pipe_name);
380 // No silly cracks about this routine's name, please!
382 static int tail_pipe(int fd, _Tt_string& pipenm, pid_t child_pid, int sink)
384 int bufsize = MAXLINE;
387 struct pollfd fds[2];
390 if (pipenm.len() == 0) {
393 pid_t child_waited_for = waitpid( child_pid, 0, 0 );
394 if (child_waited_for < 0 && errno == EINTR) continue;
395 if (child_waited_for < 0 && errno == ECHILD) break;
396 if (child_waited_for < 0) {
397 fprintf(stderr, "%s: waitpid(): ",
410 fds[0].events = POLLIN;
413 fds[1].events = POLLIN;
416 int numfds = poll(fds, ttfd ? 2 : 1, poll_timeout);
418 if (errno != EINTR) {
420 progname.cat( ": poll()" );
430 else if (numfds == 0) {
435 if (fds[ 0 ].revents & POLLHUP) {
438 // Child has exited -- do a last read
439 while ((nbytes = read(fd,
442 write(sink, buf, nbytes);
447 pid_t child_waited_for = waitpid(child_pid,
449 if (child_waited_for == child_pid) {
455 // Tracing is temporarily stopped.
456 // Periodically wake up to see if it
457 // has started again.
463 else if (fds[ 0 ].revents & POLLIN) {
468 nbytes = read(fd, buf, bufsize);
470 write(sink, buf, nbytes);
474 if (fds[ 1 ].revents & POLLIN) {
475 Tt_message msg = tt_message_receive();
476 if (tt_ptr_error(msg) == TT_ERR_NOMP) {