1 /*
2  * Copyright (c) 2003, 2019, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.  Oracle designates this
8  * particular file as subject to the "Classpath" exception as provided
9  * by Oracle in the LICENSE file that accompanied this code.
10  *
11  * This code is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14  * version 2 for more details (a copy is included in the LICENSE file that
15  * accompanied this code).
16  *
17  * You should have received a copy of the GNU General Public License version
18  * 2 along with this work; if not, write to the Free Software Foundation,
19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20  *
21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22  * or visit www.oracle.com if you need additional information or have any
23  * questions.
24  */
25 
26 #include "util.h"
27 
28 #include <time.h>
29 #include <errno.h>
30 #include <sys/types.h>
31 
32 #include "proc_md.h"
33 
34 #include "log_messages.h"
35 
36 #ifdef JDWP_LOGGING
37 
38 #define MAXLEN_INTEGER          20
39 #define MAXLEN_FILENAME         256
40 #define MAXLEN_TIMESTAMP        80
41 #define MAXLEN_LOCATION         (MAXLEN_FILENAME+MAXLEN_INTEGER+16)
42 #define MAXLEN_MESSAGE          256
43 #define MAXLEN_EXEC             (MAXLEN_FILENAME*2+MAXLEN_INTEGER+16)
44 
45 #define TIMESTAMP_SIZE          (MAXLEN_TIMESTAMP+1)
46 #define MAXLEN_DT               19 // "DD.MM.YYYY HH:MM:SS"
47 #define MAXLEN_MS               5 // ".mmm "
48 #define DT_SIZE                 (MAXLEN_DT+1)
49 #define TZ_SIZE                 (TIMESTAMP_SIZE-MAXLEN_DT-MAXLEN_MS)
50 
51 static MUTEX_T my_mutex = MUTEX_INIT;
52 
53 /* Static variables (should be protected with mutex) */
54 static int logging;
55 static FILE * log_file;
56 static char logging_filename[MAXLEN_FILENAME+1+6];
57 static char location_stamp[MAXLEN_LOCATION+1];
58 static PID_T processPid;
59 static int open_count;
60 
61 /*
62  * "DD.MM.YYYY HH:MM:SS.mmm <TZ>"
63  */
64 static void
get_time_stamp(char * tbuf,size_t ltbuf)65 get_time_stamp(char *tbuf, size_t ltbuf)
66 {
67     char timestamp_date_time[DT_SIZE];
68     char timestamp_timezone[TZ_SIZE];
69     unsigned millisecs = 0;
70     time_t t = 0;
71 
72     GETMILLSECS(millisecs);
73     if ( time(&t) == (time_t)(-1) ) {
74         t = 0;
75     }
76 
77     (void)strftime(timestamp_date_time, DT_SIZE,
78                 "%d.%m.%Y %T", localtime(&t));
79     (void)strftime(timestamp_timezone, TZ_SIZE,
80                 "%Z", localtime(&t));
81     (void)snprintf(tbuf, ltbuf,
82                    "%s.%.3d %s", timestamp_date_time,
83                    (int)(millisecs), timestamp_timezone);
84 }
85 
86 /* Get basename of filename */
87 static const char *
file_basename(const char * file)88 file_basename(const char *file)
89 {
90     char *p1;
91     char *p2;
92 
93     if ( file==NULL )
94         return "unknown";
95     p1 = strrchr(file, '\\');
96     p2 = strrchr(file, '/');
97     p1 = ((p1 > p2) ? p1 : p2);
98     if (p1 != NULL) {
99         file = p1 + 1;
100     }
101     return file;
102 }
103 
104 /* Fill in the exact source location of the LOG entry. */
105 static void
fill_location_stamp(const char * flavor,const char * file,int line)106 fill_location_stamp(const char *flavor, const char *file, int line)
107 {
108     (void)snprintf(location_stamp, sizeof(location_stamp),
109                     "%s:\"%s\":%d;",
110                     flavor, file_basename(file), line);
111     location_stamp[sizeof(location_stamp)-1] = 0;
112 }
113 
114 /* Begin a log entry. */
115 void
log_message_begin(const char * flavor,const char * file,int line)116 log_message_begin(const char *flavor, const char *file, int line)
117 {
118     MUTEX_LOCK(my_mutex); /* Unlocked in log_message_end() */
119     if ( logging ) {
120         location_stamp[0] = 0;
121         fill_location_stamp(flavor, file, line);
122     }
123 }
124 
125 /* Standard Logging Format Entry */
126 static void
standard_logging_format(FILE * fp,const char * datetime,const char * level,const char * product,const char * module,const char * optional,const char * messageID,const char * message)127 standard_logging_format(FILE *fp,
128         const char *datetime,
129         const char *level,
130         const char *product,
131         const char *module,
132         const char *optional,
133         const char *messageID,
134         const char *message)
135 {
136     const char *format;
137 
138     /* "[#|Date&Time&Zone|LogLevel|ProductName|ModuleID|
139      *     OptionalKey1=Value1;OptionalKeyN=ValueN|MessageID:MessageText|#]\n"
140      */
141 
142     format="[#|%s|%s|%s|%s|%s|%s:%s|#]\n";
143 
144     print_message(fp, "", "", format,
145             datetime,
146             level,
147             product,
148             module,
149             optional,
150             messageID,
151             message);
152 }
153 
154 /* End a log entry */
155 void
log_message_end(const char * format,...)156 log_message_end(const char *format, ...)
157 {
158     if ( logging ) {
159         va_list ap;
160         THREAD_T tid;
161         char datetime[MAXLEN_TIMESTAMP+1];
162         const char *level;
163         const char *product;
164         const char *module;
165         char optional[MAXLEN_INTEGER+6+MAXLEN_INTEGER+6+MAXLEN_LOCATION+1];
166         const char *messageID;
167         char message[MAXLEN_MESSAGE+1];
168 
169         /* Grab the location, start file if needed, and clear the lock */
170         if ( log_file == NULL && open_count == 0 && logging_filename[0] != 0 ) {
171             open_count++;
172             log_file = fopen(logging_filename, "w");
173             if ( log_file!=NULL ) {
174                 (void)setvbuf(log_file, NULL, _IOLBF, BUFSIZ);
175             } else {
176                 logging = 0;
177             }
178         }
179 
180         if ( log_file != NULL ) {
181 
182             /* Get the rest of the needed information */
183             tid = GET_THREAD_ID();
184             level = "FINEST"; /* FIXUP? */
185             product = "J2SE1.5"; /* FIXUP? */
186             module = "jdwp"; /* FIXUP? */
187             messageID = ""; /* FIXUP: Unique message string ID? */
188             (void)snprintf(optional, sizeof(optional),
189                         "LOC=%s;PID=%d;THR=t@%d",
190                         location_stamp,
191                         (int)processPid,
192                         (int)(intptr_t)tid);
193 
194             /* Construct message string. */
195             va_start(ap, format);
196             (void)vsnprintf(message, sizeof(message), format, ap);
197             message[sizeof(message) - 1] = 0;
198             va_end(ap);
199 
200             get_time_stamp(datetime, sizeof(datetime));
201 
202             /* Send out standard logging format message */
203             standard_logging_format(log_file,
204                 datetime,
205                 level,
206                 product,
207                 module,
208                 optional,
209                 messageID,
210                 message);
211         }
212         location_stamp[0] = 0;
213     }
214     MUTEX_UNLOCK(my_mutex); /* Locked in log_message_begin() */
215 }
216 
217 #endif
218 
219 /* Set up the logging with the name of a logging file. */
220 void
setup_logging(const char * filename,unsigned flags)221 setup_logging(const char *filename, unsigned flags)
222 {
223 #ifdef JDWP_LOGGING
224     FILE *fp = NULL;
225 
226     /* Turn off logging */
227     logging = 0;
228     gdata->log_flags = 0;
229 
230     /* Just return if not doing logging */
231     if ( filename==NULL || flags==0 )
232         return;
233 
234     /* Create potential filename for logging */
235     processPid = GETPID();
236     (void)snprintf(logging_filename, sizeof(logging_filename),
237                     "%s.%d", filename, (int)processPid);
238 
239     /* Turn on logging (do this last) */
240     logging = 1;
241     gdata->log_flags = flags;
242 
243 #endif
244 }
245 
246 /* Finish up logging, flush output to the logfile. */
247 void
finish_logging()248 finish_logging()
249 {
250 #ifdef JDWP_LOGGING
251     MUTEX_LOCK(my_mutex);
252     if ( logging ) {
253         logging = 0;
254         if ( log_file != NULL ) {
255             (void)fflush(log_file);
256             (void)fclose(log_file);
257             log_file = NULL;
258         }
259     }
260     MUTEX_UNLOCK(my_mutex);
261 #endif
262 }
263